api: refactor attestationconfigapi client/fetcher

There is now one SEVSNPVersions type that has a variant
property. That property is used to build the correct JSON
path. The surrounding methods handling the version objects
are also updated to receive a variant argument and work
for multiple variants. This simplifies adding AWS support.
This commit is contained in:
Otto Bittner 2023-11-14 10:03:01 +01:00
parent 5542f9c63c
commit 350397923f
16 changed files with 411 additions and 262 deletions

View File

@ -17,6 +17,7 @@ import (
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/constants"
@ -294,25 +295,23 @@ func TestConfigFetchMeasurements(t *testing.T) {
type stubAttestationFetcher struct{}
func (f stubAttestationFetcher) FetchAzureSEVSNPVersionList(_ context.Context, _ attestationconfigapi.AzureSEVSNPVersionList) (attestationconfigapi.AzureSEVSNPVersionList, error) {
return attestationconfigapi.AzureSEVSNPVersionList(
[]string{},
), nil
func (f stubAttestationFetcher) FetchSEVSNPVersionList(_ context.Context, _ attestationconfigapi.SEVSNPVersionList) (attestationconfigapi.SEVSNPVersionList, error) {
return attestationconfigapi.SEVSNPVersionList{}, nil
}
func (f stubAttestationFetcher) FetchAzureSEVSNPVersion(_ context.Context, _ attestationconfigapi.AzureSEVSNPVersionAPI) (attestationconfigapi.AzureSEVSNPVersionAPI, error) {
return attestationconfigapi.AzureSEVSNPVersionAPI{
AzureSEVSNPVersion: testCfg,
func (f stubAttestationFetcher) FetchSEVSNPVersion(_ context.Context, _ attestationconfigapi.SEVSNPVersionAPI) (attestationconfigapi.SEVSNPVersionAPI, error) {
return attestationconfigapi.SEVSNPVersionAPI{
SEVSNPVersion: testCfg,
}, nil
}
func (f stubAttestationFetcher) FetchAzureSEVSNPVersionLatest(_ context.Context) (attestationconfigapi.AzureSEVSNPVersionAPI, error) {
return attestationconfigapi.AzureSEVSNPVersionAPI{
AzureSEVSNPVersion: testCfg,
func (f stubAttestationFetcher) FetchSEVSNPVersionLatest(_ context.Context, _ variant.Variant) (attestationconfigapi.SEVSNPVersionAPI, error) {
return attestationconfigapi.SEVSNPVersionAPI{
SEVSNPVersion: testCfg,
}, nil
}
var testCfg = attestationconfigapi.AzureSEVSNPVersion{
var testCfg = attestationconfigapi.SEVSNPVersion{
Microcode: 93,
TEE: 0,
SNP: 6,

View File

@ -14,6 +14,7 @@ import (
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/constants"
@ -170,14 +171,14 @@ type stubConfigFetcher struct {
fetchLatestErr error
}
func (s *stubConfigFetcher) FetchAzureSEVSNPVersion(context.Context, attestationconfigapi.AzureSEVSNPVersionAPI) (attestationconfigapi.AzureSEVSNPVersionAPI, error) {
func (s *stubConfigFetcher) FetchSEVSNPVersion(context.Context, attestationconfigapi.SEVSNPVersionAPI) (attestationconfigapi.SEVSNPVersionAPI, error) {
panic("not implemented")
}
func (s *stubConfigFetcher) FetchAzureSEVSNPVersionList(context.Context, attestationconfigapi.AzureSEVSNPVersionList) (attestationconfigapi.AzureSEVSNPVersionList, error) {
func (s *stubConfigFetcher) FetchSEVSNPVersionList(context.Context, attestationconfigapi.SEVSNPVersionList) (attestationconfigapi.SEVSNPVersionList, error) {
panic("not implemented")
}
func (s *stubConfigFetcher) FetchAzureSEVSNPVersionLatest(context.Context) (attestationconfigapi.AzureSEVSNPVersionAPI, error) {
return attestationconfigapi.AzureSEVSNPVersionAPI{}, s.fetchLatestErr
func (s *stubConfigFetcher) FetchSEVSNPVersionLatest(context.Context, variant.Variant) (attestationconfigapi.SEVSNPVersionAPI, error) {
return attestationconfigapi.SEVSNPVersionAPI{}, s.fetchLatestErr
}

View File

@ -5,10 +5,10 @@ go_library(
name = "attestationconfigapi",
srcs = [
"attestationconfigapi.go",
"azure.go",
"client.go",
"fetcher.go",
"reporter.go",
"snp.go",
],
importpath = "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi",
visibility = ["//:__subpackages__"],
@ -31,10 +31,13 @@ go_test(
"client_test.go",
"fetcher_test.go",
"reporter_test.go",
"snp_test.go",
],
embed = [":attestationconfigapi"],
deps = [
"//internal/attestation/variant",
"//internal/constants",
"@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require",
],
)

View File

@ -1,84 +0,0 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package attestationconfigapi
import (
"fmt"
"path"
"sort"
"strings"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
)
// attestationURLPath is the URL path to the attestation versions.
const attestationURLPath = "constellation/v1/attestation"
// AzureSEVSNPVersionType is the type of the version to be requested.
type AzureSEVSNPVersionType string
// AzureSEVSNPVersion tracks the latest version of each component of the Azure SEVSNP.
type AzureSEVSNPVersion struct {
// Bootloader is the latest version of the Azure SEVSNP bootloader.
Bootloader uint8 `json:"bootloader"`
// TEE is the latest version of the Azure SEVSNP TEE.
TEE uint8 `json:"tee"`
// SNP is the latest version of the Azure SEVSNP SNP.
SNP uint8 `json:"snp"`
// Microcode is the latest version of the Azure SEVSNP microcode.
Microcode uint8 `json:"microcode"`
}
// AzureSEVSNPVersionAPI is the request to get the version information of the specific version in the config api.
type AzureSEVSNPVersionAPI struct {
Version string `json:"-"`
AzureSEVSNPVersion
}
// JSONPath returns the path to the JSON file for the request to the config api.
func (i AzureSEVSNPVersionAPI) JSONPath() string {
return path.Join(attestationURLPath, variant.AzureSEVSNP{}.String(), i.Version)
}
// ValidateRequest validates the request.
func (i AzureSEVSNPVersionAPI) ValidateRequest() error {
if !strings.HasSuffix(i.Version, ".json") {
return fmt.Errorf("version has no .json suffix")
}
return nil
}
// Validate is a No-Op at the moment.
func (i AzureSEVSNPVersionAPI) Validate() error {
return nil
}
// AzureSEVSNPVersionList is the request to list all versions in the config api.
type AzureSEVSNPVersionList []string
// JSONPath returns the path to the JSON file for the request to the config api.
func (i AzureSEVSNPVersionList) JSONPath() string {
return path.Join(attestationURLPath, variant.AzureSEVSNP{}.String(), "list")
}
// ValidateRequest is a NoOp as there is no input.
func (i AzureSEVSNPVersionList) ValidateRequest() error {
return nil
}
// SortAzureSEVSNPVersionList sorts the list of versions in reverse order.
func SortAzureSEVSNPVersionList(versions AzureSEVSNPVersionList) {
sort.Sort(sort.Reverse(sort.StringSlice(versions)))
}
// Validate validates the response.
func (i AzureSEVSNPVersionList) Validate() error {
if len(i) < 1 {
return fmt.Errorf("no versions found in /list")
}
return nil
}

View File

@ -15,13 +15,13 @@ go_library(
"azure.go",
"delete.go",
"main.go",
"objectkind_string.go",
"validargs.go",
],
importpath = "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi/cli",
visibility = ["//visibility:private"],
deps = [
"//internal/api/attestationconfigapi",
"//internal/attestation/variant",
"//internal/cloud/cloudprovider",
"//internal/constants",
"//internal/file",
@ -44,11 +44,6 @@ go_test(
"main_test.go",
],
embed = [":cli_lib"],
deps = [
"//internal/cloud/cloudprovider",
"//internal/verify",
"@com_github_stretchr_testify//assert",
],
)
sh_template(

View File

@ -15,6 +15,7 @@ import (
s3types "github.com/aws/aws-sdk-go-v2/service/s3/types"
"github.com/aws/aws-sdk-go/aws"
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
"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"
@ -36,7 +37,7 @@ func uploadAzure(ctx context.Context, client *attestationconfigapi.Client, cfg u
inputVersion := convertTCBVersionToAzureVersion(report.SNPReport.LaunchTCB)
log.Infof("Input report: %+v", inputVersion)
latestAPIVersionAPI, err := attestationconfigapi.NewFetcherWithCustomCDNAndCosignKey(cfg.url, cfg.cosignPublicKey).FetchAzureSEVSNPVersionLatest(ctx)
latestAPIVersionAPI, err := attestationconfigapi.NewFetcherWithCustomCDNAndCosignKey(cfg.url, cfg.cosignPublicKey).FetchSEVSNPVersionLatest(ctx, variant.AzureSEVSNP{})
if err != nil {
if errors.Is(err, attestationconfigapi.ErrNoVersionsFound) {
log.Infof("No versions found in API, but assuming that we are uploading the first version.")
@ -44,8 +45,8 @@ func uploadAzure(ctx context.Context, client *attestationconfigapi.Client, cfg u
return fmt.Errorf("fetching latest version: %w", err)
}
}
latestAPIVersion := latestAPIVersionAPI.AzureSEVSNPVersion
if err := client.UploadAzureSEVSNPVersionLatest(ctx, inputVersion, latestAPIVersion, cfg.uploadDate, cfg.force); err != nil {
latestAPIVersion := latestAPIVersionAPI.SEVSNPVersion
if err := client.UploadSEVSNPVersionLatest(ctx, variant.AzureSEVSNP{}, inputVersion, latestAPIVersion, cfg.uploadDate, cfg.force); err != nil {
if errors.Is(err, attestationconfigapi.ErrNoNewerVersion) {
log.Infof("Input version: %+v is not newer than latest API version: %+v", inputVersion, latestAPIVersion)
return nil
@ -56,8 +57,8 @@ func uploadAzure(ctx context.Context, client *attestationconfigapi.Client, cfg u
return nil
}
func convertTCBVersionToAzureVersion(tcb verify.TCBVersion) attestationconfigapi.AzureSEVSNPVersion {
return attestationconfigapi.AzureSEVSNPVersion{
func convertTCBVersionToAzureVersion(tcb verify.TCBVersion) attestationconfigapi.SEVSNPVersion {
return attestationconfigapi.SEVSNPVersion{
Bootloader: tcb.Bootloader,
TEE: tcb.TEE,
SNP: tcb.SNP,
@ -67,7 +68,7 @@ func convertTCBVersionToAzureVersion(tcb verify.TCBVersion) attestationconfigapi
func deleteAzure(ctx context.Context, client *attestationconfigapi.Client, cfg deleteConfig) error {
if cfg.provider == cloudprovider.Azure && cfg.kind == snpReport {
return client.DeleteAzureSEVSNPVersion(ctx, cfg.version)
return client.DeleteSEVSNPVersion(ctx, variant.AzureSEVSNP{}, cfg.version)
}
return fmt.Errorf("provider %s and kind %s not supported", cfg.provider, cfg.kind)

View File

@ -48,24 +48,25 @@ func NewClient(ctx context.Context, cfg staticupload.Config, cosignPwd, privateK
return repo, clientClose, nil
}
// uploadAzureSEVSNPVersion uploads the latest version numbers of the Azure SEVSNP. Then version name is the UTC timestamp of the date. The /list entry stores the version name + .json suffix.
func (a Client) uploadAzureSEVSNPVersion(ctx context.Context, version AzureSEVSNPVersion, date time.Time) error {
versions, err := a.List(ctx, variant.AzureSEVSNP{})
// uploadSEVSNPVersion uploads the latest version numbers of the Azure 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 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(version, versions, date)
ops := a.constructUploadCmd(attestation, version, versions, date)
return executeAllCmds(ctx, a.s3Client, ops)
}
// DeleteAzureSEVSNPVersion deletes the given version (without .json suffix) from the API.
func (a Client) DeleteAzureSEVSNPVersion(ctx context.Context, versionStr string) error {
versions, err := a.List(ctx, variant.AzureSEVSNP{})
// DeleteSEVSNPVersion deletes the given version (without .json suffix) from the API.
func (a Client) DeleteSEVSNPVersion(ctx context.Context, attestation variant.Variant, versionStr string) error {
versions, err := a.List(ctx, attestation)
if err != nil {
return fmt.Errorf("fetch version list: %w", err)
}
ops, err := a.deleteAzureSEVSNPVersion(versions, versionStr)
ops, err := a.deleteSEVSNPVersion(versions, versionStr)
if err != nil {
return err
}
@ -73,25 +74,30 @@ func (a Client) DeleteAzureSEVSNPVersion(ctx context.Context, versionStr string)
}
// List returns the list of versions for the given attestation variant.
func (a Client) List(ctx context.Context, attestation variant.Variant) ([]string, error) {
if attestation.Equal(variant.AzureSEVSNP{}) {
versions, err := apiclient.Fetch(ctx, a.s3Client, AzureSEVSNPVersionList{})
if err != nil {
var notFoundErr *apiclient.NotFoundError
if errors.As(err, &notFoundErr) {
return nil, nil
}
return nil, err
}
return versions, nil
func (a Client) List(ctx context.Context, attestation variant.Variant) (SEVSNPVersionList, error) {
if !attestation.Equal(variant.AzureSEVSNP{}) && !attestation.Equal(variant.AWSSEVSNP{}) {
return SEVSNPVersionList{}, fmt.Errorf("unsupported attestation variant: %s", attestation)
}
return nil, fmt.Errorf("unsupported attestation variant: %s", attestation)
versions, err := apiclient.Fetch(ctx, a.s3Client, SEVSNPVersionList{variant: attestation})
if err != nil {
var notFoundErr *apiclient.NotFoundError
if errors.As(err, &notFoundErr) {
return SEVSNPVersionList{variant: attestation}, nil
}
return SEVSNPVersionList{}, err
}
versions.variant = attestation
return versions, nil
}
func (a Client) deleteAzureSEVSNPVersion(versions AzureSEVSNPVersionList, versionStr string) (ops []crudCmd, err error) {
func (a Client) deleteSEVSNPVersion(versions SEVSNPVersionList, versionStr string) (ops []crudCmd, err error) {
versionStr = versionStr + ".json"
ops = append(ops, deleteCmd{
apiObject: AzureSEVSNPVersionAPI{
apiObject: SEVSNPVersionAPI{
Variant: versions.variant,
Version: versionStr,
},
})
@ -107,36 +113,42 @@ func (a Client) deleteAzureSEVSNPVersion(versions AzureSEVSNPVersionList, versio
return ops, nil
}
func (a Client) constructUploadCmd(versions AzureSEVSNPVersion, versionNames []string, date time.Time) []crudCmd {
func (a Client) constructUploadCmd(attestation variant.Variant, version SEVSNPVersion, versionNames SEVSNPVersionList, date time.Time) []crudCmd {
if !attestation.Equal(versionNames.variant) {
return nil
}
dateStr := date.Format(VersionFormat) + ".json"
var res []crudCmd
res = append(res, putCmd{
apiObject: AzureSEVSNPVersionAPI{Version: dateStr, AzureSEVSNPVersion: versions},
apiObject: SEVSNPVersionAPI{Version: dateStr, Variant: attestation, SEVSNPVersion: version},
signer: a.signer,
})
newVersions := addVersion(versionNames, dateStr)
versionNames.addVersion(dateStr)
res = append(res, putCmd{
apiObject: AzureSEVSNPVersionList(newVersions),
apiObject: versionNames,
signer: a.signer,
})
return res
}
func removeVersion(versions AzureSEVSNPVersionList, versionStr string) (removedVersions AzureSEVSNPVersionList, err error) {
func removeVersion(list SEVSNPVersionList, versionStr string) (removedVersions SEVSNPVersionList, err error) {
versions := list.List()
for i, v := range versions {
if v == versionStr {
if i == len(versions)-1 {
removedVersions = versions[:i]
removedVersions = SEVSNPVersionList{list: versions[:i], variant: list.variant}
} else {
removedVersions = append(versions[:i], versions[i+1:]...)
removedVersions = SEVSNPVersionList{list: append(versions[:i], versions[i+1:]...), variant: list.variant}
}
return removedVersions, nil
}
}
return nil, fmt.Errorf("version %s not found in list %v", versionStr, versions)
return SEVSNPVersionList{}, fmt.Errorf("version %s not found in list %v", versionStr, versions)
}
type crudCmd interface {
@ -168,10 +180,3 @@ func executeAllCmds(ctx context.Context, client *apiclient.Client, cmds []crudCm
}
return nil
}
func addVersion(versions []string, newVersion string) []string {
versions = append(versions, newVersion)
versions = variant.RemoveDuplicate(versions)
SortAzureSEVSNPVersionList(versions)
return versions
}

View File

@ -9,6 +9,7 @@ import (
"testing"
"time"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
"github.com/stretchr/testify/assert"
)
@ -17,20 +18,21 @@ func TestUploadAzureSEVSNP(t *testing.T) {
bucketID: "bucket",
signer: fakeSigner{},
}
version := AzureSEVSNPVersion{}
version := SEVSNPVersion{}
date := time.Date(2023, 1, 1, 1, 1, 1, 1, time.UTC)
ops := sut.constructUploadCmd(version, []string{"2021-01-01-01-01.json", "2019-01-01-01-01.json"}, date)
ops := sut.constructUploadCmd(variant.AzureSEVSNP{}, version, SEVSNPVersionList{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: AzureSEVSNPVersionAPI{
Version: dateStr,
AzureSEVSNPVersion: version,
apiObject: SEVSNPVersionAPI{
Variant: variant.AzureSEVSNP{},
Version: dateStr,
SEVSNPVersion: version,
},
signer: fakeSigner{},
})
assert.Contains(ops, putCmd{
apiObject: AzureSEVSNPVersionList([]string{"2023-01-01-01-01.json", "2021-01-01-01-01.json", "2019-01-01-01-01.json"}),
apiObject: SEVSNPVersionList{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{},
})
}
@ -39,20 +41,20 @@ func TestDeleteAzureSEVSNPVersions(t *testing.T) {
sut := Client{
bucketID: "bucket",
}
versions := AzureSEVSNPVersionList([]string{"2023-01-01.json", "2021-01-01.json", "2019-01-01.json"})
versions := SEVSNPVersionList{list: []string{"2023-01-01.json", "2021-01-01.json", "2019-01-01.json"}}
ops, err := sut.deleteAzureSEVSNPVersion(versions, "2021-01-01")
ops, err := sut.deleteSEVSNPVersion(versions, "2021-01-01")
assert := assert.New(t)
assert.NoError(err)
assert.Contains(ops, deleteCmd{
apiObject: AzureSEVSNPVersionAPI{
apiObject: SEVSNPVersionAPI{
Version: "2021-01-01.json",
},
})
assert.Contains(ops, putCmd{
apiObject: AzureSEVSNPVersionList([]string{"2023-01-01.json", "2019-01-01.json"}),
apiObject: SEVSNPVersionList{list: []string{"2023-01-01.json", "2019-01-01.json"}},
})
}

View File

@ -12,6 +12,7 @@ import (
"fmt"
apifetcher "github.com/edgelesssys/constellation/v2/internal/api/fetcher"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
"github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/sigstore"
)
@ -23,9 +24,9 @@ var ErrNoVersionsFound = errors.New("no versions found")
// Fetcher fetches config API resources without authentication.
type Fetcher interface {
FetchAzureSEVSNPVersion(ctx context.Context, azureVersion AzureSEVSNPVersionAPI) (AzureSEVSNPVersionAPI, error)
FetchAzureSEVSNPVersionList(ctx context.Context, attestation AzureSEVSNPVersionList) (AzureSEVSNPVersionList, error)
FetchAzureSEVSNPVersionLatest(ctx context.Context) (AzureSEVSNPVersionAPI, error)
FetchSEVSNPVersion(ctx context.Context, version SEVSNPVersionAPI) (SEVSNPVersionAPI, error)
FetchSEVSNPVersionList(ctx context.Context, list SEVSNPVersionList) (SEVSNPVersionList, error)
FetchSEVSNPVersionLatest(ctx context.Context, attesation variant.Variant) (SEVSNPVersionAPI, error)
}
// fetcher fetches AttestationCfg API resources without authentication.
@ -64,35 +65,45 @@ func newFetcherWithClientAndVerifier(client apifetcher.HTTPClient, cosignVerifie
return &fetcher{HTTPClient: client, verifier: cosignVerifier, cdnURL: url}
}
// FetchAzureSEVSNPVersionList fetches the version list information from the config API.
func (f *fetcher) FetchAzureSEVSNPVersionList(ctx context.Context, attestation AzureSEVSNPVersionList) (AzureSEVSNPVersionList, error) {
// FetchSEVSNPVersionList fetches the version list information from the config API.
func (f *fetcher) FetchSEVSNPVersionList(ctx context.Context, list SEVSNPVersionList) (SEVSNPVersionList, error) {
// TODO(derpsteb): Replace with FetchAndVerify once we move to v2 of the config API.
return apifetcher.Fetch(ctx, f.HTTPClient, f.cdnURL, attestation)
fetchedList, err := apifetcher.Fetch(ctx, f.HTTPClient, f.cdnURL, list)
if err != nil {
return list, fmt.Errorf("fetching version list: %w", err)
}
// Need to set this explicitly as the variant is not part of the marshalled JSON.
fetchedList.variant = list.variant
return fetchedList, nil
}
// FetchAzureSEVSNPVersion fetches the version information from the config API.
func (f *fetcher) FetchAzureSEVSNPVersion(ctx context.Context, azureVersion AzureSEVSNPVersionAPI) (AzureSEVSNPVersionAPI, error) {
fetchedVersion, err := apifetcher.FetchAndVerify(ctx, f.HTTPClient, f.cdnURL, azureVersion, f.verifier)
// FetchSEVSNPVersion fetches the version information from the config API.
func (f *fetcher) FetchSEVSNPVersion(ctx context.Context, version SEVSNPVersionAPI) (SEVSNPVersionAPI, error) {
fetchedVersion, err := apifetcher.FetchAndVerify(ctx, f.HTTPClient, f.cdnURL, version, f.verifier)
if err != nil {
return fetchedVersion, fmt.Errorf("fetching version %s: %w", azureVersion.Version, err)
return fetchedVersion, fmt.Errorf("fetching version %s: %w", version.Version, err)
}
// Need to set this explicitly as the variant is not part of the marshalled JSON.
fetchedVersion.Variant = version.Variant
return fetchedVersion, nil
}
// FetchAzureSEVSNPVersionLatest returns the latest versions of the given type.
func (f *fetcher) FetchAzureSEVSNPVersionLatest(ctx context.Context) (res AzureSEVSNPVersionAPI, err error) {
var list AzureSEVSNPVersionList
list, err = f.FetchAzureSEVSNPVersionList(ctx, list)
// FetchSEVSNPVersionLatest returns the latest versions of the given type.
func (f *fetcher) FetchSEVSNPVersionLatest(ctx context.Context, attesation variant.Variant) (res SEVSNPVersionAPI, err error) {
list, err := f.FetchSEVSNPVersionList(ctx, SEVSNPVersionList{variant: attesation})
if err != nil {
return res, ErrNoVersionsFound
}
if len(list) < 1 {
return res, ErrNoVersionsFound
getVersionRequest := SEVSNPVersionAPI{
Version: list.List()[0], // latest version is first in list
Variant: attesation,
}
getVersionRequest := AzureSEVSNPVersionAPI{
Version: list[0], // latest version is first in list
}
res, err = f.FetchAzureSEVSNPVersion(ctx, getVersionRequest)
res, err = f.FetchSEVSNPVersion(ctx, getVersionRequest)
if err != nil {
return res, err
}

View File

@ -16,48 +16,65 @@ import (
"testing"
"time"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
"github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/stretchr/testify/assert"
)
func TestFetchLatestAzureSEVSNPVersion(t *testing.T) {
func TestFetchLatestSEVSNPVersion(t *testing.T) {
latestStr := "2023-06-11-14-09.json"
olderStr := "2019-01-01-01-01.json"
testcases := map[string]struct {
fetcherVersions []string
timeAtTest time.Time
wantErr bool
want AzureSEVSNPVersionAPI
attestation variant.Variant
expectedVersion func() SEVSNPVersionAPI
olderVersion func() SEVSNPVersionAPI
latestVersion func() SEVSNPVersionAPI
}{
"get latest version": {
"get latest version azure": {
fetcherVersions: []string{latestStr, olderStr},
want: latestVersion,
attestation: variant.AzureSEVSNP{},
expectedVersion: func() SEVSNPVersionAPI { tmp := latestVersion; tmp.Variant = variant.AzureSEVSNP{}; return tmp },
olderVersion: func() SEVSNPVersionAPI { tmp := olderVersion; tmp.Variant = variant.AzureSEVSNP{}; return tmp },
latestVersion: func() SEVSNPVersionAPI { tmp := latestVersion; tmp.Variant = variant.AzureSEVSNP{}; return tmp },
},
"get latest version aws": {
fetcherVersions: []string{latestStr, olderStr},
attestation: variant.AWSSEVSNP{},
expectedVersion: func() SEVSNPVersionAPI { tmp := latestVersion; tmp.Variant = variant.AWSSEVSNP{}; return tmp },
olderVersion: func() SEVSNPVersionAPI { tmp := olderVersion; tmp.Variant = variant.AWSSEVSNP{}; return tmp },
latestVersion: func() SEVSNPVersionAPI { tmp := latestVersion; tmp.Variant = variant.AWSSEVSNP{}; return tmp },
},
}
for name, tc := range testcases {
t.Run(name, func(t *testing.T) {
client := &http.Client{
Transport: &fakeConfigAPIHandler{
attestation: tc.attestation,
versions: tc.fetcherVersions,
latestVersion: latestStr,
olderVersion: olderStr,
latestDate: latestStr,
latestVersion: tc.latestVersion(),
olderDate: olderStr,
olderVersion: tc.olderVersion(),
},
}
fetcher := newFetcherWithClientAndVerifier(client, dummyVerifier{}, constants.CDNRepositoryURL)
res, err := fetcher.FetchAzureSEVSNPVersionLatest(context.Background())
res, err := fetcher.FetchSEVSNPVersionLatest(context.Background(), tc.attestation)
assert := assert.New(t)
if tc.wantErr {
assert.Error(err)
} else {
assert.NoError(err)
assert.Equal(tc.want, res)
assert.Equal(tc.expectedVersion(), res)
}
})
}
}
var latestVersion = AzureSEVSNPVersionAPI{
AzureSEVSNPVersion: AzureSEVSNPVersion{
var latestVersion = SEVSNPVersionAPI{
SEVSNPVersion: SEVSNPVersion{
Microcode: 93,
TEE: 0,
SNP: 6,
@ -65,8 +82,8 @@ var latestVersion = AzureSEVSNPVersionAPI{
},
}
var olderVersion = AzureSEVSNPVersionAPI{
AzureSEVSNPVersion: AzureSEVSNPVersion{
var olderVersion = SEVSNPVersionAPI{
SEVSNPVersion: SEVSNPVersion{
Microcode: 1,
TEE: 0,
SNP: 1,
@ -75,14 +92,17 @@ var olderVersion = AzureSEVSNPVersionAPI{
}
type fakeConfigAPIHandler struct {
attestation variant.Variant
versions []string
latestVersion string
olderVersion string
latestDate string
latestVersion SEVSNPVersionAPI
olderDate string
olderVersion SEVSNPVersionAPI
}
// RoundTrip resolves the request and returns a dummy response.
func (f *fakeConfigAPIHandler) RoundTrip(req *http.Request) (*http.Response, error) {
if req.URL.Path == "/constellation/v1/attestation/azure-sev-snp/list" {
if req.URL.Path == fmt.Sprintf("/constellation/v1/attestation/%s/list", f.attestation.String()) {
res := &http.Response{}
bt, err := json.Marshal(f.versions)
if err != nil {
@ -93,9 +113,9 @@ 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/azure-sev-snp/%s", f.latestVersion) {
} else if req.URL.Path == fmt.Sprintf("/constellation/v1/attestation/%s/%s", f.attestation.String(), f.latestDate) {
res := &http.Response{}
bt, err := json.Marshal(latestVersion)
bt, err := json.Marshal(f.latestVersion)
if err != nil {
return nil, err
}
@ -103,22 +123,22 @@ 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/azure-sev-snp/%s", f.olderVersion) {
} else if req.URL.Path == fmt.Sprintf("/constellation/v1/attestation/%s/%s", f.attestation.String(), f.olderDate) {
res := &http.Response{}
bt, err := json.Marshal(olderVersion)
bt, err := json.Marshal(f.olderVersion)
if err != nil {
return nil, 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/azure-sev-snp/%s.sig", f.latestVersion) {
} else if req.URL.Path == 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/azure-sev-snp/%s.sig", f.olderVersion) {
} else if req.URL.Path == 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

View File

@ -6,6 +6,8 @@ SPDX-License-Identifier: AGPL-3.0-only
/*
The reporter contains the logic to determine a latest version for Azure SEVSNP based on cached version values observed on CVM instances.
Some code in this file (e.g. listing cached files) does not rely on dedicated API objects and instead uses the AWS SDK directly,
for no other reason than original development speed.
*/
package attestationconfigapi
@ -28,25 +30,27 @@ import (
// cachedVersionsSubDir is the subdirectory in the bucket where the cached versions are stored.
const cachedVersionsSubDir = "cached-versions"
var reportVersionDir = path.Join(attestationURLPath, variant.AzureSEVSNP{}.String(), cachedVersionsSubDir)
// ErrNoNewerVersion is returned if the input version is not newer than the latest API version.
var ErrNoNewerVersion = errors.New("input version is not newer than latest API version")
// UploadAzureSEVSNPVersionLatest saves the given version to the cache, determines the smallest
func reportVersionDir(attestation variant.Variant) string {
return path.Join(attestationURLPath, attestation.String(), cachedVersionsSubDir)
}
// UploadSEVSNPVersionLatest 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) UploadAzureSEVSNPVersionLatest(ctx context.Context, inputVersion,
latestAPIVersion AzureSEVSNPVersion, now time.Time, force bool,
func (c Client) UploadSEVSNPVersionLatest(ctx context.Context, attestation variant.Variant, inputVersion,
latestAPIVersion SEVSNPVersion, now time.Time, force bool,
) error {
if err := c.cacheAzureSEVSNPVersion(ctx, inputVersion, now); err != nil {
if err := c.cacheSEVSNPVersion(ctx, attestation, inputVersion, now); err != nil {
return fmt.Errorf("reporting version: %w", err)
}
if force {
return c.uploadAzureSEVSNPVersion(ctx, inputVersion, now)
return c.uploadSEVSNPVersion(ctx, attestation, inputVersion, now)
}
versionDates, err := c.listCachedVersions(ctx)
versionDates, err := c.listCachedVersions(ctx, attestation)
if err != nil {
return fmt.Errorf("list reported versions: %w", err)
}
@ -54,7 +58,7 @@ func (c Client) UploadAzureSEVSNPVersionLatest(ctx context.Context, inputVersion
c.s3Client.Logger.Warnf("Skipping version update, found %d, expected %d reported versions.", len(versionDates), c.cacheWindowSize)
return nil
}
minVersion, minDate, err := c.findMinVersion(ctx, versionDates)
minVersion, minDate, err := c.findMinVersion(ctx, attestation, versionDates)
if err != nil {
return fmt.Errorf("get minimal version: %w", err)
}
@ -72,27 +76,27 @@ func (c Client) UploadAzureSEVSNPVersionLatest(ctx context.Context, inputVersion
if err != nil {
return fmt.Errorf("parsing date: %w", err)
}
if err := c.uploadAzureSEVSNPVersion(ctx, minVersion, t); err != nil {
if err := c.uploadSEVSNPVersion(ctx, attestation, minVersion, t); err != nil {
return fmt.Errorf("uploading version: %w", err)
}
c.s3Client.Logger.Infof("Successfully uploaded new Azure SEV-SNP version: %+v", minVersion)
return nil
}
// cacheAzureSEVSNPVersion uploads the latest observed version numbers of the Azure SEVSNP. This version is used to later report the latest version numbers to the API.
func (c Client) cacheAzureSEVSNPVersion(ctx context.Context, version AzureSEVSNPVersion, date time.Time) error {
// cacheSEVSNPVersion uploads the latest observed version numbers of the Azure 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 SEVSNPVersion, date time.Time) error {
dateStr := date.Format(VersionFormat) + ".json"
res := putCmd{
apiObject: reportedAzureSEVSNPVersionAPI{Version: dateStr, AzureSEVSNPVersion: version},
apiObject: reportedSEVSNPVersionAPI{Version: dateStr, variant: attestation, SEVSNPVersion: version},
signer: c.signer,
}
return res.Execute(ctx, c.s3Client)
}
func (c Client) listCachedVersions(ctx context.Context) ([]string, error) {
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),
Prefix: aws.String(reportVersionDir(attestation)),
})
if err != nil {
return nil, fmt.Errorf("list objects: %w", err)
@ -108,28 +112,30 @@ func (c Client) listCachedVersions(ctx context.Context) ([]string, error) {
}
// 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, versionDates []string) (AzureSEVSNPVersion, string, error) {
var minimalVersion *AzureSEVSNPVersion
func (c Client) findMinVersion(ctx context.Context, attesation variant.Variant, versionDates []string) (SEVSNPVersion, string, error) {
var minimalVersion *SEVSNPVersion
var minimalDate string
sort.Sort(sort.Reverse(sort.StringSlice(versionDates))) // sort in reverse order to slice the latest versions
versionDates = versionDates[:c.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, reportedAzureSEVSNPVersionAPI{Version: date + ".json"})
obj, err := client.Fetch(ctx, c.s3Client, reportedSEVSNPVersionAPI{Version: date + ".json", variant: attesation})
if err != nil {
return AzureSEVSNPVersion{}, "", fmt.Errorf("get object: %w", err)
return SEVSNPVersion{}, "", fmt.Errorf("get object: %w", err)
}
// Need to set this explicitly as the variant is not part of the marshalled JSON.
obj.variant = attesation
if minimalVersion == nil {
minimalVersion = &obj.AzureSEVSNPVersion
minimalVersion = &obj.SEVSNPVersion
minimalDate = date
} else {
shouldUpdateMinimal, err := isInputNewerThanOtherVersion(*minimalVersion, obj.AzureSEVSNPVersion)
shouldUpdateMinimal, err := isInputNewerThanOtherVersion(*minimalVersion, obj.SEVSNPVersion)
if err != nil {
continue
}
if shouldUpdateMinimal {
minimalVersion = &obj.AzureSEVSNPVersion
minimalVersion = &obj.SEVSNPVersion
minimalDate = date
}
}
@ -138,7 +144,7 @@ func (c Client) findMinVersion(ctx context.Context, versionDates []string) (Azur
}
// isInputNewerThanOtherVersion compares all version fields and returns true if any input field is newer.
func isInputNewerThanOtherVersion(input, other AzureSEVSNPVersion) (bool, error) {
func isInputNewerThanOtherVersion(input, other SEVSNPVersion) (bool, error) {
if input == other {
return false, nil
}
@ -157,19 +163,20 @@ func isInputNewerThanOtherVersion(input, other AzureSEVSNPVersion) (bool, error)
return true, nil
}
// reportedAzureSEVSNPVersionAPI is the request to get the version information of the specific version in the config api.
type reportedAzureSEVSNPVersionAPI struct {
Version string `json:"-"`
AzureSEVSNPVersion
// reportedSEVSNPVersionAPI is the request to get the version information of the specific version in the config api.
type reportedSEVSNPVersionAPI struct {
Version string `json:"-"`
variant variant.Variant `json:"-"`
SEVSNPVersion
}
// JSONPath returns the path to the JSON file for the request to the config api.
func (i reportedAzureSEVSNPVersionAPI) JSONPath() string {
return path.Join(reportVersionDir, i.Version)
func (i reportedSEVSNPVersionAPI) JSONPath() string {
return path.Join(reportVersionDir(i.variant), i.Version)
}
// ValidateRequest validates the request.
func (i reportedAzureSEVSNPVersionAPI) ValidateRequest() error {
func (i reportedSEVSNPVersionAPI) ValidateRequest() error {
if !strings.HasSuffix(i.Version, ".json") {
return fmt.Errorf("version has no .json suffix")
}
@ -177,6 +184,6 @@ func (i reportedAzureSEVSNPVersionAPI) ValidateRequest() error {
}
// Validate is a No-Op at the moment.
func (i reportedAzureSEVSNPVersionAPI) Validate() error {
func (i reportedSEVSNPVersionAPI) Validate() error {
return nil
}

View File

@ -11,8 +11,8 @@ import (
)
func TestIsInputNewerThanLatestAPI(t *testing.T) {
newTestCfg := func() AzureSEVSNPVersion {
return AzureSEVSNPVersion{
newTestCfg := func() SEVSNPVersion {
return SEVSNPVersion{
Microcode: 93,
TEE: 0,
SNP: 6,
@ -21,13 +21,13 @@ func TestIsInputNewerThanLatestAPI(t *testing.T) {
}
testCases := map[string]struct {
latest AzureSEVSNPVersion
input AzureSEVSNPVersion
latest SEVSNPVersion
input SEVSNPVersion
expect bool
errMsg string
}{
"input is older than latest": {
input: func(c AzureSEVSNPVersion) AzureSEVSNPVersion {
input: func(c SEVSNPVersion) SEVSNPVersion {
c.Microcode--
return c
}(newTestCfg()),
@ -36,7 +36,7 @@ func TestIsInputNewerThanLatestAPI(t *testing.T) {
errMsg: "input Microcode version: 92 is older than latest API version: 93",
},
"input has greater and smaller version field than latest": {
input: func(c AzureSEVSNPVersion) AzureSEVSNPVersion {
input: func(c SEVSNPVersion) SEVSNPVersion {
c.Microcode++
c.Bootloader--
return c
@ -46,7 +46,7 @@ func TestIsInputNewerThanLatestAPI(t *testing.T) {
errMsg: "input Bootloader version: 1 is older than latest API version: 2",
},
"input is newer than latest": {
input: func(c AzureSEVSNPVersion) AzureSEVSNPVersion {
input: func(c SEVSNPVersion) SEVSNPVersion {
c.TEE++
return c
}(newTestCfg()),

View File

@ -0,0 +1,113 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package attestationconfigapi
import (
"encoding/json"
"fmt"
"path"
"sort"
"strings"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
)
// attestationURLPath is the URL path to the attestation versions.
const attestationURLPath = "constellation/v1/attestation"
// SEVSNPVersion tracks the latest version of each component of the Azure SEVSNP.
type SEVSNPVersion struct {
// Bootloader is the latest version of the Azure SEVSNP bootloader.
Bootloader uint8 `json:"bootloader"`
// TEE is the latest version of the Azure SEVSNP TEE.
TEE uint8 `json:"tee"`
// SNP is the latest version of the Azure SEVSNP SNP.
SNP uint8 `json:"snp"`
// Microcode is the latest version of the Azure SEVSNP microcode.
Microcode uint8 `json:"microcode"`
}
// SEVSNPVersionAPI is the request to get the version information of the specific version in the config api.
// Because variant is not part of the marshalled JSON, fetcher and client methods need to fill the variant property.
// Once we switch to v2 of the API we should embed the variant in the object.
// That would remove the possibility of some fetcher/client code forgetting to set the variant.
type SEVSNPVersionAPI struct {
Version string `json:"-"`
Variant variant.Variant `json:"-"`
SEVSNPVersion
}
// JSONPath returns the path to the JSON file for the request to the config api.
func (i SEVSNPVersionAPI) JSONPath() string {
return path.Join(attestationURLPath, i.Variant.String(), i.Version)
}
// ValidateRequest validates the request.
func (i SEVSNPVersionAPI) ValidateRequest() error {
if !strings.HasSuffix(i.Version, ".json") {
return fmt.Errorf("version has no .json suffix")
}
return nil
}
// Validate is a No-Op at the moment.
func (i SEVSNPVersionAPI) Validate() error {
return nil
}
// SEVSNPVersionList is the request to list all versions in the config api.
// Because variant is not part of the marshalled JSON, fetcher and client methods need to fill the variant property.
// Once we switch to v2 of the API we could 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 SEVSNPVersionList struct {
variant variant.Variant
list []string
}
// MarshalJSON marshals the i's list property to JSON.
func (i SEVSNPVersionList) MarshalJSON() ([]byte, error) {
return json.Marshal(i.list)
}
// UnmarshalJSON unmarshals a list of strings into i's list property.
func (i *SEVSNPVersionList) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &i.list)
}
// List returns i's list property.
func (i SEVSNPVersionList) List() []string { return i.list }
// JSONPath returns the path to the JSON file for the request to the config api.
func (i SEVSNPVersionList) JSONPath() string {
return path.Join(attestationURLPath, i.variant.String(), "list")
}
// ValidateRequest is a NoOp as there is no input.
func (i SEVSNPVersionList) ValidateRequest() error {
return nil
}
// SortReverse sorts the list of versions in reverse order.
func (i *SEVSNPVersionList) 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 *SEVSNPVersionList) addVersion(new string) {
i.list = append(i.list, new)
i.list = variant.RemoveDuplicate(i.list)
i.SortReverse()
}
// Validate validates the response.
func (i SEVSNPVersionList) Validate() error {
if len(i.list) < 1 {
return fmt.Errorf("no versions found in /list")
}
return nil
}

View File

@ -0,0 +1,77 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package attestationconfigapi
import (
"testing"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestSEVSNPVersionListMarshalUnmarshalJSON(t *testing.T) {
tests := map[string]struct {
input SEVSNPVersionList
output SEVSNPVersionList
wantDiff bool
}{
"success": {
input: SEVSNPVersionList{list: []string{"v1", "v2"}},
output: SEVSNPVersionList{list: []string{"v1", "v2"}},
},
"variant is lost": {
input: SEVSNPVersionList{list: []string{"v1", "v2"}, variant: variant.AzureSEVSNP{}},
output: SEVSNPVersionList{list: []string{"v1", "v2"}},
},
"wrong order": {
input: SEVSNPVersionList{list: []string{"v1", "v2"}},
output: SEVSNPVersionList{list: []string{"v2", "v1"}},
wantDiff: true,
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
inputRaw, err := tc.input.MarshalJSON()
require.NoError(t, err)
var actual SEVSNPVersionList
err = actual.UnmarshalJSON(inputRaw)
require.NoError(t, err)
if tc.wantDiff {
assert.NotEqual(t, tc.output, actual, "Objects are equal, expected unequal")
} else {
assert.Equal(t, tc.output, actual, "Objects are not equal, expected equal")
}
})
}
}
func TestSEVSNPVersionListAddVersion(t *testing.T) {
tests := map[string]struct {
versions []string
new string
expected []string
}{
"success": {
versions: []string{"v1", "v2"},
new: "v3",
expected: []string{"v3", "v2", "v1"},
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
v := SEVSNPVersionList{list: tc.versions}
v.addVersion(tc.new)
assert.Equal(t, tc.expected, v.list)
})
}
}

View File

@ -69,16 +69,16 @@ func (c AzureSEVSNP) EqualTo(old AttestationCfg) (bool, error) {
// FetchAndSetLatestVersionNumbers fetches the latest version numbers from the configapi and sets them.
func (c *AzureSEVSNP) FetchAndSetLatestVersionNumbers(ctx context.Context, fetcher attestationconfigapi.Fetcher) error {
versions, err := fetcher.FetchAzureSEVSNPVersionLatest(ctx)
versions, err := fetcher.FetchSEVSNPVersionLatest(ctx, variant.AzureSEVSNP{})
if err != nil {
return err
}
// set number and keep isLatest flag
c.mergeWithLatestVersion(versions.AzureSEVSNPVersion)
c.mergeWithLatestVersion(versions.SEVSNPVersion)
return nil
}
func (c *AzureSEVSNP) mergeWithLatestVersion(latest attestationconfigapi.AzureSEVSNPVersion) {
func (c *AzureSEVSNP) mergeWithLatestVersion(latest attestationconfigapi.SEVSNPVersion) {
if c.BootloaderVersion.WantLatest {
c.BootloaderVersion.Value = latest.Bootloader
}

View File

@ -23,6 +23,7 @@ import (
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config/instancetypes"
"github.com/edgelesssys/constellation/v2/internal/constants"
@ -51,10 +52,10 @@ func TestDefaultConfigMarshalsLatestVersion(t *testing.T) {
var mp configMap
require.NoError(yaml.Unmarshal(bt, &mp))
assert := assert.New(t)
assert.Equal("latest", mp.getAzureSEVSNPVersion("microcodeVersion"))
assert.Equal("latest", mp.getAzureSEVSNPVersion("teeVersion"))
assert.Equal("latest", mp.getAzureSEVSNPVersion("snpVersion"))
assert.Equal("latest", mp.getAzureSEVSNPVersion("bootloaderVersion"))
assert.Equal("latest", mp.getSEVSNPVersion("microcodeVersion"))
assert.Equal("latest", mp.getSEVSNPVersion("teeVersion"))
assert.Equal("latest", mp.getSEVSNPVersion("snpVersion"))
assert.Equal("latest", mp.getSEVSNPVersion("bootloaderVersion"))
}
func TestGetAttestationConfigMarshalsNumericalVersion(t *testing.T) {
@ -88,9 +89,9 @@ func TestNew(t *testing.T) {
conf := Default() // default configures latest version
modifyConfigForAzureToPassValidate(conf)
m := getConfigAsMap(conf, t)
m.setAzureSEVSNPVersion("microcodeVersion", "Latest") // check uppercase also works
m.setAzureSEVSNPVersion("teeVersion", 2)
m.setAzureSEVSNPVersion("bootloaderVersion", 1)
m.setSEVSNPVersion("microcodeVersion", "Latest") // check uppercase also works
m.setSEVSNPVersion("teeVersion", 2)
m.setSEVSNPVersion("bootloaderVersion", 1)
return m
}(),
@ -181,7 +182,7 @@ func TestReadConfigFile(t *testing.T) {
config: func() configMap {
conf := Default()
m := getConfigAsMap(conf, t)
m.setAzureSEVSNPVersion("microcodeVersion", "1a")
m.setSEVSNPVersion("microcodeVersion", "1a")
return m
}(),
configName: constants.ConfigFilename,
@ -1053,7 +1054,7 @@ func TestIsAppClientIDError(t *testing.T) {
// configMap is used to un-/marshal the config as an unstructured map.
type configMap map[string]interface{}
func (c configMap) setAzureSEVSNPVersion(versionType string, value interface{}) {
func (c configMap) setSEVSNPVersion(versionType string, value interface{}) {
c["attestation"].(configMap)["azureSEVSNP"].(configMap)[versionType] = value
}
@ -1061,7 +1062,7 @@ func (c configMap) setAzureProvider(azureProviderField string, value interface{}
c["provider"].(configMap)["azure"].(configMap)[azureProviderField] = value
}
func (c configMap) getAzureSEVSNPVersion(versionType string) interface{} {
func (c configMap) getSEVSNPVersion(versionType string) interface{} {
return c["attestation"].(configMap)["azureSEVSNP"].(configMap)[versionType]
}
@ -1079,25 +1080,23 @@ func getConfigAsMap(conf *Config, t *testing.T) (res configMap) {
type stubAttestationFetcher struct{}
func (f stubAttestationFetcher) FetchAzureSEVSNPVersionList(_ context.Context, _ attestationconfigapi.AzureSEVSNPVersionList) (attestationconfigapi.AzureSEVSNPVersionList, error) {
return attestationconfigapi.AzureSEVSNPVersionList(
[]string{},
), nil
func (f stubAttestationFetcher) FetchSEVSNPVersionList(_ context.Context, _ attestationconfigapi.SEVSNPVersionList) (attestationconfigapi.SEVSNPVersionList, error) {
return attestationconfigapi.SEVSNPVersionList{}, nil
}
func (f stubAttestationFetcher) FetchAzureSEVSNPVersion(_ context.Context, _ attestationconfigapi.AzureSEVSNPVersionAPI) (attestationconfigapi.AzureSEVSNPVersionAPI, error) {
return attestationconfigapi.AzureSEVSNPVersionAPI{
AzureSEVSNPVersion: testCfg,
func (f stubAttestationFetcher) FetchSEVSNPVersion(_ context.Context, _ attestationconfigapi.SEVSNPVersionAPI) (attestationconfigapi.SEVSNPVersionAPI, error) {
return attestationconfigapi.SEVSNPVersionAPI{
SEVSNPVersion: testCfg,
}, nil
}
func (f stubAttestationFetcher) FetchAzureSEVSNPVersionLatest(_ context.Context) (attestationconfigapi.AzureSEVSNPVersionAPI, error) {
return attestationconfigapi.AzureSEVSNPVersionAPI{
AzureSEVSNPVersion: testCfg,
func (f stubAttestationFetcher) FetchSEVSNPVersionLatest(_ context.Context, _ variant.Variant) (attestationconfigapi.SEVSNPVersionAPI, error) {
return attestationconfigapi.SEVSNPVersionAPI{
SEVSNPVersion: testCfg,
}, nil
}
var testCfg = attestationconfigapi.AzureSEVSNPVersion{
var testCfg = attestationconfigapi.SEVSNPVersion{
Microcode: 93,
TEE: 0,
SNP: 6,