Skip to content

Commit

Permalink
Enable upload of TDX reports to Constellation CDN
Browse files Browse the repository at this point in the history
Signed-off-by: Daniel Weiße <[email protected]>
  • Loading branch information
daniel-weisse committed Jun 27, 2024
1 parent 9159b60 commit d67d0ac
Show file tree
Hide file tree
Showing 27 changed files with 733 additions and 482 deletions.
8 changes: 4 additions & 4 deletions .github/actions/e2e_attestationconfigapi/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: E2E Attestationconfig API Test
description: "Test the attestationconfig CLI is functional."

inputs:
csp:
description: "Cloud provider to run tests against"
default: "azure"
attestationVariant:
description: "attestation variant to run tests against"
default: "azure-sev-snp"
cosignPrivateKey:
description: "Cosign private key"
required: true
Expand All @@ -30,4 +30,4 @@ runs:
COSIGN_PRIVATE_KEY: ${{ inputs.cosignPrivateKey }}
COSIGN_PASSWORD: ${{ inputs.cosignPassword }}
run: |
bazel run //internal/api/attestationconfigapi/cli:cli_e2e_test -- ${{ inputs.csp }}
bazel run //internal/api/attestationconfigapi/cli:cli_e2e_test -- ${{ inputs.attestationVariant }}
13 changes: 5 additions & 8 deletions .github/actions/e2e_verify/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ runs:
case "${{ inputs.attestationVariant }}"
in
"azure-sev-snp"|"aws-sev-snp"|"gcp-sev-snp")
"azure-sev-snp"|"azure-tdx"|"aws-sev-snp"|"gcp-sev-snp")
echo "Extracting TCB versions for API update"
constellation verify --cluster-id "${clusterID}" --node-endpoint localhost:9090 -o json > "snp-report-${node}.json"
constellation verify --cluster-id "${clusterID}" --node-endpoint localhost:9090 -o json > "attestation-report-${node}.json"
;;
*)
constellation verify --cluster-id "${clusterID}" --node-endpoint localhost:9090
Expand All @@ -88,22 +88,19 @@ runs:
aws-region: eu-central-1

