Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

config: allow "latest" pseudo-version for Azure TDX config values #3166

Merged
merged 6 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 }}
14 changes: 2 additions & 12 deletions cli/internal/cmd/configfetchmeasurements_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,18 +204,8 @@ func (f stubVerifyFetcher) FetchAndVerifyMeasurements(_ context.Context, _ strin

type stubAttestationFetcher struct{}

func (f stubAttestationFetcher) FetchSEVSNPVersionList(_ context.Context, _ attestationconfigapi.SEVSNPVersionList) (attestationconfigapi.SEVSNPVersionList, error) {
return attestationconfigapi.SEVSNPVersionList{}, nil
}

func (f stubAttestationFetcher) FetchSEVSNPVersion(_ context.Context, _ attestationconfigapi.SEVSNPVersionAPI) (attestationconfigapi.SEVSNPVersionAPI, error) {
return attestationconfigapi.SEVSNPVersionAPI{
SEVSNPVersion: testCfg,
}, nil
}

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

func (s *stubConfigFetcher) FetchSEVSNPVersion(context.Context, attestationconfigapi.SEVSNPVersionAPI) (attestationconfigapi.SEVSNPVersionAPI, error) {
panic("not implemented")
}

func (s *stubConfigFetcher) FetchSEVSNPVersionList(context.Context, attestationconfigapi.SEVSNPVersionList) (attestationconfigapi.SEVSNPVersionList, error) {
panic("not implemented")
}

func (s *stubConfigFetcher) FetchSEVSNPVersionLatest(context.Context, variant.Variant) (attestationconfigapi.SEVSNPVersionAPI, error) {
return attestationconfigapi.SEVSNPVersionAPI{}, s.fetchLatestErr
func (s *stubConfigFetcher) FetchLatestVersion(context.Context, variant.Variant) (attestationconfigapi.Entry, error) {
return attestationconfigapi.Entry{}, s.fetchLatestErr
}
12 changes: 2 additions & 10 deletions internal/api/attestationconfigapi/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,24 @@ go_library(
name = "attestationconfigapi",
srcs = [
"attestationconfigapi.go",
"client.go",
"fetcher.go",
"reporter.go",
"snp.go",
"version.go",
],
importpath = "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi",
visibility = ["//:__subpackages__"],
deps = [
"//internal/api/client",
"//internal/api/fetcher",
"//internal/attestation/variant",
"//internal/constants",
"//internal/sigstore",
"//internal/staticupload",
"@com_github_aws_aws_sdk_go//aws",
"@com_github_aws_aws_sdk_go_v2_service_s3//:s3",
],
)

go_test(
name = "attestationconfigapi_test",
srcs = [
"client_test.go",
"fetcher_test.go",
"reporter_test.go",
"snp_test.go",
"version_test.go",
],
embed = [":attestationconfigapi"],
deps = [
Expand Down
4 changes: 3 additions & 1 deletion internal/api/attestationconfigapi/cli/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ go_library(
visibility = ["//visibility:private"],
deps = [
"//internal/api/attestationconfigapi",
"//internal/api/attestationconfigapi/cli/client",
"//internal/api/fetcher",
"//internal/attestation/variant",
"//internal/cloud/cloudprovider",
"//internal/constants",
"//internal/file",
"//internal/logger",
Expand All @@ -29,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
34 changes: 34 additions & 0 deletions internal/api/attestationconfigapi/cli/client/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("//bazel/go:go_test.bzl", "go_test")

go_library(
name = "client",
srcs = [
"client.go",
"reporter.go",
],
importpath = "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi/cli/client",
visibility = ["//:__subpackages__"],
deps = [
"//internal/api/attestationconfigapi",
"//internal/api/client",
"//internal/attestation/variant",
"//internal/sigstore",
"//internal/staticupload",
"@com_github_aws_aws_sdk_go//aws",
"@com_github_aws_aws_sdk_go_v2_service_s3//:s3",
],
)

go_test(
name = "client_test",
srcs = [
"client_test.go",
"reporter_test.go",
],
embed = [":client"],
deps = [
"//internal/api/attestationconfigapi",
"@com_github_stretchr_testify//assert",
],
)
178 changes: 178 additions & 0 deletions internal/api/attestationconfigapi/cli/client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/*
Copyright (c) Edgeless Systems GmbH

SPDX-License-Identifier: AGPL-3.0-only
*/

/*
package client contains code to manage CVM versions in Constellation's CDN API.
It is used to upload and delete "latest" versions for AMD SEV-SNP and Intel TDX.
*/
package client

import (
"context"
"errors"
"fmt"
"log/slog"
"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"
"github.com/edgelesssys/constellation/v2/internal/sigstore"

"github.com/edgelesssys/constellation/v2/internal/staticupload"
)

// VersionFormat is the format of the version name in the S3 bucket.
const VersionFormat = "2006-01-02-15-04"

// Client manages (modifies) the version information for the attestation variants.
type Client struct {
s3Client *apiclient.Client
s3ClientClose func(ctx context.Context) error
bucketID string
signer sigstore.Signer
cacheWindowSize int

log *slog.Logger
}

// New returns a new Client.
func New(ctx context.Context, cfg staticupload.Config, cosignPwd, privateKey []byte, dryRun bool, versionWindowSize int, log *slog.Logger) (*Client, apiclient.CloseFunc, error) {
s3Client, clientClose, err := apiclient.NewClient(ctx, cfg.Region, cfg.Bucket, cfg.DistributionID, dryRun, log)
if err != nil {
return nil, nil, fmt.Errorf("failed to create s3 storage: %w", err)
}

repo := &Client{
s3Client: s3Client,
s3ClientClose: clientClose,
signer: sigstore.NewSigner(cosignPwd, privateKey),
bucketID: cfg.Bucket,
cacheWindowSize: versionWindowSize,
log: log,
}
return repo, clientClose, nil
}

// 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, err := c.deleteVersion(versions, versionStr)
if err != nil {
return err
}
return executeAllCmds(ctx, c.s3Client, ops)
}

// List returns the list of versions for the given attestation variant.
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.List{Variant: attestation}, nil
}
return attestationconfigapi.List{}, err
}

versions.Variant = attestation

return versions, nil
}

func (c Client) deleteVersion(versions attestationconfigapi.List, versionStr string) (ops []crudCmd, err error) {
versionStr = versionStr + ".json"
ops = append(ops, deleteCmd{
apiObject: attestationconfigapi.Entry{
Variant: versions.Variant,
Version: versionStr,
},
})

removedVersions, err := removeVersion(versions, versionStr)
if err != nil {
return nil, err
}
ops = append(ops, putCmd{
apiObject: removedVersions,
signer: c.signer,
})
return ops, nil
}

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

var dates []string
for _, obj := range list.Contents {
fileName := path.Base(*obj.Key)

// 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.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.List{List: versions[:i], Variant: list.Variant}
} else {
removedVersions = attestationconfigapi.List{List: append(versions[:i], versions[i+1:]...), Variant: list.Variant}
}
return removedVersions, nil
}
}
return attestationconfigapi.List{}, fmt.Errorf("version %s not found in list %v", versionStr, versions)
}

type crudCmd interface {
Execute(ctx context.Context, c *apiclient.Client) error
}

type deleteCmd struct {
apiObject apiclient.APIObject
}

func (d deleteCmd) Execute(ctx context.Context, c *apiclient.Client) error {
return apiclient.DeleteWithSignature(ctx, c, d.apiObject)
}

type putCmd struct {
apiObject apiclient.APIObject
signer sigstore.Signer
}

func (p putCmd) Execute(ctx context.Context, c *apiclient.Client) error {
return apiclient.SignAndUpdate(ctx, c, p.apiObject, p.signer)
}

func executeAllCmds(ctx context.Context, client *apiclient.Client, cmds []crudCmd) error {
for _, cmd := range cmds {
if err := cmd.Execute(ctx, client); err != nil {
return fmt.Errorf("execute operation %+v: %w", cmd, err)
}
}
return nil
}
Loading