terraform-provider: data skeleton for cluster resource (#2678)

This commit is contained in:
Adrian Stobbe 2023-12-05 16:16:50 +01:00 committed by GitHub
parent f5718b6655
commit c07c333d3d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 1091 additions and 454 deletions

View file

@ -8,23 +8,18 @@ package provider
import (
"context"
"encoding/hex"
"errors"
"fmt"
"net/http"
"strconv"
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/sigstore"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
)
@ -50,8 +45,6 @@ type AttestationDataSourceModel struct {
AttestationVariant types.String `tfsdk:"attestation_variant"`
ImageVersion types.String `tfsdk:"image_version"`
MaaURL types.String `tfsdk:"maa_url"`
ID types.String `tfsdk:"id"`
Measurements types.Map `tfsdk:"measurements"`
Attestation types.Object `tfsdk:"attestation"`
}
@ -79,27 +72,8 @@ func (d *AttestationDataSource) Schema(_ context.Context, _ datasource.SchemaReq
MarkdownDescription: "The data source to fetch measurements from a configured cloud provider and image.",
Attributes: map[string]schema.Attribute{
"csp": schema.StringAttribute{
Description: "CSP (Cloud Service Provider) to use. (e.g. `azure`)",
MarkdownDescription: "CSP (Cloud Service Provider) to use. (e.g. `azure`)\n" +
"See the [full list of CSPs](https://docs.edgeless.systems/constellation/overview/clouds) that Constellation supports.",
Required: true,
Validators: []validator.String{
stringvalidator.OneOf("aws", "azure", "gcp"),
},
},
"attestation_variant": schema.StringAttribute{
Description: "Attestation variant the image should work with. (e.g. `azure-sev-snp`)",
MarkdownDescription: "Attestation variant the image should work with. Can be one of:\n" +
" * `aws-sev-snp`\n" +
" * `aws-nitro-tpm`\n" +
" * `azure-sev-snp`\n" +
" * `gcp-sev-es`\n",
Required: true,
Validators: []validator.String{
stringvalidator.OneOf("aws-sev-snp", "aws-nitro-tpm", "azure-sev-snp", "gcp-sev-es"),
},
},
"csp": newCSPAttribute(),
"attestation_variant": newAttestationVariantAttribute(attributeInput),
"image_version": schema.StringAttribute{
MarkdownDescription: "The image version to use",
Required: true,
@ -108,60 +82,7 @@ func (d *AttestationDataSource) Schema(_ context.Context, _ datasource.SchemaReq
MarkdownDescription: "For Azure only, the URL of the Microsoft Azure Attestation service",
Optional: true,
},
"id": schema.StringAttribute{
Computed: true,
MarkdownDescription: "The ID of the data source",
},
"measurements": schema.MapNestedAttribute{
Computed: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"expected": schema.StringAttribute{
Computed: true,
},
"warn_only": schema.BoolAttribute{
Computed: true,
},
},
},
},
"attestation": schema.SingleNestedAttribute{
Computed: true,
MarkdownDescription: "Only relevant for SEV-SNP.",
Description: "The values provide sensible defaults. See the docs for advanced usage.", // TODO(elchead): AB#3568
Attributes: map[string]schema.Attribute{
"bootloader_version": schema.Int64Attribute{
Computed: true,
},
"tee_version": schema.Int64Attribute{
Computed: true,
},
"snp_version": schema.Int64Attribute{
Computed: true,
},
"microcode_version": schema.Int64Attribute{
Computed: true,
},
"azure_firmware_signer_config": schema.SingleNestedAttribute{
Computed: true,
Attributes: map[string]schema.Attribute{
"accepted_key_digests": schema.ListAttribute{
Computed: true,
ElementType: types.StringType,
},
"enforcement_policy": schema.StringAttribute{
Computed: true,
},
"maa_url": schema.StringAttribute{
Computed: true,
},
},
},
"amd_root_key": schema.StringAttribute{
Computed: true,
},
},
},
"attestation": newAttestationConfigAttribute(attributeOutput),
},
}
}
@ -191,27 +112,23 @@ func (d *AttestationDataSource) Read(ctx context.Context, req datasource.ReadReq
resp.Diagnostics.AddAttributeError(
path.Root("attestation_variant"),
"Invalid Attestation Variant",
fmt.Sprintf("Invalid attestation variant: %s", data.CSP.ValueString()),
fmt.Sprintf("Invalid attestation variant: %s", data.AttestationVariant.ValueString()),
)
return
}
snpVersions := attestationconfigapi.SEVSNPVersionAPI{}
if attestationVariant.Equal(variant.AzureSEVSNP{}) || attestationVariant.Equal(variant.AWSSEVSNP{}) {
snpVersions, err := d.fetcher.FetchSEVSNPVersionLatest(ctx, attestationVariant)
snpVersions, err = d.fetcher.FetchSEVSNPVersionLatest(ctx, attestationVariant)
if err != nil {
resp.Diagnostics.AddError("Fetching SNP Version numbers", err.Error())
return
}
tfSnpAttestation, err := convertSNPAttestationTfStateCompatible(attestationVariant, snpVersions)
if err != nil {
resp.Diagnostics.AddError("Converting SNP attestation", err.Error())
}
diags := resp.State.SetAttribute(ctx, path.Root("attestation"), tfSnpAttestation)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}
tfAttestation, err := convertToTfAttestation(attestationVariant, snpVersions)
if err != nil {
resp.Diagnostics.AddError("Converting SNP attestation", err.Error())
}
verifyFetcher := measurements.NewVerifyFetcher(sigstore.NewCosignVerifier, d.rekor, d.client)
fetchedMeasurements, err := verifyFetcher.FetchAndVerifyMeasurements(ctx, data.ImageVersion.ValueString(),
csp, attestationVariant, false)
@ -224,83 +141,12 @@ func (d *AttestationDataSource) Read(ctx context.Context, req datasource.ReadReq
return
}
}
tfMeasurements := convertMeasurementsTfStateCompatible(fetchedMeasurements)
diags := resp.State.SetAttribute(ctx, path.Root("measurements"), tfMeasurements)
tfAttestation.Measurements = convertToTfMeasurements(fetchedMeasurements)
diags := resp.State.SetAttribute(ctx, path.Root("attestation"), tfAttestation)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
tflog.Trace(ctx, "read constellation attestation data source")
}
func convertSNPAttestationTfStateCompatible(attestationVariant variant.Variant,
snpVersions attestationconfigapi.SEVSNPVersionAPI,
) (tfSnpAttestation sevSnpAttestation, err error) {
var cert config.Certificate
switch attestationVariant.(type) {
case variant.AWSSEVSNP:
cert = config.DefaultForAWSSEVSNP().AMDRootKey
case variant.AzureSEVSNP:
cert = config.DefaultForAzureSEVSNP().AMDRootKey
}
certBytes, err := cert.MarshalJSON()
if err != nil {
return tfSnpAttestation, err
}
tfSnpAttestation = sevSnpAttestation{
BootloaderVersion: snpVersions.Bootloader,
TEEVersion: snpVersions.TEE,
SNPVersion: snpVersions.SNP,
MicrocodeVersion: snpVersions.Microcode,
AMDRootKey: string(certBytes),
}
if attestationVariant.Equal(variant.AzureSEVSNP{}) {
firmwareCfg := config.DefaultForAzureSEVSNP().FirmwareSignerConfig
keyDigestAny, err := firmwareCfg.AcceptedKeyDigests.MarshalYAML()
if err != nil {
return tfSnpAttestation, err
}
keyDigest, ok := keyDigestAny.([]string)
if !ok {
return tfSnpAttestation, errors.New("reading Accepted Key Digests: could not convert to []string")
}
tfSnpAttestation.AzureSNPFirmwareSignerConfig = azureSnpFirmwareSignerConfig{
AcceptedKeyDigests: keyDigest,
EnforcementPolicy: firmwareCfg.EnforcementPolicy.String(),
MAAURL: firmwareCfg.MAAURL,
}
}
return tfSnpAttestation, nil
}
func convertMeasurementsTfStateCompatible(m measurements.M) map[string]measurement {
tfMeasurements := map[string]measurement{}
for key, value := range m {
keyStr := strconv.FormatUint(uint64(key), 10)
tfMeasurements[keyStr] = measurement{
Expected: hex.EncodeToString(value.Expected[:]),
WarnOnly: bool(value.ValidationOpt),
}
}
return tfMeasurements
}
type measurement struct {
Expected string `tfsdk:"expected"`
WarnOnly bool `tfsdk:"warn_only"`
}
type sevSnpAttestation struct {
BootloaderVersion uint8 `tfsdk:"bootloader_version"`
TEEVersion uint8 `tfsdk:"tee_version"`
SNPVersion uint8 `tfsdk:"snp_version"`
MicrocodeVersion uint8 `tfsdk:"microcode_version"`
AMDRootKey string `tfsdk:"amd_root_key"`
AzureSNPFirmwareSignerConfig azureSnpFirmwareSignerConfig `tfsdk:"azure_firmware_signer_config"`
}
type azureSnpFirmwareSignerConfig struct {
AcceptedKeyDigests []string `tfsdk:"accepted_key_digests"`
EnforcementPolicy string `tfsdk:"enforcement_policy"`
MAAURL string `tfsdk:"maa_url"`
}