diff --git a/bootstrapper/cmd/bootstrapper/main.go b/bootstrapper/cmd/bootstrapper/main.go index dfc760378..9f529ac78 100644 --- a/bootstrapper/cmd/bootstrapper/main.go +++ b/bootstrapper/cmd/bootstrapper/main.go @@ -13,6 +13,9 @@ import ( "os" "strconv" + "github.com/spf13/afero" + "go.uber.org/zap" + "github.com/edgelesssys/constellation/v2/bootstrapper/internal/helm" "github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes" "github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes/k8sapi" @@ -32,8 +35,6 @@ import ( "github.com/edgelesssys/constellation/v2/internal/kubernetes/kubectl" "github.com/edgelesssys/constellation/v2/internal/logger" "github.com/edgelesssys/constellation/v2/internal/variant" - "github.com/spf13/afero" - "go.uber.org/zap" ) const ( @@ -161,8 +162,7 @@ func main() { ) metadataAPI = metadata - // TODO(malt3): add OpenStack TPM support - openTPM = vtpm.OpenNOPTPM + openTPM = vtpm.OpenVTPM fs = afero.NewOsFs() default: clusterInitJoiner = &clusterFake{} diff --git a/cli/internal/cloudcmd/create.go b/cli/internal/cloudcmd/create.go index 53460c029..4c8162018 100644 --- a/cli/internal/cloudcmd/create.go +++ b/cli/internal/cloudcmd/create.go @@ -23,6 +23,7 @@ import ( "github.com/Azure/azure-sdk-for-go/profiles/latest/attestation/attestation" azpolicy "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" "github.com/Azure/azure-sdk-for-go/sdk/azidentity" + "github.com/edgelesssys/constellation/v2/cli/internal/clusterid" "github.com/edgelesssys/constellation/v2/cli/internal/image" "github.com/edgelesssys/constellation/v2/cli/internal/libvirt" diff --git a/cli/internal/cloudcmd/create_test.go b/cli/internal/cloudcmd/create_test.go index b86a17be2..f0e74c37c 100644 --- a/cli/internal/cloudcmd/create_test.go +++ b/cli/internal/cloudcmd/create_test.go @@ -13,14 +13,17 @@ import ( "runtime" "testing" + "github.com/stretchr/testify/assert" + "github.com/edgelesssys/constellation/v2/cli/internal/terraform" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/variant" - "github.com/stretchr/testify/assert" ) func TestCreator(t *testing.T) { + // TODO(malt3): remove once OpenStack is fully supported. + t.Setenv("CONSTELLATION_OPENSTACK_DEV", "1") failOnNonAMD64 := (runtime.GOARCH != "amd64") || (runtime.GOOS != "linux") ip := "192.0.2.1" someErr := errors.New("failed") @@ -110,6 +113,47 @@ func TestCreator(t *testing.T) { wantRollback: true, wantTerraformRollback: true, }, + "openstack": { + tfClient: &stubTerraformClient{ip: ip}, + libvirt: &stubLibvirtRunner{}, + provider: cloudprovider.OpenStack, + config: func() *config.Config { + cfg := config.Default() + cfg.Provider.OpenStack.Cloud = "testcloud" + return cfg + }(), + }, + "openstack without clouds.yaml": { + tfClient: &stubTerraformClient{ip: ip}, + libvirt: &stubLibvirtRunner{}, + provider: cloudprovider.OpenStack, + config: config.Default(), + wantErr: true, + }, + "openstack newTerraformClient error": { + newTfClientErr: someErr, + libvirt: &stubLibvirtRunner{}, + provider: cloudprovider.OpenStack, + config: func() *config.Config { + cfg := config.Default() + cfg.Provider.OpenStack.Cloud = "testcloud" + return cfg + }(), + wantErr: true, + }, + "openstack create cluster error": { + tfClient: &stubTerraformClient{createClusterErr: someErr}, + libvirt: &stubLibvirtRunner{}, + provider: cloudprovider.OpenStack, + config: func() *config.Config { + cfg := config.Default() + cfg.Provider.OpenStack.Cloud = "testcloud" + return cfg + }(), + wantErr: true, + wantRollback: true, + wantTerraformRollback: true, + }, "qemu": { tfClient: &stubTerraformClient{ip: ip}, libvirt: &stubLibvirtRunner{}, @@ -152,7 +196,6 @@ func TestCreator(t *testing.T) { for name, tc := range testCases { t.Run(name, func(t *testing.T) { assert := assert.New(t) - creator := &Creator{ out: &bytes.Buffer{}, image: &stubImageFetcher{ diff --git a/cli/internal/cloudcmd/validators_test.go b/cli/internal/cloudcmd/validators_test.go index 09c63b693..b96640bc6 100644 --- a/cli/internal/cloudcmd/validators_test.go +++ b/cli/internal/cloudcmd/validators_test.go @@ -12,6 +12,10 @@ import ( "encoding/hex" "testing" + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/edgelesssys/constellation/v2/internal/atls" "github.com/edgelesssys/constellation/v2/internal/attestation/azure/snp" "github.com/edgelesssys/constellation/v2/internal/attestation/azure/trustedlaunch" @@ -22,9 +26,6 @@ import ( "github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/logger" "github.com/edgelesssys/constellation/v2/internal/variant" - "github.com/spf13/cobra" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestNewValidator(t *testing.T) { @@ -71,6 +72,16 @@ func TestNewValidator(t *testing.T) { }, }, }, + "openstack": { + config: &config.Config{ + AttestationVariant: variant.QEMUVTPM{}.String(), + Provider: config.ProviderConfig{ + OpenStack: &config.OpenStackConfig{ + Measurements: testPCRs, + }, + }, + }, + }, "qemu": { config: &config.Config{ AttestationVariant: variant.QEMUVTPM{}.String(), diff --git a/cli/internal/helm/loader_test.go b/cli/internal/helm/loader_test.go index 2ee096384..a2cee8696 100644 --- a/cli/internal/helm/loader_test.go +++ b/cli/internal/helm/loader_test.go @@ -18,18 +18,19 @@ import ( "strings" "testing" - "github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest" - "github.com/edgelesssys/constellation/v2/internal/attestation/measurements" - "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" - "github.com/edgelesssys/constellation/v2/internal/config" - "github.com/edgelesssys/constellation/v2/internal/deploy/helm" - "github.com/edgelesssys/constellation/v2/internal/variant" "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "helm.sh/helm/v3/pkg/chart/loader" "helm.sh/helm/v3/pkg/chartutil" "helm.sh/helm/v3/pkg/engine" + + "github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest" + "github.com/edgelesssys/constellation/v2/internal/attestation/measurements" + "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" + "github.com/edgelesssys/constellation/v2/internal/config" + "github.com/edgelesssys/constellation/v2/internal/deploy/helm" + "github.com/edgelesssys/constellation/v2/internal/variant" ) // TestLoad checks if the serialized format that Load returns correctly preserves the dependencies of the loaded chart. @@ -97,7 +98,7 @@ func TestConstellationServices(t *testing.T) { }, "OpenStack": { config: &config.Config{ - AttestationVariant: variant.Dummy{}.String(), + AttestationVariant: variant.QEMUVTPM{}.String(), Provider: config.ProviderConfig{OpenStack: &config.OpenStackConfig{}}, }, valuesModifier: prepareOpenStackValues, diff --git a/cli/internal/helm/testdata/OpenStack/constellation-services/charts/join-service/templates/daemonset.yaml b/cli/internal/helm/testdata/OpenStack/constellation-services/charts/join-service/templates/daemonset.yaml index d4e7d56e2..0ed907f4d 100644 --- a/cli/internal/helm/testdata/OpenStack/constellation-services/charts/join-service/templates/daemonset.yaml +++ b/cli/internal/helm/testdata/OpenStack/constellation-services/charts/join-service/templates/daemonset.yaml @@ -39,7 +39,7 @@ spec: args: - --cloud-provider=OpenStack - --key-service-endpoint=key-service.testNamespace:9000 - - --attestation-variant=dummy + - --attestation-variant=qemu-vtpm volumeMounts: - mountPath: /var/config name: config diff --git a/cli/internal/helm/testdata/OpenStack/constellation-services/charts/verification-service/templates/daemonset.yaml b/cli/internal/helm/testdata/OpenStack/constellation-services/charts/verification-service/templates/daemonset.yaml index bf97e74a1..dbb9a985e 100644 --- a/cli/internal/helm/testdata/OpenStack/constellation-services/charts/verification-service/templates/daemonset.yaml +++ b/cli/internal/helm/testdata/OpenStack/constellation-services/charts/verification-service/templates/daemonset.yaml @@ -17,7 +17,7 @@ spec: spec: containers: - args: - - --attestation-variant=dummy + - --attestation-variant=qemu-vtpm image: verificationImage name: verification-service ports: diff --git a/cli/internal/terraform/terraform/openstack/main.tf b/cli/internal/terraform/terraform/openstack/main.tf index 9f7151f79..7d564ad55 100644 --- a/cli/internal/terraform/terraform/openstack/main.tf +++ b/cli/internal/terraform/terraform/openstack/main.tf @@ -112,6 +112,20 @@ resource "openstack_compute_secgroup_v2" "vpc_secgroup" { self = true } + rule { + from_port = 1 + to_port = 65535 + ip_protocol = "udp" + cidr = local.cidr_vpc_subnet_nodes + } + + rule { + from_port = 1 + to_port = 65535 + ip_protocol = "tcp" + cidr = local.cidr_vpc_subnet_nodes + } + rule { from_port = local.ports_node_range_start to_port = local.ports_node_range_end @@ -119,6 +133,13 @@ resource "openstack_compute_secgroup_v2" "vpc_secgroup" { cidr = "0.0.0.0/0" } + rule { + from_port = local.ports_node_range_start + to_port = local.ports_node_range_end + ip_protocol = "udp" + cidr = "0.0.0.0/0" + } + dynamic "rule" { for_each = flatten([ local.ports_kubernetes, diff --git a/debugd/cmd/debugd/debugd.go b/debugd/cmd/debugd/debugd.go index 652b29baf..92f7f1614 100644 --- a/debugd/cmd/debugd/debugd.go +++ b/debugd/cmd/debugd/debugd.go @@ -14,6 +14,9 @@ import ( "os" "sync" + "github.com/spf13/afero" + "go.uber.org/zap" + "github.com/edgelesssys/constellation/v2/debugd/internal/debugd/deploy" "github.com/edgelesssys/constellation/v2/debugd/internal/debugd/info" "github.com/edgelesssys/constellation/v2/debugd/internal/debugd/logcollector" @@ -30,8 +33,6 @@ import ( openstackcloud "github.com/edgelesssys/constellation/v2/internal/cloud/openstack" qemucloud "github.com/edgelesssys/constellation/v2/internal/cloud/qemu" "github.com/edgelesssys/constellation/v2/internal/logger" - "github.com/spf13/afero" - "go.uber.org/zap" ) const debugBanner = ` diff --git a/image/mkosi.files/mkosi.openstack.conf b/image/mkosi.files/mkosi.openstack.conf index 3271b3fe8..35ac99754 100644 --- a/image/mkosi.files/mkosi.openstack.conf +++ b/image/mkosi.files/mkosi.openstack.conf @@ -1,5 +1,5 @@ [Output] -KernelCommandLine=constel.csp=openstack constel.attestation-variant=dummy mem_encrypt=on kvm_amd.sev=1 module_blacklist=qemu_fw_cfg console=tty0 console=ttyS0 +KernelCommandLine=constel.csp=openstack constel.attestation-variant=qemu-vtpm mem_encrypt=on kvm_amd.sev=1 module_blacklist=qemu_fw_cfg console=tty0 console=ttyS0 OutputDirectory=mkosi.output.openstack [Content] diff --git a/internal/attestation/measurements/measurements_oss.go b/internal/attestation/measurements/measurements_oss.go index 2ef2934b2..eb17b71a8 100644 --- a/internal/attestation/measurements/measurements_oss.go +++ b/internal/attestation/measurements/measurements_oss.go @@ -53,6 +53,16 @@ func DefaultsFor(provider cloudprovider.Provider) M { 13: WithAllBytes(0x00, Enforce), uint32(PCRIndexClusterID): WithAllBytes(0x00, Enforce), } + case cloudprovider.OpenStack: + return M{ + 4: PlaceHolderMeasurement(), + 8: WithAllBytes(0x00, Enforce), + 9: PlaceHolderMeasurement(), + 11: WithAllBytes(0x00, Enforce), + 12: PlaceHolderMeasurement(), + 13: WithAllBytes(0x00, Enforce), + uint32(PCRIndexClusterID): WithAllBytes(0x00, Enforce), + } default: return nil } diff --git a/internal/config/config.go b/internal/config/config.go index eefd4fa9d..01d7c9663 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -26,6 +26,11 @@ import ( "reflect" "strings" + "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" + "github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest" "github.com/edgelesssys/constellation/v2/internal/attestation/measurements" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" @@ -33,10 +38,6 @@ import ( "github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/file" "github.com/edgelesssys/constellation/v2/internal/versions" - "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" ) // Measurements is a required alias since docgen is not able to work with @@ -249,6 +250,9 @@ type OpenStackConfig struct { // description: | // If enabled, downloads OS image directly from source URL to OpenStack. Otherwise, downloads image to local machine and uploads to OpenStack. DirectDownload *bool `yaml:"directDownload" validate:"required"` + // description: | + // Measurement used to enable measured boot. + Measurements Measurements `yaml:"measurements" validate:"required,no_placeholders"` } // QEMUConfig holds config information for QEMU based Constellation deployments. @@ -327,6 +331,7 @@ func Default() *Config { }, OpenStack: &OpenStackConfig{ DirectDownload: toPtr(true), + Measurements: measurements.DefaultsFor(cloudprovider.OpenStack), }, QEMU: &QEMUConfig{ ImageFormat: "raw", @@ -396,6 +401,8 @@ func (c *Config) HasProvider(provider cloudprovider.Provider) bool { return c.Provider.Azure != nil case cloudprovider.GCP: return c.Provider.GCP != nil + case cloudprovider.OpenStack: + return c.Provider.OpenStack != nil case cloudprovider.QEMU: return c.Provider.QEMU != nil } @@ -413,6 +420,9 @@ func (c *Config) UpdateMeasurements(newMeasurements Measurements) { if c.Provider.GCP != nil { c.Provider.GCP.Measurements.CopyFrom(newMeasurements) } + if c.Provider.OpenStack != nil { + c.Provider.OpenStack.Measurements.CopyFrom(newMeasurements) + } if c.Provider.QEMU != nil { c.Provider.QEMU.Measurements.CopyFrom(newMeasurements) } @@ -484,6 +494,9 @@ func (c *Config) GetMeasurements() measurements.M { if c.Provider.GCP != nil { return c.Provider.GCP.Measurements } + if c.Provider.OpenStack != nil { + return c.Provider.OpenStack.Measurements + } if c.Provider.QEMU != nil { return c.Provider.QEMU.Measurements } diff --git a/internal/config/config_doc.go b/internal/config/config_doc.go index 7305345e7..886d7cb37 100644 --- a/internal/config/config_doc.go +++ b/internal/config/config_doc.go @@ -299,7 +299,7 @@ func init() { FieldName: "openstack", }, } - OpenStackConfigDoc.Fields = make([]encoder.Doc, 13) + OpenStackConfigDoc.Fields = make([]encoder.Doc, 14) OpenStackConfigDoc.Fields[0].Name = "cloud" OpenStackConfigDoc.Fields[0].Type = "string" OpenStackConfigDoc.Fields[0].Note = "" @@ -365,6 +365,11 @@ func init() { OpenStackConfigDoc.Fields[12].Note = "" OpenStackConfigDoc.Fields[12].Description = "If enabled, downloads OS image directly from source URL to OpenStack. Otherwise, downloads image to local machine and uploads to OpenStack." OpenStackConfigDoc.Fields[12].Comments[encoder.LineComment] = "If enabled, downloads OS image directly from source URL to OpenStack. Otherwise, downloads image to local machine and uploads to OpenStack." + OpenStackConfigDoc.Fields[13].Name = "measurements" + OpenStackConfigDoc.Fields[13].Type = "Measurements" + OpenStackConfigDoc.Fields[13].Note = "" + OpenStackConfigDoc.Fields[13].Description = "Measurement used to enable measured boot." + OpenStackConfigDoc.Fields[13].Comments[encoder.LineComment] = "Measurement used to enable measured boot." QEMUConfigDoc.Type = "QEMUConfig" QEMUConfigDoc.Comments[encoder.LineComment] = "QEMUConfig holds config information for QEMU based Constellation deployments." diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 909775330..2c4db01fa 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -10,13 +10,6 @@ import ( "reflect" "testing" - "github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest" - "github.com/edgelesssys/constellation/v2/internal/attestation/measurements" - "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" - "github.com/edgelesssys/constellation/v2/internal/config/instancetypes" - "github.com/edgelesssys/constellation/v2/internal/constants" - "github.com/edgelesssys/constellation/v2/internal/file" - "github.com/edgelesssys/constellation/v2/internal/variant" "github.com/go-playground/locales/en" ut "github.com/go-playground/universal-translator" "github.com/go-playground/validator/v10" @@ -24,6 +17,14 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/goleak" + + "github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest" + "github.com/edgelesssys/constellation/v2/internal/attestation/measurements" + "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" + "github.com/edgelesssys/constellation/v2/internal/config/instancetypes" + "github.com/edgelesssys/constellation/v2/internal/constants" + "github.com/edgelesssys/constellation/v2/internal/file" + "github.com/edgelesssys/constellation/v2/internal/variant" ) func TestMain(m *testing.M) { @@ -187,7 +188,7 @@ func TestNewWithDefaultOptions(t *testing.T) { } func TestValidate(t *testing.T) { - const defaultErrCount = 31 // expect this number of error messages by default because user-specific values are not set and multiple providers are defined by default + const defaultErrCount = 32 // expect this number of error messages by default because user-specific values are not set and multiple providers are defined by default const azErrCount = 9 const gcpErrCount = 6 diff --git a/internal/config/validation.go b/internal/config/validation.go index d4204ceca..345c40514 100644 --- a/internal/config/validation.go +++ b/internal/config/validation.go @@ -15,6 +15,10 @@ import ( "strconv" "strings" + ut "github.com/go-playground/universal-translator" + "github.com/go-playground/validator/v10" + "golang.org/x/mod/semver" + "github.com/edgelesssys/constellation/v2/internal/attestation/measurements" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/compatibility" @@ -23,9 +27,6 @@ import ( "github.com/edgelesssys/constellation/v2/internal/variant" "github.com/edgelesssys/constellation/v2/internal/versions" "github.com/edgelesssys/constellation/v2/internal/versionsapi" - ut "github.com/go-playground/universal-translator" - "github.com/go-playground/validator/v10" - "golang.org/x/mod/semver" ) // ValidationError occurs when the validation of a config fails. @@ -191,6 +192,9 @@ func (c *Config) translateMoreThanOneProviderError(ut ut.Translator, fe validato if c.Provider.GCP != nil { definedProviders = append(definedProviders, "GCP") } + if c.Provider.OpenStack != nil { + definedProviders = append(definedProviders, "OpenStack") + } if c.Provider.QEMU != nil { definedProviders = append(definedProviders, "QEMU") } @@ -477,13 +481,10 @@ func (c *Config) validAttestVariant(_ validator.FieldLevel) bool { return c.Provider.AWS != nil case variant.AzureSEVSNP{}, variant.AzureTrustedLaunch{}: return c.Provider.Azure != nil - // TODO(malt3): remove this case once we have a vTPM for OpenStack - case variant.Dummy{}: - return c.Provider.OpenStack != nil case variant.GCPSEVES{}: return c.Provider.GCP != nil case variant.QEMUVTPM{}: - return c.Provider.QEMU != nil + return c.Provider.QEMU != nil || c.Provider.OpenStack != nil default: return false } @@ -502,6 +503,8 @@ func (c *Config) addMissingVariant() { c.AttestationVariant = variant.GCPSEVES{}.String() case cloudprovider.QEMU: c.AttestationVariant = variant.QEMUVTPM{}.String() + case cloudprovider.OpenStack: + c.AttestationVariant = variant.QEMUVTPM{}.String() } }