mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-10-01 01:36:09 -04:00
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:
parent
5542f9c63c
commit
350397923f
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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",
|
||||
],
|
||||
)
|
||||
|
@ -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
|
||||
}
|
@ -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(
|
||||
|
@ -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)
|
||||
|
@ -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, ¬FoundErr) {
|
||||
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, ¬FoundErr) {
|
||||
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
|
||||
}
|
||||
|
@ -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"}},
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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()),
|
||||
|
113
internal/api/attestationconfigapi/snp.go
Normal file
113
internal/api/attestationconfigapi/snp.go
Normal 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
|
||||
}
|
77
internal/api/attestationconfigapi/snp_test.go
Normal file
77
internal/api/attestationconfigapi/snp_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user