mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-12-24 06:59:40 -05:00
Enable upload of TDX reports to Constellation CDN
Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
parent
9159b60331
commit
d67d0ac9df
@ -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
|
||||
@ -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
.github/actions/e2e_verify/action.yml
vendored
13
.github/actions/e2e_verify/action.yml
vendored
@ -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
|
||||
@ -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
|
||||
|
@ -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
|
||||
@ -36,4 +36,4 @@ jobs:
|
||||
with:
|
||||
cosignPrivateKey: ${{ secrets.COSIGN_DEV_PRIVATE_KEY }}
|
||||
cosignPassword: ${{ secrets.COSIGN_DEV_PASSWORD }}
|
||||
csp: ${{ matrix.csp }}
|
||||
attestationVariant: ${{ matrix.attestationVariant }}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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",
|
||||
@ -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",
|
||||
],
|
||||
|
@ -29,7 +29,6 @@ go_test(
|
||||
embed = [":client"],
|
||||
deps = [
|
||||
"//internal/api/attestationconfigapi",
|
||||
"//internal/attestation/variant",
|
||||
"@com_github_stretchr_testify//assert",
|
||||
],
|
||||
)
|
||||
|
@ -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"
|
||||
@ -35,6 +38,8 @@ type Client struct {
|
||||
bucketID string
|
||||
signer sigstore.Signer
|
||||
cacheWindowSize int
|
||||
|
||||
log *slog.Logger
|
||||
}
|
||||
|
||||
// New returns a new Client.
|
||||
@ -50,50 +55,34 @@ 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)
|
||||
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)
|
||||
// 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 := 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, ¬FoundErr) {
|
||||
return attestationconfigapi.VersionList{Variant: attestation}, nil
|
||||
return attestationconfigapi.List{Variant: attestation}, nil
|
||||
}
|
||||
return attestationconfigapi.VersionList{}, err
|
||||
return attestationconfigapi.List{}, err
|
||||
}
|
||||
|
||||
versions.Variant = attestation
|
||||
@ -101,10 +90,10 @@ func (a Client) List(ctx context.Context, attestation variant.Variant) (attestat
|
||||
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,
|
||||
},
|
||||
@ -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
|
||||
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)
|
||||
}
|
||||
|
||||
dateStr := date.Format(VersionFormat) + ".json"
|
||||
var res []crudCmd
|
||||
var dates []string
|
||||
for _, obj := range list.Contents {
|
||||
fileName := path.Base(*obj.Key)
|
||||
|
||||
res = append(res, putCmd{
|
||||
apiObject: attestationconfigapi.VersionAPIEntry{Version: dateStr, Variant: attestation, SEVSNPVersion: version},
|
||||
signer: a.signer,
|
||||
})
|
||||
|
||||
versionNames.AddVersion(dateStr)
|
||||
|
||||
res = append(res, putCmd{
|
||||
apiObject: versionNames,
|
||||
signer: a.signer,
|
||||
})
|
||||
|
||||
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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
@ -15,9 +16,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/s3"
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
|
||||
"github.com/edgelesssys/constellation/v2/internal/api/client"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
|
||||
@ -33,153 +31,335 @@ func reportVersionDir(attestation variant.Variant) string {
|
||||
return path.Join(attestationconfigapi.AttestationURLPath, attestation.String(), cachedVersionsSubDir)
|
||||
}
|
||||
|
||||
// UploadSEVSNPVersionLatest saves the given version to the cache, determines the smallest
|
||||
// UploadLatestVersion saves the given version to the cache, determines the smallest
|
||||
// TCB version in the cache among the last cacheWindowSize versions and updates
|
||||
// the latest version in the API if there is an update.
|
||||
// force can be used to bypass the validation logic against the cached versions.
|
||||
func (c Client) UploadSEVSNPVersionLatest(ctx context.Context, attestation variant.Variant, inputVersion,
|
||||
latestAPIVersion attestationconfigapi.SEVSNPVersion, now time.Time, force bool,
|
||||
func (c Client) UploadLatestVersion(
|
||||
ctx context.Context, attestationVariant variant.Variant,
|
||||
inputVersion, latestVersionInAPI any,
|
||||
now time.Time, force bool,
|
||||
) error {
|
||||
if err := c.cacheSEVSNPVersion(ctx, attestation, inputVersion, now); err != nil {
|
||||
return fmt.Errorf("reporting version: %w", err)
|
||||
// Validate input versions against configured attestation variant
|
||||
// This allows us to skip these checks in the individual variant implementations
|
||||
var err error
|
||||
actionForVariant(attestationVariant,
|
||||
func() {
|
||||
if _, ok := inputVersion.(attestationconfigapi.TDXVersion); !ok {
|
||||
err = fmt.Errorf("input version %q is not a TDX version", inputVersion)
|
||||
}
|
||||
if force {
|
||||
return c.uploadSEVSNPVersion(ctx, attestation, inputVersion, now)
|
||||
if _, ok := latestVersionInAPI.(attestationconfigapi.TDXVersion); !ok {
|
||||
err = fmt.Errorf("latest API version %q is not a TDX version", latestVersionInAPI)
|
||||
}
|
||||
versionDates, err := c.listCachedVersions(ctx, attestation)
|
||||
},
|
||||
func() {
|
||||
if _, ok := inputVersion.(attestationconfigapi.SEVSNPVersion); !ok {
|
||||
err = fmt.Errorf("input version %q is not a SNP version", inputVersion)
|
||||
}
|
||||
if _, ok := latestVersionInAPI.(attestationconfigapi.SEVSNPVersion); !ok {
|
||||
err = fmt.Errorf("latest API version %q is not a SNP version", latestVersionInAPI)
|
||||
}
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("list reported versions: %w", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.addVersionToCache(ctx, attestationVariant, inputVersion, now); err != nil {
|
||||
return fmt.Errorf("adding version to cache: %w", err)
|
||||
}
|
||||
|
||||
// If force is set, immediately update the latest version to the new version in the API.
|
||||
if force {
|
||||
return c.uploadAsLatestVersion(ctx, attestationVariant, inputVersion, now)
|
||||
}
|
||||
|
||||
// Otherwise, check the cached versions and update the latest version in the API if necessary.
|
||||
versionDates, err := c.listCachedVersions(ctx, attestationVariant)
|
||||
if err != nil {
|
||||
return fmt.Errorf("listing existing cached versions: %w", err)
|
||||
}
|
||||
if len(versionDates) < c.cacheWindowSize {
|
||||
c.s3Client.Logger.Warn(fmt.Sprintf("Skipping version update, found %d, expected %d reported versions.", len(versionDates), c.cacheWindowSize))
|
||||
c.log.Warn(fmt.Sprintf("Skipping version update, found %d, expected %d reported versions.", len(versionDates), c.cacheWindowSize))
|
||||
return nil
|
||||
}
|
||||
minVersion, minDate, err := c.findMinVersion(ctx, attestation, versionDates)
|
||||
|
||||
minVersion, minDate, err := c.findMinVersion(ctx, attestationVariant, versionDates)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get minimal version: %w", err)
|
||||
return fmt.Errorf("determining minimal version in cache: %w", err)
|
||||
}
|
||||
c.s3Client.Logger.Info(fmt.Sprintf("Found minimal version: %+v with date: %s", minVersion, minDate))
|
||||
shouldUpdateAPI, err := isInputNewerThanOtherVersion(minVersion, latestAPIVersion)
|
||||
if err != nil {
|
||||
c.log.Info(fmt.Sprintf("Found minimal version: %+v with date: %s", minVersion, minDate))
|
||||
|
||||
if !isInputNewerThanOtherVersion(attestationVariant, minVersion, latestVersionInAPI) {
|
||||
c.log.Info(fmt.Sprintf("Input version: %+v is not newer than latest API version: %+v. Skipping list update", minVersion, latestVersionInAPI))
|
||||
return ErrNoNewerVersion
|
||||
}
|
||||
if !shouldUpdateAPI {
|
||||
c.s3Client.Logger.Info(fmt.Sprintf("Input version: %+v is not newer than latest API version: %+v", minVersion, latestAPIVersion))
|
||||
return nil
|
||||
}
|
||||
c.s3Client.Logger.Info(fmt.Sprintf("Input version: %+v is newer than latest API version: %+v", minVersion, latestAPIVersion))
|
||||
|
||||
c.log.Info(fmt.Sprintf("Input version: %+v is newer than latest API version: %+v", minVersion, latestVersionInAPI))
|
||||
t, err := time.Parse(VersionFormat, minDate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing date: %w", err)
|
||||
}
|
||||
if err := c.uploadSEVSNPVersion(ctx, attestation, minVersion, t); err != nil {
|
||||
return fmt.Errorf("uploading version: %w", err)
|
||||
|
||||
if err := c.uploadAsLatestVersion(ctx, attestationVariant, minVersion, t); err != nil {
|
||||
return fmt.Errorf("uploading as latest version: %w", err)
|
||||
}
|
||||
c.s3Client.Logger.Info(fmt.Sprintf("Successfully uploaded new SEV-SNP version: %+v", minVersion))
|
||||
|
||||
c.log.Info(fmt.Sprintf("Successfully uploaded new %s version: %+v", attestationVariant, minVersion))
|
||||
return nil
|
||||
}
|
||||
|
||||
// uploadAsLatestVersion uploads the given version and updates the list to set it as the "latest" version.
|
||||
// The version's name is the UTC timestamp of the date.
|
||||
// The /list entry stores the version name + .json suffix.
|
||||
func (c Client) uploadAsLatestVersion(ctx context.Context, variant variant.Variant, inputVersion any, date time.Time) error {
|
||||
versions, err := c.List(ctx, variant)
|
||||
if err != nil {
|
||||
return fmt.Errorf("fetch version list: %w", err)
|
||||
}
|
||||
if !variant.Equal(versions.Variant) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// cacheSEVSNPVersion uploads the latest observed version numbers of the SEVSNP. This version is used to later report the latest version numbers to the API.
|
||||
func (c Client) cacheSEVSNPVersion(ctx context.Context, attestation variant.Variant, version attestationconfigapi.SEVSNPVersion, date time.Time) error {
|
||||
dateStr := date.Format(VersionFormat) + ".json"
|
||||
res := putCmd{
|
||||
apiObject: reportedSEVSNPVersionAPI{Version: dateStr, variant: attestation, SEVSNPVersion: version},
|
||||
var ops []crudCmd
|
||||
|
||||
obj := apiVersionObject{version: dateStr, variant: variant, cached: false}
|
||||
obj.setVersion(inputVersion)
|
||||
ops = append(ops, putCmd{
|
||||
apiObject: obj,
|
||||
signer: c.signer,
|
||||
})
|
||||
|
||||
versions.AddVersion(dateStr)
|
||||
|
||||
ops = append(ops, putCmd{
|
||||
apiObject: versions,
|
||||
signer: c.signer,
|
||||
})
|
||||
|
||||
return executeAllCmds(ctx, c.s3Client, ops)
|
||||
}
|
||||
|
||||
// addVersionToCache adds the given version to the cache.
|
||||
func (c Client) addVersionToCache(ctx context.Context, variant variant.Variant, inputVersion any, date time.Time) error {
|
||||
dateStr := date.Format(VersionFormat) + ".json"
|
||||
obj := apiVersionObject{version: dateStr, variant: variant, cached: true}
|
||||
obj.setVersion(inputVersion)
|
||||
cmd := putCmd{
|
||||
apiObject: obj,
|
||||
signer: c.signer,
|
||||
}
|
||||
return res.Execute(ctx, c.s3Client)
|
||||
return cmd.Execute(ctx, c.s3Client)
|
||||
}
|
||||
|
||||
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)
|
||||
// findMinVersion returns the minimal version in the cache among the last cacheWindowSize versions.
|
||||
func (c Client) findMinVersion(
|
||||
ctx context.Context, attestationVariant variant.Variant, versionDates []string,
|
||||
) (any, string, error) {
|
||||
var getMinimalVersion func() (any, string, error)
|
||||
|
||||
actionForVariant(attestationVariant,
|
||||
func() {
|
||||
getMinimalVersion = func() (any, string, error) {
|
||||
return findMinimalVersion[attestationconfigapi.TDXVersion](ctx, attestationVariant, versionDates, c.s3Client, c.cacheWindowSize)
|
||||
}
|
||||
var dates []string
|
||||
for _, obj := range list.Contents {
|
||||
fileName := path.Base(*obj.Key)
|
||||
if strings.HasSuffix(fileName, ".json") {
|
||||
dates = append(dates, fileName[:len(fileName)-5])
|
||||
},
|
||||
func() {
|
||||
getMinimalVersion = func() (any, string, error) {
|
||||
return findMinimalVersion[attestationconfigapi.SEVSNPVersion](ctx, attestationVariant, versionDates, c.s3Client, c.cacheWindowSize)
|
||||
}
|
||||
}
|
||||
return dates, nil
|
||||
},
|
||||
)
|
||||
return getMinimalVersion()
|
||||
}
|
||||
|
||||
// findMinVersion finds the minimal version of the given version dates among the latest values in the version window size.
|
||||
func (c Client) findMinVersion(ctx context.Context, attesation variant.Variant, versionDates []string) (attestationconfigapi.SEVSNPVersion, string, error) {
|
||||
var minimalVersion *attestationconfigapi.SEVSNPVersion
|
||||
func findMinimalVersion[T attestationconfigapi.TDXVersion | attestationconfigapi.SEVSNPVersion](
|
||||
ctx context.Context, variant variant.Variant, versionDates []string,
|
||||
s3Client *client.Client, cacheWindowSize int,
|
||||
) (T, string, error) {
|
||||
var minimalVersion *T
|
||||
var minimalDate string
|
||||
sort.Sort(sort.Reverse(sort.StringSlice(versionDates))) // sort in reverse order to slice the latest versions
|
||||
versionDates = versionDates[:c.cacheWindowSize]
|
||||
versionDates = versionDates[:cacheWindowSize]
|
||||
sort.Strings(versionDates) // sort with oldest first to to take the minimal version with the oldest date
|
||||
|
||||
for _, date := range versionDates {
|
||||
obj, err := client.Fetch(ctx, c.s3Client, reportedSEVSNPVersionAPI{Version: date + ".json", variant: attesation})
|
||||
obj, err := client.Fetch(ctx, s3Client, apiVersionObject{version: date + ".json", variant: variant, cached: true})
|
||||
if err != nil {
|
||||
return attestationconfigapi.SEVSNPVersion{}, "", fmt.Errorf("get object: %w", err)
|
||||
return *new(T), "", fmt.Errorf("get object: %w", err)
|
||||
}
|
||||
// Need to set this explicitly as the variant is not part of the marshalled JSON.
|
||||
obj.variant = attesation
|
||||
obj.variant = variant // variant is not set by Fetch, set it manually
|
||||
|
||||
if minimalVersion == nil {
|
||||
minimalVersion = &obj.SEVSNPVersion
|
||||
v := obj.getVersion().(T)
|
||||
minimalVersion = &v
|
||||
minimalDate = date
|
||||
} else {
|
||||
shouldUpdateMinimal, err := isInputNewerThanOtherVersion(*minimalVersion, obj.SEVSNPVersion)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if shouldUpdateMinimal {
|
||||
minimalVersion = &obj.SEVSNPVersion
|
||||
|
||||
// If the current minimal version has newer versions than the one we just fetched,
|
||||
// update the minimal version to the older version.
|
||||
if isInputNewerThanOtherVersion(variant, *minimalVersion, obj.getVersion()) {
|
||||
v := obj.getVersion().(T)
|
||||
minimalVersion = &v
|
||||
minimalDate = date
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *minimalVersion, minimalDate, nil
|
||||
}
|
||||
|
||||
// isInputNewerThanOtherVersion compares all version fields and returns true if any input field is newer.
|
||||
func isInputNewerThanOtherVersion(input, other attestationconfigapi.SEVSNPVersion) (bool, error) {
|
||||
if input == other {
|
||||
return false, nil
|
||||
}
|
||||
if input.TEE < other.TEE {
|
||||
return false, fmt.Errorf("input TEE version: %d is older than latest API version: %d", input.TEE, other.TEE)
|
||||
}
|
||||
if input.SNP < other.SNP {
|
||||
return false, fmt.Errorf("input SNP version: %d is older than latest API version: %d", input.SNP, other.SNP)
|
||||
}
|
||||
if input.Microcode < other.Microcode {
|
||||
return false, fmt.Errorf("input Microcode version: %d is older than latest API version: %d", input.Microcode, other.Microcode)
|
||||
}
|
||||
if input.Bootloader < other.Bootloader {
|
||||
return false, fmt.Errorf("input Bootloader version: %d is older than latest API version: %d", input.Bootloader, other.Bootloader)
|
||||
}
|
||||
return true, nil
|
||||
func isInputNewerThanOtherVersion(variant variant.Variant, inputVersion, otherVersion any) bool {
|
||||
var result bool
|
||||
actionForVariant(variant,
|
||||
func() {
|
||||
input := inputVersion.(attestationconfigapi.TDXVersion)
|
||||
other := otherVersion.(attestationconfigapi.TDXVersion)
|
||||
result = isInputNewerThanOtherTDXVersion(input, other)
|
||||
},
|
||||
func() {
|
||||
input := inputVersion.(attestationconfigapi.SEVSNPVersion)
|
||||
other := otherVersion.(attestationconfigapi.SEVSNPVersion)
|
||||
result = isInputNewerThanOtherSEVSNPVersion(input, other)
|
||||
},
|
||||
)
|
||||
return result
|
||||
}
|
||||
|
||||
// reportedSEVSNPVersionAPI is the request to get the version information of the specific version in the config api.
|
||||
type reportedSEVSNPVersionAPI struct {
|
||||
Version string `json:"-"`
|
||||
type apiVersionObject struct {
|
||||
version string `json:"-"`
|
||||
variant variant.Variant `json:"-"`
|
||||
attestationconfigapi.SEVSNPVersion
|
||||
cached bool `json:"-"`
|
||||
snp attestationconfigapi.SEVSNPVersion
|
||||
tdx attestationconfigapi.TDXVersion
|
||||
}
|
||||
|
||||
func (a apiVersionObject) MarshalJSON() ([]byte, error) {
|
||||
var res []byte
|
||||
var err error
|
||||
actionForVariant(a.variant,
|
||||
func() {
|
||||
res, err = json.Marshal(a.tdx)
|
||||
},
|
||||
func() {
|
||||
res, err = json.Marshal(a.snp)
|
||||
},
|
||||
)
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (a *apiVersionObject) UnmarshalJSON(data []byte) error {
|
||||
errTDX := json.Unmarshal(data, &a.tdx)
|
||||
errSNP := json.Unmarshal(data, &a.snp)
|
||||
if errTDX == nil || errSNP == nil {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("trying to unmarshal data into both TDX and SNP versions: %w", errors.Join(errTDX, errSNP))
|
||||
}
|
||||
|
||||
// JSONPath returns the path to the JSON file for the request to the config api.
|
||||
func (i reportedSEVSNPVersionAPI) JSONPath() string {
|
||||
return path.Join(reportVersionDir(i.variant), i.Version)
|
||||
// This is the path to the cached version in the S3 bucket.
|
||||
func (a apiVersionObject) JSONPath() string {
|
||||
if a.cached {
|
||||
return path.Join(reportVersionDir(a.variant), a.version)
|
||||
}
|
||||
return path.Join(attestationconfigapi.AttestationURLPath, a.variant.String(), a.version)
|
||||
}
|
||||
|
||||
// ValidateRequest validates the request.
|
||||
func (i reportedSEVSNPVersionAPI) ValidateRequest() error {
|
||||
if !strings.HasSuffix(i.Version, ".json") {
|
||||
func (a apiVersionObject) ValidateRequest() error {
|
||||
if !strings.HasSuffix(a.version, ".json") {
|
||||
return fmt.Errorf("version has no .json suffix")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate is a No-Op at the moment.
|
||||
func (i reportedSEVSNPVersionAPI) Validate() error {
|
||||
// Validate is a No-Op.
|
||||
func (a apiVersionObject) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// getVersion returns the version.
|
||||
func (a apiVersionObject) getVersion() any {
|
||||
var res any
|
||||
actionForVariant(a.variant,
|
||||
func() {
|
||||
res = a.tdx
|
||||
},
|
||||
func() {
|
||||
res = a.snp
|
||||
},
|
||||
)
|
||||
return res
|
||||
}
|
||||
|
||||
// setVersion sets the version.
|
||||
func (a *apiVersionObject) setVersion(version any) {
|
||||
actionForVariant(a.variant,
|
||||
func() {
|
||||
a.tdx = version.(attestationconfigapi.TDXVersion)
|
||||
},
|
||||
func() {
|
||||
a.snp = version.(attestationconfigapi.SEVSNPVersion)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// actionForVariant performs the given action based on the whether variant is a TDX or SEV-SNP variant.
|
||||
func actionForVariant(
|
||||
attestationVariant variant.Variant,
|
||||
tdxAction func(), snpAction func(),
|
||||
) {
|
||||
switch attestationVariant {
|
||||
case variant.AWSSEVSNP{}, variant.AzureSEVSNP{}, variant.GCPSEVSNP{}:
|
||||
snpAction()
|
||||
case variant.AzureTDX{}:
|
||||
tdxAction()
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported attestation variant: %s", attestationVariant))
|
||||
}
|
||||
}
|
||||
|
||||
// isInputNewerThanOtherSEVSNPVersion compares all version fields and returns false if any input field is older, or the versions are equal.
|
||||
func isInputNewerThanOtherSEVSNPVersion(input, other attestationconfigapi.SEVSNPVersion) bool {
|
||||
if input == other {
|
||||
return false
|
||||
}
|
||||
if input.TEE < other.TEE {
|
||||
return false
|
||||
}
|
||||
if input.SNP < other.SNP {
|
||||
return false
|
||||
}
|
||||
if input.Microcode < other.Microcode {
|
||||
return false
|
||||
}
|
||||
if input.Bootloader < other.Bootloader {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// isInputNewerThanOtherSEVSNPVersion compares all version fields and returns false if any input field is older, or the versions are equal.
|
||||
func isInputNewerThanOtherTDXVersion(input, other attestationconfigapi.TDXVersion) bool {
|
||||
if input == other {
|
||||
return false
|
||||
}
|
||||
|
||||
if input.PCESVN < other.PCESVN {
|
||||
return false
|
||||
}
|
||||
if input.QESVN < other.QESVN {
|
||||
return false
|
||||
}
|
||||
|
||||
// Validate component-wise security version numbers
|
||||
for idx, inputVersion := range input.TEETCBSVN {
|
||||
if inputVersion < other.TEETCBSVN[idx] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIsInputNewerThanLatestAPI(t *testing.T) {
|
||||
func TestIsInputNewerThanOtherSEVSNPVersion(t *testing.T) {
|
||||
newTestCfg := func() attestationconfigapi.SEVSNPVersion {
|
||||
return attestationconfigapi.SEVSNPVersion{
|
||||
Microcode: 93,
|
||||
@ -25,7 +25,6 @@ func TestIsInputNewerThanLatestAPI(t *testing.T) {
|
||||
latest attestationconfigapi.SEVSNPVersion
|
||||
input attestationconfigapi.SEVSNPVersion
|
||||
expect bool
|
||||
errMsg string
|
||||
}{
|
||||
"input is older than latest": {
|
||||
input: func(c attestationconfigapi.SEVSNPVersion) attestationconfigapi.SEVSNPVersion {
|
||||
@ -34,7 +33,6 @@ func TestIsInputNewerThanLatestAPI(t *testing.T) {
|
||||
}(newTestCfg()),
|
||||
latest: newTestCfg(),
|
||||
expect: false,
|
||||
errMsg: "input Microcode version: 92 is older than latest API version: 93",
|
||||
},
|
||||
"input has greater and smaller version field than latest": {
|
||||
input: func(c attestationconfigapi.SEVSNPVersion) attestationconfigapi.SEVSNPVersion {
|
||||
@ -44,7 +42,6 @@ func TestIsInputNewerThanLatestAPI(t *testing.T) {
|
||||
}(newTestCfg()),
|
||||
latest: newTestCfg(),
|
||||
expect: false,
|
||||
errMsg: "input Bootloader version: 1 is older than latest API version: 2",
|
||||
},
|
||||
"input is newer than latest": {
|
||||
input: func(c attestationconfigapi.SEVSNPVersion) attestationconfigapi.SEVSNPVersion {
|
||||
@ -62,14 +59,80 @@ func TestIsInputNewerThanLatestAPI(t *testing.T) {
|
||||
}
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
isNewer, err := isInputNewerThanOtherVersion(tc.input, tc.latest)
|
||||
assert := assert.New(t)
|
||||
if tc.errMsg != "" {
|
||||
assert.EqualError(err, tc.errMsg)
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
assert.Equal(tc.expect, isNewer)
|
||||
}
|
||||
isNewer := isInputNewerThanOtherSEVSNPVersion(tc.input, tc.latest)
|
||||
assert.Equal(t, tc.expect, isNewer)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsInputNewerThanOtherTDXVersion(t *testing.T) {
|
||||
newTestVersion := func() attestationconfigapi.TDXVersion {
|
||||
return attestationconfigapi.TDXVersion{
|
||||
QESVN: 1,
|
||||
PCESVN: 2,
|
||||
TEETCBSVN: [16]byte{2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
|
||||
QEVendorID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
||||
XFAM: [8]byte{0, 1, 2, 3, 4, 5, 6, 7},
|
||||
}
|
||||
}
|
||||
|
||||
testCases := map[string]struct {
|
||||
latest attestationconfigapi.TDXVersion
|
||||
input attestationconfigapi.TDXVersion
|
||||
expect bool
|
||||
}{
|
||||
"input is older than latest": {
|
||||
input: func(c attestationconfigapi.TDXVersion) attestationconfigapi.TDXVersion {
|
||||
c.QESVN--
|
||||
return c
|
||||
}(newTestVersion()),
|
||||
latest: newTestVersion(),
|
||||
expect: false,
|
||||
},
|
||||
"input has greater and smaller version field than latest": {
|
||||
input: func(c attestationconfigapi.TDXVersion) attestationconfigapi.TDXVersion {
|
||||
c.QESVN++
|
||||
c.PCESVN--
|
||||
return c
|
||||
}(newTestVersion()),
|
||||
latest: newTestVersion(),
|
||||
expect: false,
|
||||
},
|
||||
"input is newer than latest": {
|
||||
input: func(c attestationconfigapi.TDXVersion) attestationconfigapi.TDXVersion {
|
||||
c.QESVN++
|
||||
return c
|
||||
}(newTestVersion()),
|
||||
latest: newTestVersion(),
|
||||
expect: true,
|
||||
},
|
||||
"input is equal to latest": {
|
||||
input: newTestVersion(),
|
||||
latest: newTestVersion(),
|
||||
expect: false,
|
||||
},
|
||||
"tee tcb svn is newer": {
|
||||
input: func(c attestationconfigapi.TDXVersion) attestationconfigapi.TDXVersion {
|
||||
c.TEETCBSVN[4]++
|
||||
return c
|
||||
}(newTestVersion()),
|
||||
latest: newTestVersion(),
|
||||
expect: true,
|
||||
},
|
||||
"xfam is different": {
|
||||
input: func(c attestationconfigapi.TDXVersion) attestationconfigapi.TDXVersion {
|
||||
c.XFAM[3]++
|
||||
return c
|
||||
}(newTestVersion()),
|
||||
latest: newTestVersion(),
|
||||
expect: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
isNewer := isInputNewerThanOtherTDXVersion(tc.input, tc.latest)
|
||||
assert.Equal(t, tc.expect, isNewer)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ import (
|
||||
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
|
||||
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi/cli/client"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
"github.com/edgelesssys/constellation/v2/internal/staticupload"
|
||||
"github.com/spf13/cobra"
|
||||
@ -27,21 +26,21 @@ import (
|
||||
// newDeleteCmd creates the delete command.
|
||||
func newDeleteCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "delete {aws|azure|gcp} {snp-report|guest-firmware} <version>",
|
||||
Use: "delete {aws-sev-snp|azure-sev-snp|azure-tdx|gcp-sev-snp} {attestation-report|guest-firmware} <version>",
|
||||
Short: "Delete an object from the attestationconfig API",
|
||||
Long: "Delete a specific object version from the config api. <version> is the name of the object to delete (without .json suffix)",
|
||||
Example: "COSIGN_PASSWORD=$CPW COSIGN_PRIVATE_KEY=$CKEY cli delete azure snp-report 1.0.0",
|
||||
Args: cobra.MatchAll(cobra.ExactArgs(3), isCloudProvider(0), isValidKind(1)),
|
||||
Example: "COSIGN_PASSWORD=$CPW COSIGN_PRIVATE_KEY=$CKEY cli delete azure-sev-snp attestation-report 1.0.0",
|
||||
Args: cobra.MatchAll(cobra.ExactArgs(3), isAttestationVariant(0), isValidKind(1)),
|
||||
PreRunE: envCheck,
|
||||
RunE: runDelete,
|
||||
}
|
||||
|
||||
recursivelyCmd := &cobra.Command{
|
||||
Use: "recursive {aws|azure|gcp}",
|
||||
Use: "recursive {aws-sev-snp|azure-sev-snp|azure-tdx|gcp-sev-snp}",
|
||||
Short: "delete all objects from the API path constellation/v1/attestation/<csp>",
|
||||
Long: "Delete all objects from the API path constellation/v1/attestation/<csp>",
|
||||
Example: "COSIGN_PASSWORD=$CPW COSIGN_PRIVATE_KEY=$CKEY cli delete recursive azure",
|
||||
Args: cobra.MatchAll(cobra.ExactArgs(1), isCloudProvider(0)),
|
||||
Example: "COSIGN_PASSWORD=$CPW COSIGN_PRIVATE_KEY=$CKEY cli delete recursive azure-sev-snp",
|
||||
Args: cobra.MatchAll(cobra.ExactArgs(1), isAttestationVariant(0)),
|
||||
RunE: runRecursiveDelete,
|
||||
}
|
||||
|
||||
@ -75,16 +74,7 @@ func runDelete(cmd *cobra.Command, args []string) (retErr error) {
|
||||
}
|
||||
}()
|
||||
|
||||
switch deleteCfg.provider {
|
||||
case cloudprovider.AWS:
|
||||
return deleteEntry(cmd.Context(), variant.AWSSEVSNP{}, client, deleteCfg)
|
||||
case cloudprovider.Azure:
|
||||
return deleteEntry(cmd.Context(), variant.AzureSEVSNP{}, client, deleteCfg)
|
||||
case cloudprovider.GCP:
|
||||
return deleteEntry(cmd.Context(), variant.GCPSEVSNP{}, client, deleteCfg)
|
||||
default:
|
||||
return fmt.Errorf("unsupported cloud provider: %s", deleteCfg.provider)
|
||||
}
|
||||
return deleteEntry(cmd.Context(), client, deleteCfg)
|
||||
}
|
||||
|
||||
func runRecursiveDelete(cmd *cobra.Command, args []string) (retErr error) {
|
||||
@ -112,23 +102,13 @@ func runRecursiveDelete(cmd *cobra.Command, args []string) (retErr error) {
|
||||
}
|
||||
}()
|
||||
|
||||
var deletePath string
|
||||
switch deleteCfg.provider {
|
||||
case cloudprovider.AWS:
|
||||
deletePath = path.Join(attestationconfigapi.AttestationURLPath, variant.AWSSEVSNP{}.String())
|
||||
case cloudprovider.Azure:
|
||||
deletePath = path.Join(attestationconfigapi.AttestationURLPath, variant.AzureSEVSNP{}.String())
|
||||
case cloudprovider.GCP:
|
||||
deletePath = path.Join(attestationconfigapi.AttestationURLPath, variant.GCPSEVSNP{}.String())
|
||||
default:
|
||||
return fmt.Errorf("unsupported cloud provider: %s", deleteCfg.provider)
|
||||
}
|
||||
deletePath := path.Join(attestationconfigapi.AttestationURLPath, deleteCfg.variant.String())
|
||||
|
||||
return deleteEntryRecursive(cmd.Context(), deletePath, client, deleteCfg)
|
||||
}
|
||||
|
||||
type deleteConfig struct {
|
||||
provider cloudprovider.Provider
|
||||
variant variant.Variant
|
||||
kind objectKind
|
||||
version string
|
||||
region string
|
||||
@ -155,12 +135,15 @@ func newDeleteConfig(cmd *cobra.Command, args [3]string) (deleteConfig, error) {
|
||||
}
|
||||
apiCfg := getAPIEnvironment(testing)
|
||||
|
||||
provider := cloudprovider.FromString(args[0])
|
||||
variant, err := variant.FromString(args[0])
|
||||
if err != nil {
|
||||
return deleteConfig{}, fmt.Errorf("invalid attestation variant: %q: %w", args[0], err)
|
||||
}
|
||||
kind := kindFromString(args[1])
|
||||
version := args[2]
|
||||
|
||||
return deleteConfig{
|
||||
provider: provider,
|
||||
variant: variant,
|
||||
kind: kind,
|
||||
version: version,
|
||||
region: region,
|
||||
@ -171,12 +154,12 @@ func newDeleteConfig(cmd *cobra.Command, args [3]string) (deleteConfig, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func deleteEntry(ctx context.Context, attvar variant.Variant, client *client.Client, cfg deleteConfig) error {
|
||||
if cfg.kind != snpReport {
|
||||
func deleteEntry(ctx context.Context, client *client.Client, cfg deleteConfig) error {
|
||||
if cfg.kind != attestationReport {
|
||||
return fmt.Errorf("kind %s not supported", cfg.kind)
|
||||
}
|
||||
|
||||
return client.DeleteSEVSNPVersion(ctx, attvar, cfg.version)
|
||||
return client.DeleteVersion(ctx, cfg.variant, cfg.version)
|
||||
}
|
||||
|
||||
func deleteEntryRecursive(ctx context.Context, path string, client *staticupload.Client, cfg deleteConfig) error {
|
||||
|
@ -19,95 +19,117 @@ configapi_cli=$(realpath @@CONFIGAPI_CLI@@)
|
||||
stat "${configapi_cli}" >> /dev/null
|
||||
configapi_cli="${configapi_cli} --testing"
|
||||
###### script body ######
|
||||
function variant() {
|
||||
if [[ $1 == "aws" ]]; then
|
||||
echo "aws-sev-snp"
|
||||
return 0
|
||||
elif [[ $1 == "azure" ]]; then
|
||||
echo "azure-sev-snp"
|
||||
return 0
|
||||
elif [[ $1 == "gcp" ]]; then
|
||||
echo "gcp-sev-snp"
|
||||
return 0
|
||||
else
|
||||
echo "Unknown CSP: $1"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
csp=$1
|
||||
readonly csp
|
||||
attestationType=$(variant "$csp")
|
||||
attestationVariant=$1
|
||||
readonly attestationVariant
|
||||
|
||||
readonly region="eu-west-1"
|
||||
readonly bucket="resource-api-testing"
|
||||
|
||||
tmpdir=$(mktemp -d)
|
||||
readonly tmpdir
|
||||
registerExitHandler "rm -rf $tmpdir"
|
||||
registerExitHandler "rm -rf ${tmpdir}"
|
||||
|
||||
# empty the bucket version state
|
||||
${configapi_cli} delete recursive "$csp" --region "$region" --bucket "$bucket"
|
||||
${configapi_cli} delete recursive "${attestationVariant}" --region "${region}" --bucket "${bucket}"
|
||||
|
||||
# the high version numbers ensure that it's newer than the current latest value
|
||||
readonly current_report_path="$tmpdir/currentSnpReport.json"
|
||||
cat << EOF > "$current_report_path"
|
||||
readonly current_report_path="${tmpdir}/attestationReportCurrent.json"
|
||||
readonly report_path="${tmpdir}/attestationReport.json"
|
||||
readonly older_report_path="${tmpdir}/attestationReportOld.json"
|
||||
|
||||
if [[ ${attestationVariant} == *-tdx ]]; then
|
||||
cat << EOF > "${current_report_path}"
|
||||
{
|
||||
"snp_report": {
|
||||
"reported_tcb": {
|
||||
"bootloader": 1,
|
||||
"tee": 1,
|
||||
"snp": 1,
|
||||
"microcode": 1
|
||||
"header": {
|
||||
"qe_svn": "AAA=",
|
||||
"pce_svn": "AAA=",
|
||||
"qe_vendor_id": "KioqKioqKioqKioqKioqKg=="
|
||||
},
|
||||
"committed_tcb": {
|
||||
"bootloader": 1,
|
||||
"tee": 1,
|
||||
"snp": 1,
|
||||
"microcode": 1
|
||||
},
|
||||
"launch_tcb": {
|
||||
"bootloader": 1,
|
||||
"tee": 1,
|
||||
"snp": 1,
|
||||
"microcode": 1
|
||||
}
|
||||
"td_quote_body": {
|
||||
"tee_tcb_svn": "AAAAAAAAAAAAAAAAAAAAAA==",
|
||||
"xfam": "AAAAAAAAAAA="
|
||||
}
|
||||
}
|
||||
EOF
|
||||
# upload a fake latest version for the fetcher
|
||||
${configapi_cli} upload "$csp" snp-report "$current_report_path" --force --upload-date "2000-01-01-01-01" --region "$region" --bucket "$bucket"
|
||||
|
||||
# the high version numbers ensure that it's newer than the current latest value
|
||||
readonly report_path="$tmpdir/snpReport.json"
|
||||
cat << EOF > "$report_path"
|
||||
cat << EOF > "${report_path}"
|
||||
{
|
||||
"snp_report": {
|
||||
"reported_tcb": {
|
||||
"bootloader": 255,
|
||||
"tee": 255,
|
||||
"snp": 255,
|
||||
"microcode": 255
|
||||
"header": {
|
||||
"qe_svn": "//8=",
|
||||
"pce_svn": "//8=",
|
||||
"qe_vendor_id": "KioqKioqKioqKioqKioqKg=="
|
||||
},
|
||||
"committed_tcb": {
|
||||
"bootloader": 255,
|
||||
"tee": 255,
|
||||
"snp": 255,
|
||||
"microcode": 255
|
||||
},
|
||||
"launch_tcb": {
|
||||
"bootloader": 255,
|
||||
"tee": 255,
|
||||
"snp": 255,
|
||||
"microcode": 255
|
||||
}
|
||||
"td_quote_body": {
|
||||
"tee_tcb_svn": "/////////////////////w==",
|
||||
"xfam": "AQIDBAUGBwg="
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# has an older version
|
||||
readonly older_report_path="$tmpdir/snpReportOld.json"
|
||||
cat << EOF > "$older_report_path"
|
||||
cat << EOF > "${older_report_path}"
|
||||
{
|
||||
"header": {
|
||||
"qe_svn": "//8=",
|
||||
"pce_svn": "/v8=",
|
||||
"qe_vendor_id": "KioqKioqKioqKioqKioqKg=="
|
||||
},
|
||||
"td_quote_body": {
|
||||
"tee_tcb_svn": "/////////////////////g==",
|
||||
"xfam": "AQIDBAUGBwg="
|
||||
}
|
||||
}
|
||||
EOF
|
||||
elif [[ ${attestationVariant} == *-sev-snp ]]; then
|
||||
cat << EOF > "${current_report_path}"
|
||||
{
|
||||
"snp_report": {
|
||||
"reported_tcb": {
|
||||
"bootloader": 1,
|
||||
"tee": 1,
|
||||
"snp": 1,
|
||||
"microcode": 1
|
||||
},
|
||||
"committed_tcb": {
|
||||
"bootloader": 1,
|
||||
"tee": 1,
|
||||
"snp": 1,
|
||||
"microcode": 1
|
||||
},
|
||||
"launch_tcb": {
|
||||
"bootloader": 1,
|
||||
"tee": 1,
|
||||
"snp": 1,
|
||||
"microcode": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
# the high version numbers ensure that it's newer than the current latest value
|
||||
cat << EOF > "${report_path}"
|
||||
{
|
||||
"snp_report": {
|
||||
"reported_tcb": {
|
||||
"bootloader": 255,
|
||||
"tee": 255,
|
||||
"snp": 255,
|
||||
"microcode": 255
|
||||
},
|
||||
"committed_tcb": {
|
||||
"bootloader": 255,
|
||||
"tee": 255,
|
||||
"snp": 255,
|
||||
"microcode": 255
|
||||
},
|
||||
"launch_tcb": {
|
||||
"bootloader": 255,
|
||||
"tee": 255,
|
||||
"snp": 255,
|
||||
"microcode": 255
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
# has an older version
|
||||
cat << EOF > "${older_report_path}"
|
||||
{
|
||||
"snp_report": {
|
||||
"reported_tcb": {
|
||||
@ -131,22 +153,40 @@ cat << EOF > "$older_report_path"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
else
|
||||
echo "Unknown attestation variant: ${attestationVariant}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# upload a fake latest version for the fetcher
|
||||
${configapi_cli} upload "${attestationVariant}" attestation-report "${current_report_path}" --force --upload-date "2000-01-01-01-01" --region "${region}" --bucket "${bucket}"
|
||||
|
||||
# report 3 versions with different dates to fill the reporter cache
|
||||
readonly date_oldest="2023-02-01-03-04"
|
||||
${configapi_cli} upload "$csp" snp-report "$older_report_path" --upload-date "$date_oldest" --region "$region" --bucket "$bucket" --cache-window-size 3
|
||||
${configapi_cli} upload "${attestationVariant}" attestation-report "${older_report_path}" --upload-date "${date_oldest}" --region "${region}" --bucket "${bucket}" --cache-window-size 3
|
||||
readonly date_older="2023-02-02-03-04"
|
||||
${configapi_cli} upload "$csp" snp-report "$older_report_path" --upload-date "$date_older" --region "$region" --bucket "$bucket" --cache-window-size 3
|
||||
${configapi_cli} upload "${attestationVariant}" attestation-report "${older_report_path}" --upload-date "${date_older}" --region "${region}" --bucket "${bucket}" --cache-window-size 3
|
||||
readonly date="2023-02-03-03-04"
|
||||
${configapi_cli} upload "$csp" snp-report "$report_path" --upload-date "$date" --region "$region" --bucket "$bucket" --cache-window-size 3
|
||||
${configapi_cli} upload "${attestationVariant}" attestation-report "${report_path}" --upload-date "${date}" --region "${region}" --bucket "${bucket}" --cache-window-size 3
|
||||
|
||||
# expect that $date_oldest is served as latest version
|
||||
basepath="constellation/v1/attestation/${attestationType}"
|
||||
basepath="constellation/v1/attestation/${attestationVariant}"
|
||||
baseurl="https://d33dzgxuwsgbpw.cloudfront.net/${basepath}"
|
||||
if ! curl -fsSL "${baseurl}"/${date_oldest}.json > version.json; then
|
||||
if ! curl -fsSL "${baseurl}/${date_oldest}.json" > version.json; then
|
||||
echo "Checking for uploaded version file ${basepath}/${date_oldest}.json: request returned ${?}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ${attestationVariant} == *-tdx ]]; then
|
||||
# check that version values are equal to expected
|
||||
if ! cmp -s <(echo -n '{"qeSVN":65535,"pceSVN":65534,"teeTCBSVN":[255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,254],"qeVendorID":[42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42],"xfam":[1,2,3,4,5,6,7,8]}') version.json; then
|
||||
echo "The version content:"
|
||||
cat version.json
|
||||
echo " is not equal to the expected version content:"
|
||||
echo '{"qeSVN":65535,"pceSVN":65534,"teeTCBSVN":[255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,254],"qeVendorID":[42,42,42,42,42,42,42,42,42,42,42,42,42,42,42,42],"xfam":[1,2,3,4,5,6,7,8]}'
|
||||
exit 1
|
||||
fi
|
||||
elif [[ ${attestationVariant} == *-sev-snp ]]; then
|
||||
# check that version values are equal to expected
|
||||
if ! cmp -s <(echo -n '{"bootloader":255,"tee":255,"snp":255,"microcode":254}') version.json; then
|
||||
echo "The version content:"
|
||||
@ -155,10 +195,13 @@ if ! cmp -s <(echo -n '{"bootloader":255,"tee":255,"snp":255,"microcode":254}')
|
||||
echo '{"bootloader":255,"tee":255,"snp":255,"microcode":254}'
|
||||
exit 1
|
||||
fi
|
||||
if ! curl -fsSL "${baseurl}"/${date_oldest}.json.sig > /dev/null; then
|
||||
fi
|
||||
|
||||
if ! curl -fsSL "${baseurl}/${date_oldest}.json.sig" > /dev/null; then
|
||||
echo "Checking for uploaded version signature file ${basepath}/${date_oldest}.json.sig: request returned ${?}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# check list endpoint
|
||||
if ! curl -fsSL "${baseurl}"/list > list.json; then
|
||||
echo "Checking for uploaded list file ${basepath}/list: request returned ${?}"
|
||||
@ -174,28 +217,28 @@ if ! cmp -s <(echo -n '["2023-02-01-03-04.json","2000-01-01-01-01.json"]') list.
|
||||
fi
|
||||
|
||||
# check that the other versions are not uploaded
|
||||
http_code=$(curl -sSL -w '%{http_code}\n' -o /dev/null "${baseurl}"/${date_older}.json)
|
||||
if [[ $http_code -ne 404 ]]; then
|
||||
http_code=$(curl -sSL -w '%{http_code}\n' -o /dev/null "${baseurl}/${date_older}.json")
|
||||
if [[ ${http_code} -ne 404 ]]; then
|
||||
echo "Expected HTTP code 404 for: ${basepath}/${date_older}.json, but got ${http_code}"
|
||||
exit 1
|
||||
fi
|
||||
http_code=$(curl -sSL -w '%{http_code}\n' -o /dev/null "${baseurl}"/${date}.json.sig)
|
||||
if [[ $http_code -ne 404 ]]; then
|
||||
http_code=$(curl -sSL -w '%{http_code}\n' -o /dev/null "${baseurl}/${date}.json.sig")
|
||||
if [[ ${http_code} -ne 404 ]]; then
|
||||
echo "Expected HTTP code 404 for: ${basepath}/${date}.json, but got ${http_code}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
${configapi_cli} delete "$csp" snp-report "$date_oldest" --region "$region" --bucket "$bucket"
|
||||
${configapi_cli} delete "${attestationVariant}" attestation-report "${date_oldest}" --region "${region}" --bucket "${bucket}"
|
||||
|
||||
# Omit -f to check for 404. We want to check that a file was deleted, therefore we expect the query to fail.
|
||||
http_code=$(curl -sSL -w '%{http_code}\n' -o /dev/null "${baseurl}"/${date_oldest}.json)
|
||||
if [[ $http_code -ne 404 ]]; then
|
||||
http_code=$(curl -sSL -w '%{http_code}\n' -o /dev/null "${baseurl}/${date_oldest}.json")
|
||||
if [[ ${http_code} -ne 404 ]]; then
|
||||
echo "Expected HTTP code 404 for: ${basepath}/${date_oldest}.json, but got ${http_code}"
|
||||
exit 1
|
||||
fi
|
||||
# Omit -f to check for 404. We want to check that a file was deleted, therefore we expect the query to fail.
|
||||
http_code=$(curl -sSL -w '%{http_code}\n' -o /dev/null "${baseurl}"/${date_oldest}.json.sig)
|
||||
if [[ $http_code -ne 404 ]]; then
|
||||
http_code=$(curl -sSL -w '%{http_code}\n' -o /dev/null "${baseurl}/${date_oldest}.json.sig")
|
||||
if [[ ${http_code} -ne 404 ]]; then
|
||||
echo "Expected HTTP code 404 for: ${basepath}/${date_oldest}.json, but got ${http_code}"
|
||||
exit 1
|
||||
fi
|
||||
|
@ -7,6 +7,7 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
@ -17,30 +18,30 @@ import (
|
||||
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi/cli/client"
|
||||
"github.com/edgelesssys/constellation/v2/internal/api/fetcher"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
"github.com/edgelesssys/constellation/v2/internal/staticupload"
|
||||
"github.com/edgelesssys/constellation/v2/internal/verify"
|
||||
"github.com/google/go-tdx-guest/proto/tdx"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newUploadCmd() *cobra.Command {
|
||||
uploadCmd := &cobra.Command{
|
||||
Use: "upload {aws|azure|gcp} {snp-report|guest-firmware} <path>",
|
||||
Use: "upload {aws-sev-snp|azure-sev-snp|azure-tdx|gcp-sev-snp} {attestation-report|guest-firmware} <path>",
|
||||
Short: "Upload an object to the attestationconfig API",
|
||||
|
||||
Long: fmt.Sprintf("Upload a new object to the attestationconfig API. For snp-reports the new object is added to a cache folder first."+
|
||||
Long: fmt.Sprintf("Upload a new object to the attestationconfig API. For snp-reports the new object is added to a cache folder first.\n"+
|
||||
"The CLI then determines the lowest version within the cache-window present in the cache and writes that value to the config api if necessary. "+
|
||||
"For guest-firmware objects the object is added to the API directly. "+
|
||||
"For guest-firmware objects the object is added to the API directly.\n"+
|
||||
"Please authenticate with AWS through your preferred method (e.g. environment variables, CLI) "+
|
||||
"to be able to upload to S3. Set the %s and %s environment variables to authenticate with cosign.",
|
||||
envCosignPrivateKey, envCosignPwd,
|
||||
),
|
||||
Example: "COSIGN_PASSWORD=$CPW COSIGN_PRIVATE_KEY=$CKEY cli upload azure snp-report /some/path/report.json",
|
||||
Example: "COSIGN_PASSWORD=$CPW COSIGN_PRIVATE_KEY=$CKEY cli upload azure-sev-snp attestation-report /some/path/report.json",
|
||||
|
||||
Args: cobra.MatchAll(cobra.ExactArgs(3), isCloudProvider(0), isValidKind(1)),
|
||||
Args: cobra.MatchAll(cobra.ExactArgs(3), isAttestationVariant(0), isValidKind(1)),
|
||||
PreRunE: envCheck,
|
||||
RunE: runUpload,
|
||||
}
|
||||
@ -91,42 +92,19 @@ func runUpload(cmd *cobra.Command, args []string) (retErr error) {
|
||||
return fmt.Errorf("creating client: %w", err)
|
||||
}
|
||||
|
||||
var attestation variant.Variant
|
||||
switch uploadCfg.provider {
|
||||
case cloudprovider.AWS:
|
||||
attestation = variant.AWSSEVSNP{}
|
||||
case cloudprovider.Azure:
|
||||
attestation = variant.AzureSEVSNP{}
|
||||
case cloudprovider.GCP:
|
||||
attestation = variant.GCPSEVSNP{}
|
||||
default:
|
||||
return fmt.Errorf("unsupported cloud provider: %s", uploadCfg.provider)
|
||||
return uploadReport(ctx, client, uploadCfg, file.NewHandler(afero.NewOsFs()), log)
|
||||
}
|
||||
|
||||
return uploadReport(ctx, attestation, client, uploadCfg, file.NewHandler(afero.NewOsFs()), log)
|
||||
}
|
||||
|
||||
func uploadReport(ctx context.Context,
|
||||
attestation variant.Variant,
|
||||
apiClient *client.Client,
|
||||
cfg uploadConfig,
|
||||
fs file.Handler,
|
||||
log *slog.Logger,
|
||||
func uploadReport(
|
||||
ctx context.Context, apiClient *client.Client,
|
||||
cfg uploadConfig, fs file.Handler, log *slog.Logger,
|
||||
) error {
|
||||
if cfg.kind != snpReport {
|
||||
if cfg.kind != attestationReport {
|
||||
return fmt.Errorf("kind %s not supported", cfg.kind)
|
||||
}
|
||||
|
||||
log.Info(fmt.Sprintf("Reading SNP report from file: %s", cfg.path))
|
||||
var report verify.Report
|
||||
if err := fs.ReadJSON(cfg.path, &report); err != nil {
|
||||
return fmt.Errorf("reading snp report: %w", err)
|
||||
}
|
||||
|
||||
inputVersion := convertTCBVersionToSNPVersion(report.SNPReport.LaunchTCB)
|
||||
log.Info(fmt.Sprintf("Input report: %+v", inputVersion))
|
||||
|
||||
latestAPIVersionAPI, err := attestationconfigapi.NewFetcherWithCustomCDNAndCosignKey(cfg.url, cfg.cosignPublicKey).FetchLatestVersion(ctx, attestation)
|
||||
apiFetcher := attestationconfigapi.NewFetcherWithCustomCDNAndCosignKey(cfg.url, cfg.cosignPublicKey)
|
||||
latestVersionInAPI, err := apiFetcher.FetchLatestVersion(ctx, cfg.variant)
|
||||
if err != nil {
|
||||
var notFoundErr *fetcher.NotFoundError
|
||||
if errors.As(err, ¬FoundErr) {
|
||||
@ -136,12 +114,39 @@ func uploadReport(ctx context.Context,
|
||||
}
|
||||
}
|
||||
|
||||
latestAPIVersion := latestAPIVersionAPI.SEVSNPVersion
|
||||
if err := apiClient.UploadSEVSNPVersionLatest(ctx, attestation, inputVersion, latestAPIVersion, cfg.uploadDate, cfg.force); err != nil {
|
||||
if errors.Is(err, client.ErrNoNewerVersion) {
|
||||
log.Info(fmt.Sprintf("Input version: %+v is not newer than latest API version: %+v", inputVersion, latestAPIVersion))
|
||||
return nil
|
||||
var newVersion, latestVersion any
|
||||
switch cfg.variant {
|
||||
case variant.AWSSEVSNP{}, variant.AzureSEVSNP{}, variant.GCPSEVSNP{}:
|
||||
latestVersion = latestVersionInAPI.SEVSNPVersion
|
||||
|
||||
log.Info(fmt.Sprintf("Reading SNP report from file: %s", cfg.path))
|
||||
var report verify.Report
|
||||
if err := fs.ReadJSON(cfg.path, &report); err != nil {
|
||||
return fmt.Errorf("reading snp report: %w", err)
|
||||
}
|
||||
|
||||
newVersion = convertTCBVersionToSNPVersion(report.SNPReport.LaunchTCB)
|
||||
log.Info(fmt.Sprintf("Input SNP report: %+v", newVersion))
|
||||
|
||||
case variant.AzureTDX{}:
|
||||
latestVersion = latestVersionInAPI.TDXVersion
|
||||
|
||||
log.Info(fmt.Sprintf("Reading TDX report from file: %s", cfg.path))
|
||||
var report *tdx.QuoteV4
|
||||
if err := fs.ReadJSON(cfg.path, &report); err != nil {
|
||||
return fmt.Errorf("reading tdx report: %w", err)
|
||||
}
|
||||
|
||||
newVersion = convertQuoteToTDXVersion(report)
|
||||
log.Info(fmt.Sprintf("Input TDX report: %+v", newVersion))
|
||||
|
||||
default:
|
||||
return fmt.Errorf("variant %s not supported", cfg.variant)
|
||||
}
|
||||
|
||||
if err := apiClient.UploadLatestVersion(
|
||||
ctx, cfg.variant, newVersion, latestVersion, cfg.uploadDate, cfg.force,
|
||||
); err != nil && !errors.Is(err, client.ErrNoNewerVersion) {
|
||||
return fmt.Errorf("updating latest version: %w", err)
|
||||
}
|
||||
|
||||
@ -157,8 +162,18 @@ func convertTCBVersionToSNPVersion(tcb verify.TCBVersion) attestationconfigapi.S
|
||||
}
|
||||
}
|
||||
|
||||
func convertQuoteToTDXVersion(quote *tdx.QuoteV4) attestationconfigapi.TDXVersion {
|
||||
return attestationconfigapi.TDXVersion{
|
||||
QESVN: binary.LittleEndian.Uint16(quote.Header.QeSvn),
|
||||
PCESVN: binary.LittleEndian.Uint16(quote.Header.PceSvn),
|
||||
QEVendorID: [16]byte(quote.Header.QeVendorId),
|
||||
XFAM: [8]byte(quote.TdQuoteBody.Xfam),
|
||||
TEETCBSVN: [16]byte(quote.TdQuoteBody.TeeTcbSvn),
|
||||
}
|
||||
}
|
||||
|
||||
type uploadConfig struct {
|
||||
provider cloudprovider.Provider
|
||||
variant variant.Variant
|
||||
kind objectKind
|
||||
path string
|
||||
uploadDate time.Time
|
||||
@ -210,12 +225,16 @@ func newConfig(cmd *cobra.Command, args [3]string) (uploadConfig, error) {
|
||||
return uploadConfig{}, fmt.Errorf("getting cache window size: %w", err)
|
||||
}
|
||||
|
||||
provider := cloudprovider.FromString(args[0])
|
||||
variant, err := variant.FromString(args[0])
|
||||
if err != nil {
|
||||
return uploadConfig{}, fmt.Errorf("invalid attestation variant: %q: %w", args[0], err)
|
||||
}
|
||||
|
||||
kind := kindFromString(args[1])
|
||||
path := args[2]
|
||||
|
||||
return uploadConfig{
|
||||
provider: provider,
|
||||
variant: variant,
|
||||
kind: kind,
|
||||
path: path,
|
||||
uploadDate: uploadDate,
|
||||
|
@ -10,16 +10,22 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func isCloudProvider(arg int) cobra.PositionalArgs {
|
||||
func isAttestationVariant(arg int) cobra.PositionalArgs {
|
||||
return func(_ *cobra.Command, args []string) error {
|
||||
if provider := cloudprovider.FromString(args[arg]); provider == cloudprovider.Unknown {
|
||||
return fmt.Errorf("argument %s isn't a valid cloud provider", args[arg])
|
||||
attestationVariant, err := variant.FromString(args[arg])
|
||||
if err != nil {
|
||||
return fmt.Errorf("argument %s isn't a valid attestation variant", args[arg])
|
||||
}
|
||||
switch attestationVariant {
|
||||
case variant.AWSSEVSNP{}, variant.AzureSEVSNP{}, variant.AzureTDX{}, variant.GCPSEVSNP{}:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("argument %s isn't a supported attestation variant", args[arg])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,15 +44,14 @@ type objectKind string
|
||||
const (
|
||||
// unknown is the default objectKind and does nothing.
|
||||
unknown objectKind = "unknown-kind"
|
||||
snpReport objectKind = "snp-report"
|
||||
tdxReport objectKind = "tdx-report"
|
||||
attestationReport objectKind = "attestation-report"
|
||||
guestFirmware objectKind = "guest-firmware"
|
||||
)
|
||||
|
||||
func kindFromString(s string) objectKind {
|
||||
lower := strings.ToLower(s)
|
||||
switch objectKind(lower) {
|
||||
case snpReport, guestFirmware, tdxReport:
|
||||
case attestationReport, guestFirmware:
|
||||
return objectKind(lower)
|
||||
default:
|
||||
return unknown
|
||||
|
@ -20,7 +20,7 @@ const cosignPublicKey = constants.CosignPublicKeyReleases
|
||||
|
||||
// Fetcher fetches config API resources without authentication.
|
||||
type Fetcher interface {
|
||||
FetchLatestVersion(ctx context.Context, attestation variant.Variant) (VersionAPIEntry, error)
|
||||
FetchLatestVersion(ctx context.Context, attestation variant.Variant) (Entry, error)
|
||||
}
|
||||
|
||||
// fetcher fetches AttestationCfg API resources without authentication.
|
||||
@ -60,10 +60,10 @@ func newFetcherWithClientAndVerifier(client apifetcher.HTTPClient, cosignVerifie
|
||||
}
|
||||
|
||||
// FetchLatestVersion returns the latest versions of the given type.
|
||||
func (f *fetcher) FetchLatestVersion(ctx context.Context, variant variant.Variant) (VersionAPIEntry, error) {
|
||||
func (f *fetcher) FetchLatestVersion(ctx context.Context, variant variant.Variant) (Entry, error) {
|
||||
list, err := f.fetchVersionList(ctx, variant)
|
||||
if err != nil {
|
||||
return VersionAPIEntry{}, err
|
||||
return Entry{}, err
|
||||
}
|
||||
|
||||
// latest version is first in list
|
||||
@ -71,11 +71,10 @@ func (f *fetcher) FetchLatestVersion(ctx context.Context, variant variant.Varian
|
||||
}
|
||||
|
||||
// fetchVersionList fetches the version list information from the config API.
|
||||
func (f *fetcher) fetchVersionList(ctx context.Context, variant variant.Variant) (VersionList, error) {
|
||||
// TODO(derpsteb): Replace with FetchAndVerify once we move to v2 of the config API and the list is saved as (.json) file.
|
||||
fetchedList, err := apifetcher.Fetch(ctx, f.HTTPClient, f.cdnURL, VersionList{Variant: variant})
|
||||
func (f *fetcher) fetchVersionList(ctx context.Context, variant variant.Variant) (List, error) {
|
||||
fetchedList, err := apifetcher.FetchAndVerify(ctx, f.HTTPClient, f.cdnURL, List{Variant: variant}, f.verifier)
|
||||
if err != nil {
|
||||
return VersionList{}, fmt.Errorf("fetching version list: %w", err)
|
||||
return List{}, fmt.Errorf("fetching version list: %w", err)
|
||||
}
|
||||
|
||||
// Set the attestation variant of the list as it is not part of the marshalled JSON retrieved by Fetch
|
||||
@ -85,14 +84,14 @@ func (f *fetcher) fetchVersionList(ctx context.Context, variant variant.Variant)
|
||||
}
|
||||
|
||||
// fetchVersion fetches the version information from the config API.
|
||||
func (f *fetcher) fetchVersion(ctx context.Context, version string, variant variant.Variant) (VersionAPIEntry, error) {
|
||||
obj := VersionAPIEntry{
|
||||
func (f *fetcher) fetchVersion(ctx context.Context, version string, variant variant.Variant) (Entry, error) {
|
||||
obj := Entry{
|
||||
Version: version,
|
||||
Variant: variant,
|
||||
}
|
||||
fetchedVersion, err := apifetcher.FetchAndVerify(ctx, f.HTTPClient, f.cdnURL, obj, f.verifier)
|
||||
if err != nil {
|
||||
return VersionAPIEntry{}, fmt.Errorf("fetching version %q: %w", version, err)
|
||||
return Entry{}, fmt.Errorf("fetching version %q: %w", version, err)
|
||||
}
|
||||
|
||||
// Set the attestation variant of the list as it is not part of the marshalled JSON retrieved by FetchAndVerify
|
||||
|
@ -22,7 +22,7 @@ import (
|
||||
)
|
||||
|
||||
func TestFetchLatestSEVSNPVersion(t *testing.T) {
|
||||
latestVersionSNP := VersionAPIEntry{
|
||||
latestVersionSNP := Entry{
|
||||
SEVSNPVersion: SEVSNPVersion{
|
||||
Microcode: 93,
|
||||
TEE: 0,
|
||||
@ -30,7 +30,7 @@ func TestFetchLatestSEVSNPVersion(t *testing.T) {
|
||||
Bootloader: 2,
|
||||
},
|
||||
}
|
||||
olderVersionSNP := VersionAPIEntry{
|
||||
olderVersionSNP := Entry{
|
||||
SEVSNPVersion: SEVSNPVersion{
|
||||
Microcode: 1,
|
||||
TEE: 0,
|
||||
@ -38,7 +38,7 @@ func TestFetchLatestSEVSNPVersion(t *testing.T) {
|
||||
Bootloader: 1,
|
||||
},
|
||||
}
|
||||
latestVersionTDX := VersionAPIEntry{
|
||||
latestVersionTDX := Entry{
|
||||
TDXVersion: TDXVersion{
|
||||
QESVN: 2,
|
||||
PCESVN: 3,
|
||||
@ -47,7 +47,7 @@ func TestFetchLatestSEVSNPVersion(t *testing.T) {
|
||||
XFAM: [8]byte{6},
|
||||
},
|
||||
}
|
||||
olderVersionTDX := VersionAPIEntry{
|
||||
olderVersionTDX := Entry{
|
||||
TDXVersion: TDXVersion{
|
||||
QESVN: 1,
|
||||
PCESVN: 2,
|
||||
@ -64,30 +64,30 @@ func TestFetchLatestSEVSNPVersion(t *testing.T) {
|
||||
timeAtTest time.Time
|
||||
wantErr bool
|
||||
attestation variant.Variant
|
||||
expectedVersion VersionAPIEntry
|
||||
olderVersion VersionAPIEntry
|
||||
latestVersion VersionAPIEntry
|
||||
expectedVersion Entry
|
||||
olderVersion Entry
|
||||
latestVersion Entry
|
||||
}{
|
||||
"get latest version azure-sev-snp": {
|
||||
fetcherVersions: []string{latestStr, olderStr},
|
||||
attestation: variant.AzureSEVSNP{},
|
||||
expectedVersion: func() VersionAPIEntry { tmp := latestVersionSNP; tmp.Variant = variant.AzureSEVSNP{}; return tmp }(),
|
||||
olderVersion: func() VersionAPIEntry { tmp := olderVersionSNP; tmp.Variant = variant.AzureSEVSNP{}; return tmp }(),
|
||||
latestVersion: func() VersionAPIEntry { tmp := latestVersionSNP; tmp.Variant = variant.AzureSEVSNP{}; return tmp }(),
|
||||
expectedVersion: func() Entry { tmp := latestVersionSNP; tmp.Variant = variant.AzureSEVSNP{}; return tmp }(),
|
||||
olderVersion: func() Entry { tmp := olderVersionSNP; tmp.Variant = variant.AzureSEVSNP{}; return tmp }(),
|
||||
latestVersion: func() Entry { tmp := latestVersionSNP; tmp.Variant = variant.AzureSEVSNP{}; return tmp }(),
|
||||
},
|
||||
"get latest version aws-sev-snp": {
|
||||
fetcherVersions: []string{latestStr, olderStr},
|
||||
attestation: variant.AWSSEVSNP{},
|
||||
expectedVersion: func() VersionAPIEntry { tmp := latestVersionSNP; tmp.Variant = variant.AWSSEVSNP{}; return tmp }(),
|
||||
olderVersion: func() VersionAPIEntry { tmp := olderVersionSNP; tmp.Variant = variant.AWSSEVSNP{}; return tmp }(),
|
||||
latestVersion: func() VersionAPIEntry { tmp := latestVersionSNP; tmp.Variant = variant.AWSSEVSNP{}; return tmp }(),
|
||||
expectedVersion: func() Entry { tmp := latestVersionSNP; tmp.Variant = variant.AWSSEVSNP{}; return tmp }(),
|
||||
olderVersion: func() Entry { tmp := olderVersionSNP; tmp.Variant = variant.AWSSEVSNP{}; return tmp }(),
|
||||
latestVersion: func() Entry { tmp := latestVersionSNP; tmp.Variant = variant.AWSSEVSNP{}; return tmp }(),
|
||||
},
|
||||
"get latest version azure-tdx": {
|
||||
fetcherVersions: []string{latestStr, olderStr},
|
||||
attestation: variant.AzureTDX{},
|
||||
expectedVersion: func() VersionAPIEntry { tmp := latestVersionTDX; tmp.Variant = variant.AzureTDX{}; return tmp }(),
|
||||
olderVersion: func() VersionAPIEntry { tmp := olderVersionTDX; tmp.Variant = variant.AzureTDX{}; return tmp }(),
|
||||
latestVersion: func() VersionAPIEntry { tmp := latestVersionTDX; tmp.Variant = variant.AzureTDX{}; return tmp }(),
|
||||
expectedVersion: func() Entry { tmp := latestVersionTDX; tmp.Variant = variant.AzureTDX{}; return tmp }(),
|
||||
olderVersion: func() Entry { tmp := olderVersionTDX; tmp.Variant = variant.AzureTDX{}; return tmp }(),
|
||||
latestVersion: func() Entry { tmp := latestVersionTDX; tmp.Variant = variant.AzureTDX{}; return tmp }(),
|
||||
},
|
||||
}
|
||||
for name, tc := range testCases {
|
||||
@ -119,14 +119,15 @@ type fakeConfigAPIHandler struct {
|
||||
attestation variant.Variant
|
||||
versions []string
|
||||
latestDate string
|
||||
latestVersion VersionAPIEntry
|
||||
latestVersion Entry
|
||||
olderDate string
|
||||
olderVersion VersionAPIEntry
|
||||
olderVersion Entry
|
||||
}
|
||||
|
||||
// RoundTrip resolves the request and returns a dummy response.
|
||||
func (f *fakeConfigAPIHandler) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
if req.URL.Path == fmt.Sprintf("/constellation/v1/attestation/%s/list", f.attestation.String()) {
|
||||
switch req.URL.Path {
|
||||
case fmt.Sprintf("/constellation/v1/attestation/%s/list", f.attestation.String()):
|
||||
res := &http.Response{}
|
||||
bt, err := json.Marshal(f.versions)
|
||||
if err != nil {
|
||||
@ -137,7 +138,14 @@ func (f *fakeConfigAPIHandler) RoundTrip(req *http.Request) (*http.Response, err
|
||||
res.Header.Set("Content-Type", "application/json")
|
||||
res.StatusCode = http.StatusOK
|
||||
return res, nil
|
||||
} else if req.URL.Path == fmt.Sprintf("/constellation/v1/attestation/%s/%s", f.attestation.String(), f.latestDate) {
|
||||
|
||||
case fmt.Sprintf("/constellation/v1/attestation/%s/list.sig", f.attestation.String()):
|
||||
res := &http.Response{}
|
||||
res.Body = io.NopCloser(bytes.NewReader([]byte("null")))
|
||||
res.StatusCode = http.StatusOK
|
||||
return res, nil
|
||||
|
||||
case fmt.Sprintf("/constellation/v1/attestation/%s/%s", f.attestation.String(), f.latestDate):
|
||||
res := &http.Response{}
|
||||
bt, err := json.Marshal(f.latestVersion)
|
||||
if err != nil {
|
||||
@ -147,7 +155,7 @@ func (f *fakeConfigAPIHandler) RoundTrip(req *http.Request) (*http.Response, err
|
||||
res.StatusCode = http.StatusOK
|
||||
return res, nil
|
||||
|
||||
} else if req.URL.Path == fmt.Sprintf("/constellation/v1/attestation/%s/%s", f.attestation.String(), f.olderDate) {
|
||||
case fmt.Sprintf("/constellation/v1/attestation/%s/%s", f.attestation.String(), f.olderDate):
|
||||
res := &http.Response{}
|
||||
bt, err := json.Marshal(f.olderVersion)
|
||||
if err != nil {
|
||||
@ -156,13 +164,14 @@ func (f *fakeConfigAPIHandler) RoundTrip(req *http.Request) (*http.Response, err
|
||||
res.Body = io.NopCloser(bytes.NewReader(bt))
|
||||
res.StatusCode = http.StatusOK
|
||||
return res, nil
|
||||
} else if req.URL.Path == fmt.Sprintf("/constellation/v1/attestation/%s/%s.sig", f.attestation.String(), f.latestDate) {
|
||||
|
||||
case fmt.Sprintf("/constellation/v1/attestation/%s/%s.sig", f.attestation.String(), f.latestDate):
|
||||
res := &http.Response{}
|
||||
res.Body = io.NopCloser(bytes.NewReader([]byte("null")))
|
||||
res.StatusCode = http.StatusOK
|
||||
return res, nil
|
||||
|
||||
} else if req.URL.Path == fmt.Sprintf("/constellation/v1/attestation/%s/%s.sig", f.attestation.String(), f.olderDate) {
|
||||
case fmt.Sprintf("/constellation/v1/attestation/%s/%s.sig", f.attestation.String(), f.olderDate):
|
||||
res := &http.Response{}
|
||||
res.Body = io.NopCloser(bytes.NewReader([]byte("null")))
|
||||
res.StatusCode = http.StatusOK
|
||||
|
@ -45,12 +45,12 @@ type TDXVersion struct {
|
||||
XFAM [8]byte `json:"xfam"`
|
||||
}
|
||||
|
||||
// VersionAPIEntry is the request to get the version information of the specific version in the config api.
|
||||
// Entry is the request to get the version information of the specific version in the config api.
|
||||
//
|
||||
// TODO: Because variant is not part of the marshalled JSON, fetcher and client methods need to fill the variant property.
|
||||
// In API v2 we should embed the variant in the object and remove some code from fetcher & client.
|
||||
// That would remove the possibility of some fetcher/client code forgetting to set the variant.
|
||||
type VersionAPIEntry struct {
|
||||
type Entry struct {
|
||||
Version string `json:"-"`
|
||||
Variant variant.Variant `json:"-"`
|
||||
SEVSNPVersion
|
||||
@ -58,12 +58,12 @@ type VersionAPIEntry struct {
|
||||
}
|
||||
|
||||
// JSONPath returns the path to the JSON file for the request to the config api.
|
||||
func (i VersionAPIEntry) JSONPath() string {
|
||||
func (i Entry) JSONPath() string {
|
||||
return path.Join(AttestationURLPath, i.Variant.String(), i.Version)
|
||||
}
|
||||
|
||||
// ValidateRequest validates the request.
|
||||
func (i VersionAPIEntry) ValidateRequest() error {
|
||||
func (i Entry) ValidateRequest() error {
|
||||
if !strings.HasSuffix(i.Version, ".json") {
|
||||
return fmt.Errorf("version has no .json suffix")
|
||||
}
|
||||
@ -71,47 +71,47 @@ func (i VersionAPIEntry) ValidateRequest() error {
|
||||
}
|
||||
|
||||
// Validate is a No-Op at the moment.
|
||||
func (i VersionAPIEntry) Validate() error {
|
||||
func (i Entry) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// VersionList is the request to retrieve of all versions in the API for one attestation variant.
|
||||
// List is the request to retrieve of all versions in the API for one attestation variant.
|
||||
//
|
||||
// TODO: Because variant is not part of the marshalled JSON, fetcher and client methods need to fill the variant property.
|
||||
// In API v2 we should embed the variant in the object and remove some code from fetcher & client.
|
||||
// That would remove the possibility of some fetcher/client code forgetting to set the variant.
|
||||
type VersionList struct {
|
||||
type List struct {
|
||||
Variant variant.Variant
|
||||
List []string
|
||||
}
|
||||
|
||||
// MarshalJSON marshals the i's list property to JSON.
|
||||
func (i VersionList) MarshalJSON() ([]byte, error) {
|
||||
func (i List) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(i.List)
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals a list of strings into i's list property.
|
||||
func (i *VersionList) UnmarshalJSON(data []byte) error {
|
||||
func (i *List) UnmarshalJSON(data []byte) error {
|
||||
return json.Unmarshal(data, &i.List)
|
||||
}
|
||||
|
||||
// JSONPath returns the path to the JSON file for the request to the config api.
|
||||
func (i VersionList) JSONPath() string {
|
||||
func (i List) JSONPath() string {
|
||||
return path.Join(AttestationURLPath, i.Variant.String(), "list")
|
||||
}
|
||||
|
||||
// ValidateRequest is a NoOp as there is no input.
|
||||
func (i VersionList) ValidateRequest() error {
|
||||
func (i List) ValidateRequest() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SortReverse sorts the list of versions in reverse order.
|
||||
func (i *VersionList) SortReverse() {
|
||||
func (i *List) SortReverse() {
|
||||
sort.Sort(sort.Reverse(sort.StringSlice(i.List)))
|
||||
}
|
||||
|
||||
// AddVersion adds new to i's list and sorts the element in descending order.
|
||||
func (i *VersionList) AddVersion(new string) {
|
||||
func (i *List) AddVersion(new string) {
|
||||
i.List = append(i.List, new)
|
||||
i.List = variant.RemoveDuplicate(i.List)
|
||||
|
||||
@ -119,7 +119,7 @@ func (i *VersionList) AddVersion(new string) {
|
||||
}
|
||||
|
||||
// Validate validates the response.
|
||||
func (i VersionList) Validate() error {
|
||||
func (i List) Validate() error {
|
||||
if len(i.List) < 1 {
|
||||
return fmt.Errorf("no versions found in /list")
|
||||
}
|
||||
|
@ -16,21 +16,21 @@ import (
|
||||
|
||||
func TestVersionListMarshalUnmarshalJSON(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
input VersionList
|
||||
output VersionList
|
||||
input List
|
||||
output List
|
||||
wantDiff bool
|
||||
}{
|
||||
"success": {
|
||||
input: VersionList{List: []string{"v1", "v2"}},
|
||||
output: VersionList{List: []string{"v1", "v2"}},
|
||||
input: List{List: []string{"v1", "v2"}},
|
||||
output: List{List: []string{"v1", "v2"}},
|
||||
},
|
||||
"variant is lost": {
|
||||
input: VersionList{List: []string{"v1", "v2"}, Variant: variant.AzureSEVSNP{}},
|
||||
output: VersionList{List: []string{"v1", "v2"}},
|
||||
input: List{List: []string{"v1", "v2"}, Variant: variant.AzureSEVSNP{}},
|
||||
output: List{List: []string{"v1", "v2"}},
|
||||
},
|
||||
"wrong order": {
|
||||
input: VersionList{List: []string{"v1", "v2"}},
|
||||
output: VersionList{List: []string{"v2", "v1"}},
|
||||
input: List{List: []string{"v1", "v2"}},
|
||||
output: List{List: []string{"v2", "v1"}},
|
||||
wantDiff: true,
|
||||
},
|
||||
}
|
||||
@ -40,7 +40,7 @@ func TestVersionListMarshalUnmarshalJSON(t *testing.T) {
|
||||
inputRaw, err := tc.input.MarshalJSON()
|
||||
require.NoError(t, err)
|
||||
|
||||
var actual VersionList
|
||||
var actual List
|
||||
err = actual.UnmarshalJSON(inputRaw)
|
||||
require.NoError(t, err)
|
||||
|
||||
@ -68,7 +68,7 @@ func TestVersionListAddVersion(t *testing.T) {
|
||||
|
||||
for name, tc := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
v := VersionList{List: tc.versions}
|
||||
v := List{List: tc.versions}
|
||||
v.AddVersion(tc.new)
|
||||
|
||||
assert.Equal(t, tc.expected, v.List)
|
||||
|
@ -53,7 +53,7 @@ type Client struct {
|
||||
dirtyPaths []string // written paths to be invalidated
|
||||
DryRun bool // no write operations are performed
|
||||
|
||||
Logger *slog.Logger
|
||||
log *slog.Logger
|
||||
}
|
||||
|
||||
// NewReadOnlyClient creates a new read-only client.
|
||||
@ -77,7 +77,7 @@ func NewReadOnlyClient(ctx context.Context, region, bucket, distributionID strin
|
||||
s3ClientClose: staticUploadClientClose,
|
||||
bucket: bucket,
|
||||
DryRun: true,
|
||||
Logger: log,
|
||||
log: log,
|
||||
}
|
||||
clientClose := func(ctx context.Context) error {
|
||||
return client.Close(ctx)
|
||||
@ -106,7 +106,7 @@ func NewClient(ctx context.Context, region, bucket, distributionID string, dryRu
|
||||
s3ClientClose: staticUploadClientClose,
|
||||
bucket: bucket,
|
||||
DryRun: dryRun,
|
||||
Logger: log,
|
||||
log: log,
|
||||
}
|
||||
clientClose := func(ctx context.Context) error {
|
||||
return client.Close(ctx)
|
||||
@ -119,7 +119,7 @@ func NewClient(ctx context.Context, region, bucket, distributionID string, dryRu
|
||||
// It invalidates the CDN cache for all uploaded files.
|
||||
func (c *Client) Close(ctx context.Context) error {
|
||||
if c.s3ClientClose == nil {
|
||||
c.Logger.Debug("Client has no s3ClientClose")
|
||||
c.log.Debug("Client has no s3ClientClose")
|
||||
return nil
|
||||
}
|
||||
return c.s3ClientClose(ctx)
|
||||
@ -131,7 +131,7 @@ func (c *Client) DeletePath(ctx context.Context, path string) error {
|
||||
Bucket: &c.bucket,
|
||||
Prefix: &path,
|
||||
}
|
||||
c.Logger.Debug(fmt.Sprintf("Listing objects in %q", path))
|
||||
c.log.Debug(fmt.Sprintf("Listing objects in %q", path))
|
||||
objs := []s3types.Object{}
|
||||
out := &s3.ListObjectsV2Output{IsTruncated: ptr(true)}
|
||||
for out.IsTruncated != nil && *out.IsTruncated {
|
||||
@ -142,10 +142,10 @@ func (c *Client) DeletePath(ctx context.Context, path string) error {
|
||||
}
|
||||
objs = append(objs, out.Contents...)
|
||||
}
|
||||
c.Logger.Debug(fmt.Sprintf("Found %d objects in %q", len(objs), path))
|
||||
c.log.Debug(fmt.Sprintf("Found %d objects in %q", len(objs), path))
|
||||
|
||||
if len(objs) == 0 {
|
||||
c.Logger.Warn(fmt.Sprintf("Path %s is already empty", path))
|
||||
c.log.Warn(fmt.Sprintf("Path %s is already empty", path))
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -155,7 +155,7 @@ func (c *Client) DeletePath(ctx context.Context, path string) error {
|
||||
}
|
||||
|
||||
if c.DryRun {
|
||||
c.Logger.Debug(fmt.Sprintf("DryRun: Deleting %d objects with IDs %v", len(objs), objIDs))
|
||||
c.log.Debug(fmt.Sprintf("DryRun: Deleting %d objects with IDs %v", len(objs), objIDs))
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -167,7 +167,7 @@ func (c *Client) DeletePath(ctx context.Context, path string) error {
|
||||
Objects: objIDs,
|
||||
},
|
||||
}
|
||||
c.Logger.Debug(fmt.Sprintf("Deleting %d objects in %q", len(objs), path))
|
||||
c.log.Debug(fmt.Sprintf("Deleting %d objects in %q", len(objs), path))
|
||||
if _, err := c.s3Client.DeleteObjects(ctx, deleteIn); err != nil {
|
||||
return fmt.Errorf("deleting objects in %s: %w", path, err)
|
||||
}
|
||||
@ -197,7 +197,7 @@ func Fetch[T APIObject](ctx context.Context, c *Client, obj T) (T, error) {
|
||||
Key: ptr(obj.JSONPath()),
|
||||
}
|
||||
|
||||
c.Logger.Debug(fmt.Sprintf("Fetching %T from s3: %q", obj, obj.JSONPath()))
|
||||
c.log.Debug(fmt.Sprintf("Fetching %T from s3: %q", obj, obj.JSONPath()))
|
||||
out, err := c.s3Client.GetObject(ctx, in)
|
||||
var noSuchkey *s3types.NoSuchKey
|
||||
if errors.As(err, &noSuchkey) {
|
||||
@ -231,7 +231,7 @@ func Update(ctx context.Context, c *Client, obj APIObject) error {
|
||||
}
|
||||
|
||||
if c.DryRun {
|
||||
c.Logger.With(slog.String("bucket", c.bucket), slog.String("key", obj.JSONPath()), slog.String("body", string(rawJSON))).Debug("DryRun: s3 put object")
|
||||
c.log.With(slog.String("bucket", c.bucket), slog.String("key", obj.JSONPath()), slog.String("body", string(rawJSON))).Debug("DryRun: s3 put object")
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -243,7 +243,7 @@ func Update(ctx context.Context, c *Client, obj APIObject) error {
|
||||
|
||||
c.dirtyPaths = append(c.dirtyPaths, "/"+obj.JSONPath())
|
||||
|
||||
c.Logger.Debug(fmt.Sprintf("Uploading %T to s3: %q", obj, obj.JSONPath()))
|
||||
c.log.Debug(fmt.Sprintf("Uploading %T to s3: %q", obj, obj.JSONPath()))
|
||||
if _, err := c.Upload(ctx, in); err != nil {
|
||||
return fmt.Errorf("uploading %T: %w", obj, err)
|
||||
}
|
||||
@ -306,7 +306,7 @@ func Delete(ctx context.Context, c *Client, obj APIObject) error {
|
||||
Key: ptr(obj.JSONPath()),
|
||||
}
|
||||
|
||||
c.Logger.Debug(fmt.Sprintf("Deleting %T from s3: %q", obj, obj.JSONPath()))
|
||||
c.log.Debug(fmt.Sprintf("Deleting %T from s3: %q", obj, obj.JSONPath()))
|
||||
if _, err := c.DeleteObject(ctx, in); err != nil {
|
||||
return fmt.Errorf("deleting s3 object at %s: %w", obj.JSONPath(), err)
|
||||
}
|
||||
|
@ -20,12 +20,10 @@ package fetcher
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/sigstore"
|
||||
)
|
||||
@ -145,7 +143,7 @@ type apiObject interface {
|
||||
// signature manages the signature of a object saved at location 'Signed'.
|
||||
type signature struct {
|
||||
// Signed is the object that is signed.
|
||||
Signed string `json:"-"`
|
||||
Signed string `json:"signed"`
|
||||
// Signature is the signature of `Signed`.
|
||||
Signature []byte `json:"signature"`
|
||||
}
|
||||
@ -155,12 +153,8 @@ func (s signature) JSONPath() string {
|
||||
return s.Signed + ".sig"
|
||||
}
|
||||
|
||||
// ValidateRequest validates the request.
|
||||
// ValidateRequest is a no-op.
|
||||
func (s signature) ValidateRequest() error {
|
||||
if !strings.HasSuffix(s.Signed, ".json") {
|
||||
return errors.New("signed object missing .json suffix")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,8 @@ import (
|
||||
type Client struct {
|
||||
*apiclient.Client
|
||||
clientClose func(ctx context.Context) error
|
||||
|
||||
log *slog.Logger
|
||||
}
|
||||
|
||||
// NewClient creates a new client for the versions API.
|
||||
@ -31,8 +33,9 @@ func NewClient(ctx context.Context, region, bucket, distributionID string, dryRu
|
||||
) (*Client, CloseFunc, error) {
|
||||
genericClient, genericClientClose, err := apiclient.NewClient(ctx, region, bucket, distributionID, dryRun, log)
|
||||
versionsClient := &Client{
|
||||
genericClient,
|
||||
genericClientClose,
|
||||
Client: genericClient,
|
||||
clientClose: genericClientClose,
|
||||
log: log,
|
||||
}
|
||||
versionsClientClose := func(ctx context.Context) error {
|
||||
return versionsClient.Close(ctx)
|
||||
@ -50,8 +53,9 @@ func NewReadOnlyClient(ctx context.Context, region, bucket, distributionID strin
|
||||
return nil, nil, err
|
||||
}
|
||||
versionsClient := &Client{
|
||||
genericClient,
|
||||
genericClientClose,
|
||||
Client: genericClient,
|
||||
clientClose: genericClientClose,
|
||||
log: log,
|
||||
}
|
||||
versionsClientClose := func(ctx context.Context) error {
|
||||
return versionsClient.Close(ctx)
|
||||
@ -131,18 +135,18 @@ func (c *Client) DeleteRef(ctx context.Context, ref string) error {
|
||||
func (c *Client) DeleteVersion(ctx context.Context, ver Version) error {
|
||||
var retErr error
|
||||
|
||||
c.Client.Logger.Debug(fmt.Sprintf("Deleting version %q from minor version list", ver.version))
|
||||
c.log.Debug(fmt.Sprintf("Deleting version %q from minor version list", ver.version))
|
||||
possibleNewLatest, err := c.deleteVersionFromMinorVersionList(ctx, ver)
|
||||
if err != nil {
|
||||
retErr = errors.Join(retErr, fmt.Errorf("removing from minor version list: %w", err))
|
||||
}
|
||||
|
||||
c.Client.Logger.Debug(fmt.Sprintf("Checking latest version for %q", ver.version))
|
||||
c.log.Debug(fmt.Sprintf("Checking latest version for %q", ver.version))
|
||||
if err := c.deleteVersionFromLatest(ctx, ver, possibleNewLatest); err != nil {
|
||||
retErr = errors.Join(retErr, fmt.Errorf("updating latest version: %w", err))
|
||||
}
|
||||
|
||||
c.Client.Logger.Debug(fmt.Sprintf("Deleting artifact path %q for %q", ver.ArtifactPath(APIV1), ver.version))
|
||||
c.log.Debug(fmt.Sprintf("Deleting artifact path %q for %q", ver.ArtifactPath(APIV1), ver.version))
|
||||
if err := c.Client.DeletePath(ctx, ver.ArtifactPath(APIV1)); err != nil {
|
||||
retErr = errors.Join(retErr, fmt.Errorf("deleting artifact path: %w", err))
|
||||
}
|
||||
@ -159,20 +163,20 @@ func (c *Client) deleteVersionFromMinorVersionList(ctx context.Context, ver Vers
|
||||
Base: ver.WithGranularity(GranularityMinor),
|
||||
Kind: VersionKindImage,
|
||||
}
|
||||
c.Client.Logger.Debug(fmt.Sprintf("Fetching minor version list for version %q", ver.version))
|
||||
c.log.Debug(fmt.Sprintf("Fetching minor version list for version %q", ver.version))
|
||||
minorList, err := c.FetchVersionList(ctx, minorList)
|
||||
var notFoundErr *apiclient.NotFoundError
|
||||
if errors.As(err, ¬FoundErr) {
|
||||
c.Client.Logger.Warn(fmt.Sprintf("Minor version list for version %s not found", ver.version))
|
||||
c.Client.Logger.Warn("Skipping update of minor version list")
|
||||
c.log.Warn(fmt.Sprintf("Minor version list for version %s not found", ver.version))
|
||||
c.log.Warn("Skipping update of minor version list")
|
||||
return nil, nil
|
||||
} else if err != nil {
|
||||
return nil, fmt.Errorf("fetching minor version list for version %s: %w", ver.version, err)
|
||||
}
|
||||
|
||||
if !minorList.Contains(ver.version) {
|
||||
c.Client.Logger.Warn(fmt.Sprintf("Version %s is not in minor version list %s", ver.version, minorList.JSONPath()))
|
||||
c.Client.Logger.Warn("Skipping update of minor version list")
|
||||
c.log.Warn(fmt.Sprintf("Version %s is not in minor version list %s", ver.version, minorList.JSONPath()))
|
||||
c.log.Warn("Skipping update of minor version list")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@ -192,20 +196,20 @@ func (c *Client) deleteVersionFromMinorVersionList(ctx context.Context, ver Vers
|
||||
Kind: VersionKindImage,
|
||||
Version: minorList.Versions[len(minorList.Versions)-1],
|
||||
}
|
||||
c.Client.Logger.Debug(fmt.Sprintf("Possible latest version replacement %q", latest.Version))
|
||||
c.log.Debug(fmt.Sprintf("Possible latest version replacement %q", latest.Version))
|
||||
}
|
||||
|
||||
if c.Client.DryRun {
|
||||
c.Client.Logger.Debug(fmt.Sprintf("DryRun: Updating minor version list %q to %v", minorList.JSONPath(), minorList))
|
||||
c.log.Debug(fmt.Sprintf("DryRun: Updating minor version list %q to %v", minorList.JSONPath(), minorList))
|
||||
return latest, nil
|
||||
}
|
||||
|
||||
c.Client.Logger.Debug(fmt.Sprintf("Updating minor version list %q", minorList.JSONPath()))
|
||||
c.log.Debug(fmt.Sprintf("Updating minor version list %q", minorList.JSONPath()))
|
||||
if err := c.UpdateVersionList(ctx, minorList); err != nil {
|
||||
return latest, fmt.Errorf("updating minor version list %s: %w", minorList.JSONPath(), err)
|
||||
}
|
||||
|
||||
c.Client.Logger.Debug(fmt.Sprintf("Removed version %q from minor version list %q", ver.version, minorList.JSONPath()))
|
||||
c.log.Debug(fmt.Sprintf("Removed version %q from minor version list %q", ver.version, minorList.JSONPath()))
|
||||
return latest, nil
|
||||
}
|
||||
|
||||
@ -216,33 +220,33 @@ func (c *Client) deleteVersionFromLatest(ctx context.Context, ver Version, possi
|
||||
Stream: ver.stream,
|
||||
Kind: VersionKindImage,
|
||||
}
|
||||
c.Client.Logger.Debug(fmt.Sprintf("Fetching latest version from %q", latest.JSONPath()))
|
||||
c.log.Debug(fmt.Sprintf("Fetching latest version from %q", latest.JSONPath()))
|
||||
latest, err := c.FetchVersionLatest(ctx, latest)
|
||||
var notFoundErr *apiclient.NotFoundError
|
||||
if errors.As(err, ¬FoundErr) {
|
||||
c.Client.Logger.Warn(fmt.Sprintf("Latest version for %s not found", latest.JSONPath()))
|
||||
c.log.Warn(fmt.Sprintf("Latest version for %s not found", latest.JSONPath()))
|
||||
return nil
|
||||
} else if err != nil {
|
||||
return fmt.Errorf("fetching latest version: %w", err)
|
||||
}
|
||||
|
||||
if latest.Version != ver.version {
|
||||
c.Client.Logger.Debug(fmt.Sprintf("Latest version is %q, not the deleted version %q", latest.Version, ver.version))
|
||||
c.log.Debug(fmt.Sprintf("Latest version is %q, not the deleted version %q", latest.Version, ver.version))
|
||||
return nil
|
||||
}
|
||||
|
||||
if possibleNewLatest == nil {
|
||||
c.Client.Logger.Error(fmt.Sprintf("Latest version is %s, but no new latest version was found", latest.Version))
|
||||
c.Client.Logger.Error(fmt.Sprintf("A manual update of latest at %s might be needed", latest.JSONPath()))
|
||||
c.log.Error(fmt.Sprintf("Latest version is %s, but no new latest version was found", latest.Version))
|
||||
c.log.Error(fmt.Sprintf("A manual update of latest at %s might be needed", latest.JSONPath()))
|
||||
return fmt.Errorf("latest version is %s, but no new latest version was found", latest.Version)
|
||||
}
|
||||
|
||||
if c.Client.DryRun {
|
||||
c.Client.Logger.Debug(fmt.Sprintf("Would update latest version from %q to %q", latest.Version, possibleNewLatest.Version))
|
||||
c.log.Debug(fmt.Sprintf("Would update latest version from %q to %q", latest.Version, possibleNewLatest.Version))
|
||||
return nil
|
||||
}
|
||||
|
||||
c.Client.Logger.Info(fmt.Sprintf("Updating latest version from %s to %s", latest.Version, possibleNewLatest.Version))
|
||||
c.log.Info(fmt.Sprintf("Updating latest version from %s to %s", latest.Version, possibleNewLatest.Version))
|
||||
if err := c.UpdateVersionLatest(ctx, *possibleNewLatest); err != nil {
|
||||
return fmt.Errorf("updating latest version: %w", err)
|
||||
}
|
||||
|
@ -1165,7 +1165,7 @@ type AzureTDX struct {
|
||||
// Expected 48 byte hex-encoded MR_SEAM value.
|
||||
MRSeam encoding.HexBytes `json:"mrSeam,omitempty" yaml:"mrSeam,omitempty"`
|
||||
// description: |
|
||||
// Expected 8 byte hex-encoded XFAM field.
|
||||
// Expected 8 byte hex-encoded eXtended Features Available Mask (XFAM) field. Defaults to the latest available XFAM on Azure VMs. Unset to disable validation.
|
||||
XFAM AttestationVersion[encoding.HexBytes] `json:"xfam" yaml:"xfam"`
|
||||
// description: |
|
||||
// Intel Root Key certificate used to verify the TDX certificate chain.
|
||||
|
@ -545,22 +545,22 @@ func init() {
|
||||
GCPSEVSNPDoc.Fields[0].Description = "Expected TPM measurements."
|
||||
GCPSEVSNPDoc.Fields[0].Comments[encoder.LineComment] = "Expected TPM measurements."
|
||||
GCPSEVSNPDoc.Fields[1].Name = "bootloaderVersion"
|
||||
GCPSEVSNPDoc.Fields[1].Type = "AttestationVersion"
|
||||
GCPSEVSNPDoc.Fields[1].Type = ""
|
||||
GCPSEVSNPDoc.Fields[1].Note = ""
|
||||
GCPSEVSNPDoc.Fields[1].Description = "Lowest acceptable bootloader version."
|
||||
GCPSEVSNPDoc.Fields[1].Comments[encoder.LineComment] = "Lowest acceptable bootloader version."
|
||||
GCPSEVSNPDoc.Fields[2].Name = "teeVersion"
|
||||
GCPSEVSNPDoc.Fields[2].Type = "AttestationVersion"
|
||||
GCPSEVSNPDoc.Fields[2].Type = ""
|
||||
GCPSEVSNPDoc.Fields[2].Note = ""
|
||||
GCPSEVSNPDoc.Fields[2].Description = "Lowest acceptable TEE version."
|
||||
GCPSEVSNPDoc.Fields[2].Comments[encoder.LineComment] = "Lowest acceptable TEE version."
|
||||
GCPSEVSNPDoc.Fields[3].Name = "snpVersion"
|
||||
GCPSEVSNPDoc.Fields[3].Type = "AttestationVersion"
|
||||
GCPSEVSNPDoc.Fields[3].Type = ""
|
||||
GCPSEVSNPDoc.Fields[3].Note = ""
|
||||
GCPSEVSNPDoc.Fields[3].Description = "Lowest acceptable SEV-SNP version."
|
||||
GCPSEVSNPDoc.Fields[3].Comments[encoder.LineComment] = "Lowest acceptable SEV-SNP version."
|
||||
GCPSEVSNPDoc.Fields[4].Name = "microcodeVersion"
|
||||
GCPSEVSNPDoc.Fields[4].Type = "AttestationVersion"
|
||||
GCPSEVSNPDoc.Fields[4].Type = ""
|
||||
GCPSEVSNPDoc.Fields[4].Note = ""
|
||||
GCPSEVSNPDoc.Fields[4].Description = "Lowest acceptable microcode version."
|
||||
GCPSEVSNPDoc.Fields[4].Comments[encoder.LineComment] = "Lowest acceptable microcode version."
|
||||
@ -623,22 +623,22 @@ func init() {
|
||||
AWSSEVSNPDoc.Fields[0].Description = "Expected TPM measurements."
|
||||
AWSSEVSNPDoc.Fields[0].Comments[encoder.LineComment] = "Expected TPM measurements."
|
||||
AWSSEVSNPDoc.Fields[1].Name = "bootloaderVersion"
|
||||
AWSSEVSNPDoc.Fields[1].Type = "AttestationVersion"
|
||||
AWSSEVSNPDoc.Fields[1].Type = ""
|
||||
AWSSEVSNPDoc.Fields[1].Note = ""
|
||||
AWSSEVSNPDoc.Fields[1].Description = "Lowest acceptable bootloader version."
|
||||
AWSSEVSNPDoc.Fields[1].Comments[encoder.LineComment] = "Lowest acceptable bootloader version."
|
||||
AWSSEVSNPDoc.Fields[2].Name = "teeVersion"
|
||||
AWSSEVSNPDoc.Fields[2].Type = "AttestationVersion"
|
||||
AWSSEVSNPDoc.Fields[2].Type = ""
|
||||
AWSSEVSNPDoc.Fields[2].Note = ""
|
||||
AWSSEVSNPDoc.Fields[2].Description = "Lowest acceptable TEE version."
|
||||
AWSSEVSNPDoc.Fields[2].Comments[encoder.LineComment] = "Lowest acceptable TEE version."
|
||||
AWSSEVSNPDoc.Fields[3].Name = "snpVersion"
|
||||
AWSSEVSNPDoc.Fields[3].Type = "AttestationVersion"
|
||||
AWSSEVSNPDoc.Fields[3].Type = ""
|
||||
AWSSEVSNPDoc.Fields[3].Note = ""
|
||||
AWSSEVSNPDoc.Fields[3].Description = "Lowest acceptable SEV-SNP version."
|
||||
AWSSEVSNPDoc.Fields[3].Comments[encoder.LineComment] = "Lowest acceptable SEV-SNP version."
|
||||
AWSSEVSNPDoc.Fields[4].Name = "microcodeVersion"
|
||||
AWSSEVSNPDoc.Fields[4].Type = "AttestationVersion"
|
||||
AWSSEVSNPDoc.Fields[4].Type = ""
|
||||
AWSSEVSNPDoc.Fields[4].Note = ""
|
||||
AWSSEVSNPDoc.Fields[4].Description = "Lowest acceptable microcode version."
|
||||
AWSSEVSNPDoc.Fields[4].Comments[encoder.LineComment] = "Lowest acceptable microcode version."
|
||||
@ -685,22 +685,22 @@ func init() {
|
||||
AzureSEVSNPDoc.Fields[0].Description = "Expected TPM measurements."
|
||||
AzureSEVSNPDoc.Fields[0].Comments[encoder.LineComment] = "Expected TPM measurements."
|
||||
AzureSEVSNPDoc.Fields[1].Name = "bootloaderVersion"
|
||||
AzureSEVSNPDoc.Fields[1].Type = "AttestationVersion"
|
||||
AzureSEVSNPDoc.Fields[1].Type = ""
|
||||
AzureSEVSNPDoc.Fields[1].Note = ""
|
||||
AzureSEVSNPDoc.Fields[1].Description = "Lowest acceptable bootloader version."
|
||||
AzureSEVSNPDoc.Fields[1].Comments[encoder.LineComment] = "Lowest acceptable bootloader version."
|
||||
AzureSEVSNPDoc.Fields[2].Name = "teeVersion"
|
||||
AzureSEVSNPDoc.Fields[2].Type = "AttestationVersion"
|
||||
AzureSEVSNPDoc.Fields[2].Type = ""
|
||||
AzureSEVSNPDoc.Fields[2].Note = ""
|
||||
AzureSEVSNPDoc.Fields[2].Description = "Lowest acceptable TEE version."
|
||||
AzureSEVSNPDoc.Fields[2].Comments[encoder.LineComment] = "Lowest acceptable TEE version."
|
||||
AzureSEVSNPDoc.Fields[3].Name = "snpVersion"
|
||||
AzureSEVSNPDoc.Fields[3].Type = "AttestationVersion"
|
||||
AzureSEVSNPDoc.Fields[3].Type = ""
|
||||
AzureSEVSNPDoc.Fields[3].Note = ""
|
||||
AzureSEVSNPDoc.Fields[3].Description = "Lowest acceptable SEV-SNP version."
|
||||
AzureSEVSNPDoc.Fields[3].Comments[encoder.LineComment] = "Lowest acceptable SEV-SNP version."
|
||||
AzureSEVSNPDoc.Fields[4].Name = "microcodeVersion"
|
||||
AzureSEVSNPDoc.Fields[4].Type = "AttestationVersion"
|
||||
AzureSEVSNPDoc.Fields[4].Type = ""
|
||||
AzureSEVSNPDoc.Fields[4].Note = ""
|
||||
AzureSEVSNPDoc.Fields[4].Description = "Lowest acceptable microcode version."
|
||||
AzureSEVSNPDoc.Fields[4].Comments[encoder.LineComment] = "Lowest acceptable microcode version."
|
||||
@ -752,22 +752,22 @@ func init() {
|
||||
AzureTDXDoc.Fields[0].Description = "Expected TPM measurements."
|
||||
AzureTDXDoc.Fields[0].Comments[encoder.LineComment] = "Expected TPM measurements."
|
||||
AzureTDXDoc.Fields[1].Name = "qeSVN"
|
||||
AzureTDXDoc.Fields[1].Type = "uint16"
|
||||
AzureTDXDoc.Fields[1].Type = ""
|
||||
AzureTDXDoc.Fields[1].Note = ""
|
||||
AzureTDXDoc.Fields[1].Description = "Minimum required QE security version number (SVN)."
|
||||
AzureTDXDoc.Fields[1].Comments[encoder.LineComment] = "Minimum required QE security version number (SVN)."
|
||||
AzureTDXDoc.Fields[2].Name = "pceSVN"
|
||||
AzureTDXDoc.Fields[2].Type = "uint16"
|
||||
AzureTDXDoc.Fields[2].Type = ""
|
||||
AzureTDXDoc.Fields[2].Note = ""
|
||||
AzureTDXDoc.Fields[2].Description = "Minimum required PCE security version number (SVN)."
|
||||
AzureTDXDoc.Fields[2].Comments[encoder.LineComment] = "Minimum required PCE security version number (SVN)."
|
||||
AzureTDXDoc.Fields[3].Name = "teeTCBSVN"
|
||||
AzureTDXDoc.Fields[3].Type = "HexBytes"
|
||||
AzureTDXDoc.Fields[3].Type = ""
|
||||
AzureTDXDoc.Fields[3].Note = ""
|
||||
AzureTDXDoc.Fields[3].Description = "Component-wise minimum required 16 byte hex-encoded TEE_TCB security version number (SVN)."
|
||||
AzureTDXDoc.Fields[3].Comments[encoder.LineComment] = "Component-wise minimum required 16 byte hex-encoded TEE_TCB security version number (SVN)."
|
||||
AzureTDXDoc.Fields[4].Name = "qeVendorID"
|
||||
AzureTDXDoc.Fields[4].Type = "HexBytes"
|
||||
AzureTDXDoc.Fields[4].Type = ""
|
||||
AzureTDXDoc.Fields[4].Note = ""
|
||||
AzureTDXDoc.Fields[4].Description = "Expected 16 byte hex-encoded QE_VENDOR_ID field."
|
||||
AzureTDXDoc.Fields[4].Comments[encoder.LineComment] = "Expected 16 byte hex-encoded QE_VENDOR_ID field."
|
||||
@ -777,10 +777,10 @@ func init() {
|
||||
AzureTDXDoc.Fields[5].Description = "Expected 48 byte hex-encoded MR_SEAM value."
|
||||
AzureTDXDoc.Fields[5].Comments[encoder.LineComment] = "Expected 48 byte hex-encoded MR_SEAM value."
|
||||
AzureTDXDoc.Fields[6].Name = "xfam"
|
||||
AzureTDXDoc.Fields[6].Type = "HexBytes"
|
||||
AzureTDXDoc.Fields[6].Type = ""
|
||||
AzureTDXDoc.Fields[6].Note = ""
|
||||
AzureTDXDoc.Fields[6].Description = "Expected 8 byte hex-encoded XFAM field."
|
||||
AzureTDXDoc.Fields[6].Comments[encoder.LineComment] = "Expected 8 byte hex-encoded XFAM field."
|
||||
AzureTDXDoc.Fields[6].Description = "Expected 8 byte hex-encoded eXtended Features Available Mask (XFAM) field. Defaults to the latest available XFAM on Azure VMs. Unset to disable validation."
|
||||
AzureTDXDoc.Fields[6].Comments[encoder.LineComment] = "Expected 8 byte hex-encoded eXtended Features Available Mask (XFAM) field. Defaults to the latest available XFAM on Azure VMs. Unset to disable validation."
|
||||
AzureTDXDoc.Fields[7].Name = "intelRootKey"
|
||||
AzureTDXDoc.Fields[7].Type = "Certificate"
|
||||
AzureTDXDoc.Fields[7].Note = ""
|
||||
|
@ -1051,8 +1051,8 @@ func getConfigAsMap(conf *Config, t *testing.T) (res configMap) {
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -162,7 +162,7 @@ func (d *AttestationDataSource) Read(ctx context.Context, req datasource.ReadReq
|
||||
|
||||
insecureFetch := data.Insecure.ValueBool()
|
||||
|
||||
latestVersions := attestationconfigapi.VersionAPIEntry{}
|
||||
latestVersions := attestationconfigapi.Entry{}
|
||||
if attestationVariant.Equal(variant.AWSSEVSNP{}) ||
|
||||
attestationVariant.Equal(variant.AzureSEVSNP{}) ||
|
||||
attestationVariant.Equal(variant.AzureTDX{}) ||
|
||||
|
@ -138,7 +138,7 @@ func convertFromTfAttestationCfg(tfAttestation attestationAttribute, attestation
|
||||
}
|
||||
|
||||
// convertToTfAttestationCfg converts the constellation attestation config to the related terraform structs.
|
||||
func convertToTfAttestation(attVar variant.Variant, latestVersions attestationconfigapi.VersionAPIEntry) (tfAttestation attestationAttribute, err error) {
|
||||
func convertToTfAttestation(attVar variant.Variant, latestVersions attestationconfigapi.Entry) (tfAttestation attestationAttribute, err error) {
|
||||
tfAttestation = attestationAttribute{
|
||||
Variant: attVar.String(),
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user