diff --git a/cli/internal/cmd/BUILD.bazel b/cli/internal/cmd/BUILD.bazel index f9e4827e0..4980efa12 100644 --- a/cli/internal/cmd/BUILD.bazel +++ b/cli/internal/cmd/BUILD.bazel @@ -64,7 +64,6 @@ go_library( "//internal/compatibility", "//internal/config", "//internal/config/instancetypes", - "//internal/config/migration", "//internal/constants", "//internal/crypto", "//internal/file", diff --git a/cli/internal/cmd/configmigrate.go b/cli/internal/cmd/configmigrate.go index 72e89909e..be627239e 100644 --- a/cli/internal/cmd/configmigrate.go +++ b/cli/internal/cmd/configmigrate.go @@ -10,7 +10,6 @@ import ( "fmt" "github.com/edgelesssys/constellation/v2/internal/config" - "github.com/edgelesssys/constellation/v2/internal/config/migration" "github.com/edgelesssys/constellation/v2/internal/file" "github.com/spf13/afero" "github.com/spf13/cobra" @@ -45,17 +44,12 @@ func configMigrate(cmd *cobra.Command, configPath string, handler file.Handler) return err } + // TODO(malt3): add migration from v3 to v4 switch cfgVersion.Version { - case config.Version3: - cmd.Printf("Config already at version %s, nothing to do\n", config.Version3) - return nil - case migration.Version2: - if err := migration.V2ToV3(configPath, handler); err != nil { - return fmt.Errorf("migrating config: %w", err) - } - cmd.Printf("Successfully migrated config to %s\n", config.Version3) + case config.Version4: + cmd.Printf("Config already at version %s, nothing to do\n", config.Version4) return nil default: - return fmt.Errorf("cannot convert config version %s to %s", cfgVersion.Version, config.Version3) + return fmt.Errorf("cannot convert config version %s to %s", cfgVersion.Version, config.Version4) } } diff --git a/internal/config/migration/BUILD.bazel b/internal/config/migration/BUILD.bazel index cb4e27da7..66e50ddd0 100644 --- a/internal/config/migration/BUILD.bazel +++ b/internal/config/migration/BUILD.bazel @@ -5,12 +5,5 @@ go_library( srcs = ["migration.go"], importpath = "github.com/edgelesssys/constellation/v2/internal/config/migration", visibility = ["//:__subpackages__"], - deps = [ - "//internal/attestation/idkeydigest", - "//internal/attestation/measurements", - "//internal/attestation/variant", - "//internal/config", - "//internal/file", - "//internal/semver", - ], + deps = ["//internal/file"], ) diff --git a/internal/config/migration/migration.go b/internal/config/migration/migration.go index 4ebee195c..85844741e 100644 --- a/internal/config/migration/migration.go +++ b/internal/config/migration/migration.go @@ -9,305 +9,12 @@ package migration import ( "errors" - "fmt" - "os" - "strings" - "github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest" - "github.com/edgelesssys/constellation/v2/internal/attestation/measurements" - "github.com/edgelesssys/constellation/v2/internal/attestation/variant" - "github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/file" - "github.com/edgelesssys/constellation/v2/internal/semver" ) -const ( - // Version2 is the second version number for Constellation config file. - Version2 = "v2" -) - -// Config defines configuration used by CLI. -type Config struct { - Version string `yaml:"version" validate:"eq=v2"` - Image string `yaml:"image" validate:"required,image_compatibility"` - Name string `yaml:"name" validate:"valid_name,required"` - StateDiskSizeGB int `yaml:"stateDiskSizeGB" validate:"min=0"` - KubernetesVersion string `yaml:"kubernetesVersion" validate:"required,supported_k8s_version"` - MicroserviceVersion string `yaml:"microserviceVersion" validate:"required"` - DebugCluster *bool `yaml:"debugCluster" validate:"required"` - AttestationVariant string `yaml:"attestationVariant,omitempty" validate:"valid_attestation_variant"` - Provider ProviderConfig `yaml:"provider" validate:"dive"` -} - -// 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 { - AWS *AWSConfig `yaml:"aws,omitempty" validate:"omitempty,dive"` - Azure *AzureConfig `yaml:"azure,omitempty" validate:"omitempty,dive"` - GCP *GCPConfig `yaml:"gcp,omitempty" validate:"omitempty,dive"` - OpenStack *OpenStackConfig `yaml:"openstack,omitempty" validate:"omitempty,dive"` - QEMU *QEMUConfig `yaml:"qemu,omitempty" validate:"omitempty,dive"` -} - -// AWSConfig are AWS specific configuration values used by the CLI. -type AWSConfig struct { - Region string `yaml:"region" validate:"required"` - Zone string `yaml:"zone" validate:"required"` - InstanceType string `yaml:"instanceType" validate:"lowercase,aws_instance_type"` - StateDiskType string `yaml:"stateDiskType" validate:"oneof=standard gp2 gp3 st1 sc1 io1"` - IAMProfileControlPlane string `yaml:"iamProfileControlPlane" validate:"required"` - IAMProfileWorkerNodes string `yaml:"iamProfileWorkerNodes" validate:"required"` - Measurements measurements.M `yaml:"measurements" validate:"required,no_placeholders"` -} - -// AzureConfig are Azure specific configuration values used by the CLI. -type AzureConfig struct { - SubscriptionID string `yaml:"subscription" validate:"uuid"` - TenantID string `yaml:"tenant" validate:"uuid"` - Location string `yaml:"location" validate:"required"` - ResourceGroup string `yaml:"resourceGroup" validate:"required"` - UserAssignedIdentity string `yaml:"userAssignedIdentity" validate:"required"` - InstanceType string `yaml:"instanceType" validate:"azure_instance_type"` - StateDiskType string `yaml:"stateDiskType" validate:"oneof=Premium_LRS Premium_ZRS Standard_LRS StandardSSD_LRS StandardSSD_ZRS"` - DeployCSIDriver *bool `yaml:"deployCSIDriver" validate:"required"` - ConfidentialVM *bool `yaml:"confidentialVM,omitempty" validate:"omitempty,deprecated"` - SecureBoot *bool `yaml:"secureBoot" validate:"required"` - IDKeyDigest idkeydigest.List `yaml:"idKeyDigest" validate:"required_if=EnforceIdKeyDigest true,omitempty"` - EnforceIDKeyDigest IDKeyDigestEnforcement `yaml:"enforceIdKeyDigest" validate:"required"` - Measurements measurements.M `yaml:"measurements" validate:"required,no_placeholders"` -} - -// GCPConfig are GCP specific configuration values used by the CLI. -type GCPConfig struct { - Project string `yaml:"project" validate:"required"` - Region string `yaml:"region" validate:"required"` - Zone string `yaml:"zone" validate:"required"` - ServiceAccountKeyPath string `yaml:"serviceAccountKeyPath" validate:"required"` - InstanceType string `yaml:"instanceType" validate:"gcp_instance_type"` - StateDiskType string `yaml:"stateDiskType" validate:"oneof=pd-standard pd-balanced pd-ssd"` - DeployCSIDriver *bool `yaml:"deployCSIDriver" validate:"required"` - Measurements measurements.M `yaml:"measurements" validate:"required,no_placeholders"` -} - -// OpenStackConfig holds config information for OpenStack based Constellation deployments. -type OpenStackConfig struct { - Cloud string `yaml:"cloud"` - AvailabilityZone string `yaml:"availabilityZone" validate:"required"` - FlavorID string `yaml:"flavorID" validate:"required"` - FloatingIPPoolID string `yaml:"floatingIPPoolID" validate:"required"` - AuthURL string `yaml:"authURL" validate:"required"` - ProjectID string `yaml:"projectID" validate:"required"` - ProjectName string `yaml:"projectName" validate:"required"` - UserDomainName string `yaml:"userDomainName" validate:"required"` - ProjectDomainName string `yaml:"projectDomainName" validate:"required"` - RegionName string `yaml:"regionName" validate:"required"` - Username string `yaml:"username" validate:"required"` - Password string `yaml:"password"` - DirectDownload *bool `yaml:"directDownload" validate:"required"` - Measurements measurements.M `yaml:"measurements" validate:"required,no_placeholders"` -} - -// QEMUConfig holds config information for QEMU based Constellation deployments. -type QEMUConfig struct { - ImageFormat string `yaml:"imageFormat" validate:"oneof=qcow2 raw"` - VCPUs int `yaml:"vcpus" validate:"required"` - Memory int `yaml:"memory" validate:"required"` - MetadataAPIImage string `yaml:"metadataAPIServer" validate:"required"` - LibvirtURI string `yaml:"libvirtSocket"` - LibvirtContainerImage string `yaml:"libvirtContainerImage"` - NVRAM string `yaml:"nvram" validate:"required"` - Firmware string `yaml:"firmware"` - Measurements measurements.M `yaml:"measurements" validate:"required,no_placeholders"` -} - -// IDKeyDigestEnforcement is the legacy format of idkeydigest.Enforcement. -type IDKeyDigestEnforcement uint32 - -const ( - // Unknown is reserved for invalid configurations. - Unknown IDKeyDigestEnforcement = iota - // StrictChecking will return an error if the ID key digest is not found in the expected list. - StrictChecking - // MAAFallback attempts to verify the attestation using Microsoft Azure Attestation (MAA), - // if the ID key digest is not found in the expected list. - MAAFallback - // WarnOnly logs a warning if the ID key digest is not found in the expected list. - // No error is returned. - WarnOnly -) - -// UnmarshalYAML implements the yaml.Unmarshaler interface. -func (e *IDKeyDigestEnforcement) UnmarshalYAML(unmarshal func(any) error) error { - return e.unmarshal(unmarshal) -} - -func (e *IDKeyDigestEnforcement) unmarshal(unmarshalFunc func(any) error) error { - // Check for legacy format: IDKeyDigestEnforcement might be a boolean. - // If set to true, the value will be set to StrictChecking. - // If set to false, the value will be set to WarnOnly. - var legacyEnforce bool - legacyErr := unmarshalFunc(&legacyEnforce) - if legacyErr == nil { - if legacyEnforce { - *e = StrictChecking - } else { - *e = WarnOnly - } - return nil - } - - var enforce string - if err := unmarshalFunc(&enforce); err != nil { - return errors.Join( - err, - fmt.Errorf("trying legacy format: %w", legacyErr), - ) - } - - *e = e.enforcePolicyFromString(enforce) - if *e == Unknown { - return fmt.Errorf("unknown EnforceIDKeyDigest value: %q", enforce) - } - - return nil -} - -// enforcePolicyFromString returns IDKeyDigestEnforcement from string. -func (e *IDKeyDigestEnforcement) enforcePolicyFromString(s string) IDKeyDigestEnforcement { - s = strings.ToLower(s) - switch s { - case "strictchecking": - return StrictChecking - case "maafallback": - return MAAFallback - case "warnonly": - return WarnOnly - default: - return Unknown - } -} - // V2ToV3 converts an existing v2 config to a v3 config. -func V2ToV3(path string, fileHandler file.Handler) error { - // Read old format - var cfgV2 Config - if err := fileHandler.ReadYAML(path, &cfgV2); err != nil { - return fmt.Errorf("reading config file %s using v2 format: %w", path, err) - } - - microserviceVersion, err := semver.New(cfgV2.MicroserviceVersion) - if err != nil { - return fmt.Errorf("parsing microservice version: %w", err) - } - - // Migrate to new format - var cfgV3 config.Config - cfgV3.Version = config.Version3 - cfgV3.Image = cfgV2.Image - cfgV3.Name = cfgV2.Name - cfgV3.StateDiskSizeGB = cfgV2.StateDiskSizeGB - cfgV3.KubernetesVersion = cfgV2.KubernetesVersion - cfgV3.MicroserviceVersion = microserviceVersion - cfgV3.DebugCluster = cfgV2.DebugCluster - - switch { - case cfgV2.Provider.AWS != nil: - cfgV3.Provider.AWS = &config.AWSConfig{ - Region: cfgV2.Provider.AWS.Region, - Zone: cfgV2.Provider.AWS.Zone, - InstanceType: cfgV2.Provider.AWS.InstanceType, - StateDiskType: cfgV2.Provider.AWS.StateDiskType, - IAMProfileControlPlane: cfgV2.Provider.AWS.IAMProfileControlPlane, - IAMProfileWorkerNodes: cfgV2.Provider.AWS.IAMProfileWorkerNodes, - } - cfgV3.Attestation.AWSNitroTPM = &config.AWSNitroTPM{ - Measurements: cfgV2.Provider.AWS.Measurements, - } - case cfgV2.Provider.Azure != nil: - cfgV3.Provider.Azure = &config.AzureConfig{ - SubscriptionID: cfgV2.Provider.Azure.SubscriptionID, - TenantID: cfgV2.Provider.Azure.TenantID, - Location: cfgV2.Provider.Azure.Location, - ResourceGroup: cfgV2.Provider.Azure.ResourceGroup, - UserAssignedIdentity: cfgV2.Provider.Azure.UserAssignedIdentity, - InstanceType: cfgV2.Provider.Azure.InstanceType, - StateDiskType: cfgV2.Provider.Azure.StateDiskType, - DeployCSIDriver: cfgV2.Provider.Azure.DeployCSIDriver, - SecureBoot: cfgV2.Provider.Azure.SecureBoot, - } - - // If an attestation war explicitly set to AzureTrustedLaunch, - // generate a config for AzureTrustedLaunch. Otherwise, generate one for AzureSEVSNP. - if attestVariant, err := variant.FromString(cfgV2.AttestationVariant); err == nil && attestVariant.Equal(variant.AzureTrustedLaunch{}) { - cfgV3.Attestation.AzureTrustedLaunch = &config.AzureTrustedLaunch{ - Measurements: cfgV2.Provider.Azure.Measurements, - } - } else { - cfgV3.Attestation.AzureSEVSNP = config.DefaultForAzureSEVSNP() - cfgV3.Attestation.AzureSEVSNP.Measurements = cfgV2.Provider.Azure.Measurements - cfgV3.Attestation.AzureSEVSNP.FirmwareSignerConfig = config.SNPFirmwareSignerConfig{ - AcceptedKeyDigests: cfgV2.Provider.Azure.IDKeyDigest, - EnforcementPolicy: idkeydigest.Enforcement(cfgV2.Provider.Azure.EnforceIDKeyDigest), - } - } - case cfgV2.Provider.GCP != nil: - cfgV3.Provider.GCP = &config.GCPConfig{ - Project: cfgV2.Provider.GCP.Project, - Region: cfgV2.Provider.GCP.Region, - Zone: cfgV2.Provider.GCP.Zone, - ServiceAccountKeyPath: cfgV2.Provider.GCP.ServiceAccountKeyPath, - InstanceType: cfgV2.Provider.GCP.InstanceType, - StateDiskType: cfgV2.Provider.GCP.StateDiskType, - DeployCSIDriver: cfgV2.Provider.GCP.DeployCSIDriver, - } - cfgV3.Attestation.GCPSEVES = &config.GCPSEVES{ - Measurements: cfgV2.Provider.GCP.Measurements, - } - case cfgV2.Provider.OpenStack != nil: - cfgV3.Provider.OpenStack = &config.OpenStackConfig{ - Cloud: cfgV2.Provider.OpenStack.Cloud, - AvailabilityZone: cfgV2.Provider.OpenStack.AvailabilityZone, - FlavorID: cfgV2.Provider.OpenStack.FlavorID, - FloatingIPPoolID: cfgV2.Provider.OpenStack.FloatingIPPoolID, - AuthURL: cfgV2.Provider.OpenStack.AuthURL, - ProjectID: cfgV2.Provider.OpenStack.ProjectID, - ProjectName: cfgV2.Provider.OpenStack.ProjectName, - UserDomainName: cfgV2.Provider.OpenStack.UserDomainName, - ProjectDomainName: cfgV2.Provider.OpenStack.ProjectDomainName, - RegionName: cfgV2.Provider.OpenStack.RegionName, - Username: cfgV2.Provider.OpenStack.Username, - Password: cfgV2.Provider.OpenStack.Password, - DirectDownload: cfgV2.Provider.OpenStack.DirectDownload, - } - cfgV3.Attestation.QEMUVTPM = &config.QEMUVTPM{ - Measurements: cfgV2.Provider.OpenStack.Measurements, - } - case cfgV2.Provider.QEMU != nil: - cfgV3.Provider.QEMU = &config.QEMUConfig{ - ImageFormat: cfgV2.Provider.QEMU.ImageFormat, - VCPUs: cfgV2.Provider.QEMU.VCPUs, - Memory: cfgV2.Provider.QEMU.Memory, - MetadataAPIImage: cfgV2.Provider.QEMU.MetadataAPIImage, - LibvirtURI: cfgV2.Provider.QEMU.LibvirtURI, - LibvirtContainerImage: cfgV2.Provider.QEMU.LibvirtContainerImage, - NVRAM: cfgV2.Provider.QEMU.NVRAM, - Firmware: cfgV2.Provider.QEMU.Firmware, - } - cfgV3.Attestation.QEMUVTPM = &config.QEMUVTPM{ - Measurements: cfgV2.Provider.QEMU.Measurements, - } - } - - // Create backup - if err := os.Rename(path, path+".backup.v2"); err != nil { - return fmt.Errorf("creating backup: %w", err) - } - - // Write migrated config - if err := fileHandler.WriteYAML(path, cfgV3, file.OptOverwrite); err != nil { - return fmt.Errorf("writing %s: %w", path, err) - } - - return nil +func V2ToV3(_ string, _ file.Handler) error { + // TODO(malt3): add migration from v3 to v4 + return errors.New("not implemented") }