diff --git a/.github/actions/terraform_apply/action.yml b/.github/actions/terraform_apply/action.yml index 2b2cfabce..f66b18ace 100644 --- a/.github/actions/terraform_apply/action.yml +++ b/.github/actions/terraform_apply/action.yml @@ -20,6 +20,9 @@ runs: "azureSEVSNP") attestationVariant="azure-sev-snp" ;; + "azureTDX") + attestationVariant="azure-tdx" + ;; "gcpSEVES") attestationVariant="gcp-sev-es" ;; diff --git a/internal/config/azure.go b/internal/config/azure.go index 3573216aa..2b1f29a03 100644 --- a/internal/config/azure.go +++ b/internal/config/azure.go @@ -141,12 +141,13 @@ func (c AzureTrustedLaunch) EqualTo(other AttestationCfg) (bool, error) { func DefaultForAzureTDX() *AzureTDX { return &AzureTDX{ Measurements: measurements.DefaultsFor(cloudprovider.Azure, variant.AzureTDX{}), - QESVN: 0, - PCESVN: 0, - TEETCBSVN: encoding.HexBytes{0x02, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - QEVendorID: encoding.HexBytes{0x93, 0x9a, 0x72, 0x33, 0xf7, 0x9c, 0x4c, 0xa9, 0x94, 0x0a, 0x0d, 0xb3, 0x95, 0x7f, 0x06, 0x07}, - MRSeam: encoding.HexBytes{0x36, 0x03, 0x04, 0xd3, 0x4a, 0x16, 0xaa, 0xce, 0x0a, 0x18, 0xe0, 0x9a, 0xd2, 0xd0, 0x7d, 0x2b, 0x9f, 0xd3, 0xc1, 0x74, 0x37, 0x8e, 0x5b, 0xf1, 0x08, 0x38, 0x80, 0x79, 0x82, 0x7f, 0x89, 0xff, 0x62, 0xac, 0xc5, 0xf8, 0xc4, 0x73, 0xdd, 0x40, 0x70, 0x63, 0x24, 0x83, 0x4e, 0x20, 0x29, 0x46}, - XFAM: encoding.HexBytes{0xe7, 0x18, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00}, + // TODO(AB#3798): Enable latest versioning for Azure TDX + QESVN: 0, + PCESVN: 0, + TEETCBSVN: encoding.HexBytes{0x02, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + QEVendorID: encoding.HexBytes{0x93, 0x9a, 0x72, 0x33, 0xf7, 0x9c, 0x4c, 0xa9, 0x94, 0x0a, 0x0d, 0xb3, 0x95, 0x7f, 0x06, 0x07}, + MRSeam: encoding.HexBytes{0x36, 0x03, 0x04, 0xd3, 0x4a, 0x16, 0xaa, 0xce, 0x0a, 0x18, 0xe0, 0x9a, 0xd2, 0xd0, 0x7d, 0x2b, 0x9f, 0xd3, 0xc1, 0x74, 0x37, 0x8e, 0x5b, 0xf1, 0x08, 0x38, 0x80, 0x79, 0x82, 0x7f, 0x89, 0xff, 0x62, 0xac, 0xc5, 0xf8, 0xc4, 0x73, 0xdd, 0x40, 0x70, 0x63, 0x24, 0x83, 0x4e, 0x20, 0x29, 0x46}, + XFAM: encoding.HexBytes{0xe7, 0x18, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00}, IntelRootKey: mustParsePEM(tdxRootPEM), } diff --git a/terraform-provider-constellation/docs/data-sources/attestation.md b/terraform-provider-constellation/docs/data-sources/attestation.md index a8668dfdd..bd578314c 100644 --- a/terraform-provider-constellation/docs/data-sources/attestation.md +++ b/terraform-provider-constellation/docs/data-sources/attestation.md @@ -31,6 +31,7 @@ data "constellation_attestation" "test" { * `aws-sev-snp` * `aws-nitro-tpm` * `azure-sev-snp` + * `azure-tdx` * `gcp-sev-es` - `csp` (String) CSP (Cloud Service Provider) to use. (e.g. `azure`) See the [full list of CSPs](https://docs.edgeless.systems/constellation/overview/clouds) that Constellation supports. @@ -43,7 +44,7 @@ See the [full list of CSPs](https://docs.edgeless.systems/constellation/overview ### Read-Only -- `attestation` (Attributes) Attestation comprises the measurements and SEV-SNP specific parameters. (see [below for nested schema](#nestedatt--attestation)) +- `attestation` (Attributes) Attestation comprises the measurements and CVM specific parameters. (see [below for nested schema](#nestedatt--attestation)) ### Nested Schema for `image` @@ -69,11 +70,13 @@ Read-Only: - `measurements` (Attributes Map) (see [below for nested schema](#nestedatt--attestation--measurements)) - `microcode_version` (Number) - `snp_version` (Number) +- `tdx` (Attributes) (see [below for nested schema](#nestedatt--attestation--tdx)) - `tee_version` (Number) - `variant` (String) Attestation variant the image should work with. Can be one of: * `aws-sev-snp` * `aws-nitro-tpm` * `azure-sev-snp` + * `azure-tdx` * `gcp-sev-es` @@ -93,3 +96,17 @@ Read-Only: - `expected` (String) - `warn_only` (Boolean) + + + +### Nested Schema for `attestation.tdx` + +Read-Only: + +- `intel_root_key` (String) +- `mr_seam` (String) +- `pce_svn` (Number) +- `qe_svn` (Number) +- `qe_vendor_id` (String) +- `tee_tcb_svn` (String) +- `xfam` (String) diff --git a/terraform-provider-constellation/docs/data-sources/image.md b/terraform-provider-constellation/docs/data-sources/image.md index 245036503..6d0370eaf 100644 --- a/terraform-provider-constellation/docs/data-sources/image.md +++ b/terraform-provider-constellation/docs/data-sources/image.md @@ -30,6 +30,7 @@ data "constellation_image" "example" { * `aws-sev-snp` * `aws-nitro-tpm` * `azure-sev-snp` + * `azure-tdx` * `gcp-sev-es` - `csp` (String) CSP (Cloud Service Provider) to use. (e.g. `azure`) See the [full list of CSPs](https://docs.edgeless.systems/constellation/overview/clouds) that Constellation supports. diff --git a/terraform-provider-constellation/docs/resources/cluster.md b/terraform-provider-constellation/docs/resources/cluster.md index f00da4bb4..d5deed553 100644 --- a/terraform-provider-constellation/docs/resources/cluster.md +++ b/terraform-provider-constellation/docs/resources/cluster.md @@ -63,7 +63,7 @@ resource "constellation_cluster" "azure_example" { ### Required -- `attestation` (Attributes) Attestation comprises the measurements and SEV-SNP specific parameters. The output of the [constellation_attestation](../data-sources/attestation.md) data source provides sensible defaults. (see [below for nested schema](#nestedatt--attestation)) +- `attestation` (Attributes) Attestation comprises the measurements and CVM specific parameters. The output of the [constellation_attestation](../data-sources/attestation.md) data source provides sensible defaults. (see [below for nested schema](#nestedatt--attestation)) - `constellation_microservice_version` (String) The version of Constellation's microservices used within the cluster. - `csp` (String) CSP (Cloud Service Provider) to use. (e.g. `azure`) See the [full list of CSPs](https://docs.edgeless.systems/constellation/overview/clouds) that Constellation supports. @@ -108,11 +108,13 @@ Required: * `aws-sev-snp` * `aws-nitro-tpm` * `azure-sev-snp` + * `azure-tdx` * `gcp-sev-es` Optional: - `azure_firmware_signer_config` (Attributes) (see [below for nested schema](#nestedatt--attestation--azure_firmware_signer_config)) +- `tdx` (Attributes) (see [below for nested schema](#nestedatt--attestation--tdx)) ### Nested Schema for `attestation.measurements` @@ -133,6 +135,20 @@ Optional: - `maa_url` (String) + +### Nested Schema for `attestation.tdx` + +Optional: + +- `intel_root_key` (String) +- `mr_seam` (String) +- `pce_svn` (Number) +- `qe_svn` (Number) +- `qe_vendor_id` (String) +- `tee_tcb_svn` (String) +- `xfam` (String) + + ### Nested Schema for `image` diff --git a/terraform-provider-constellation/internal/provider/attestation_data_source.go b/terraform-provider-constellation/internal/provider/attestation_data_source.go index 00afa0fe5..56815ae22 100644 --- a/terraform-provider-constellation/internal/provider/attestation_data_source.go +++ b/terraform-provider-constellation/internal/provider/attestation_data_source.go @@ -126,7 +126,7 @@ func (d *AttestationDataSource) ValidateConfig(ctx context.Context, req datasour return } if data.AttestationVariant.Equal(types.StringValue("azure-sev-snp")) && data.MaaURL.IsNull() { - tflog.Info(ctx, "MAA URL not set, MAA fallback will be unavaiable") + tflog.Info(ctx, "MAA URL not set, MAA fallback will be unavailable") } } @@ -172,7 +172,7 @@ func (d *AttestationDataSource) Read(ctx context.Context, req datasource.ReadReq } tfAttestation, err := convertToTfAttestation(attestationVariant, snpVersions) if err != nil { - resp.Diagnostics.AddError("Converting SNP attestation", err.Error()) + resp.Diagnostics.AddError("Converting attestation", err.Error()) } verifyFetcher := measurements.NewVerifyFetcher(sigstore.NewCosignVerifier, d.rekor, d.client) diff --git a/terraform-provider-constellation/internal/provider/attestation_data_source_test.go b/terraform-provider-constellation/internal/provider/attestation_data_source_test.go index 567fc55c4..5f08cc91d 100644 --- a/terraform-provider-constellation/internal/provider/attestation_data_source_test.go +++ b/terraform-provider-constellation/internal/provider/attestation_data_source_test.go @@ -53,6 +53,38 @@ func TestAccAttestationSource(t *testing.T) { }, }, }, + "azure tdx success": { + // TODO(v2.15): Use regular image tag instead of pseudo version + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + PreCheck: bazelPreCheck, + Steps: []resource.TestStep{ + { + Config: testingConfig + ` + data "constellation_attestation" "test" { + csp = "azure" + attestation_variant = "azure-tdx" + image = { + version = "ref/main/stream/debug/v2.15.0-pre.0.20240124172919-4431ac3233bd" + reference = "ref/main/stream/debug/v2.15.0-pre.0.20240124172919-4431ac3233bd" + short_path = "ref/main/stream/debug/v2.15.0-pre.0.20240124172919-4431ac3233bd" + } + insecure = true + } + `, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.constellation_attestation.test", "attestation.variant", "azure-tdx"), + + resource.TestCheckResourceAttr("data.constellation_attestation.test", "attestation.bootloader_version", "0"), // not support for TDX + resource.TestCheckResourceAttr("data.constellation_attestation.test", "attestation.tdx.pce_svn", "0"), // Current default value for TDX + + resource.TestCheckResourceAttr("data.constellation_attestation.test", "attestation.tdx.intel_root_key", `"-----BEGIN CERTIFICATE-----\nMIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw\naDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv\ncnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ\nBgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG\nA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0\naW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT\nAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7\n1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB\nuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ\nMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50\nZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV\nUr9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI\nKoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg\nAiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=\n-----END CERTIFICATE-----\n"`), + + resource.TestCheckResourceAttr("data.constellation_attestation.test", "attestation.measurements.15.expected", "0000000000000000000000000000000000000000000000000000000000000000"), + resource.TestCheckResourceAttr("data.constellation_attestation.test", "attestation.measurements.15.warn_only", "false"), + ), + }, + }, + }, "gcp sev-snp succcess": { ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, PreCheck: bazelPreCheck, diff --git a/terraform-provider-constellation/internal/provider/convert.go b/terraform-provider-constellation/internal/provider/convert.go index c5ab4f1fe..552bdcdd2 100644 --- a/terraform-provider-constellation/internal/provider/convert.go +++ b/terraform-provider-constellation/internal/provider/convert.go @@ -86,6 +86,38 @@ func convertFromTfAttestationCfg(tfAttestation attestationAttribute, attestation MicrocodeVersion: newVersion(tfAttestation.MicrocodeVersion), AMDRootKey: rootKey, } + case variant.AzureTDX{}: + var rootKey config.Certificate + if err := json.Unmarshal([]byte(tfAttestation.TDX.IntelRootKey), &rootKey); err != nil { + return nil, fmt.Errorf("unmarshalling root key: %w", err) + } + teeTCBSVN, err := hex.DecodeString(tfAttestation.TDX.TEETCBSVN) + if err != nil { + return nil, fmt.Errorf("decoding tee_tcb_svn: %w", err) + } + qeVendorID, err := hex.DecodeString(tfAttestation.TDX.QEVendorID) + if err != nil { + return nil, fmt.Errorf("decoding qe_vendor_id: %w", err) + } + mrSeam, err := hex.DecodeString(tfAttestation.TDX.MRSeam) + if err != nil { + return nil, fmt.Errorf("decoding mr_seam: %w", err) + } + xfam, err := hex.DecodeString(tfAttestation.TDX.XFAM) + if err != nil { + return nil, fmt.Errorf("decoding xfam: %w", err) + } + + attestationConfig = &config.AzureTDX{ + Measurements: c11nMeasurements, + QESVN: tfAttestation.TDX.QESVN, + PCESVN: tfAttestation.TDX.PCESVN, + TEETCBSVN: teeTCBSVN, + QEVendorID: qeVendorID, + MRSeam: mrSeam, + XFAM: xfam, + IntelRootKey: rootKey, + } case variant.GCPSEVES{}: attestationConfig = &config.GCPSEVES{ Measurements: c11nMeasurements, @@ -127,6 +159,24 @@ func convertToTfAttestation(attVar variant.Variant, snpVersions attestationconfi return tfAttestation, err } tfAttestation.AzureSNPFirmwareSignerConfig = tfFirmwareCfg + case variant.AzureTDX{}: + tdxCfg := config.DefaultForAzureTDX() + certStr, err := certAsString(tdxCfg.IntelRootKey) + if err != nil { + return tfAttestation, err + } + + tfTdxCfg := tdxConfigAttribute{ + IntelRootKey: certStr, + // TODO(AB#3798): Load these values dynamically from our attestation API + QESVN: tdxCfg.QESVN, + PCESVN: tdxCfg.PCESVN, + TEETCBSVN: hex.EncodeToString(tdxCfg.TEETCBSVN), + QEVendorID: hex.EncodeToString(tdxCfg.QEVendorID), + MRSeam: hex.EncodeToString(tdxCfg.MRSeam), + XFAM: hex.EncodeToString(tdxCfg.XFAM), + } + tfAttestation.TDX = tfTdxCfg case variant.GCPSEVES{}: // no additional fields default: diff --git a/terraform-provider-constellation/internal/provider/shared_attributes.go b/terraform-provider-constellation/internal/provider/shared_attributes.go index a31786735..79535a53c 100644 --- a/terraform-provider-constellation/internal/provider/shared_attributes.go +++ b/terraform-provider-constellation/internal/provider/shared_attributes.go @@ -30,11 +30,12 @@ func newAttestationVariantAttributeSchema(t attributeType) schema.Attribute { " * `aws-sev-snp`\n" + " * `aws-nitro-tpm`\n" + " * `azure-sev-snp`\n" + + " * `azure-tdx`\n" + " * `gcp-sev-es`\n", Required: isInput, Computed: !isInput, Validators: []validator.String{ - stringvalidator.OneOf("aws-sev-snp", "aws-nitro-tpm", "azure-sev-snp", "gcp-sev-es"), + stringvalidator.OneOf("aws-sev-snp", "aws-nitro-tpm", "azure-sev-snp", "azure-tdx", "gcp-sev-es"), }, } } @@ -86,8 +87,8 @@ func newAttestationConfigAttributeSchema(t attributeType) schema.Attribute { return schema.SingleNestedAttribute{ Computed: !isInput, Required: isInput, - MarkdownDescription: "Attestation comprises the measurements and SEV-SNP specific parameters." + additionalDescription, - Description: "Attestation comprises the measurements and SEV-SNP specific parameters." + additionalDescription, + MarkdownDescription: "Attestation comprises the measurements and CVM specific parameters." + additionalDescription, + Description: "Attestation comprises the measurements and CVM specific parameters." + additionalDescription, Attributes: map[string]schema.Attribute{ "variant": newAttestationVariantAttributeSchema(t), // duplicated for convenience in cluster resource "bootloader_version": schema.Int64Attribute{ @@ -129,6 +130,40 @@ func newAttestationConfigAttributeSchema(t attributeType) schema.Attribute { Computed: !isInput, Required: isInput, }, + "tdx": schema.SingleNestedAttribute{ + Computed: !isInput, + Optional: isInput, + Attributes: map[string]schema.Attribute{ + "qe_svn": schema.Int64Attribute{ + Computed: !isInput, + Optional: isInput, + }, + "pce_svn": schema.Int64Attribute{ + Computed: !isInput, + Optional: isInput, + }, + "tee_tcb_svn": schema.StringAttribute{ + Computed: !isInput, + Optional: isInput, + }, + "qe_vendor_id": schema.StringAttribute{ + Computed: !isInput, + Optional: isInput, + }, + "mr_seam": schema.StringAttribute{ + Computed: !isInput, + Optional: isInput, + }, + "xfam": schema.StringAttribute{ + Computed: !isInput, + Optional: isInput, + }, + "intel_root_key": schema.StringAttribute{ + Computed: !isInput, + Optional: isInput, + }, + }, + }, "measurements": newMeasurementsAttributeSchema(t), }, } @@ -142,6 +177,7 @@ type attestationAttribute struct { MicrocodeVersion uint8 `tfsdk:"microcode_version"` AMDRootKey string `tfsdk:"amd_root_key"` AzureSNPFirmwareSignerConfig azureSnpFirmwareSignerConfigAttribute `tfsdk:"azure_firmware_signer_config"` + TDX tdxConfigAttribute `tfsdk:"tdx"` Variant string `tfsdk:"variant"` Measurements map[string]measurementAttribute `tfsdk:"measurements"` } @@ -153,6 +189,17 @@ type azureSnpFirmwareSignerConfigAttribute struct { MAAURL string `tfsdk:"maa_url"` } +// tdxConfigAttribute groups the TDX specific attributes for Constellation. +type tdxConfigAttribute struct { + QESVN uint16 `tfsdk:"qe_svn"` + PCESVN uint16 `tfsdk:"pce_svn"` + TEETCBSVN string `tfsdk:"tee_tcb_svn"` + QEVendorID string `tfsdk:"qe_vendor_id"` + MRSeam string `tfsdk:"mr_seam"` + XFAM string `tfsdk:"xfam"` + IntelRootKey string `tfsdk:"intel_root_key"` +} + func newImageAttributeSchema(t attributeType) schema.Attribute { isInput := bool(t) return schema.SingleNestedAttribute{