terraform-provider: enable Azure TDX (#2854)

Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
Daniel Weiße 2024-01-26 15:46:21 +01:00 committed by GitHub
parent d58d888f54
commit 78b9b0fc96
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 180 additions and 13 deletions

View File

@ -20,6 +20,9 @@ runs:
"azureSEVSNP")
attestationVariant="azure-sev-snp"
;;
"azureTDX")
attestationVariant="azure-tdx"
;;
"gcpSEVES")
attestationVariant="gcp-sev-es"
;;

View File

@ -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),
}

View File

@ -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))
<a id="nestedatt--image"></a>
### 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`
<a id="nestedatt--attestation--azure_firmware_signer_config"></a>
@ -93,3 +96,17 @@ Read-Only:
- `expected` (String)
- `warn_only` (Boolean)
<a id="nestedatt--attestation--tdx"></a>
### 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)

View File

@ -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.

View File

@ -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))
<a id="nestedatt--attestation--measurements"></a>
### Nested Schema for `attestation.measurements`
@ -133,6 +135,20 @@ Optional:
- `maa_url` (String)
<a id="nestedatt--attestation--tdx"></a>
### 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)
<a id="nestedatt--image"></a>
### Nested Schema for `image`

View File

@ -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)

View File

@ -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,

View File

@ -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:

View File

@ -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{