mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-05-02 22:34:56 -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
16 changed files with 411 additions and 262 deletions
|
@ -17,6 +17,7 @@ import (
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
|
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
|
"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/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
|
@ -294,25 +295,23 @@ func TestConfigFetchMeasurements(t *testing.T) {
|
||||||
|
|
||||||
type stubAttestationFetcher struct{}
|
type stubAttestationFetcher struct{}
|
||||||
|
|
||||||
func (f stubAttestationFetcher) FetchAzureSEVSNPVersionList(_ context.Context, _ attestationconfigapi.AzureSEVSNPVersionList) (attestationconfigapi.AzureSEVSNPVersionList, error) {
|
func (f stubAttestationFetcher) FetchSEVSNPVersionList(_ context.Context, _ attestationconfigapi.SEVSNPVersionList) (attestationconfigapi.SEVSNPVersionList, error) {
|
||||||
return attestationconfigapi.AzureSEVSNPVersionList(
|
return attestationconfigapi.SEVSNPVersionList{}, nil
|
||||||
[]string{},
|
|
||||||
), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f stubAttestationFetcher) FetchAzureSEVSNPVersion(_ context.Context, _ attestationconfigapi.AzureSEVSNPVersionAPI) (attestationconfigapi.AzureSEVSNPVersionAPI, error) {
|
func (f stubAttestationFetcher) FetchSEVSNPVersion(_ context.Context, _ attestationconfigapi.SEVSNPVersionAPI) (attestationconfigapi.SEVSNPVersionAPI, error) {
|
||||||
return attestationconfigapi.AzureSEVSNPVersionAPI{
|
return attestationconfigapi.SEVSNPVersionAPI{
|
||||||
AzureSEVSNPVersion: testCfg,
|
SEVSNPVersion: testCfg,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f stubAttestationFetcher) FetchAzureSEVSNPVersionLatest(_ context.Context) (attestationconfigapi.AzureSEVSNPVersionAPI, error) {
|
func (f stubAttestationFetcher) FetchSEVSNPVersionLatest(_ context.Context, _ variant.Variant) (attestationconfigapi.SEVSNPVersionAPI, error) {
|
||||||
return attestationconfigapi.AzureSEVSNPVersionAPI{
|
return attestationconfigapi.SEVSNPVersionAPI{
|
||||||
AzureSEVSNPVersion: testCfg,
|
SEVSNPVersion: testCfg,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var testCfg = attestationconfigapi.AzureSEVSNPVersion{
|
var testCfg = attestationconfigapi.SEVSNPVersion{
|
||||||
Microcode: 93,
|
Microcode: 93,
|
||||||
TEE: 0,
|
TEE: 0,
|
||||||
SNP: 6,
|
SNP: 6,
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
|
"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/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
|
@ -170,14 +171,14 @@ type stubConfigFetcher struct {
|
||||||
fetchLatestErr error
|
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")
|
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")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stubConfigFetcher) FetchAzureSEVSNPVersionLatest(context.Context) (attestationconfigapi.AzureSEVSNPVersionAPI, error) {
|
func (s *stubConfigFetcher) FetchSEVSNPVersionLatest(context.Context, variant.Variant) (attestationconfigapi.SEVSNPVersionAPI, error) {
|
||||||
return attestationconfigapi.AzureSEVSNPVersionAPI{}, s.fetchLatestErr
|
return attestationconfigapi.SEVSNPVersionAPI{}, s.fetchLatestErr
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,10 @@ go_library(
|
||||||
name = "attestationconfigapi",
|
name = "attestationconfigapi",
|
||||||
srcs = [
|
srcs = [
|
||||||
"attestationconfigapi.go",
|
"attestationconfigapi.go",
|
||||||
"azure.go",
|
|
||||||
"client.go",
|
"client.go",
|
||||||
"fetcher.go",
|
"fetcher.go",
|
||||||
"reporter.go",
|
"reporter.go",
|
||||||
|
"snp.go",
|
||||||
],
|
],
|
||||||
importpath = "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi",
|
importpath = "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi",
|
||||||
visibility = ["//:__subpackages__"],
|
visibility = ["//:__subpackages__"],
|
||||||
|
@ -31,10 +31,13 @@ go_test(
|
||||||
"client_test.go",
|
"client_test.go",
|
||||||
"fetcher_test.go",
|
"fetcher_test.go",
|
||||||
"reporter_test.go",
|
"reporter_test.go",
|
||||||
|
"snp_test.go",
|
||||||
],
|
],
|
||||||
embed = [":attestationconfigapi"],
|
embed = [":attestationconfigapi"],
|
||||||
deps = [
|
deps = [
|
||||||
|
"//internal/attestation/variant",
|
||||||
"//internal/constants",
|
"//internal/constants",
|
||||||
"@com_github_stretchr_testify//assert",
|
"@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",
|
"azure.go",
|
||||||
"delete.go",
|
"delete.go",
|
||||||
"main.go",
|
"main.go",
|
||||||
"objectkind_string.go",
|
|
||||||
"validargs.go",
|
"validargs.go",
|
||||||
],
|
],
|
||||||
importpath = "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi/cli",
|
importpath = "github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi/cli",
|
||||||
visibility = ["//visibility:private"],
|
visibility = ["//visibility:private"],
|
||||||
deps = [
|
deps = [
|
||||||
"//internal/api/attestationconfigapi",
|
"//internal/api/attestationconfigapi",
|
||||||
|
"//internal/attestation/variant",
|
||||||
"//internal/cloud/cloudprovider",
|
"//internal/cloud/cloudprovider",
|
||||||
"//internal/constants",
|
"//internal/constants",
|
||||||
"//internal/file",
|
"//internal/file",
|
||||||
|
@ -44,11 +44,6 @@ go_test(
|
||||||
"main_test.go",
|
"main_test.go",
|
||||||
],
|
],
|
||||||
embed = [":cli_lib"],
|
embed = [":cli_lib"],
|
||||||
deps = [
|
|
||||||
"//internal/cloud/cloudprovider",
|
|
||||||
"//internal/verify",
|
|
||||||
"@com_github_stretchr_testify//assert",
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
sh_template(
|
sh_template(
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
s3types "github.com/aws/aws-sdk-go-v2/service/s3/types"
|
s3types "github.com/aws/aws-sdk-go-v2/service/s3/types"
|
||||||
"github.com/aws/aws-sdk-go/aws"
|
"github.com/aws/aws-sdk-go/aws"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
|
"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/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
"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)
|
inputVersion := convertTCBVersionToAzureVersion(report.SNPReport.LaunchTCB)
|
||||||
log.Infof("Input report: %+v", inputVersion)
|
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 err != nil {
|
||||||
if errors.Is(err, attestationconfigapi.ErrNoVersionsFound) {
|
if errors.Is(err, attestationconfigapi.ErrNoVersionsFound) {
|
||||||
log.Infof("No versions found in API, but assuming that we are uploading the first version.")
|
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)
|
return fmt.Errorf("fetching latest version: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
latestAPIVersion := latestAPIVersionAPI.AzureSEVSNPVersion
|
latestAPIVersion := latestAPIVersionAPI.SEVSNPVersion
|
||||||
if err := client.UploadAzureSEVSNPVersionLatest(ctx, inputVersion, latestAPIVersion, cfg.uploadDate, cfg.force); err != nil {
|
if err := client.UploadSEVSNPVersionLatest(ctx, variant.AzureSEVSNP{}, inputVersion, latestAPIVersion, cfg.uploadDate, cfg.force); err != nil {
|
||||||
if errors.Is(err, attestationconfigapi.ErrNoNewerVersion) {
|
if errors.Is(err, attestationconfigapi.ErrNoNewerVersion) {
|
||||||
log.Infof("Input version: %+v is not newer than latest API version: %+v", inputVersion, latestAPIVersion)
|
log.Infof("Input version: %+v is not newer than latest API version: %+v", inputVersion, latestAPIVersion)
|
||||||
return nil
|
return nil
|
||||||
|
@ -56,8 +57,8 @@ func uploadAzure(ctx context.Context, client *attestationconfigapi.Client, cfg u
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertTCBVersionToAzureVersion(tcb verify.TCBVersion) attestationconfigapi.AzureSEVSNPVersion {
|
func convertTCBVersionToAzureVersion(tcb verify.TCBVersion) attestationconfigapi.SEVSNPVersion {
|
||||||
return attestationconfigapi.AzureSEVSNPVersion{
|
return attestationconfigapi.SEVSNPVersion{
|
||||||
Bootloader: tcb.Bootloader,
|
Bootloader: tcb.Bootloader,
|
||||||
TEE: tcb.TEE,
|
TEE: tcb.TEE,
|
||||||
SNP: tcb.SNP,
|
SNP: tcb.SNP,
|
||||||
|
@ -67,7 +68,7 @@ func convertTCBVersionToAzureVersion(tcb verify.TCBVersion) attestationconfigapi
|
||||||
|
|
||||||
func deleteAzure(ctx context.Context, client *attestationconfigapi.Client, cfg deleteConfig) error {
|
func deleteAzure(ctx context.Context, client *attestationconfigapi.Client, cfg deleteConfig) error {
|
||||||
if cfg.provider == cloudprovider.Azure && cfg.kind == snpReport {
|
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)
|
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
|
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.
|
// 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) uploadAzureSEVSNPVersion(ctx context.Context, version AzureSEVSNPVersion, date time.Time) error {
|
func (a Client) uploadSEVSNPVersion(ctx context.Context, attestation variant.Variant, version SEVSNPVersion, date time.Time) error {
|
||||||
versions, err := a.List(ctx, variant.AzureSEVSNP{})
|
versions, err := a.List(ctx, attestation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("fetch version list: %w", err)
|
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)
|
return executeAllCmds(ctx, a.s3Client, ops)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteAzureSEVSNPVersion deletes the given version (without .json suffix) from the API.
|
// DeleteSEVSNPVersion deletes the given version (without .json suffix) from the API.
|
||||||
func (a Client) DeleteAzureSEVSNPVersion(ctx context.Context, versionStr string) error {
|
func (a Client) DeleteSEVSNPVersion(ctx context.Context, attestation variant.Variant, versionStr string) error {
|
||||||
versions, err := a.List(ctx, variant.AzureSEVSNP{})
|
versions, err := a.List(ctx, attestation)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("fetch version list: %w", err)
|
return fmt.Errorf("fetch version list: %w", err)
|
||||||
}
|
}
|
||||||
ops, err := a.deleteAzureSEVSNPVersion(versions, versionStr)
|
|
||||||
|
ops, err := a.deleteSEVSNPVersion(versions, versionStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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.
|
// List returns the list of versions for the given attestation variant.
|
||||||
func (a Client) List(ctx context.Context, attestation variant.Variant) ([]string, error) {
|
func (a Client) List(ctx context.Context, attestation variant.Variant) (SEVSNPVersionList, error) {
|
||||||
if attestation.Equal(variant.AzureSEVSNP{}) {
|
if !attestation.Equal(variant.AzureSEVSNP{}) && !attestation.Equal(variant.AWSSEVSNP{}) {
|
||||||
versions, err := apiclient.Fetch(ctx, a.s3Client, AzureSEVSNPVersionList{})
|
return SEVSNPVersionList{}, fmt.Errorf("unsupported attestation variant: %s", attestation)
|
||||||
if err != nil {
|
|
||||||
var notFoundErr *apiclient.NotFoundError
|
|
||||||
if errors.As(err, ¬FoundErr) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return versions, nil
|
|
||||||
}
|
}
|
||||||
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"
|
versionStr = versionStr + ".json"
|
||||||
ops = append(ops, deleteCmd{
|
ops = append(ops, deleteCmd{
|
||||||
apiObject: AzureSEVSNPVersionAPI{
|
apiObject: SEVSNPVersionAPI{
|
||||||
|
Variant: versions.variant,
|
||||||
Version: versionStr,
|
Version: versionStr,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -107,36 +113,42 @@ func (a Client) deleteAzureSEVSNPVersion(versions AzureSEVSNPVersionList, versio
|
||||||
return ops, nil
|
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"
|
dateStr := date.Format(VersionFormat) + ".json"
|
||||||
var res []crudCmd
|
var res []crudCmd
|
||||||
|
|
||||||
res = append(res, putCmd{
|
res = append(res, putCmd{
|
||||||
apiObject: AzureSEVSNPVersionAPI{Version: dateStr, AzureSEVSNPVersion: versions},
|
apiObject: SEVSNPVersionAPI{Version: dateStr, Variant: attestation, SEVSNPVersion: version},
|
||||||
signer: a.signer,
|
signer: a.signer,
|
||||||
})
|
})
|
||||||
|
|
||||||
newVersions := addVersion(versionNames, dateStr)
|
versionNames.addVersion(dateStr)
|
||||||
|
|
||||||
res = append(res, putCmd{
|
res = append(res, putCmd{
|
||||||
apiObject: AzureSEVSNPVersionList(newVersions),
|
apiObject: versionNames,
|
||||||
signer: a.signer,
|
signer: a.signer,
|
||||||
})
|
})
|
||||||
|
|
||||||
return res
|
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 {
|
for i, v := range versions {
|
||||||
if v == versionStr {
|
if v == versionStr {
|
||||||
if i == len(versions)-1 {
|
if i == len(versions)-1 {
|
||||||
removedVersions = versions[:i]
|
removedVersions = SEVSNPVersionList{list: versions[:i], variant: list.variant}
|
||||||
} else {
|
} else {
|
||||||
removedVersions = append(versions[:i], versions[i+1:]...)
|
removedVersions = SEVSNPVersionList{list: append(versions[:i], versions[i+1:]...), variant: list.variant}
|
||||||
}
|
}
|
||||||
return removedVersions, nil
|
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 {
|
type crudCmd interface {
|
||||||
|
@ -168,10 +180,3 @@ func executeAllCmds(ctx context.Context, client *apiclient.Client, cmds []crudCm
|
||||||
}
|
}
|
||||||
return nil
|
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"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,20 +18,21 @@ func TestUploadAzureSEVSNP(t *testing.T) {
|
||||||
bucketID: "bucket",
|
bucketID: "bucket",
|
||||||
signer: fakeSigner{},
|
signer: fakeSigner{},
|
||||||
}
|
}
|
||||||
version := AzureSEVSNPVersion{}
|
version := SEVSNPVersion{}
|
||||||
date := time.Date(2023, 1, 1, 1, 1, 1, 1, time.UTC)
|
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"
|
dateStr := "2023-01-01-01-01.json"
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
assert.Contains(ops, putCmd{
|
assert.Contains(ops, putCmd{
|
||||||
apiObject: AzureSEVSNPVersionAPI{
|
apiObject: SEVSNPVersionAPI{
|
||||||
Version: dateStr,
|
Variant: variant.AzureSEVSNP{},
|
||||||
AzureSEVSNPVersion: version,
|
Version: dateStr,
|
||||||
|
SEVSNPVersion: version,
|
||||||
},
|
},
|
||||||
signer: fakeSigner{},
|
signer: fakeSigner{},
|
||||||
})
|
})
|
||||||
assert.Contains(ops, putCmd{
|
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{},
|
signer: fakeSigner{},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -39,20 +41,20 @@ func TestDeleteAzureSEVSNPVersions(t *testing.T) {
|
||||||
sut := Client{
|
sut := Client{
|
||||||
bucketID: "bucket",
|
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 := assert.New(t)
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
assert.Contains(ops, deleteCmd{
|
assert.Contains(ops, deleteCmd{
|
||||||
apiObject: AzureSEVSNPVersionAPI{
|
apiObject: SEVSNPVersionAPI{
|
||||||
Version: "2021-01-01.json",
|
Version: "2021-01-01.json",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
assert.Contains(ops, putCmd{
|
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"
|
"fmt"
|
||||||
|
|
||||||
apifetcher "github.com/edgelesssys/constellation/v2/internal/api/fetcher"
|
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/constants"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/sigstore"
|
"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.
|
// Fetcher fetches config API resources without authentication.
|
||||||
type Fetcher interface {
|
type Fetcher interface {
|
||||||
FetchAzureSEVSNPVersion(ctx context.Context, azureVersion AzureSEVSNPVersionAPI) (AzureSEVSNPVersionAPI, error)
|
FetchSEVSNPVersion(ctx context.Context, version SEVSNPVersionAPI) (SEVSNPVersionAPI, error)
|
||||||
FetchAzureSEVSNPVersionList(ctx context.Context, attestation AzureSEVSNPVersionList) (AzureSEVSNPVersionList, error)
|
FetchSEVSNPVersionList(ctx context.Context, list SEVSNPVersionList) (SEVSNPVersionList, error)
|
||||||
FetchAzureSEVSNPVersionLatest(ctx context.Context) (AzureSEVSNPVersionAPI, error)
|
FetchSEVSNPVersionLatest(ctx context.Context, attesation variant.Variant) (SEVSNPVersionAPI, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetcher fetches AttestationCfg API resources without authentication.
|
// 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}
|
return &fetcher{HTTPClient: client, verifier: cosignVerifier, cdnURL: url}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FetchAzureSEVSNPVersionList fetches the version list information from the config API.
|
// FetchSEVSNPVersionList fetches the version list information from the config API.
|
||||||
func (f *fetcher) FetchAzureSEVSNPVersionList(ctx context.Context, attestation AzureSEVSNPVersionList) (AzureSEVSNPVersionList, error) {
|
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.
|
// 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.
|
// FetchSEVSNPVersion fetches the version information from the config API.
|
||||||
func (f *fetcher) FetchAzureSEVSNPVersion(ctx context.Context, azureVersion AzureSEVSNPVersionAPI) (AzureSEVSNPVersionAPI, error) {
|
func (f *fetcher) FetchSEVSNPVersion(ctx context.Context, version SEVSNPVersionAPI) (SEVSNPVersionAPI, error) {
|
||||||
fetchedVersion, err := apifetcher.FetchAndVerify(ctx, f.HTTPClient, f.cdnURL, azureVersion, f.verifier)
|
fetchedVersion, err := apifetcher.FetchAndVerify(ctx, f.HTTPClient, f.cdnURL, version, f.verifier)
|
||||||
if err != nil {
|
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
|
return fetchedVersion, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FetchAzureSEVSNPVersionLatest returns the latest versions of the given type.
|
// FetchSEVSNPVersionLatest returns the latest versions of the given type.
|
||||||
func (f *fetcher) FetchAzureSEVSNPVersionLatest(ctx context.Context) (res AzureSEVSNPVersionAPI, err error) {
|
func (f *fetcher) FetchSEVSNPVersionLatest(ctx context.Context, attesation variant.Variant) (res SEVSNPVersionAPI, err error) {
|
||||||
var list AzureSEVSNPVersionList
|
list, err := f.FetchSEVSNPVersionList(ctx, SEVSNPVersionList{variant: attesation})
|
||||||
list, err = f.FetchAzureSEVSNPVersionList(ctx, list)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, ErrNoVersionsFound
|
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{
|
res, err = f.FetchSEVSNPVersion(ctx, getVersionRequest)
|
||||||
Version: list[0], // latest version is first in list
|
|
||||||
}
|
|
||||||
res, err = f.FetchAzureSEVSNPVersion(ctx, getVersionRequest)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,48 +16,65 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFetchLatestAzureSEVSNPVersion(t *testing.T) {
|
func TestFetchLatestSEVSNPVersion(t *testing.T) {
|
||||||
latestStr := "2023-06-11-14-09.json"
|
latestStr := "2023-06-11-14-09.json"
|
||||||
olderStr := "2019-01-01-01-01.json"
|
olderStr := "2019-01-01-01-01.json"
|
||||||
testcases := map[string]struct {
|
testcases := map[string]struct {
|
||||||
fetcherVersions []string
|
fetcherVersions []string
|
||||||
timeAtTest time.Time
|
timeAtTest time.Time
|
||||||
wantErr bool
|
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},
|
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 {
|
for name, tc := range testcases {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
Transport: &fakeConfigAPIHandler{
|
Transport: &fakeConfigAPIHandler{
|
||||||
|
attestation: tc.attestation,
|
||||||
versions: tc.fetcherVersions,
|
versions: tc.fetcherVersions,
|
||||||
latestVersion: latestStr,
|
latestDate: latestStr,
|
||||||
olderVersion: olderStr,
|
latestVersion: tc.latestVersion(),
|
||||||
|
olderDate: olderStr,
|
||||||
|
olderVersion: tc.olderVersion(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
fetcher := newFetcherWithClientAndVerifier(client, dummyVerifier{}, constants.CDNRepositoryURL)
|
fetcher := newFetcherWithClientAndVerifier(client, dummyVerifier{}, constants.CDNRepositoryURL)
|
||||||
res, err := fetcher.FetchAzureSEVSNPVersionLatest(context.Background())
|
res, err := fetcher.FetchSEVSNPVersionLatest(context.Background(), tc.attestation)
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
if tc.wantErr {
|
if tc.wantErr {
|
||||||
assert.Error(err)
|
assert.Error(err)
|
||||||
} else {
|
} else {
|
||||||
assert.NoError(err)
|
assert.NoError(err)
|
||||||
assert.Equal(tc.want, res)
|
assert.Equal(tc.expectedVersion(), res)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var latestVersion = AzureSEVSNPVersionAPI{
|
var latestVersion = SEVSNPVersionAPI{
|
||||||
AzureSEVSNPVersion: AzureSEVSNPVersion{
|
SEVSNPVersion: SEVSNPVersion{
|
||||||
Microcode: 93,
|
Microcode: 93,
|
||||||
TEE: 0,
|
TEE: 0,
|
||||||
SNP: 6,
|
SNP: 6,
|
||||||
|
@ -65,8 +82,8 @@ var latestVersion = AzureSEVSNPVersionAPI{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var olderVersion = AzureSEVSNPVersionAPI{
|
var olderVersion = SEVSNPVersionAPI{
|
||||||
AzureSEVSNPVersion: AzureSEVSNPVersion{
|
SEVSNPVersion: SEVSNPVersion{
|
||||||
Microcode: 1,
|
Microcode: 1,
|
||||||
TEE: 0,
|
TEE: 0,
|
||||||
SNP: 1,
|
SNP: 1,
|
||||||
|
@ -75,14 +92,17 @@ var olderVersion = AzureSEVSNPVersionAPI{
|
||||||
}
|
}
|
||||||
|
|
||||||
type fakeConfigAPIHandler struct {
|
type fakeConfigAPIHandler struct {
|
||||||
|
attestation variant.Variant
|
||||||
versions []string
|
versions []string
|
||||||
latestVersion string
|
latestDate string
|
||||||
olderVersion string
|
latestVersion SEVSNPVersionAPI
|
||||||
|
olderDate string
|
||||||
|
olderVersion SEVSNPVersionAPI
|
||||||
}
|
}
|
||||||
|
|
||||||
// RoundTrip resolves the request and returns a dummy response.
|
// RoundTrip resolves the request and returns a dummy response.
|
||||||
func (f *fakeConfigAPIHandler) RoundTrip(req *http.Request) (*http.Response, error) {
|
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{}
|
res := &http.Response{}
|
||||||
bt, err := json.Marshal(f.versions)
|
bt, err := json.Marshal(f.versions)
|
||||||
if err != nil {
|
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.Header.Set("Content-Type", "application/json")
|
||||||
res.StatusCode = http.StatusOK
|
res.StatusCode = http.StatusOK
|
||||||
return res, nil
|
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{}
|
res := &http.Response{}
|
||||||
bt, err := json.Marshal(latestVersion)
|
bt, err := json.Marshal(f.latestVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -103,22 +123,22 @@ func (f *fakeConfigAPIHandler) RoundTrip(req *http.Request) (*http.Response, err
|
||||||
res.StatusCode = http.StatusOK
|
res.StatusCode = http.StatusOK
|
||||||
return res, nil
|
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{}
|
res := &http.Response{}
|
||||||
bt, err := json.Marshal(olderVersion)
|
bt, err := json.Marshal(f.olderVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
res.Body = io.NopCloser(bytes.NewReader(bt))
|
res.Body = io.NopCloser(bytes.NewReader(bt))
|
||||||
res.StatusCode = http.StatusOK
|
res.StatusCode = http.StatusOK
|
||||||
return res, nil
|
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 := &http.Response{}
|
||||||
res.Body = io.NopCloser(bytes.NewReader([]byte("null")))
|
res.Body = io.NopCloser(bytes.NewReader([]byte("null")))
|
||||||
res.StatusCode = http.StatusOK
|
res.StatusCode = http.StatusOK
|
||||||
return res, nil
|
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 := &http.Response{}
|
||||||
res.Body = io.NopCloser(bytes.NewReader([]byte("null")))
|
res.Body = io.NopCloser(bytes.NewReader([]byte("null")))
|
||||||
res.StatusCode = http.StatusOK
|
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.
|
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
|
package attestationconfigapi
|
||||||
|
|
||||||
|
@ -28,25 +30,27 @@ import (
|
||||||
// cachedVersionsSubDir is the subdirectory in the bucket where the cached versions are stored.
|
// cachedVersionsSubDir is the subdirectory in the bucket where the cached versions are stored.
|
||||||
const cachedVersionsSubDir = "cached-versions"
|
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.
|
// 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")
|
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
|
// TCB version in the cache among the last cacheWindowSize versions and updates
|
||||||
// the latest version in the API if there is an update.
|
// the latest version in the API if there is an update.
|
||||||
// force can be used to bypass the validation logic against the cached versions.
|
// force can be used to bypass the validation logic against the cached versions.
|
||||||
func (c Client) UploadAzureSEVSNPVersionLatest(ctx context.Context, inputVersion,
|
func (c Client) UploadSEVSNPVersionLatest(ctx context.Context, attestation variant.Variant, inputVersion,
|
||||||
latestAPIVersion AzureSEVSNPVersion, now time.Time, force bool,
|
latestAPIVersion SEVSNPVersion, now time.Time, force bool,
|
||||||
) error {
|
) 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)
|
return fmt.Errorf("reporting version: %w", err)
|
||||||
}
|
}
|
||||||
if force {
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("list reported versions: %w", err)
|
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)
|
c.s3Client.Logger.Warnf("Skipping version update, found %d, expected %d reported versions.", len(versionDates), c.cacheWindowSize)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
minVersion, minDate, err := c.findMinVersion(ctx, versionDates)
|
minVersion, minDate, err := c.findMinVersion(ctx, attestation, versionDates)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("get minimal version: %w", err)
|
return fmt.Errorf("get minimal version: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -72,27 +76,27 @@ func (c Client) UploadAzureSEVSNPVersionLatest(ctx context.Context, inputVersion
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("parsing date: %w", err)
|
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)
|
return fmt.Errorf("uploading version: %w", err)
|
||||||
}
|
}
|
||||||
c.s3Client.Logger.Infof("Successfully uploaded new Azure SEV-SNP version: %+v", minVersion)
|
c.s3Client.Logger.Infof("Successfully uploaded new Azure SEV-SNP version: %+v", minVersion)
|
||||||
return nil
|
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.
|
// 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) cacheAzureSEVSNPVersion(ctx context.Context, version AzureSEVSNPVersion, date time.Time) error {
|
func (c Client) cacheSEVSNPVersion(ctx context.Context, attestation variant.Variant, version SEVSNPVersion, date time.Time) error {
|
||||||
dateStr := date.Format(VersionFormat) + ".json"
|
dateStr := date.Format(VersionFormat) + ".json"
|
||||||
res := putCmd{
|
res := putCmd{
|
||||||
apiObject: reportedAzureSEVSNPVersionAPI{Version: dateStr, AzureSEVSNPVersion: version},
|
apiObject: reportedSEVSNPVersionAPI{Version: dateStr, variant: attestation, SEVSNPVersion: version},
|
||||||
signer: c.signer,
|
signer: c.signer,
|
||||||
}
|
}
|
||||||
return res.Execute(ctx, c.s3Client)
|
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{
|
list, err := c.s3Client.ListObjectsV2(ctx, &s3.ListObjectsV2Input{
|
||||||
Bucket: aws.String(c.bucketID),
|
Bucket: aws.String(c.bucketID),
|
||||||
Prefix: aws.String(reportVersionDir),
|
Prefix: aws.String(reportVersionDir(attestation)),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("list objects: %w", err)
|
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.
|
// 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) {
|
func (c Client) findMinVersion(ctx context.Context, attesation variant.Variant, versionDates []string) (SEVSNPVersion, string, error) {
|
||||||
var minimalVersion *AzureSEVSNPVersion
|
var minimalVersion *SEVSNPVersion
|
||||||
var minimalDate string
|
var minimalDate string
|
||||||
sort.Sort(sort.Reverse(sort.StringSlice(versionDates))) // sort in reverse order to slice the latest versions
|
sort.Sort(sort.Reverse(sort.StringSlice(versionDates))) // sort in reverse order to slice the latest versions
|
||||||
versionDates = versionDates[:c.cacheWindowSize]
|
versionDates = versionDates[:c.cacheWindowSize]
|
||||||
sort.Strings(versionDates) // sort with oldest first to to take the minimal version with the oldest date
|
sort.Strings(versionDates) // sort with oldest first to to take the minimal version with the oldest date
|
||||||
for _, date := range versionDates {
|
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 {
|
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 {
|
if minimalVersion == nil {
|
||||||
minimalVersion = &obj.AzureSEVSNPVersion
|
minimalVersion = &obj.SEVSNPVersion
|
||||||
minimalDate = date
|
minimalDate = date
|
||||||
} else {
|
} else {
|
||||||
shouldUpdateMinimal, err := isInputNewerThanOtherVersion(*minimalVersion, obj.AzureSEVSNPVersion)
|
shouldUpdateMinimal, err := isInputNewerThanOtherVersion(*minimalVersion, obj.SEVSNPVersion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if shouldUpdateMinimal {
|
if shouldUpdateMinimal {
|
||||||
minimalVersion = &obj.AzureSEVSNPVersion
|
minimalVersion = &obj.SEVSNPVersion
|
||||||
minimalDate = date
|
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.
|
// 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 {
|
if input == other {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
@ -157,19 +163,20 @@ func isInputNewerThanOtherVersion(input, other AzureSEVSNPVersion) (bool, error)
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// reportedAzureSEVSNPVersionAPI is the request to get the version information of the specific version in the config api.
|
// reportedSEVSNPVersionAPI is the request to get the version information of the specific version in the config api.
|
||||||
type reportedAzureSEVSNPVersionAPI struct {
|
type reportedSEVSNPVersionAPI struct {
|
||||||
Version string `json:"-"`
|
Version string `json:"-"`
|
||||||
AzureSEVSNPVersion
|
variant variant.Variant `json:"-"`
|
||||||
|
SEVSNPVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSONPath returns the path to the JSON file for the request to the config api.
|
// JSONPath returns the path to the JSON file for the request to the config api.
|
||||||
func (i reportedAzureSEVSNPVersionAPI) JSONPath() string {
|
func (i reportedSEVSNPVersionAPI) JSONPath() string {
|
||||||
return path.Join(reportVersionDir, i.Version)
|
return path.Join(reportVersionDir(i.variant), i.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateRequest validates the request.
|
// ValidateRequest validates the request.
|
||||||
func (i reportedAzureSEVSNPVersionAPI) ValidateRequest() error {
|
func (i reportedSEVSNPVersionAPI) ValidateRequest() error {
|
||||||
if !strings.HasSuffix(i.Version, ".json") {
|
if !strings.HasSuffix(i.Version, ".json") {
|
||||||
return fmt.Errorf("version has no .json suffix")
|
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.
|
// Validate is a No-Op at the moment.
|
||||||
func (i reportedAzureSEVSNPVersionAPI) Validate() error {
|
func (i reportedSEVSNPVersionAPI) Validate() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIsInputNewerThanLatestAPI(t *testing.T) {
|
func TestIsInputNewerThanLatestAPI(t *testing.T) {
|
||||||
newTestCfg := func() AzureSEVSNPVersion {
|
newTestCfg := func() SEVSNPVersion {
|
||||||
return AzureSEVSNPVersion{
|
return SEVSNPVersion{
|
||||||
Microcode: 93,
|
Microcode: 93,
|
||||||
TEE: 0,
|
TEE: 0,
|
||||||
SNP: 6,
|
SNP: 6,
|
||||||
|
@ -21,13 +21,13 @@ func TestIsInputNewerThanLatestAPI(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
latest AzureSEVSNPVersion
|
latest SEVSNPVersion
|
||||||
input AzureSEVSNPVersion
|
input SEVSNPVersion
|
||||||
expect bool
|
expect bool
|
||||||
errMsg string
|
errMsg string
|
||||||
}{
|
}{
|
||||||
"input is older than latest": {
|
"input is older than latest": {
|
||||||
input: func(c AzureSEVSNPVersion) AzureSEVSNPVersion {
|
input: func(c SEVSNPVersion) SEVSNPVersion {
|
||||||
c.Microcode--
|
c.Microcode--
|
||||||
return c
|
return c
|
||||||
}(newTestCfg()),
|
}(newTestCfg()),
|
||||||
|
@ -36,7 +36,7 @@ func TestIsInputNewerThanLatestAPI(t *testing.T) {
|
||||||
errMsg: "input Microcode version: 92 is older than latest API version: 93",
|
errMsg: "input Microcode version: 92 is older than latest API version: 93",
|
||||||
},
|
},
|
||||||
"input has greater and smaller version field than latest": {
|
"input has greater and smaller version field than latest": {
|
||||||
input: func(c AzureSEVSNPVersion) AzureSEVSNPVersion {
|
input: func(c SEVSNPVersion) SEVSNPVersion {
|
||||||
c.Microcode++
|
c.Microcode++
|
||||||
c.Bootloader--
|
c.Bootloader--
|
||||||
return c
|
return c
|
||||||
|
@ -46,7 +46,7 @@ func TestIsInputNewerThanLatestAPI(t *testing.T) {
|
||||||
errMsg: "input Bootloader version: 1 is older than latest API version: 2",
|
errMsg: "input Bootloader version: 1 is older than latest API version: 2",
|
||||||
},
|
},
|
||||||
"input is newer than latest": {
|
"input is newer than latest": {
|
||||||
input: func(c AzureSEVSNPVersion) AzureSEVSNPVersion {
|
input: func(c SEVSNPVersion) SEVSNPVersion {
|
||||||
c.TEE++
|
c.TEE++
|
||||||
return c
|
return c
|
||||||
}(newTestCfg()),
|
}(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.
|
// FetchAndSetLatestVersionNumbers fetches the latest version numbers from the configapi and sets them.
|
||||||
func (c *AzureSEVSNP) FetchAndSetLatestVersionNumbers(ctx context.Context, fetcher attestationconfigapi.Fetcher) error {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// set number and keep isLatest flag
|
// set number and keep isLatest flag
|
||||||
c.mergeWithLatestVersion(versions.AzureSEVSNPVersion)
|
c.mergeWithLatestVersion(versions.SEVSNPVersion)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *AzureSEVSNP) mergeWithLatestVersion(latest attestationconfigapi.AzureSEVSNPVersion) {
|
func (c *AzureSEVSNP) mergeWithLatestVersion(latest attestationconfigapi.SEVSNPVersion) {
|
||||||
if c.BootloaderVersion.WantLatest {
|
if c.BootloaderVersion.WantLatest {
|
||||||
c.BootloaderVersion.Value = latest.Bootloader
|
c.BootloaderVersion.Value = latest.Bootloader
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
|
|
||||||
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
|
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
|
"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/cloud/cloudprovider"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/config/instancetypes"
|
"github.com/edgelesssys/constellation/v2/internal/config/instancetypes"
|
||||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||||
|
@ -51,10 +52,10 @@ func TestDefaultConfigMarshalsLatestVersion(t *testing.T) {
|
||||||
var mp configMap
|
var mp configMap
|
||||||
require.NoError(yaml.Unmarshal(bt, &mp))
|
require.NoError(yaml.Unmarshal(bt, &mp))
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
assert.Equal("latest", mp.getAzureSEVSNPVersion("microcodeVersion"))
|
assert.Equal("latest", mp.getSEVSNPVersion("microcodeVersion"))
|
||||||
assert.Equal("latest", mp.getAzureSEVSNPVersion("teeVersion"))
|
assert.Equal("latest", mp.getSEVSNPVersion("teeVersion"))
|
||||||
assert.Equal("latest", mp.getAzureSEVSNPVersion("snpVersion"))
|
assert.Equal("latest", mp.getSEVSNPVersion("snpVersion"))
|
||||||
assert.Equal("latest", mp.getAzureSEVSNPVersion("bootloaderVersion"))
|
assert.Equal("latest", mp.getSEVSNPVersion("bootloaderVersion"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetAttestationConfigMarshalsNumericalVersion(t *testing.T) {
|
func TestGetAttestationConfigMarshalsNumericalVersion(t *testing.T) {
|
||||||
|
@ -88,9 +89,9 @@ func TestNew(t *testing.T) {
|
||||||
conf := Default() // default configures latest version
|
conf := Default() // default configures latest version
|
||||||
modifyConfigForAzureToPassValidate(conf)
|
modifyConfigForAzureToPassValidate(conf)
|
||||||
m := getConfigAsMap(conf, t)
|
m := getConfigAsMap(conf, t)
|
||||||
m.setAzureSEVSNPVersion("microcodeVersion", "Latest") // check uppercase also works
|
m.setSEVSNPVersion("microcodeVersion", "Latest") // check uppercase also works
|
||||||
m.setAzureSEVSNPVersion("teeVersion", 2)
|
m.setSEVSNPVersion("teeVersion", 2)
|
||||||
m.setAzureSEVSNPVersion("bootloaderVersion", 1)
|
m.setSEVSNPVersion("bootloaderVersion", 1)
|
||||||
return m
|
return m
|
||||||
}(),
|
}(),
|
||||||
|
|
||||||
|
@ -181,7 +182,7 @@ func TestReadConfigFile(t *testing.T) {
|
||||||
config: func() configMap {
|
config: func() configMap {
|
||||||
conf := Default()
|
conf := Default()
|
||||||
m := getConfigAsMap(conf, t)
|
m := getConfigAsMap(conf, t)
|
||||||
m.setAzureSEVSNPVersion("microcodeVersion", "1a")
|
m.setSEVSNPVersion("microcodeVersion", "1a")
|
||||||
return m
|
return m
|
||||||
}(),
|
}(),
|
||||||
configName: constants.ConfigFilename,
|
configName: constants.ConfigFilename,
|
||||||
|
@ -1053,7 +1054,7 @@ func TestIsAppClientIDError(t *testing.T) {
|
||||||
// configMap is used to un-/marshal the config as an unstructured map.
|
// configMap is used to un-/marshal the config as an unstructured map.
|
||||||
type configMap map[string]interface{}
|
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
|
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
|
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]
|
return c["attestation"].(configMap)["azureSEVSNP"].(configMap)[versionType]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1079,25 +1080,23 @@ func getConfigAsMap(conf *Config, t *testing.T) (res configMap) {
|
||||||
|
|
||||||
type stubAttestationFetcher struct{}
|
type stubAttestationFetcher struct{}
|
||||||
|
|
||||||
func (f stubAttestationFetcher) FetchAzureSEVSNPVersionList(_ context.Context, _ attestationconfigapi.AzureSEVSNPVersionList) (attestationconfigapi.AzureSEVSNPVersionList, error) {
|
func (f stubAttestationFetcher) FetchSEVSNPVersionList(_ context.Context, _ attestationconfigapi.SEVSNPVersionList) (attestationconfigapi.SEVSNPVersionList, error) {
|
||||||
return attestationconfigapi.AzureSEVSNPVersionList(
|
return attestationconfigapi.SEVSNPVersionList{}, nil
|
||||||
[]string{},
|
|
||||||
), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f stubAttestationFetcher) FetchAzureSEVSNPVersion(_ context.Context, _ attestationconfigapi.AzureSEVSNPVersionAPI) (attestationconfigapi.AzureSEVSNPVersionAPI, error) {
|
func (f stubAttestationFetcher) FetchSEVSNPVersion(_ context.Context, _ attestationconfigapi.SEVSNPVersionAPI) (attestationconfigapi.SEVSNPVersionAPI, error) {
|
||||||
return attestationconfigapi.AzureSEVSNPVersionAPI{
|
return attestationconfigapi.SEVSNPVersionAPI{
|
||||||
AzureSEVSNPVersion: testCfg,
|
SEVSNPVersion: testCfg,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f stubAttestationFetcher) FetchAzureSEVSNPVersionLatest(_ context.Context) (attestationconfigapi.AzureSEVSNPVersionAPI, error) {
|
func (f stubAttestationFetcher) FetchSEVSNPVersionLatest(_ context.Context, _ variant.Variant) (attestationconfigapi.SEVSNPVersionAPI, error) {
|
||||||
return attestationconfigapi.AzureSEVSNPVersionAPI{
|
return attestationconfigapi.SEVSNPVersionAPI{
|
||||||
AzureSEVSNPVersion: testCfg,
|
SEVSNPVersion: testCfg,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var testCfg = attestationconfigapi.AzureSEVSNPVersion{
|
var testCfg = attestationconfigapi.SEVSNPVersion{
|
||||||
Microcode: 93,
|
Microcode: 93,
|
||||||
TEE: 0,
|
TEE: 0,
|
||||||
SNP: 6,
|
SNP: 6,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue