2022-05-16 12:54:25 -04:00
// This binary can be build from siderolabs/talos projects. Located at:
// https://github.com/siderolabs/talos/tree/master/hack/docgen
2022-08-05 09:30:23 -04:00
//
//go:generate docgen ./config.go ./config_doc.go Configuration
2022-03-22 11:03:15 -04:00
package config
import (
2022-05-13 05:56:43 -04:00
"errors"
2022-03-22 11:03:15 -04:00
"fmt"
2022-05-13 05:56:43 -04:00
"io/fs"
2022-08-16 09:53:54 -04:00
"regexp"
2022-03-22 11:03:15 -04:00
2022-06-07 05:08:44 -04:00
"github.com/edgelesssys/constellation/internal/cloud/cloudprovider"
2022-04-06 04:36:58 -04:00
"github.com/edgelesssys/constellation/internal/constants"
2022-05-16 11:32:00 -04:00
"github.com/edgelesssys/constellation/internal/file"
2022-07-18 06:28:02 -04:00
"github.com/edgelesssys/constellation/internal/versions"
2022-05-23 09:01:39 -04:00
"github.com/go-playground/locales/en"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
en_translations "github.com/go-playground/validator/v10/translations/en"
2022-03-22 11:03:15 -04:00
)
2022-05-18 12:10:57 -04:00
const (
Version1 = "v1"
)
2022-05-16 12:54:25 -04:00
// Config defines configuration used by CLI.
type Config struct {
2022-05-18 12:10:57 -04:00
// description: |
// Schema version of this configuration file.
2022-05-23 09:01:39 -04:00
Version string ` yaml:"version" validate:"eq=v1" `
2022-05-16 12:54:25 -04:00
// description: |
2022-05-24 05:57:48 -04:00
// Minimum number of worker nodes in autoscaling group.
AutoscalingNodeGroupMin int ` yaml:"autoscalingNodeGroupMin" validate:"min=0" `
2022-05-16 12:54:25 -04:00
// description: |
2022-05-24 05:57:48 -04:00
// Maximum number of worker nodes in autoscaling group.
AutoscalingNodeGroupMax int ` yaml:"autoscalingNodeGroupMax" validate:"gtefield=AutoscalingNodeGroupMin" `
2022-05-16 12:54:25 -04:00
// description: |
2022-05-24 05:57:48 -04:00
// Size (in GB) of a node's disk to store the non-volatile state.
StateDiskSizeGB int ` yaml:"stateDiskSizeGB" validate:"min=0" `
2022-05-16 12:54:25 -04:00
// description: |
// Ingress firewall rules for node network.
2022-05-23 09:01:39 -04:00
IngressFirewall Firewall ` yaml:"ingressFirewall,omitempty" validate:"dive" `
2022-05-16 12:54:25 -04:00
// description: |
// Egress firewall rules for node network.
2022-05-17 04:52:37 -04:00
// examples:
// - value: 'Firewall{
// {
// Name: "rule#1",
// Description: "the first rule",
// Protocol: "tcp",
// IPRange: "0.0.0.0/0",
// FromPort: 443,
// ToPort: 443,
// },
// }'
2022-05-23 09:01:39 -04:00
EgressFirewall Firewall ` yaml:"egressFirewall,omitempty" validate:"dive" `
2022-05-16 12:54:25 -04:00
// description: |
2022-05-24 05:57:48 -04:00
// Supported cloud providers and their specific configurations.
2022-05-23 09:01:39 -04:00
Provider ProviderConfig ` yaml:"provider" validate:"dive" `
2022-05-16 12:54:25 -04:00
// description: |
// Create SSH users on Constellation nodes.
2022-05-17 04:52:37 -04:00
// examples:
// - value: '[]UserKey{ { Username: "Alice", PublicKey: "ssh-rsa AAAAB3NzaC...5QXHKW1rufgtJeSeJ8= alice@domain.com" } }'
2022-05-23 09:01:39 -04:00
SSHUsers [ ] UserKey ` yaml:"sshUsers,omitempty" validate:"dive" `
2022-07-18 06:28:02 -04:00
// description: |
// Kubernetes version installed in the cluster.
KubernetesVersion string ` yaml:"kubernetesVersion" validate:"supported_k8s_version" `
2022-05-17 04:52:37 -04:00
}
// UserKey describes a user that should be created with corresponding public SSH key.
type UserKey struct {
// description: |
// Username of new SSH user.
2022-05-23 09:01:39 -04:00
Username string ` yaml:"username" validate:"required" `
2022-05-17 04:52:37 -04:00
// description: |
// Public key of new SSH user.
2022-05-23 09:01:39 -04:00
PublicKey string ` yaml:"publicKey" validate:"required" `
2022-05-16 12:54:25 -04:00
}
2022-03-22 11:03:15 -04:00
2022-05-16 12:54:25 -04:00
type FirewallRule struct {
// description: |
// Name of rule.
2022-05-23 09:01:39 -04:00
Name string ` yaml:"name" validate:"required" `
2022-05-16 12:54:25 -04:00
// description: |
// Description for rule.
Description string ` yaml:"description" `
// description: |
// Protocol, such as 'udp' or 'tcp'.
2022-05-23 09:01:39 -04:00
Protocol string ` yaml:"protocol" validate:"required" `
2022-05-16 12:54:25 -04:00
// description: |
// CIDR range for which this rule is applied.
2022-05-23 09:01:39 -04:00
IPRange string ` yaml:"iprange" validate:"required" `
2022-05-16 12:54:25 -04:00
// description: |
2022-05-24 05:57:48 -04:00
// Start port of a range.
2022-05-23 09:01:39 -04:00
FromPort int ` yaml:"fromport" validate:"min=0,max=65535" `
2022-05-16 12:54:25 -04:00
// description: |
2022-05-24 05:57:48 -04:00
// End port of a range, or 0 if a single port is given by fromport.
2022-05-23 09:01:39 -04:00
ToPort int ` yaml:"toport" validate:"omitempty,gtefield=FromPort,max=65535" `
2022-05-16 12:54:25 -04:00
}
2022-05-02 04:54:54 -04:00
2022-05-16 12:54:25 -04:00
type Firewall [ ] FirewallRule
2022-03-22 11:03:15 -04:00
2022-05-16 12:54:25 -04:00
// ProviderConfig are cloud-provider specific configuration values used by the CLI.
// Fields should remain pointer-types so custom specific configs can nil them
// if not required.
type ProviderConfig struct {
// description: |
// Configuration for Azure as provider.
2022-05-24 05:57:48 -04:00
Azure * AzureConfig ` yaml:"azure,omitempty" validate:"omitempty,dive" `
2022-05-16 12:54:25 -04:00
// description: |
// Configuration for Google Cloud as provider.
2022-05-24 05:57:48 -04:00
GCP * GCPConfig ` yaml:"gcp,omitempty" validate:"omitempty,dive" `
2022-05-16 12:54:25 -04:00
// description: |
// Configuration for QEMU as provider.
2022-05-24 05:57:48 -04:00
QEMU * QEMUConfig ` yaml:"qemu,omitempty" validate:"omitempty,dive" `
2022-05-16 12:54:25 -04:00
}
// AzureConfig are Azure specific configuration values used by the CLI.
type AzureConfig struct {
// description: |
// Subscription ID of the used Azure account. See: https://docs.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id#find-your-azure-subscription
2022-05-23 09:01:39 -04:00
SubscriptionID string ` yaml:"subscription" validate:"uuid" `
2022-05-16 12:54:25 -04:00
// description: |
// Tenant ID of the used Azure account. See: https://docs.microsoft.com/en-us/azure/azure-portal/get-subscription-tenant-id#find-your-azure-ad-tenant
2022-05-23 09:01:39 -04:00
TenantID string ` yaml:"tenant" validate:"uuid" `
2022-05-16 12:54:25 -04:00
// description: |
// Azure datacenter region to be used. See: https://docs.microsoft.com/en-us/azure/availability-zones/az-overview#azure-regions-with-availability-zones
2022-05-23 09:01:39 -04:00
Location string ` yaml:"location" validate:"required" `
2022-05-16 12:54:25 -04:00
// description: |
// Machine image used to create Constellation nodes.
2022-05-23 09:01:39 -04:00
Image string ` yaml:"image" validate:"required" `
2022-05-16 12:54:25 -04:00
// description: |
2022-08-02 06:24:55 -04:00
// Type of a node's state disk. The type influences boot time and I/O performance. See: https://docs.microsoft.com/en-us/azure/virtual-machines/disks-types#disk-type-comparison
StateDiskType string ` yaml:"stateDiskType" validate:"oneof=Premium_LRS Premium_ZRS Standard_LRS StandardSSD_LRS StandardSSD_ZRS" `
// description: |
2022-05-16 12:54:25 -04:00
// Expected confidential VM measurements.
Measurements Measurements ` yaml:"measurements" `
// description: |
2022-08-12 09:59:45 -04:00
// List of values that should be enforced to be equal to the ones from the measurement list. Any non-equal values not in this list will only result in a warning.
EnforcedMeasurements [ ] uint32 ` yaml:"enforcedMeasurements" `
// description: |
2022-08-23 07:46:06 -04:00
// Authorize spawned VMs to access Azure API. See: https://docs.edgeless.systems/constellation/latest/#/getting-started/install?id=azure
2022-05-24 05:57:48 -04:00
UserAssignedIdentity string ` yaml:"userAssignedIdentity" validate:"required" `
2022-08-25 09:24:31 -04:00
// description: |
// Use VMs with security type Confidential VM. If set to false, Trusted Launch VMs will be used instead. See: https://docs.microsoft.com/en-us/azure/confidential-computing/confidential-vm-overview
ConfidentialVM * bool ` yaml:"confidentialVM" validate:"required" `
2022-05-16 12:54:25 -04:00
}
// GCPConfig are GCP specific configuration values used by the CLI.
type GCPConfig struct {
// description: |
// GCP project. See: https://support.google.com/googleapi/answer/7014113?hl=en
2022-05-23 09:01:39 -04:00
Project string ` yaml:"project" validate:"required" `
2022-05-16 12:54:25 -04:00
// description: |
// GCP datacenter region. See: https://cloud.google.com/compute/docs/regions-zones#available
2022-05-23 09:01:39 -04:00
Region string ` yaml:"region" validate:"required" `
2022-05-16 12:54:25 -04:00
// description: |
// GCP datacenter zone. See: https://cloud.google.com/compute/docs/regions-zones#available
2022-05-23 09:01:39 -04:00
Zone string ` yaml:"zone" validate:"required" `
2022-05-16 12:54:25 -04:00
// description: |
// Machine image used to create Constellation nodes.
2022-05-23 09:01:39 -04:00
Image string ` yaml:"image" validate:"required" `
2022-05-16 12:54:25 -04:00
// description: |
2022-08-02 06:24:55 -04:00
// Type of a node's state disk. The type influences boot time and I/O performance. See: https://cloud.google.com/compute/docs/disks#disk-types
StateDiskType string ` yaml:"stateDiskType" validate:"oneof=pd-standard pd-balanced pd-ssd" `
// description: |
2022-08-23 11:49:55 -04:00
// Path of service account key file. For needed service account roles, see https://constellation-docs.edgeless.systems/constellation/latest/#/getting-started/install?id=authorization
ServiceAccountKeyPath string ` yaml:"serviceAccountKeyPath" `
2022-05-16 12:54:25 -04:00
// description: |
2022-05-24 05:57:48 -04:00
// Expected confidential VM measurements.
2022-05-16 12:54:25 -04:00
Measurements Measurements ` yaml:"measurements" `
2022-08-12 09:59:45 -04:00
// description: |
// List of values that should be enforced to be equal to the ones from the measurement list. Any non-equal values not in this list will only result in a warning.
EnforcedMeasurements [ ] uint32 ` yaml:"enforcedMeasurements" `
2022-05-16 12:54:25 -04:00
}
type QEMUConfig struct {
// description: |
// Measurement used to enable measured boot.
Measurements Measurements ` yaml:"measurements" `
2022-08-12 09:59:45 -04:00
// description: |
// List of values that should be enforced to be equal to the ones from the measurement list. Any non-equal values not in this list will only result in a warning.
EnforcedMeasurements [ ] uint32 ` yaml:"enforcedMeasurements" `
2022-03-22 11:03:15 -04:00
}
// Default returns a struct with the default config.
func Default ( ) * Config {
return & Config {
2022-05-24 05:57:48 -04:00
Version : Version1 ,
AutoscalingNodeGroupMin : 1 ,
AutoscalingNodeGroupMax : 10 ,
StateDiskSizeGB : 30 ,
2022-05-16 12:54:25 -04:00
IngressFirewall : Firewall {
{
2022-06-29 09:26:29 -04:00
Name : "bootstrapper" ,
Description : "bootstrapper default port" ,
2022-05-16 12:54:25 -04:00
Protocol : "tcp" ,
IPRange : "0.0.0.0/0" ,
2022-06-29 09:26:29 -04:00
FromPort : constants . BootstrapperPort ,
2022-05-16 12:54:25 -04:00
} ,
{
Name : "ssh" ,
Description : "SSH" ,
Protocol : "tcp" ,
IPRange : "0.0.0.0/0" ,
FromPort : constants . SSHPort ,
2022-03-22 11:03:15 -04:00
} ,
2022-05-16 12:54:25 -04:00
{
Name : "nodeport" ,
Description : "NodePort" ,
Protocol : "tcp" ,
IPRange : "0.0.0.0/0" ,
FromPort : constants . NodePortFrom ,
ToPort : constants . NodePortTo ,
} ,
2022-05-24 04:04:42 -04:00
{
Name : "kubernetes" ,
Description : "Kubernetes" ,
Protocol : "tcp" ,
IPRange : "0.0.0.0/0" ,
FromPort : constants . KubernetesPort ,
} ,
2022-05-16 12:54:25 -04:00
} ,
Provider : ProviderConfig {
2022-05-24 05:57:48 -04:00
// TODO remove our subscriptions from the default config
2022-03-22 11:03:15 -04:00
Azure : & AzureConfig {
2022-08-08 05:04:17 -04:00
SubscriptionID : "" ,
TenantID : "" ,
Location : "" ,
UserAssignedIdentity : "" ,
2022-08-25 08:06:29 -04:00
Image : DefaultImageAzure ,
2022-08-02 06:24:55 -04:00
StateDiskType : "StandardSSD_LRS" , // TODO: Replace with Premium_LRS when we replace the default VM size (Standard_D2a_v4) since the size does not support Premium_LRS
2022-08-12 09:59:45 -04:00
Measurements : copyPCRMap ( azurePCRs ) ,
EnforcedMeasurements : [ ] uint32 { 8 , 9 , 11 , 12 } ,
2022-08-25 09:24:31 -04:00
ConfidentialVM : func ( ) * bool { b := true ; return & b } ( ) ,
2022-03-22 11:03:15 -04:00
} ,
GCP : & GCPConfig {
2022-08-23 11:49:55 -04:00
Project : "" ,
Region : "" ,
Zone : "" ,
2022-08-25 08:06:29 -04:00
Image : DefaultImageGCP ,
2022-08-23 11:49:55 -04:00
StateDiskType : "pd-ssd" ,
ServiceAccountKeyPath : "serviceAccountKey.json" ,
Measurements : copyPCRMap ( gcpPCRs ) ,
EnforcedMeasurements : [ ] uint32 { 0 , 8 , 9 , 11 , 12 } ,
2022-03-22 11:03:15 -04:00
} ,
2022-05-02 04:54:54 -04:00
QEMU : & QEMUConfig {
2022-08-12 09:59:45 -04:00
Measurements : copyPCRMap ( qemuPCRs ) ,
EnforcedMeasurements : [ ] uint32 { 11 , 12 } ,
2022-05-02 04:54:54 -04:00
} ,
2022-03-22 11:03:15 -04:00
} ,
2022-07-22 09:05:04 -04:00
KubernetesVersion : string ( versions . Latest ) ,
2022-03-22 11:03:15 -04:00
}
}
2022-07-18 06:28:02 -04:00
func validateK8sVersion ( fl validator . FieldLevel ) bool {
return versions . IsSupportedK8sVersion ( fl . Field ( ) . String ( ) )
}
2022-05-23 09:01:39 -04:00
// Validate checks the config values and returns validation error messages.
// The function only returns an error if the validation itself fails.
func ( c * Config ) Validate ( ) ( [ ] string , error ) {
trans := ut . New ( en . New ( ) ) . GetFallback ( )
validate := validator . New ( )
if err := en_translations . RegisterDefaultTranslations ( validate , trans ) ; err != nil {
return nil , err
}
2022-07-18 06:28:02 -04:00
// register custom validator with label supported_k8s_version to validate version based on available versionConfigs.
if err := validate . RegisterValidation ( "supported_k8s_version" , validateK8sVersion ) ; err != nil {
return nil , err
}
2022-05-23 09:01:39 -04:00
err := validate . Struct ( c )
if err == nil {
return nil , nil
}
var errs validator . ValidationErrors
if ! errors . As ( err , & errs ) {
return nil , err
}
var msgs [ ] string
for _ , e := range errs {
msgs = append ( msgs , e . Translate ( trans ) )
}
return msgs , nil
}
// HasProvider checks whether the config contains the provider.
func ( c * Config ) HasProvider ( provider cloudprovider . Provider ) bool {
switch provider {
case cloudprovider . Azure :
return c . Provider . Azure != nil
case cloudprovider . GCP :
return c . Provider . GCP != nil
case cloudprovider . QEMU :
return c . Provider . QEMU != nil
}
return false
}
2022-08-01 03:37:05 -04:00
// Image returns OS image for the configured cloud provider.
// If multiple cloud providers are configured (which is not supported)
// only a single image is returned.
func ( c * Config ) Image ( ) string {
if c . HasProvider ( cloudprovider . Azure ) {
return c . Provider . Azure . Image
}
if c . HasProvider ( cloudprovider . GCP ) {
return c . Provider . GCP . Image
}
return ""
}
func ( c * Config ) UpdateMeasurements ( newMeasurements Measurements ) {
if c . Provider . Azure != nil {
c . Provider . Azure . Measurements . CopyFrom ( newMeasurements )
}
if c . Provider . GCP != nil {
c . Provider . GCP . Measurements . CopyFrom ( newMeasurements )
}
if c . Provider . QEMU != nil {
c . Provider . QEMU . Measurements . CopyFrom ( newMeasurements )
}
}
2022-05-18 05:39:14 -04:00
// RemoveProviderExcept removes all provider specific configurations, i.e.,
// sets them to nil, except the one specified.
// If an unknown provider is passed, the same configuration is returned.
func ( c * Config ) RemoveProviderExcept ( provider cloudprovider . Provider ) {
currentProviderConfigs := c . Provider
c . Provider = ProviderConfig { }
switch provider {
case cloudprovider . Azure :
c . Provider . Azure = currentProviderConfigs . Azure
case cloudprovider . GCP :
c . Provider . GCP = currentProviderConfigs . GCP
case cloudprovider . QEMU :
c . Provider . QEMU = currentProviderConfigs . QEMU
default :
c . Provider = currentProviderConfigs
}
}
2022-08-16 09:53:54 -04:00
// IsImageDebug checks whether image name looks like a release image, if not it is
// probably a debug image. In the end we do not if bootstrapper or debugd
// was put inside an image just by looking at its name.
func ( c * Config ) IsImageDebug ( ) bool {
switch {
case c . Provider . GCP != nil :
gcpRegex := regexp . MustCompile ( ` ^projects\/constellation-images\/global\/images\/constellation-v[\d]+-[\d]+-[\d]+$ ` )
return ! gcpRegex . MatchString ( c . Provider . GCP . Image )
case c . Provider . Azure != nil :
azureRegex := regexp . MustCompile ( ` ^\/subscriptions\/0d202bbb-4fa7-4af8-8125-58c269a05435\/resourceGroups\/constellation-images\/providers\/Microsoft.Compute\/galleries\/Constellation\/images\/constellation\/versions\/[\d]+.[\d]+.[\d]+$ ` )
return ! azureRegex . MatchString ( c . Provider . Azure . Image )
default :
return false
}
}
2022-08-25 09:24:31 -04:00
// IsAzureNonCVM checks whether the chosen provider is azure and confidential VMs are disabled.
func ( c * Config ) IsAzureNonCVM ( ) bool {
return c . Provider . Azure != nil && c . Provider . Azure . ConfidentialVM != nil && ! * c . Provider . Azure . ConfidentialVM
}
2022-05-16 12:54:25 -04:00
// FromFile returns config file with `name` read from `fileHandler` by parsing
// it as YAML.
2022-03-22 11:03:15 -04:00
func FromFile ( fileHandler file . Handler , name string ) ( * Config , error ) {
2022-08-08 05:04:17 -04:00
var conf Config
if err := fileHandler . ReadYAMLStrict ( name , & conf ) ; err != nil {
2022-05-13 05:56:43 -04:00
if errors . Is ( err , fs . ErrNotExist ) {
2022-05-16 08:20:21 -04:00
return nil , fmt . Errorf ( "unable to find %s - use `constellation config generate` to generate it first" , name )
2022-05-13 05:56:43 -04:00
}
2022-03-22 11:03:15 -04:00
return nil , fmt . Errorf ( "could not load config from file %s: %w" , name , err )
}
2022-08-08 05:04:17 -04:00
return & conf , nil
2022-03-22 11:03:15 -04:00
}
2022-08-12 09:59:45 -04:00
func copyPCRMap ( m map [ uint32 ] [ ] byte ) map [ uint32 ] [ ] byte {
res := make ( Measurements )
res . CopyFrom ( m )
return res
}