From 259e5406ee60da6f433fdf61faed59e60f87a218 Mon Sep 17 00:00:00 2001 From: Adrian Stobbe Date: Fri, 24 Nov 2023 13:04:06 +0100 Subject: [PATCH] fixup! init --- cli/internal/cmd/configfetchmeasurements.go | 54 +----- internal/attestation/measurements/BUILD.bazel | 3 + .../measurements/fetchmeasurements.go | 123 +++++++++++++ .../measurements/measurements_test.go | 7 + internal/config/azure.go | 9 +- internal/constellation/azureattestation.go | 105 ------------ internal/constellation/fetchmeasurements.go | 70 -------- .../attestation_example/data-source.tf | 6 +- terraform-provider-constellation/go.mod | 3 + terraform-provider-constellation/go.sum | 7 + .../internal/provider/BUILD.bazel | 5 + .../provider/attestation_data_source.go | 162 +++++++++--------- .../provider/attestation_data_source_test.go | 31 ++-- 13 files changed, 259 insertions(+), 326 deletions(-) create mode 100644 internal/attestation/measurements/fetchmeasurements.go delete mode 100644 internal/constellation/azureattestation.go delete mode 100644 internal/constellation/fetchmeasurements.go diff --git a/cli/internal/cmd/configfetchmeasurements.go b/cli/internal/cmd/configfetchmeasurements.go index ccaa13a91f1..522ec131c09 100644 --- a/cli/internal/cmd/configfetchmeasurements.go +++ b/cli/internal/cmd/configfetchmeasurements.go @@ -22,7 +22,6 @@ import ( "github.com/edgelesssys/constellation/v2/internal/featureset" "github.com/edgelesssys/constellation/v2/internal/file" "github.com/edgelesssys/constellation/v2/internal/sigstore" - "github.com/edgelesssys/constellation/v2/internal/sigstore/keyselect" "github.com/spf13/afero" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -132,58 +131,15 @@ func (cfm *configFetchMeasurementsCmd) configFetchMeasurements( if err := cfm.flags.updateURLs(conf); err != nil { return err } - - cfm.log.Debugf("Fetching and verifying measurements") - imageVersion, err := versionsapi.NewVersionFromShortPath(conf.Image, versionsapi.VersionKindImage) - if err != nil { - return err - } - - publicKey, err := keyselect.CosignPublicKeyForVersion(imageVersion) - if err != nil { - return fmt.Errorf("getting public key: %w", err) - } - cosign, err := newCosignVerifier(publicKey) + verifyFetcher := measurements.NewVerifyFetcherWith(newCosignVerifier, cfm.flags.insecure, rekor, client) + fetchedMeasurements, err := verifyFetcher.FetchAndVerifyMeasurements(ctx, conf.Image, conf.GetProvider(), conf.GetAttestationConfig().GetVariant()) if err != nil { - return fmt.Errorf("creating cosign verifier: %w", err) - } - - var fetchedMeasurements measurements.M - var hash string - if cfm.flags.insecure { - if err := fetchedMeasurements.FetchNoVerify( - ctx, - client, - cfm.flags.measurementsURL, - imageVersion, - conf.GetProvider(), - conf.GetAttestationConfig().GetVariant(), - ); err != nil { - return fmt.Errorf("fetching measurements without verification: %w", err) - } - - cfm.log.Debugf("Fetched measurements without verification") - } else { - hash, err = fetchedMeasurements.FetchAndVerify( - ctx, - client, - cosign, - cfm.flags.measurementsURL, - cfm.flags.signatureURL, - imageVersion, - conf.GetProvider(), - conf.GetAttestationConfig().GetVariant(), - ) - if err != nil { - return fmt.Errorf("fetching and verifying measurements: %w", err) - } - cfm.log.Debugf("Fetched and verified measurements, hash is %s", hash) - if err := sigstore.VerifyWithRekor(cmd.Context(), publicKey, rekor, hash); err != nil { + if _, ok := err.(measurements.ErrRekor); ok { cmd.PrintErrf("Ignoring Rekor related error: %v\n", err) cmd.PrintErrln("Make sure the downloaded measurements are trustworthy!") + } else { + return fmt.Errorf("fetching and verifying measurements: %w", err) } - - cfm.log.Debugf("Verified measurements with Rekor") } cfm.log.Debugf("Measurements:\n", fetchedMeasurements) diff --git a/internal/attestation/measurements/BUILD.bazel b/internal/attestation/measurements/BUILD.bazel index 07660450d2b..c1970544171 100644 --- a/internal/attestation/measurements/BUILD.bazel +++ b/internal/attestation/measurements/BUILD.bazel @@ -4,6 +4,7 @@ load("//bazel/go:go_test.bzl", "go_test") go_library( name = "measurements", srcs = [ + "fetchmeasurements.go", "measurements.go", # keep "measurements_enterprise.go", @@ -16,6 +17,8 @@ go_library( "//internal/api/versionsapi", "//internal/attestation/variant", "//internal/cloud/cloudprovider", + "//internal/sigstore", + "//internal/sigstore/keyselect", "@com_github_google_go_tpm//tpmutil", "@com_github_siderolabs_talos_pkg_machinery//config/encoder", "@in_gopkg_yaml_v3//:yaml_v3", diff --git a/internal/attestation/measurements/fetchmeasurements.go b/internal/attestation/measurements/fetchmeasurements.go new file mode 100644 index 00000000000..cbc6c94c924 --- /dev/null +++ b/internal/attestation/measurements/fetchmeasurements.go @@ -0,0 +1,123 @@ +/* +Copyright (c) Edgeless Systems GmbH + +SPDX-License-Identifier: AGPL-3.0-only +*/ + +package measurements + +import ( + "context" + "fmt" + "net/http" + + "github.com/edgelesssys/constellation/v2/internal/api/versionsapi" + "github.com/edgelesssys/constellation/v2/internal/attestation/variant" + "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" + "github.com/edgelesssys/constellation/v2/internal/sigstore" + "github.com/edgelesssys/constellation/v2/internal/sigstore/keyselect" +) + +// ErrRekor is returned when verifying measurements with Rekor fails. +type ErrRekor struct { + error +} + +func newRekorErr(err error) error { + return ErrRekor{ + error: fmt.Errorf("verifying measurements with Rekor: %w", err), + } +} + +type cosignVerifierConstructor func([]byte) (sigstore.Verifier, error) + +// VerifyFetcher is a high-level fetcher that fetches measurements and verifies them. +type VerifyFetcher struct { + client *http.Client + newCosignVerifier cosignVerifierConstructor + rekor rekorVerifier + noVerify bool // do not verify measurements +} + +// NewVerifyFetcher creates a new MeasurementFetcher. +func NewVerifyFetcher(client *http.Client) (*VerifyFetcher, error) { + rekor, err := sigstore.NewRekor() + if err != nil { + return nil, fmt.Errorf("constructing Rekor client: %w", err) + } + return &VerifyFetcher{ + newCosignVerifier: sigstore.NewCosignVerifier, + rekor: rekor, + client: client, + }, nil +} + +type rekorVerifier interface { + SearchByHash(context.Context, string) ([]string, error) + VerifyEntry(context.Context, string, string) error +} + +// NewVerifyFetcherWith creates a new MeasurementFetcher with custom clients. +func NewVerifyFetcherWith(newCosignVerifier func([]byte) (sigstore.Verifier, error), noVerify bool, rekor rekorVerifier, client *http.Client) *VerifyFetcher { + return &VerifyFetcher{ + newCosignVerifier: newCosignVerifier, + rekor: rekor, + client: client, + noVerify: noVerify, + } +} + +// FetchAndVerifyMeasurements fetches and verifies measurements for the given version and attestation variant. +func (m *VerifyFetcher) FetchAndVerifyMeasurements(ctx context.Context, + image string, csp cloudprovider.Provider, attestationVariant variant.Variant, +) (M, error) { + version, err := versionsapi.NewVersionFromShortPath(image, versionsapi.VersionKindImage) + if err != nil { + return nil, fmt.Errorf("parsing image version: %w", err) + } + publicKey, err := keyselect.CosignPublicKeyForVersion(version) + if err != nil { + return nil, fmt.Errorf("getting public key: %w", err) + } + + cosign, err := m.newCosignVerifier(publicKey) + if err != nil { + return nil, fmt.Errorf("creating cosign verifier: %w", err) + } + + measurementsURL, signatureURL, err := versionsapi.MeasurementURL(version) + if err != nil { + return nil, err + } + var fetchedMeasurements M + if m.noVerify { + if err := fetchedMeasurements.FetchNoVerify( + ctx, + m.client, + measurementsURL, + version, + csp, + attestationVariant, + ); err != nil { + return nil, fmt.Errorf("fetching measurements: %w", err) + } + } else { + hash, err := fetchedMeasurements.FetchAndVerify( + ctx, + m.client, + cosign, + measurementsURL, + signatureURL, + version, + csp, + attestationVariant, + ) + if err != nil { + return nil, fmt.Errorf("fetching and verifying measurements: %w", err) + } + if err := sigstore.VerifyWithRekor(ctx, publicKey, m.rekor, hash); err != nil { + return nil, newRekorErr(err) + } + } + return fetchedMeasurements, nil +} diff --git a/internal/attestation/measurements/measurements_test.go b/internal/attestation/measurements/measurements_test.go index 73cee747959..f0793158277 100644 --- a/internal/attestation/measurements/measurements_test.go +++ b/internal/attestation/measurements/measurements_test.go @@ -10,6 +10,7 @@ import ( "bytes" "context" "encoding/json" + "errors" "io" "net/http" "net/url" @@ -1043,3 +1044,9 @@ func TestMeasurementsCompare(t *testing.T) { }) } } + +func TestRekorErrCheck(t *testing.T) { + err := newRekorErr(errors.New("test")) + _, ok := err.(ErrRekor) + assert.True(t, ok) +} diff --git a/internal/config/azure.go b/internal/config/azure.go index 5c88cd90a18..473dcd5cfee 100644 --- a/internal/config/azure.go +++ b/internal/config/azure.go @@ -11,25 +11,24 @@ import ( "fmt" "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi" + "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/cloud/cloudprovider" - "github.com/edgelesssys/constellation/v2/internal/constellation" ) // DefaultForAzureSEVSNP returns the default configuration for Azure SEV-SNP attestation. // Version numbers have placeholder values and the latest available values can be fetched using [AzureSEVSNP.FetchAndSetLatestVersionNumbers]. func DefaultForAzureSEVSNP() *AzureSEVSNP { - azureFirmwareSignerCfg := constellation.DefaultAzureSEVSNPFirmwareSignerConfig() return &AzureSEVSNP{ Measurements: measurements.DefaultsFor(cloudprovider.Azure, variant.AzureSEVSNP{}), BootloaderVersion: NewLatestPlaceholderVersion(), TEEVersion: NewLatestPlaceholderVersion(), SNPVersion: NewLatestPlaceholderVersion(), MicrocodeVersion: NewLatestPlaceholderVersion(), - FirmwareSignerConfig: SNPFirmwareSignerConfig{ // need to duplicate the struct here to generate docs - AcceptedKeyDigests: azureFirmwareSignerCfg.AcceptedKeyDigests, - EnforcementPolicy: azureFirmwareSignerCfg.EnforcementPolicy, + FirmwareSignerConfig: SNPFirmwareSignerConfig{ + AcceptedKeyDigests: idkeydigest.DefaultList(), + EnforcementPolicy: idkeydigest.MAAFallback, }, // AMD root key. Received from the AMD Key Distribution System API (KDS). AMDRootKey: mustParsePEM(`-----BEGIN CERTIFICATE-----\nMIIGYzCCBBKgAwIBAgIDAQAAMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC\nBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS\nBgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg\nQ2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp\nY2VzMRIwEAYDVQQDDAlBUkstTWlsYW4wHhcNMjAxMDIyMTcyMzA1WhcNNDUxMDIy\nMTcyMzA1WjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS\nBgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j\nZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJQVJLLU1pbGFuMIICIjANBgkqhkiG\n9w0BAQEFAAOCAg8AMIICCgKCAgEA0Ld52RJOdeiJlqK2JdsVmD7FktuotWwX1fNg\nW41XY9Xz1HEhSUmhLz9Cu9DHRlvgJSNxbeYYsnJfvyjx1MfU0V5tkKiU1EesNFta\n1kTA0szNisdYc9isqk7mXT5+KfGRbfc4V/9zRIcE8jlHN61S1ju8X93+6dxDUrG2\nSzxqJ4BhqyYmUDruPXJSX4vUc01P7j98MpqOS95rORdGHeI52Naz5m2B+O+vjsC0\n60d37jY9LFeuOP4Meri8qgfi2S5kKqg/aF6aPtuAZQVR7u3KFYXP59XmJgtcog05\ngmI0T/OitLhuzVvpZcLph0odh/1IPXqx3+MnjD97A7fXpqGd/y8KxX7jksTEzAOg\nbKAeam3lm+3yKIcTYMlsRMXPcjNbIvmsBykD//xSniusuHBkgnlENEWx1UcbQQrs\n+gVDkuVPhsnzIRNgYvM48Y+7LGiJYnrmE8xcrexekBxrva2V9TJQqnN3Q53kt5vi\nQi3+gCfmkwC0F0tirIZbLkXPrPwzZ0M9eNxhIySb2npJfgnqz55I0u33wh4r0ZNQ\neTGfw03MBUtyuzGesGkcw+loqMaq1qR4tjGbPYxCvpCq7+OgpCCoMNit2uLo9M18\nfHz10lOMT8nWAUvRZFzteXCm+7PHdYPlmQwUw3LvenJ/ILXoQPHfbkH0CyPfhl1j\nWhJFZasCAwEAAaN+MHwwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSFrBrRQ/fI\nrFXUxR1BSKvVeErUUzAPBgNVHRMBAf8EBTADAQH/MDoGA1UdHwQzMDEwL6AtoCuG\nKWh0dHBzOi8va2RzaW50Zi5hbWQuY29tL3ZjZWsvdjEvTWlsYW4vY3JsMEYGCSqG\nSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZI\nAWUDBAICBQCiAwIBMKMDAgEBA4ICAQC6m0kDp6zv4Ojfgy+zleehsx6ol0ocgVel\nETobpx+EuCsqVFRPK1jZ1sp/lyd9+0fQ0r66n7kagRk4Ca39g66WGTJMeJdqYriw\nSTjjDCKVPSesWXYPVAyDhmP5n2v+BYipZWhpvqpaiO+EGK5IBP+578QeW/sSokrK\ndHaLAxG2LhZxj9aF73fqC7OAJZ5aPonw4RE299FVarh1Tx2eT3wSgkDgutCTB1Yq\nzT5DuwvAe+co2CIVIzMDamYuSFjPN0BCgojl7V+bTou7dMsqIu/TW/rPCX9/EUcp\nKGKqPQ3P+N9r1hjEFY1plBg93t53OOo49GNI+V1zvXPLI6xIFVsh+mto2RtgEX/e\npmMKTNN6psW88qg7c1hTWtN6MbRuQ0vm+O+/2tKBF2h8THb94OvvHHoFDpbCELlq\nHnIYhxy0YKXGyaW1NjfULxrrmxVW4wcn5E8GddmvNa6yYm8scJagEi13mhGu4Jqh\n3QU3sf8iUSUr09xQDwHtOQUVIqx4maBZPBtSMf+qUDtjXSSq8lfWcd8bLr9mdsUn\nJZJ0+tuPMKmBnSH860llKk+VpVQsgqbzDIvOLvD6W1Umq25boxCYJ+TuBoa4s+HH\nCViAvgT9kf/rBq1d+ivj6skkHxuzcxbk1xv6ZGxrteJxVH7KlX7YRdZ6eARKwLe4\nAFZEAwoKCQ==\n-----END CERTIFICATE-----\n`), diff --git a/internal/constellation/azureattestation.go b/internal/constellation/azureattestation.go deleted file mode 100644 index fd71e1627eb..00000000000 --- a/internal/constellation/azureattestation.go +++ /dev/null @@ -1,105 +0,0 @@ -/* -Copyright (c) Edgeless Systems GmbH - -SPDX-License-Identifier: AGPL-3.0-only -*/ - -package constellation - -import ( - "crypto/x509" - "encoding/json" - "encoding/pem" - "fmt" - - "github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest" -) - -// DefaultAzureSEVSNPFirmwareSignerConfig returns the default configuration for validating the firmware signer. -func DefaultAzureSEVSNPFirmwareSignerConfig() SNPFirmwareSignerConfig { - return SNPFirmwareSignerConfig{ - AcceptedKeyDigests: idkeydigest.DefaultList(), - EnforcementPolicy: idkeydigest.MAAFallback, - } -} - -// AzureSEVSNPAMDRootKey contains the AMD root key. -// TODO avoid duplication with CLI cfg. -func AzureSEVSNPAMDRootKey() Certificate { - return mustParsePEM(`-----BEGIN CERTIFICATE-----\nMIIGYzCCBBKgAwIBAgIDAQAAMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC\nBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS\nBgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg\nQ2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp\nY2VzMRIwEAYDVQQDDAlBUkstTWlsYW4wHhcNMjAxMDIyMTcyMzA1WhcNNDUxMDIy\nMTcyMzA1WjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS\nBgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j\nZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJQVJLLU1pbGFuMIICIjANBgkqhkiG\n9w0BAQEFAAOCAg8AMIICCgKCAgEA0Ld52RJOdeiJlqK2JdsVmD7FktuotWwX1fNg\nW41XY9Xz1HEhSUmhLz9Cu9DHRlvgJSNxbeYYsnJfvyjx1MfU0V5tkKiU1EesNFta\n1kTA0szNisdYc9isqk7mXT5+KfGRbfc4V/9zRIcE8jlHN61S1ju8X93+6dxDUrG2\nSzxqJ4BhqyYmUDruPXJSX4vUc01P7j98MpqOS95rORdGHeI52Naz5m2B+O+vjsC0\n60d37jY9LFeuOP4Meri8qgfi2S5kKqg/aF6aPtuAZQVR7u3KFYXP59XmJgtcog05\ngmI0T/OitLhuzVvpZcLph0odh/1IPXqx3+MnjD97A7fXpqGd/y8KxX7jksTEzAOg\nbKAeam3lm+3yKIcTYMlsRMXPcjNbIvmsBykD//xSniusuHBkgnlENEWx1UcbQQrs\n+gVDkuVPhsnzIRNgYvM48Y+7LGiJYnrmE8xcrexekBxrva2V9TJQqnN3Q53kt5vi\nQi3+gCfmkwC0F0tirIZbLkXPrPwzZ0M9eNxhIySb2npJfgnqz55I0u33wh4r0ZNQ\neTGfw03MBUtyuzGesGkcw+loqMaq1qR4tjGbPYxCvpCq7+OgpCCoMNit2uLo9M18\nfHz10lOMT8nWAUvRZFzteXCm+7PHdYPlmQwUw3LvenJ/ILXoQPHfbkH0CyPfhl1j\nWhJFZasCAwEAAaN+MHwwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSFrBrRQ/fI\nrFXUxR1BSKvVeErUUzAPBgNVHRMBAf8EBTADAQH/MDoGA1UdHwQzMDEwL6AtoCuG\nKWh0dHBzOi8va2RzaW50Zi5hbWQuY29tL3ZjZWsvdjEvTWlsYW4vY3JsMEYGCSqG\nSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZI\nAWUDBAICBQCiAwIBMKMDAgEBA4ICAQC6m0kDp6zv4Ojfgy+zleehsx6ol0ocgVel\nETobpx+EuCsqVFRPK1jZ1sp/lyd9+0fQ0r66n7kagRk4Ca39g66WGTJMeJdqYriw\nSTjjDCKVPSesWXYPVAyDhmP5n2v+BYipZWhpvqpaiO+EGK5IBP+578QeW/sSokrK\ndHaLAxG2LhZxj9aF73fqC7OAJZ5aPonw4RE299FVarh1Tx2eT3wSgkDgutCTB1Yq\nzT5DuwvAe+co2CIVIzMDamYuSFjPN0BCgojl7V+bTou7dMsqIu/TW/rPCX9/EUcp\nKGKqPQ3P+N9r1hjEFY1plBg93t53OOo49GNI+V1zvXPLI6xIFVsh+mto2RtgEX/e\npmMKTNN6psW88qg7c1hTWtN6MbRuQ0vm+O+/2tKBF2h8THb94OvvHHoFDpbCELlq\nHnIYhxy0YKXGyaW1NjfULxrrmxVW4wcn5E8GddmvNa6yYm8scJagEi13mhGu4Jqh\n3QU3sf8iUSUr09xQDwHtOQUVIqx4maBZPBtSMf+qUDtjXSSq8lfWcd8bLr9mdsUn\nJZJ0+tuPMKmBnSH860llKk+VpVQsgqbzDIvOLvD6W1Umq25boxCYJ+TuBoa4s+HH\nCViAvgT9kf/rBq1d+ivj6skkHxuzcxbk1xv6ZGxrteJxVH7KlX7YRdZ6eARKwLe4\nAFZEAwoKCQ==\n-----END CERTIFICATE-----\n`) -} - -func mustParsePEM(data string) Certificate { - jsonData := fmt.Sprintf("\"%s\"", data) - var cert Certificate - if err := json.Unmarshal([]byte(jsonData), &cert); err != nil { - panic(err) - } - return cert -} - -// Certificate is a wrapper around x509.Certificate allowing custom marshaling. -type Certificate x509.Certificate - -// MarshalJSON marshals the certificate to PEM. -func (c Certificate) MarshalJSON() ([]byte, error) { - if len(c.Raw) == 0 { - return json.Marshal(new(string)) - } - pem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: c.Raw}) - return json.Marshal(string(pem)) -} - -// MarshalYAML marshals the certificate to PEM. -func (c Certificate) MarshalYAML() (any, error) { - if len(c.Raw) == 0 { - return "", nil - } - pem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: c.Raw}) - return string(pem), nil -} - -// UnmarshalJSON unmarshals the certificate from PEM. -func (c *Certificate) UnmarshalJSON(data []byte) error { - if len(data) == 0 { - return nil - } - return c.unmarshal(func(val any) error { - return json.Unmarshal(data, val) - }) -} - -// UnmarshalYAML unmarshals the certificate from PEM. -func (c *Certificate) UnmarshalYAML(unmarshal func(any) error) error { - return c.unmarshal(unmarshal) -} - -func (c *Certificate) unmarshal(unmarshalFunc func(any) error) error { - var pemData string - if err := unmarshalFunc(&pemData); err != nil { - return err - } - if pemData == "" { - return nil - } - block, _ := pem.Decode([]byte(pemData)) - cert, err := x509.ParseCertificate(block.Bytes) - if err != nil { - return err - } - *c = Certificate(*cert) - return nil -} - -// SNPFirmwareSignerConfig is the configuration for validating the firmware signer. -type SNPFirmwareSignerConfig struct { - // description: | - // List of accepted values for the firmware signing key digest.\nValues are enforced according to the 'enforcementPolicy'\n - 'equal' : Error if the reported signing key digest does not match any of the values in 'acceptedKeyDigests'\n - 'maaFallback' : Use 'equal' checking for validation, but fallback to using Microsoft Azure Attestation (MAA) for validation if the reported digest does not match any of the values in 'acceptedKeyDigests'. See the Azure docs for more details: https://learn.microsoft.com/en-us/azure/attestation/overview#amd-sev-snp-attestation\n - 'warnOnly' : Same as 'equal', but only prints a warning instead of returning an error if no match is found - AcceptedKeyDigests idkeydigest.List `json:"acceptedKeyDigests" yaml:"acceptedKeyDigests" tfsdk:"accepted_key_digests"` - // description: | - // Key digest enforcement policy. One of {'equal', 'maaFallback', 'warnOnly'} - EnforcementPolicy idkeydigest.Enforcement `json:"enforcementPolicy" yaml:"enforcementPolicy" validate:"required" tfsdk:"enforcement_policy"` - // description: | - // URL of the Microsoft Azure Attestation (MAA) instance to use for fallback validation. Only used if 'enforcementPolicy' is set to 'maaFallback'. - MAAURL string `json:"maaURL,omitempty" yaml:"maaURL,omitempty" validate:"len=0" tfsdk:"maa_url"` -} diff --git a/internal/constellation/fetchmeasurements.go b/internal/constellation/fetchmeasurements.go deleted file mode 100644 index 62f09e5de9c..00000000000 --- a/internal/constellation/fetchmeasurements.go +++ /dev/null @@ -1,70 +0,0 @@ -/* -Copyright (c) Edgeless Systems GmbH - -SPDX-License-Identifier: AGPL-3.0-only -*/ - -package constellation - -import ( - "context" - "fmt" - "net/http" - - "github.com/edgelesssys/constellation/v2/internal/api/versionsapi" - "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/sigstore" - "github.com/edgelesssys/constellation/v2/internal/sigstore/keyselect" -) - -// TODO use same in the CLI -// FetchMeasurements fetches measurements for the given version and attestation variant. -func FetchMeasurements(ctx context.Context, client *http.Client, - image string, csp cloudprovider.Provider, attestationVariant variant.Variant, -) (measurements.M, error) { - version, err := versionsapi.NewVersionFromShortPath(image, versionsapi.VersionKindImage) - if err != nil { - return nil, fmt.Errorf("parsing image version: %w", err) - } - publicKey, err := keyselect.CosignPublicKeyForVersion(version) - if err != nil { - return nil, fmt.Errorf("getting public key: %w", err) - } - cosign, err := sigstore.NewCosignVerifier(publicKey) - if err != nil { - return nil, fmt.Errorf("creating cosign verifier: %w", err) - } - rekor, err := sigstore.NewRekor() - if err != nil { - return nil, fmt.Errorf("constructing Rekor client: %w", err) - } - - measurementsURL, signatureURL, err := versionsapi.MeasurementURL(version) - if err != nil { - return nil, err - } - var fetchedMeasurements measurements.M - var hash string - hash, err = fetchedMeasurements.FetchAndVerify( - ctx, - client, - cosign, - measurementsURL, - signatureURL, - version, - csp, - attestationVariant, - ) - if err != nil { - return nil, fmt.Errorf("fetching and verifying measurements: %w", err) - } - // cfm.log.Debugf("Fetched and verified measurements, hash is %s", hash) - if err := sigstore.VerifyWithRekor(ctx, publicKey, rekor, hash); err != nil { - return nil, fmt.Errorf("verifying measurements with Rekor: %w", err) // TODO changed behavior - // cmd.PrintErrf("Ignoring Rekor related error: %v\n", err) - // cmd.PrintErrln("Make sure the downloaded measurements are trustworthy!") - } - return fetchedMeasurements, nil -} diff --git a/terraform-provider-constellation/examples/data-sources/attestation_example/data-source.tf b/terraform-provider-constellation/examples/data-sources/attestation_example/data-source.tf index 6ab7e60f067..8308418e652 100644 --- a/terraform-provider-constellation/examples/data-sources/attestation_example/data-source.tf +++ b/terraform-provider-constellation/examples/data-sources/attestation_example/data-source.tf @@ -10,9 +10,9 @@ provider "constellation" { } data "constellation_attestation" "test" { - csp = "azure" - attestation_variant = "azure-sev-snp" - image_version = "v2.13.0" + csp = "azure" + attestation_variant = "azure-sev-snp" + image_version = "v2.13.0" } output "measurements" { diff --git a/terraform-provider-constellation/go.mod b/terraform-provider-constellation/go.mod index 64c97bba62b..93c3686ebe9 100644 --- a/terraform-provider-constellation/go.mod +++ b/terraform-provider-constellation/go.mod @@ -21,6 +21,7 @@ require ( github.com/agext/levenshtein v1.2.2 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect + github.com/aws/aws-sdk-go v1.44.297 // indirect github.com/aws/aws-sdk-go-v2 v1.18.1 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect github.com/aws/aws-sdk-go-v2/config v1.18.27 // indirect @@ -65,6 +66,7 @@ require ( github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-containerregistry v0.15.2 // indirect + github.com/google/go-tpm v0.9.0 // indirect github.com/google/uuid v1.4.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect @@ -112,6 +114,7 @@ require ( github.com/sassoftware/relic v7.2.1+incompatible // indirect github.com/schollz/progressbar/v3 v3.13.1 // indirect github.com/secure-systems-lab/go-securesystemslib v0.6.0 // indirect + github.com/siderolabs/talos/pkg/machinery v1.4.6 // indirect github.com/sigstore/rekor v1.2.2 // indirect github.com/sigstore/sigstore v1.7.1 // indirect github.com/spf13/afero v1.10.0 // indirect diff --git a/terraform-provider-constellation/go.sum b/terraform-provider-constellation/go.sum index 1eb64707693..35f496c5baf 100644 --- a/terraform-provider-constellation/go.sum +++ b/terraform-provider-constellation/go.sum @@ -60,6 +60,8 @@ github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmms github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/aws/aws-sdk-go v1.44.297 h1:uL4EV0gQxotQVYegIoBqK079328MOJqgG95daFYSkAM= +github.com/aws/aws-sdk-go v1.44.297/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-sdk-go-v2 v1.18.1 h1:+tefE750oAb7ZQGzla6bLkOwfcQCEtC5y2RqoqCeqKo= github.com/aws/aws-sdk-go-v2 v1.18.1/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 h1:dK82zF6kkPeCo8J1e+tGx4JdvDIQzj7ygIoLg8WMuGs= @@ -291,6 +293,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-containerregistry v0.15.2 h1:MMkSh+tjSdnmJZO7ljvEqV1DjfekB6VUEAZgy3a+TQE= github.com/google/go-containerregistry v0.15.2/go.mod h1:wWK+LnOv4jXMM23IT/F1wdYftGWGr47Is8CG+pmHK1Q= +github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk= +github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -689,6 +693,7 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= @@ -765,6 +770,7 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -773,6 +779,7 @@ golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= diff --git a/terraform-provider-constellation/internal/provider/BUILD.bazel b/terraform-provider-constellation/internal/provider/BUILD.bazel index bc72f0c9b3d..f4e1c9cb618 100644 --- a/terraform-provider-constellation/internal/provider/BUILD.bazel +++ b/terraform-provider-constellation/internal/provider/BUILD.bazel @@ -4,6 +4,7 @@ load("//bazel/go:go_test.bzl", "go_test") go_library( name = "provider", srcs = [ + "attestation_data_source.go", "example_resource.go", "image_data_source.go", "provider.go", @@ -11,8 +12,11 @@ go_library( importpath = "github.com/edgelesssys/constellation/v2/terraform-provider-constellation/internal/provider", visibility = ["//terraform-provider-constellation:__subpackages__"], deps = [ + "//internal/api/attestationconfigapi", + "//internal/attestation/measurements", "//internal/attestation/variant", "//internal/cloud/cloudprovider", + "//internal/config", "//internal/imagefetcher", "//terraform-provider-constellation/internal/data", "@com_github_hashicorp_terraform_plugin_framework//datasource", @@ -35,6 +39,7 @@ go_library( go_test( name = "provider_test", srcs = [ + "attestation_data_source_test.go", "image_data_source_test.go", "provider_test.go", ], diff --git a/terraform-provider-constellation/internal/provider/attestation_data_source.go b/terraform-provider-constellation/internal/provider/attestation_data_source.go index d98d7d2d13d..079b6efbc76 100644 --- a/terraform-provider-constellation/internal/provider/attestation_data_source.go +++ b/terraform-provider-constellation/internal/provider/attestation_data_source.go @@ -17,10 +17,12 @@ import ( "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/constellation" + "github.com/edgelesssys/constellation/v2/internal/config" + "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" ) @@ -35,7 +37,8 @@ func NewAttestationDataSource() datasource.DataSource { // AttestationDataSource defines the data source implementation. type AttestationDataSource struct { - client *http.Client + client *http.Client + fetcher attestationconfigapi.Fetcher } // AttestationDataSourceModel describes the data source data model. @@ -49,6 +52,12 @@ type AttestationDataSourceModel struct { Attestation types.Object `tfsdk:"attestation"` } +// Configure configures the data source. +func (d *AttestationDataSource) Configure(_ context.Context, _ datasource.ConfigureRequest, _ *datasource.ConfigureResponse) { + d.client = http.DefaultClient + d.fetcher = attestationconfigapi.NewFetcher() +} + // Metadata returns the metadata for the data source. func (d *AttestationDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { resp.TypeName = req.ProviderTypeName + "_attestation" @@ -62,12 +71,25 @@ func (d *AttestationDataSource) Schema(_ context.Context, _ datasource.SchemaReq Attributes: map[string]schema.Attribute{ "csp": schema.StringAttribute{ - MarkdownDescription: "The cloud service provider to use", - Required: true, + 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{ - MarkdownDescription: "The attestation variant to use", - Required: true, + 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"), + }, }, "image_version": schema.StringAttribute{ MarkdownDescription: "The image version to use", @@ -134,27 +156,6 @@ func (d *AttestationDataSource) Schema(_ context.Context, _ datasource.SchemaReq } } -// Configure configures the data source. -func (d *AttestationDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { - // Prevent panic if the provider has not been configured. - if req.ProviderData == nil { - return - } - - client, ok := req.ProviderData.(*http.Client) - - if !ok { - resp.Diagnostics.AddError( - "Unexpected Data Source Configure Type", - fmt.Sprintf("Expected *http.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), - ) - - return - } - - d.client = client -} - // Read reads from the data source. func (d *AttestationDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { var data AttestationDataSourceModel @@ -171,86 +172,79 @@ func (d *AttestationDataSource) Read(ctx context.Context, req datasource.ReadReq resp.Diagnostics.AddError("Unknown CSP", fmt.Sprintf("Unknown CSP: %s", data.CSP.ValueString())) return } - if csp == cloudprovider.Azure { - fetcher := attestationconfigapi.NewFetcher() - snpVersions, err := fetcher.FetchAzureSEVSNPVersionLatest(ctx) + attestationVariant, err := variant.FromString(data.AttestationVariant.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Unknown Attestation Variant", fmt.Sprintf("Unknown Attestation Variant: %s", data.AttestationVariant.ValueString())) + return + } + if csp == cloudprovider.Azure && attestationVariant.Equal(variant.AzureSEVSNP{}) { + snpVersions, err := d.fetcher.FetchAzureSEVSNPVersionLatest(ctx) if err != nil { resp.Diagnostics.AddError("Fetching SNP Version numbers", err.Error()) return } - - cert, err := constellation.AzureSEVSNPAMDRootKey().MarshalJSON() - if err != nil { - resp.Diagnostics.AddError("Marshalling AMD Root Key", err.Error()) - } - firmwareCfg := constellation.DefaultAzureSEVSNPFirmwareSignerConfig() - keyDigestAny, err := firmwareCfg.AcceptedKeyDigests.MarshalYAML() - if err != nil { - resp.Diagnostics.AddError("Marshalling Accepted Key Digests", err.Error()) - } - keyDigest := keyDigestAny.([]string) - tfSnpVersions := sevSnpAttestation{ - BootloaderVersion: snpVersions.Bootloader, - TEEVersion: snpVersions.TEE, - SNPVersion: snpVersions.SNP, - MicrocodeVersion: snpVersions.Microcode, - AMDRootKey: string(cert), - SNPFirmwareSignerConfig: snpFirmwareSignerConfig{ - AcceptedKeyDigests: keyDigest, - EnforcementPolicy: firmwareCfg.EnforcementPolicy.String(), - MAAURL: firmwareCfg.MAAURL, - }, - } + tfSnpVersions := convertSNPAttestationTfStateCompatible(resp, snpVersions) diags := resp.State.SetAttribute(ctx, path.Root("attestation"), tfSnpVersions) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } - } - variant, err := variant.FromString(data.AttestationVariant.ValueString()) + // TODO align with reenable AWS PR + if attestationVariant.Equal(variant.AWSSEVSNP{}) { + resp.Diagnostics.AddWarning("AWS attestation not supported", "AWS is not supported yet") + } + + verifyFetcher, err := measurements.NewVerifyFetcher(d.client) if err != nil { - resp.Diagnostics.AddError("Unknown Attestation Variant", fmt.Sprintf("Unknown Attestation Variant: %s", data.AttestationVariant.ValueString())) + resp.Diagnostics.AddError("creating verify fetcher", fmt.Sprintf("Error creating verify fetcher: %s", err)) return } - measurements, err := constellation.FetchMeasurements(ctx, http.DefaultClient, data.ImageVersion.ValueString(), csp, variant) + fetchedMeasurements, err := verifyFetcher.FetchAndVerifyMeasurements(ctx, data.ImageVersion.ValueString(), csp, attestationVariant) if err != nil { - resp.Diagnostics.AddError("Error fetching measurements", fmt.Sprintf("Error fetching measurements: %s", err)) - return + if _, ok := err.(measurements.ErrRekor); ok { + resp.Diagnostics.AddWarning("Ignoring Rekor related error", err.Error()) + } else { + resp.Diagnostics.AddError("fetching and verifying measurements", err.Error()) + return + } } - - tfMeasurements := convertMeasurementsToTfState(measurements) - + tfMeasurements := convertMeasurementsTfStateCompatible(fetchedMeasurements) diags := resp.State.SetAttribute(ctx, path.Root("measurements"), tfMeasurements) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } - //if err != nil { - // // Handle error - //} - //diags. - //types.MapValue(types.StringType, elements) - - // resp.Diagnostics.Append(resp.State.Set(ctx, &measurements)...) - // If applicable, this is a great opportunity to initialize any necessary - // provider client data and make a call using it. - // httpResp, err := d.client.Do(httpReq) - // if err != nil { - // resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read example, got error: %s", err)) - // return - // } - - // For the purposes of this example code, hardcoding a response value to - // save into the Terraform state. - // data.ID = types.StringValue("example-id") - - // Write logs using the tflog package - // Documentation: https://terraform.io/plugin/log tflog.Trace(ctx, "read constellation attestation data source") } -func convertMeasurementsToTfState(m measurements.M) map[string]measurement { +func convertSNPAttestationTfStateCompatible(resp *datasource.ReadResponse, snpVersions attestationconfigapi.AzureSEVSNPVersionAPI) sevSnpAttestation { + cert, err := config.DefaultForAzureSEVSNP().AMDRootKey.MarshalJSON() + if err != nil { + resp.Diagnostics.AddError("Marshalling AMD Root Key", err.Error()) + } + firmwareCfg := config.DefaultForAzureSEVSNP().FirmwareSignerConfig + keyDigestAny, err := firmwareCfg.AcceptedKeyDigests.MarshalYAML() + if err != nil { + resp.Diagnostics.AddError("Marshalling Accepted Key Digests", err.Error()) + } + keyDigest := keyDigestAny.([]string) + tfSnpVersions := sevSnpAttestation{ + BootloaderVersion: snpVersions.Bootloader, + TEEVersion: snpVersions.TEE, + SNPVersion: snpVersions.SNP, + MicrocodeVersion: snpVersions.Microcode, + AMDRootKey: string(cert), + SNPFirmwareSignerConfig: snpFirmwareSignerConfig{ + AcceptedKeyDigests: keyDigest, + EnforcementPolicy: firmwareCfg.EnforcementPolicy.String(), + MAAURL: firmwareCfg.MAAURL, + }, + } + return tfSnpVersions +} + +func convertMeasurementsTfStateCompatible(m measurements.M) map[string]measurement { tfMeasurements := map[string]measurement{} for key, value := range m { keyStr := strconv.FormatUint(uint64(key), 10) 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 6297bdefe14..a5f72f18701 100644 --- a/terraform-provider-constellation/internal/provider/attestation_data_source_test.go +++ b/terraform-provider-constellation/internal/provider/attestation_data_source_test.go @@ -19,7 +19,13 @@ func TestAccAttestationSource(t *testing.T) { Steps: []resource.TestStep{ // Read testing { - Config: testingConfig + testAccAttestationDataSourceConfig, + Config: testingConfig + ` + data "constellation_attestation" "test" { + csp = "azure" + attestation_variant = "azure-sev-snp" + image_version = "v2.13.0" + } + `, Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("data.constellation_attestation.test", "attestation.bootloader_version", "3"), resource.TestCheckResourceAttr("data.constellation_attestation.test", "attestation.microcode_version", "115"), @@ -28,19 +34,24 @@ func TestAccAttestationSource(t *testing.T) { resource.TestCheckResourceAttr("data.constellation_attestation.test", "attestation.firmware_signer_config.accepted_key_digests.0", "0356215882a825279a85b300b0b742931d113bf7e32dde2e50ffde7ec743ca491ecdd7f336dc28a6e0b2bb57af7a44a3"), resource.TestCheckResourceAttr("data.constellation_attestation.test", "attestation.firmware_signer_config.enforcement_policy", "MAAFallback"), resource.TestCheckResourceAttr("data.constellation_attestation.test", "attestation.amd_root_key", "\"-----BEGIN CERTIFICATE-----\\nMIIGYzCCBBKgAwIBAgIDAQAAMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC\\nBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS\\nBgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg\\nQ2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp\\nY2VzMRIwEAYDVQQDDAlBUkstTWlsYW4wHhcNMjAxMDIyMTcyMzA1WhcNNDUxMDIy\\nMTcyMzA1WjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS\\nBgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j\\nZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJQVJLLU1pbGFuMIICIjANBgkqhkiG\\n9w0BAQEFAAOCAg8AMIICCgKCAgEA0Ld52RJOdeiJlqK2JdsVmD7FktuotWwX1fNg\\nW41XY9Xz1HEhSUmhLz9Cu9DHRlvgJSNxbeYYsnJfvyjx1MfU0V5tkKiU1EesNFta\\n1kTA0szNisdYc9isqk7mXT5+KfGRbfc4V/9zRIcE8jlHN61S1ju8X93+6dxDUrG2\\nSzxqJ4BhqyYmUDruPXJSX4vUc01P7j98MpqOS95rORdGHeI52Naz5m2B+O+vjsC0\\n60d37jY9LFeuOP4Meri8qgfi2S5kKqg/aF6aPtuAZQVR7u3KFYXP59XmJgtcog05\\ngmI0T/OitLhuzVvpZcLph0odh/1IPXqx3+MnjD97A7fXpqGd/y8KxX7jksTEzAOg\\nbKAeam3lm+3yKIcTYMlsRMXPcjNbIvmsBykD//xSniusuHBkgnlENEWx1UcbQQrs\\n+gVDkuVPhsnzIRNgYvM48Y+7LGiJYnrmE8xcrexekBxrva2V9TJQqnN3Q53kt5vi\\nQi3+gCfmkwC0F0tirIZbLkXPrPwzZ0M9eNxhIySb2npJfgnqz55I0u33wh4r0ZNQ\\neTGfw03MBUtyuzGesGkcw+loqMaq1qR4tjGbPYxCvpCq7+OgpCCoMNit2uLo9M18\\nfHz10lOMT8nWAUvRZFzteXCm+7PHdYPlmQwUw3LvenJ/ILXoQPHfbkH0CyPfhl1j\\nWhJFZasCAwEAAaN+MHwwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSFrBrRQ/fI\\nrFXUxR1BSKvVeErUUzAPBgNVHRMBAf8EBTADAQH/MDoGA1UdHwQzMDEwL6AtoCuG\\nKWh0dHBzOi8va2RzaW50Zi5hbWQuY29tL3ZjZWsvdjEvTWlsYW4vY3JsMEYGCSqG\\nSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZI\\nAWUDBAICBQCiAwIBMKMDAgEBA4ICAQC6m0kDp6zv4Ojfgy+zleehsx6ol0ocgVel\\nETobpx+EuCsqVFRPK1jZ1sp/lyd9+0fQ0r66n7kagRk4Ca39g66WGTJMeJdqYriw\\nSTjjDCKVPSesWXYPVAyDhmP5n2v+BYipZWhpvqpaiO+EGK5IBP+578QeW/sSokrK\\ndHaLAxG2LhZxj9aF73fqC7OAJZ5aPonw4RE299FVarh1Tx2eT3wSgkDgutCTB1Yq\\nzT5DuwvAe+co2CIVIzMDamYuSFjPN0BCgojl7V+bTou7dMsqIu/TW/rPCX9/EUcp\\nKGKqPQ3P+N9r1hjEFY1plBg93t53OOo49GNI+V1zvXPLI6xIFVsh+mto2RtgEX/e\\npmMKTNN6psW88qg7c1hTWtN6MbRuQ0vm+O+/2tKBF2h8THb94OvvHHoFDpbCELlq\\nHnIYhxy0YKXGyaW1NjfULxrrmxVW4wcn5E8GddmvNa6yYm8scJagEi13mhGu4Jqh\\n3QU3sf8iUSUr09xQDwHtOQUVIqx4maBZPBtSMf+qUDtjXSSq8lfWcd8bLr9mdsUn\\nJZJ0+tuPMKmBnSH860llKk+VpVQsgqbzDIvOLvD6W1Umq25boxCYJ+TuBoa4s+HH\\nCViAvgT9kf/rBq1d+ivj6skkHxuzcxbk1xv6ZGxrteJxVH7KlX7YRdZ6eARKwLe4\\nAFZEAwoKCQ==\\n-----END CERTIFICATE-----\\n\""), - resource.TestCheckResourceAttr("data.constellation_attestation.test", "measurements.1.expected", "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969"), resource.TestCheckResourceAttr("data.constellation_attestation.test", "measurements.1.warn_only", "true"), ), }, + { + Config: testingConfig + ` + data "constellation_attestation" "aws_test" { + csp = "aws" + attestation_variant = "aws-sev-snp" + image_version = "v2.13.0" + } + `, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.constellation_attestation.aws_test", "measurements.0.expected", "7b068c0c3ac29afe264134536b9be26f1d4ccd575b88d3c3ceabf36ac99c0278"), + resource.TestCheckResourceAttr("data.constellation_attestation.aws_test", "measurements.0.warn_only", "true"), + // TODO(elchead): waiting for attestation from PR. + ), + }, }, }) } - -const testAccAttestationDataSourceConfig = ` -data "constellation_attestation" "test" { - csp = "azure" - attestation_variant = "azure-sev-snp" - image_version = "v2.13.0" -} -`