- name: Upload extracted TCBs
if: github.ref_name == 'main' && (inputs.attestationVariant == 'azure-sev-snp' || inputs.attestationVariant == 'aws-sev-snp' || inputs.attestationVariant == 'gcp-sev-snp')
if: github.ref_name == 'main' && (inputs.attestationVariant == 'azure-sev-snp' || inputs.attestationVariant == 'azure-tdx' || inputs.attestationVariant == 'aws-sev-snp' || inputs.attestationVariant == 'gcp-sev-snp')
shell: bash
env:
COSIGN_PASSWORD: ${{ inputs.cosignPassword }}
COSIGN_PRIVATE_KEY: ${{ inputs.cosignPrivateKey }}
run: |
reports=(snp-report-*.json)
reports=(attestation-report-*.json)
if [ -z ${#reports[@]} ]; then
exit 1
fi
attestationVariant=${{ inputs.attestationVariant }}
cloudProvider=${attestationVariant%%-*}
for file in "${reports[@]}"; do
path=$(realpath "${file}")
cat "${path}"
bazel run //internal/api/attestationconfigapi/cli -- upload "${cloudProvider}" snp-report "${path}"
bazel run //internal/api/attestationconfigapi/cli -- upload ${{ inputs.attestationVariant }} attestation-report "${path}"
done
6 changes: 3 additions & 3 deletions .github/workflows/e2e-attestationconfigapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ jobs:
e2e-api:
strategy:
fail-fast: false
max-parallel: 1
max-parallel: 2
matrix:
csp: ["azure", "aws", "gcp"]
attestationVariant: ["azure-sev-snp", "azure-tdx", "aws-sev-snp", "gcp-sev-snp"]
runs-on: ubuntu-22.04
permissions:
id-token: write
Expand All @@ -36,4 +36,4 @@ jobs:
with:
cosignPrivateKey: ${{ secrets.COSIGN_DEV_PRIVATE_KEY }}
cosignPassword: ${{ secrets.COSIGN_DEV_PASSWORD }}
csp: ${{ matrix.csp }}
attestationVariant: ${{ matrix.attestationVariant }}
4 changes: 2 additions & 2 deletions cli/internal/cmd/configfetchmeasurements_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,8 @@ func (f stubVerifyFetcher) FetchAndVerifyMeasurements(_ context.Context, _ strin

type stubAttestationFetcher struct{}

func (f stubAttestationFetcher) FetchLatestVersion(_ context.Context, _ variant.Variant) (attestationconfigapi.VersionAPIEntry, error) {
return attestationconfigapi.VersionAPIEntry{
func (f stubAttestationFetcher) FetchLatestVersion(_ context.Context, _ variant.Variant) (attestationconfigapi.Entry, error) {
return attestationconfigapi.Entry{
SEVSNPVersion: testCfg,
}, nil
}
Expand Down
4 changes: 2 additions & 2 deletions cli/internal/cmd/iamupgradeapply_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,6 @@ type stubConfigFetcher struct {
fetchLatestErr error
}

func (s *stubConfigFetcher) FetchLatestVersion(context.Context, variant.Variant) (attestationconfigapi.VersionAPIEntry, error) {
return attestationconfigapi.VersionAPIEntry{}, s.fetchLatestErr
func (s *stubConfigFetcher) FetchLatestVersion(context.Context, variant.Variant) (attestationconfigapi.Entry, error) {
return attestationconfigapi.Entry{}, s.fetchLatestErr
}
2 changes: 1 addition & 1 deletion internal/api/attestationconfigapi/cli/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ go_library(
"//internal/api/attestationconfigapi/cli/client",
"//internal/api/fetcher",
"//internal/attestation/variant",
"//internal/cloud/cloudprovider",
"//internal/constants",
"//internal/file",
"//internal/logger",
Expand All @@ -31,6 +30,7 @@ go_library(
"@com_github_aws_aws_sdk_go_v2//aws",
"@com_github_aws_aws_sdk_go_v2_service_s3//:s3",
"@com_github_aws_aws_sdk_go_v2_service_s3//types",
"@com_github_google_go_tdx_guest//proto/tdx",
"@com_github_spf13_afero//:afero",
"@com_github_spf13_cobra//:cobra",
],
Expand Down
1 change: 0 additions & 1 deletion internal/api/attestationconfigapi/cli/client/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ go_test(
embed = [":client"],
deps = [
"//internal/api/attestationconfigapi",
"//internal/attestation/variant",
"@com_github_stretchr_testify//assert",
],
)
92 changes: 40 additions & 52 deletions internal/api/attestationconfigapi/cli/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@ import (
"errors"
"fmt"
"log/slog"
"time"
"path"
"strings"

"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/aws/aws-sdk-go/aws"
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
apiclient "github.com/edgelesssys/constellation/v2/internal/api/client"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
Expand All @@ -35,6 +38,8 @@ type Client struct {
bucketID string
signer sigstore.Signer
cacheWindowSize int

log *slog.Logger
}

// New returns a new Client.
Expand All @@ -50,61 +55,45 @@ func New(ctx context.Context, cfg staticupload.Config, cosignPwd, privateKey []b
signer: sigstore.NewSigner(cosignPwd, privateKey),
bucketID: cfg.Bucket,
cacheWindowSize: versionWindowSize,
log: log,
}
return repo, clientClose, nil
}

// uploadSEVSNPVersion uploads the latest version numbers of the SEVSNP. Then version name is the UTC timestamp of the date. The /list entry stores the version name + .json suffix.
func (a Client) uploadSEVSNPVersion(ctx context.Context, attestation variant.Variant, version attestationconfigapi.SEVSNPVersion, date time.Time) error {
versions, err := a.List(ctx, attestation)
// DeleteVersion deletes the given version (without .json suffix) from the API.
func (c Client) DeleteVersion(ctx context.Context, attestation variant.Variant, versionStr string) error {
versions, err := c.List(ctx, attestation)
if err != nil {
return fmt.Errorf("fetch version list: %w", err)
}
ops := a.constructUploadCmd(attestation, version, versions, date)

return executeAllCmds(ctx, a.s3Client, ops)
}

// DeleteSEVSNPVersion deletes the given version (without .json suffix) from the API.
func (a Client) DeleteSEVSNPVersion(ctx context.Context, attestation variant.Variant, versionStr string) error {
versions, err := a.List(ctx, attestation)
if err != nil {
return fmt.Errorf("fetch version list: %w", err)
}

ops, err := a.deleteSEVSNPVersion(versions, versionStr)
ops, err := c.deleteVersion(versions, versionStr)
if err != nil {
return err
}
return executeAllCmds(ctx, a.s3Client, ops)
return executeAllCmds(ctx, c.s3Client, ops)
}

// List returns the list of versions for the given attestation variant.
func (a Client) List(ctx context.Context, attestation variant.Variant) (attestationconfigapi.VersionList, error) {
if !attestation.Equal(variant.AzureSEVSNP{}) &&
!attestation.Equal(variant.AWSSEVSNP{}) &&
!attestation.Equal(variant.GCPSEVSNP{}) {
return attestationconfigapi.VersionList{}, fmt.Errorf("unsupported attestation variant: %s", attestation)
}

versions, err := apiclient.Fetch(ctx, a.s3Client, attestationconfigapi.VersionList{Variant: attestation})
func (c Client) List(ctx context.Context, attestation variant.Variant) (attestationconfigapi.List, error) {
versions, err := apiclient.Fetch(ctx, c.s3Client, attestationconfigapi.List{Variant: attestation})
if err != nil {
var notFoundErr *apiclient.NotFoundError
if errors.As(err, &notFoundErr) {
return attestationconfigapi.VersionList{Variant: attestation}, nil
return attestationconfigapi.List{Variant: attestation}, nil
}
return attestationconfigapi.VersionList{}, err
return attestationconfigapi.List{}, err
}

versions.Variant = attestation

return versions, nil
}

func (a Client) deleteSEVSNPVersion(versions attestationconfigapi.VersionList, versionStr string) (ops []crudCmd, err error) {
func (c Client) deleteVersion(versions attestationconfigapi.List, versionStr string) (ops []crudCmd, err error) {
versionStr = versionStr + ".json"
ops = append(ops, deleteCmd{
apiObject: attestationconfigapi.VersionAPIEntry{
apiObject: attestationconfigapi.Entry{
Variant: versions.Variant,
Version: versionStr,
},
Expand All @@ -116,47 +105,46 @@ func (a Client) deleteSEVSNPVersion(versions attestationconfigapi.VersionList, v
}
ops = append(ops, putCmd{
apiObject: removedVersions,
signer: a.signer,
signer: c.signer,
})
return ops, nil
}

func (a Client) constructUploadCmd(attestation variant.Variant, version attestationconfigapi.SEVSNPVersion, versionNames attestationconfigapi.VersionList, date time.Time) []crudCmd {
if !attestation.Equal(versionNames.Variant) {
return nil
}

dateStr := date.Format(VersionFormat) + ".json"
var res []crudCmd

res = append(res, putCmd{
apiObject: attestationconfigapi.VersionAPIEntry{Version: dateStr, Variant: attestation, SEVSNPVersion: version},
signer: a.signer,
func (c Client) listCachedVersions(ctx context.Context, attestation variant.Variant) ([]string, error) {
list, err := c.s3Client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{
Bucket: aws.String(c.bucketID),
Prefix: aws.String(reportVersionDir(attestation)),
})
if err != nil {
return nil, fmt.Errorf("list objects: %w", err)
}

versionNames.AddVersion(dateStr)

res = append(res, putCmd{
apiObject: versionNames,
signer: a.signer,
})
var dates []string
for _, obj := range list.Contents {
fileName := path.Base(*obj.Key)

return res
// The cache contains signature and json files
// We only want the json files
if date, ok := strings.CutSuffix(fileName, ".json"); ok {
dates = append(dates, date)
}
}
return dates, nil
}

func removeVersion(list attestationconfigapi.VersionList, versionStr string) (removedVersions attestationconfigapi.VersionList, err error) {
func removeVersion(list attestationconfigapi.List, versionStr string) (removedVersions attestationconfigapi.List, err error) {
versions := list.List
for i, v := range versions {
if v == versionStr {
if i == len(versions)-1 {
removedVersions = attestationconfigapi.VersionList{List: versions[:i], Variant: list.Variant}
removedVersions = attestationconfigapi.List{List: versions[:i], Variant: list.Variant}
} else {
removedVersions = attestationconfigapi.VersionList{List: append(versions[:i], versions[i+1:]...), Variant: list.Variant}
removedVersions = attestationconfigapi.List{List: append(versions[:i], versions[i+1:]...), Variant: list.Variant}
}
return removedVersions, nil
}
}
return attestationconfigapi.VersionList{}, fmt.Errorf("version %s not found in list %v", versionStr, versions)
return attestationconfigapi.List{}, fmt.Errorf("version %s not found in list %v", versionStr, versions)
}

type crudCmd interface {
Expand Down
40 changes: 4 additions & 36 deletions internal/api/attestationconfigapi/cli/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,60 +7,28 @@ package client

import (
"testing"
"time"

"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
"github.com/stretchr/testify/assert"
)

func TestUploadAzureSEVSNP(t *testing.T) {
sut := Client{
bucketID: "bucket",
signer: fakeSigner{},
}
version := attestationconfigapi.SEVSNPVersion{}
date := time.Date(2023, 1, 1, 1, 1, 1, 1, time.UTC)
ops := sut.constructUploadCmd(variant.AzureSEVSNP{}, version, attestationconfigapi.VersionList{List: []string{"2021-01-01-01-01.json", "2019-01-01-01-01.json"}, Variant: variant.AzureSEVSNP{}}, date)
dateStr := "2023-01-01-01-01.json"
assert := assert.New(t)
assert.Contains(ops, putCmd{
apiObject: attestationconfigapi.VersionAPIEntry{
Variant: variant.AzureSEVSNP{},
Version: dateStr,
SEVSNPVersion: version,
},
signer: fakeSigner{},
})
assert.Contains(ops, putCmd{
apiObject: attestationconfigapi.VersionList{Variant: variant.AzureSEVSNP{}, List: []string{"2023-01-01-01-01.json", "2021-01-01-01-01.json", "2019-01-01-01-01.json"}},
signer: fakeSigner{},
})
}

func TestDeleteAzureSEVSNPVersions(t *testing.T) {
sut := Client{
bucketID: "bucket",
}
versions := attestationconfigapi.VersionList{List: []string{"2023-01-01.json", "2021-01-01.json", "2019-01-01.json"}}
versions := attestationconfigapi.List{List: []string{"2023-01-01.json", "2021-01-01.json", "2019-01-01.json"}}

ops, err := sut.deleteSEVSNPVersion(versions, "2021-01-01")
ops, err := sut.deleteVersion(versions, "2021-01-01")

assert := assert.New(t)
assert.NoError(err)
assert.Contains(ops, deleteCmd{
apiObject: attestationconfigapi.VersionAPIEntry{
apiObject: attestationconfigapi.Entry{
Version: "2021-01-01.json",
},
})

assert.Contains(ops, putCmd{
apiObject: attestationconfigapi.VersionList{List: []string{"2023-01-01.json", "2019-01-01.json"}},
apiObject: attestationconfigapi.List{List: []string{"2023-01-01.json", "2019-01-01.json"}},
})
}

type fakeSigner struct{}

func (fakeSigner) Sign(_ []byte) ([]byte, error) {
return []byte("signature"), nil
}
Loading

0 comments on commit d67d0ac

Please sign in to comment.