mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-10-01 01:36:09 -04:00
config: enable azure snp version fetcher again + minimum age for latest version (#1899)
* fetch latest version when older than 2 weeks
* extend hack upload tool to pass an upload date
* Revert "config: disable user-facing version Azure SEV SNP fetch for v2.8 (#1882)"
This reverts commit c7b22d314a
.
* fix tests
* use NewAzureSEVSNPVersionList for type guarantees
* Revert "use NewAzureSEVSNPVersionList for type guarantees"
This reverts commit 942566453f4b4a2b6dc16f8689248abf1dc47db4.
* assure list is sorted
* improve root.go style
* daniel feedback
This commit is contained in:
parent
72e168e653
commit
3fde118b33
@ -15,6 +15,7 @@ import (
|
||||
"net/url"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
|
||||
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
|
||||
@ -314,7 +315,7 @@ func (f stubAttestationFetcher) FetchAzureSEVSNPVersion(_ context.Context, _ att
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f stubAttestationFetcher) FetchAzureSEVSNPVersionLatest(_ context.Context) (attestationconfigapi.AzureSEVSNPVersionAPI, error) {
|
||||
func (f stubAttestationFetcher) FetchAzureSEVSNPVersionLatest(_ context.Context, _ time.Time) (attestationconfigapi.AzureSEVSNPVersionAPI, error) {
|
||||
return attestationconfigapi.AzureSEVSNPVersionAPI{
|
||||
AzureSEVSNPVersion: testCfg,
|
||||
}, nil
|
||||
|
@ -56,6 +56,7 @@ func newRootCmd() *cobra.Command {
|
||||
}
|
||||
rootCmd.Flags().StringVarP(&versionFilePath, "version-file", "f", "", "File path to the version json file.")
|
||||
rootCmd.Flags().BoolVar(&force, "force", false, "force to upload version regardless of comparison to latest API value.")
|
||||
rootCmd.Flags().StringP("upload-date", "d", "", "upload a version with this date as version name. Setting it implies --force.")
|
||||
must(enforceRequiredFlags(rootCmd, "version-file"))
|
||||
rootCmd.AddCommand(newDeleteCmd())
|
||||
return rootCmd
|
||||
@ -85,21 +86,40 @@ func runCmd(cmd *cobra.Command, _ []string) error {
|
||||
return fmt.Errorf("unmarshalling version file: %w", err)
|
||||
}
|
||||
|
||||
latestAPIVersion, err := attestationconfigapi.NewFetcher().FetchAzureSEVSNPVersionLatest(ctx)
|
||||
dateStr, err := cmd.Flags().GetString("upload-date")
|
||||
if err != nil {
|
||||
return fmt.Errorf("fetching latest version: %w", err)
|
||||
return fmt.Errorf("getting upload date: %w", err)
|
||||
}
|
||||
var uploadDate time.Time
|
||||
if dateStr != "" {
|
||||
uploadDate, err = time.Parse("2006-01-01-01-01", dateStr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing date: %w", err)
|
||||
}
|
||||
} else {
|
||||
uploadDate = time.Now()
|
||||
force = true
|
||||
}
|
||||
|
||||
isNewer, err := isInputNewerThanLatestAPI(inputVersion, latestAPIVersion.AzureSEVSNPVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("comparing versions: %w", err)
|
||||
}
|
||||
if isNewer || force {
|
||||
if force {
|
||||
cmd.Println("Forcing upload of new version")
|
||||
} else {
|
||||
cmd.Printf("Input version: %+v is newer than latest API version: %+v\n", inputVersion, latestAPIVersion)
|
||||
doUpload := false
|
||||
if !force {
|
||||
latestAPIVersion, err := attestationconfigapi.NewFetcher().FetchAzureSEVSNPVersionLatest(ctx, time.Now())
|
||||
if err != nil {
|
||||
return fmt.Errorf("fetching latest version: %w", err)
|
||||
}
|
||||
|
||||
isNewer, err := isInputNewerThanLatestAPI(inputVersion, latestAPIVersion.AzureSEVSNPVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("comparing versions: %w", err)
|
||||
}
|
||||
cmd.Print(versionComparisonInformation(isNewer, inputVersion, latestAPIVersion.AzureSEVSNPVersion))
|
||||
doUpload = isNewer
|
||||
} else {
|
||||
doUpload = true
|
||||
cmd.Println("Forcing upload of new version")
|
||||
}
|
||||
|
||||
if doUpload {
|
||||
sut, sutClose, err := attestationconfigapi.NewClient(ctx, cfg, []byte(cosignPwd), []byte(privateKey), false, log())
|
||||
defer func() {
|
||||
if err := sutClose(ctx); err != nil {
|
||||
@ -110,16 +130,21 @@ func runCmd(cmd *cobra.Command, _ []string) error {
|
||||
return fmt.Errorf("creating repo: %w", err)
|
||||
}
|
||||
|
||||
if err := sut.UploadAzureSEVSNP(ctx, inputVersion, time.Now()); err != nil {
|
||||
if err := sut.UploadAzureSEVSNP(ctx, inputVersion, uploadDate); err != nil {
|
||||
return fmt.Errorf("uploading version: %w", err)
|
||||
}
|
||||
cmd.Printf("Successfully uploaded new Azure SEV-SNP version: %+v\n", inputVersion)
|
||||
} else {
|
||||
cmd.Printf("Input version: %+v is not newer than latest API version: %+v\n", inputVersion, latestAPIVersion)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func versionComparisonInformation(isNewer bool, inputVersion attestationconfigapi.AzureSEVSNPVersion, latestAPIVersion attestationconfigapi.AzureSEVSNPVersion) string {
|
||||
if isNewer {
|
||||
return fmt.Sprintf("Input version: %+v is newer than latest API version: %+v\n", inputVersion, latestAPIVersion)
|
||||
}
|
||||
return fmt.Sprintf("Input version: %+v is not newer than latest API version: %+v\n", inputVersion, latestAPIVersion)
|
||||
}
|
||||
|
||||
// isInputNewerThanLatestAPI compares all version fields with the latest API version and returns true if any input field is newer.
|
||||
func isInputNewerThanLatestAPI(input, latest attestationconfigapi.AzureSEVSNPVersion) (bool, error) {
|
||||
inputValues := reflect.ValueOf(input)
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
@ -110,6 +111,11 @@ 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 {
|
||||
|
@ -9,7 +9,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
apiclient "github.com/edgelesssys/constellation/v2/internal/api/client"
|
||||
@ -43,7 +42,7 @@ func NewClient(ctx context.Context, cfg staticupload.Config, cosignPwd, privateK
|
||||
return repo, clientClose, nil
|
||||
}
|
||||
|
||||
// UploadAzureSEVSNP uploads the latest version numbers of the Azure SEVSNP.
|
||||
// UploadAzureSEVSNP 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) UploadAzureSEVSNP(ctx context.Context, version AzureSEVSNPVersion, date time.Time) error {
|
||||
versions, err := a.List(ctx, variant.AzureSEVSNP{})
|
||||
if err != nil {
|
||||
@ -181,6 +180,6 @@ func executeAllCmds(ctx context.Context, client *apiclient.Client, cmds []crudCm
|
||||
func addVersion(versions []string, newVersion string) []string {
|
||||
versions = append(versions, newVersion)
|
||||
versions = variant.RemoveDuplicate(versions)
|
||||
sort.Sort(sort.Reverse(sort.StringSlice(versions)))
|
||||
SortAzureSEVSNPVersionList(versions)
|
||||
return versions
|
||||
}
|
||||
|
@ -10,24 +10,30 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
apifetcher "github.com/edgelesssys/constellation/v2/internal/api/fetcher"
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
"github.com/edgelesssys/constellation/v2/internal/sigstore"
|
||||
)
|
||||
|
||||
// minimumAgeVersion is the minimum age to accept the version as latest.
|
||||
const minimumAgeVersion = 14 * 24 * time.Hour
|
||||
|
||||
const cosignPublicKey = constants.CosignPublicKeyReleases
|
||||
|
||||
// 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)
|
||||
FetchAzureSEVSNPVersionLatest(ctx context.Context, now time.Time) (AzureSEVSNPVersionAPI, error)
|
||||
}
|
||||
|
||||
// fetcher fetches AttestationCfg API resources without authentication.
|
||||
type fetcher struct {
|
||||
apifetcher.HTTPClient
|
||||
verifier sigstore.Verifier
|
||||
}
|
||||
|
||||
// NewFetcher returns a new apifetcher.
|
||||
@ -37,7 +43,11 @@ func NewFetcher() Fetcher {
|
||||
|
||||
// NewFetcherWithClient returns a new fetcher with custom http client.
|
||||
func NewFetcherWithClient(client apifetcher.HTTPClient) Fetcher {
|
||||
return &fetcher{client}
|
||||
return newFetcherWithClientAndVerifier(client, sigstore.CosignVerifier{})
|
||||
}
|
||||
|
||||
func newFetcherWithClientAndVerifier(client apifetcher.HTTPClient, cosignVerifier sigstore.Verifier) Fetcher {
|
||||
return &fetcher{client, cosignVerifier}
|
||||
}
|
||||
|
||||
// FetchAzureSEVSNPVersionList fetches the version list information from the config API.
|
||||
@ -63,7 +73,7 @@ func (f *fetcher) FetchAzureSEVSNPVersion(ctx context.Context, azureVersion Azur
|
||||
return fetchedVersion, fmt.Errorf("fetch version %s signature: %w", azureVersion.Version, err)
|
||||
}
|
||||
|
||||
err = sigstore.CosignVerifier{}.VerifySignature(versionBytes, signature.Signature, []byte(cosignPublicKey))
|
||||
err = f.verifier.VerifySignature(versionBytes, signature.Signature, []byte(cosignPublicKey))
|
||||
if err != nil {
|
||||
return fetchedVersion, fmt.Errorf("verify version %s signature: %w", azureVersion.Version, err)
|
||||
}
|
||||
@ -71,16 +81,34 @@ func (f *fetcher) FetchAzureSEVSNPVersion(ctx context.Context, azureVersion Azur
|
||||
}
|
||||
|
||||
// FetchAzureSEVSNPVersionLatest returns the latest versions of the given type.
|
||||
func (f *fetcher) FetchAzureSEVSNPVersionLatest(ctx context.Context) (res AzureSEVSNPVersionAPI, err error) {
|
||||
func (f *fetcher) FetchAzureSEVSNPVersionLatest(ctx context.Context, now time.Time) (res AzureSEVSNPVersionAPI, err error) {
|
||||
var list AzureSEVSNPVersionList
|
||||
list, err = f.FetchAzureSEVSNPVersionList(ctx, list)
|
||||
if err != nil {
|
||||
return res, fmt.Errorf("fetching versions list: %w", err)
|
||||
}
|
||||
get := AzureSEVSNPVersionAPI{Version: list[0]} // get latest version (as sorted reversely alphanumerically)
|
||||
get, err = f.FetchAzureSEVSNPVersion(ctx, get)
|
||||
getVersionRequest, err := getLatestVersionOlderThanMinimumAge(list, now, minimumAgeVersion)
|
||||
if err != nil {
|
||||
return res, fmt.Errorf("finding latest valid version: %w", err)
|
||||
}
|
||||
res, err = f.FetchAzureSEVSNPVersion(ctx, getVersionRequest)
|
||||
if err != nil {
|
||||
return res, fmt.Errorf("fetching version: %w", err)
|
||||
}
|
||||
return get, nil
|
||||
return
|
||||
}
|
||||
|
||||
func getLatestVersionOlderThanMinimumAge(list AzureSEVSNPVersionList, now time.Time, minimumAgeVersion time.Duration) (AzureSEVSNPVersionAPI, error) {
|
||||
SortAzureSEVSNPVersionList(list)
|
||||
for _, v := range list {
|
||||
dateStr := strings.TrimSuffix(v, ".json")
|
||||
versionDate, err := time.Parse("2006-01-01-01-01", dateStr)
|
||||
if err != nil {
|
||||
return AzureSEVSNPVersionAPI{}, fmt.Errorf("parsing version date %s: %w", dateStr, err)
|
||||
}
|
||||
if now.Sub(versionDate) > minimumAgeVersion {
|
||||
return AzureSEVSNPVersionAPI{Version: v}, nil
|
||||
}
|
||||
}
|
||||
return AzureSEVSNPVersionAPI{}, fmt.Errorf("no valid version fulfilling minimum age found")
|
||||
}
|
||||
|
@ -13,11 +13,56 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var testCfg = AzureSEVSNPVersionAPI{
|
||||
func TestFetchLatestAzureSEVSNPVersion(t *testing.T) {
|
||||
now := time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
testcases := map[string]struct {
|
||||
fetcherVersions []string
|
||||
timeAtTest time.Time
|
||||
wantErr bool
|
||||
want AzureSEVSNPVersionAPI
|
||||
}{
|
||||
"get latest version if older than 2 weeks": {
|
||||
fetcherVersions: []string{"2021-01-01-01-01.json", "2019-01-01-01-01.json"},
|
||||
timeAtTest: now.Add(days(15)),
|
||||
want: latestVersion,
|
||||
},
|
||||
"get older version if latest version is not older than minimum age": {
|
||||
fetcherVersions: []string{"2021-01-01-01-01.json", "2019-01-01-01-01.json"},
|
||||
timeAtTest: now.Add(days(7)),
|
||||
want: olderVersion,
|
||||
},
|
||||
"fail when no version is older minimum age": {
|
||||
fetcherVersions: []string{"2021-01-01-01-01.json", "2020-12-31-00-00.json"},
|
||||
timeAtTest: now.Add(days(2)),
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for name, tc := range testcases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
client := &http.Client{
|
||||
Transport: &fakeConfigAPIHandler{
|
||||
versions: tc.fetcherVersions,
|
||||
},
|
||||
}
|
||||
fetcher := newFetcherWithClientAndVerifier(client, dummyVerifier{})
|
||||
res, err := fetcher.FetchAzureSEVSNPVersionLatest(context.Background(), tc.timeAtTest)
|
||||
assert := assert.New(t)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
assert.Equal(tc.want, res)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var latestVersion = AzureSEVSNPVersionAPI{
|
||||
AzureSEVSNPVersion: AzureSEVSNPVersion{
|
||||
Microcode: 93,
|
||||
TEE: 0,
|
||||
@ -26,52 +71,29 @@ var testCfg = AzureSEVSNPVersionAPI{
|
||||
},
|
||||
}
|
||||
|
||||
func TestFetchLatestAzureSEVSNPVersion(t *testing.T) {
|
||||
testcases := map[string]struct {
|
||||
signature []byte
|
||||
wantErr bool
|
||||
want AzureSEVSNPVersionAPI
|
||||
}{
|
||||
"get version with valid signature": {
|
||||
signature: []byte("MEQCIBPEbYg89MIQuaGStLhKGLGMKvKFoYCaAniDLwoIwulqAiB+rj7KMaMOMGxmUsjI7KheCXSNM8NzN+tuDw6AywI75A=="), // signed with release key
|
||||
want: testCfg,
|
||||
},
|
||||
"fail with invalid signature": {
|
||||
signature: []byte("invalid"),
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for name, tc := range testcases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
client := &http.Client{
|
||||
Transport: &fakeConfigAPIHandler{
|
||||
signature: tc.signature,
|
||||
},
|
||||
}
|
||||
fetcher := NewFetcherWithClient(client)
|
||||
res, err := fetcher.FetchAzureSEVSNPVersionLatest(context.Background())
|
||||
var olderVersion = AzureSEVSNPVersionAPI{
|
||||
AzureSEVSNPVersion: AzureSEVSNPVersion{
|
||||
Microcode: 1,
|
||||
TEE: 0,
|
||||
SNP: 1,
|
||||
Bootloader: 1,
|
||||
},
|
||||
}
|
||||
|
||||
assert := assert.New(t)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
assert.Equal(testCfg, res)
|
||||
}
|
||||
})
|
||||
}
|
||||
func days(days int) time.Duration {
|
||||
return time.Duration(days*24) * time.Hour
|
||||
}
|
||||
|
||||
type fakeConfigAPIHandler struct {
|
||||
signature []byte
|
||||
versions []string
|
||||
}
|
||||
|
||||
// RoundTrip resolves the request and returns a dummy response.
|
||||
func (f *fakeConfigAPIHandler) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
signature := []byte("placeholderSignature")
|
||||
if req.URL.Path == "/constellation/v1/attestation/azure-sev-snp/list" {
|
||||
res := &http.Response{}
|
||||
data := []string{"2021-01-01-01-01.json", "2019-01-01-01-02.json"} // return multiple versions to check that latest version is correctly selected
|
||||
bt, err := json.Marshal(data)
|
||||
bt, err := json.Marshal(f.versions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -82,7 +104,17 @@ func (f *fakeConfigAPIHandler) RoundTrip(req *http.Request) (*http.Response, err
|
||||
return res, nil
|
||||
} else if req.URL.Path == "/constellation/v1/attestation/azure-sev-snp/2021-01-01-01-01.json" {
|
||||
res := &http.Response{}
|
||||
bt, err := json.Marshal(testCfg)
|
||||
bt, err := json.Marshal(latestVersion)
|
||||
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 == "/constellation/v1/attestation/azure-sev-snp/2019-01-01-01-01.json" {
|
||||
res := &http.Response{}
|
||||
bt, err := json.Marshal(olderVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -93,7 +125,20 @@ func (f *fakeConfigAPIHandler) RoundTrip(req *http.Request) (*http.Response, err
|
||||
} else if req.URL.Path == "/constellation/v1/attestation/azure-sev-snp/2021-01-01-01-01.json.sig" {
|
||||
res := &http.Response{}
|
||||
obj := AzureSEVSNPVersionSignature{
|
||||
Signature: f.signature,
|
||||
Signature: signature,
|
||||
}
|
||||
bt, err := json.Marshal(obj)
|
||||
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 == "/constellation/v1/attestation/azure-sev-snp/2019-01-01-01-01.json.sig" {
|
||||
res := &http.Response{}
|
||||
obj := AzureSEVSNPVersionSignature{
|
||||
Signature: signature,
|
||||
}
|
||||
bt, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
@ -106,3 +151,9 @@ func (f *fakeConfigAPIHandler) RoundTrip(req *http.Request) (*http.Response, err
|
||||
}
|
||||
return nil, errors.New("no endpoint found")
|
||||
}
|
||||
|
||||
type dummyVerifier struct{}
|
||||
|
||||
func (s dummyVerifier) VerifySignature(_, _, _ []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
@ -218,10 +218,6 @@ func TestTrustedKeyFromSNP(t *testing.T) {
|
||||
AcceptedKeyDigests: tc.idkeydigests,
|
||||
EnforcementPolicy: tc.enforceIDKeyDigest,
|
||||
}
|
||||
cfg.BootloaderVersion = config.AttestationVersion{Value: 2}
|
||||
cfg.TEEVersion = config.AttestationVersion{Value: 0}
|
||||
cfg.MicrocodeVersion = config.AttestationVersion{Value: 93}
|
||||
cfg.SNPVersion = config.AttestationVersion{Value: 6}
|
||||
|
||||
validator := &Validator{
|
||||
hclValidator: &instanceInfo,
|
||||
@ -353,12 +349,6 @@ func TestNewSNPReportFromBytes(t *testing.T) {
|
||||
},
|
||||
}
|
||||
cfg := config.DefaultForAzureSEVSNP()
|
||||
|
||||
cfg.BootloaderVersion = config.AttestationVersion{Value: 2}
|
||||
cfg.TEEVersion = config.AttestationVersion{Value: 0}
|
||||
cfg.MicrocodeVersion = config.AttestationVersion{Value: 93}
|
||||
cfg.SNPVersion = config.AttestationVersion{Value: 6}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
@ -8,7 +8,6 @@ package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
@ -34,7 +33,7 @@ func (v AttestationVersion) MarshalYAML() (any, error) {
|
||||
if v.IsLatest {
|
||||
return "latest", nil
|
||||
}
|
||||
return int(v.Value), nil
|
||||
return v.Value, nil
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements a custom unmarshaller to resolve "atest" values.
|
||||
@ -68,12 +67,11 @@ func (v *AttestationVersion) parseRawUnmarshal(rawUnmarshal any) error {
|
||||
switch s := rawUnmarshal.(type) {
|
||||
case string:
|
||||
if strings.ToLower(s) == "latest" {
|
||||
// TODO(elchead): activate latest logic for next release AB#3036
|
||||
return errors.New("latest is not supported as a version value")
|
||||
// v.IsLatest = true
|
||||
// v.Value = placeholderVersionValue
|
||||
v.IsLatest = true
|
||||
v.Value = placeholderVersionValue
|
||||
} else {
|
||||
return fmt.Errorf("invalid version value: %s", s)
|
||||
}
|
||||
return fmt.Errorf("invalid version value: %s", s)
|
||||
case int:
|
||||
v.Value = uint8(s)
|
||||
// yaml spec allows "1" as float64, so version number might come as a float: https://github.com/go-yaml/yaml/issues/430
|
||||
|
@ -9,7 +9,6 @@ package config
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
@ -45,39 +44,3 @@ func TestVersionMarshalYAML(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersionUnmarshalYAML(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
expected AttestationVersion
|
||||
yamlInput string
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO(elchead): activate latest logic for next release AB#3036
|
||||
{
|
||||
name: "latest value is not allowed",
|
||||
expected: AttestationVersion{},
|
||||
yamlInput: "latest\n",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "value 5 resolves to 5",
|
||||
expected: AttestationVersion{
|
||||
Value: 5,
|
||||
},
|
||||
yamlInput: "5\n",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sut := AttestationVersion{}
|
||||
err := yaml.Unmarshal([]byte(tt.yamlInput), &sut)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.expected.IsLatest, sut.IsLatest)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import (
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-playground/locales/en"
|
||||
ut "github.com/go-playground/universal-translator"
|
||||
@ -386,19 +387,18 @@ func fromFile(fileHandler file.Handler, name string) (*Config, error) {
|
||||
// 2. For "latest" version values of the attestation variants fetch the version numbers.
|
||||
// 3. Read secrets from environment variables.
|
||||
// 4. Validate config. If `--force` is set the version validation will be disabled and any version combination is allowed.
|
||||
func New(fileHandler file.Handler, name string, _ attestationconfigapi.Fetcher, force bool) (*Config, error) {
|
||||
func New(fileHandler file.Handler, name string, fetcher attestationconfigapi.Fetcher, force bool) (*Config, error) {
|
||||
// Read config file
|
||||
c, err := fromFile(fileHandler, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO(elchead): activate latest logic for next release AB#3036
|
||||
//if azure := c.Attestation.AzureSEVSNP; azure != nil {
|
||||
// if err := azure.FetchAndSetLatestVersionNumbers(fetcher); err != nil {
|
||||
// return c, err
|
||||
// }
|
||||
//}
|
||||
if azure := c.Attestation.AzureSEVSNP; azure != nil {
|
||||
if err := azure.FetchAndSetLatestVersionNumbers(context.Background(), fetcher, time.Now()); err != nil {
|
||||
return c, err
|
||||
}
|
||||
}
|
||||
|
||||
// Read secrets from env-vars.
|
||||
clientSecretValue := os.Getenv(constants.EnvVarAzureClientSecretValue)
|
||||
@ -925,19 +925,12 @@ type AzureSEVSNP struct {
|
||||
// DefaultForAzureSEVSNP returns the default configuration for Azure SEV-SNP attestation.
|
||||
// Version numbers have placeholder values and the latest available values can be fetched using [AzureSEVSNP.FetchAndSetLatestVersionNumbers].
|
||||
func DefaultForAzureSEVSNP() *AzureSEVSNP {
|
||||
// TODO(elchead): activate latest logic for next release AB#3036
|
||||
azureSNPCfg := attestationconfigapi.AzureSEVSNPVersion{
|
||||
Bootloader: 3,
|
||||
TEE: 0,
|
||||
SNP: 8,
|
||||
Microcode: 115,
|
||||
}
|
||||
return &AzureSEVSNP{
|
||||
Measurements: measurements.DefaultsFor(cloudprovider.Azure, variant.AzureSEVSNP{}),
|
||||
BootloaderVersion: AttestationVersion{Value: azureSNPCfg.Bootloader}, // NewLatestPlaceholderVersion(),
|
||||
TEEVersion: AttestationVersion{Value: azureSNPCfg.TEE}, // NewLatestPlaceholderVersion(),
|
||||
SNPVersion: AttestationVersion{Value: azureSNPCfg.SNP}, // NewLatestPlaceholderVersion(),
|
||||
MicrocodeVersion: AttestationVersion{Value: azureSNPCfg.Microcode}, // NewLatestPlaceholderVersion(),
|
||||
BootloaderVersion: NewLatestPlaceholderVersion(),
|
||||
TEEVersion: NewLatestPlaceholderVersion(),
|
||||
SNPVersion: NewLatestPlaceholderVersion(),
|
||||
MicrocodeVersion: NewLatestPlaceholderVersion(),
|
||||
FirmwareSignerConfig: SNPFirmwareSignerConfig{
|
||||
AcceptedKeyDigests: idkeydigest.DefaultList(),
|
||||
EnforcementPolicy: idkeydigest.MAAFallback,
|
||||
@ -981,8 +974,8 @@ func (c AzureSEVSNP) EqualTo(old AttestationCfg) (bool, error) {
|
||||
}
|
||||
|
||||
// FetchAndSetLatestVersionNumbers fetches the latest version numbers from the configapi and sets them.
|
||||
func (c *AzureSEVSNP) FetchAndSetLatestVersionNumbers(fetcher attestationconfigapi.Fetcher) error {
|
||||
versions, err := fetcher.FetchAzureSEVSNPVersionLatest(context.Background())
|
||||
func (c *AzureSEVSNP) FetchAndSetLatestVersionNumbers(ctx context.Context, fetcher attestationconfigapi.Fetcher, now time.Time) error {
|
||||
versions, err := fetcher.FetchAzureSEVSNPVersionLatest(ctx, now)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-playground/locales/en"
|
||||
ut "github.com/go-playground/universal-translator"
|
||||
@ -41,21 +42,20 @@ func TestDefaultConfig(t *testing.T) {
|
||||
assert.NotNil(def)
|
||||
}
|
||||
|
||||
// TODO(elchead): activate latest logic for next release AB#3036
|
||||
// func TestDefaultConfigWritesLatestVersion(t *testing.T) {
|
||||
// conf := Default()
|
||||
// bt, err := yaml.Marshal(conf)
|
||||
// require := require.New(t)
|
||||
// require.NoError(err)
|
||||
func TestDefaultConfigWritesLatestVersion(t *testing.T) {
|
||||
conf := Default()
|
||||
bt, err := yaml.Marshal(conf)
|
||||
require := require.New(t)
|
||||
require.NoError(err)
|
||||
|
||||
// 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"))
|
||||
//}
|
||||
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"))
|
||||
}
|
||||
|
||||
func TestReadConfigFile(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
@ -64,41 +64,29 @@ func TestReadConfigFile(t *testing.T) {
|
||||
wantResult *Config
|
||||
wantErr bool
|
||||
}{
|
||||
// TODO(elchead): activate latest logic for next release AB#3036
|
||||
//"mix of Latest and uint as version value": {
|
||||
// config: func() configMap {
|
||||
// conf := Default()
|
||||
// m := getConfigAsMap(conf, t)
|
||||
// m.setAzureSEVSNPVersion("microcodeVersion", "Latest") // check uppercase also works
|
||||
// m.setAzureSEVSNPVersion("teeVersion", 2)
|
||||
// m.setAzureSEVSNPVersion("bootloaderVersion", 1)
|
||||
// return m
|
||||
// }(),
|
||||
|
||||
// configName: constants.ConfigFilename,
|
||||
// wantResult: func() *Config {
|
||||
// conf := Default()
|
||||
// conf.Attestation.AzureSEVSNP.BootloaderVersion = AttestationVersion{
|
||||
// Value: 1,
|
||||
// IsLatest: false,
|
||||
// }
|
||||
// conf.Attestation.AzureSEVSNP.TEEVersion = AttestationVersion{
|
||||
// Value: 2,
|
||||
// IsLatest: false,
|
||||
// }
|
||||
// return conf
|
||||
// }(),
|
||||
//},
|
||||
// TODO(elchead): activate latest logic for next release AB#3036
|
||||
"refuse invalid latest value": {
|
||||
"mix of Latest and uint as version value": {
|
||||
config: func() configMap {
|
||||
conf := Default()
|
||||
m := getConfigAsMap(conf, t)
|
||||
m.setAzureSEVSNPVersion("microcodeVersion", "latest")
|
||||
m.setAzureSEVSNPVersion("microcodeVersion", "Latest") // check uppercase also works
|
||||
m.setAzureSEVSNPVersion("teeVersion", 2)
|
||||
m.setAzureSEVSNPVersion("bootloaderVersion", 1)
|
||||
return m
|
||||
}(),
|
||||
|
||||
configName: constants.ConfigFilename,
|
||||
wantErr: true,
|
||||
wantResult: func() *Config {
|
||||
conf := Default()
|
||||
conf.Attestation.AzureSEVSNP.BootloaderVersion = AttestationVersion{
|
||||
Value: 1,
|
||||
IsLatest: false,
|
||||
}
|
||||
conf.Attestation.AzureSEVSNP.TEEVersion = AttestationVersion{
|
||||
Value: 2,
|
||||
IsLatest: false,
|
||||
}
|
||||
return conf
|
||||
}(),
|
||||
},
|
||||
"refuse invalid version value": {
|
||||
config: func() configMap {
|
||||
@ -271,7 +259,7 @@ func TestNewWithDefaultOptions(t *testing.T) {
|
||||
}
|
||||
|
||||
// Test
|
||||
c, err := New(fileHandler, constants.ConfigFilename, fakeConfigFetcher{}, false)
|
||||
c, err := New(fileHandler, constants.ConfigFilename, stubAttestationFetcher{}, false)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
@ -889,9 +877,9 @@ func (c configMap) setAzureSEVSNPVersion(versionType string, value interface{})
|
||||
c["attestation"].(configMap)["azureSEVSNP"].(configMap)[versionType] = value
|
||||
}
|
||||
|
||||
//func (c configMap) getAzureSEVSNPVersion(versionType string) interface{} {
|
||||
// return c["attestation"].(configMap)["azureSEVSNP"].(configMap)[versionType]
|
||||
//}
|
||||
func (c configMap) getAzureSEVSNPVersion(versionType string) interface{} {
|
||||
return c["attestation"].(configMap)["azureSEVSNP"].(configMap)[versionType]
|
||||
}
|
||||
|
||||
// getConfigAsMap returns a map of the config.
|
||||
func getConfigAsMap(conf *Config, t *testing.T) (res configMap) {
|
||||
@ -905,21 +893,21 @@ func getConfigAsMap(conf *Config, t *testing.T) (res configMap) {
|
||||
return
|
||||
}
|
||||
|
||||
type fakeConfigFetcher struct{}
|
||||
type stubAttestationFetcher struct{}
|
||||
|
||||
func (f fakeConfigFetcher) FetchAzureSEVSNPVersionList(_ context.Context, _ configapi.AzureSEVSNPVersionList) (configapi.AzureSEVSNPVersionList, error) {
|
||||
func (f stubAttestationFetcher) FetchAzureSEVSNPVersionList(_ context.Context, _ configapi.AzureSEVSNPVersionList) (configapi.AzureSEVSNPVersionList, error) {
|
||||
return configapi.AzureSEVSNPVersionList(
|
||||
[]string{},
|
||||
), nil
|
||||
}
|
||||
|
||||
func (f fakeConfigFetcher) FetchAzureSEVSNPVersion(_ context.Context, _ configapi.AzureSEVSNPVersionAPI) (configapi.AzureSEVSNPVersionAPI, error) {
|
||||
func (f stubAttestationFetcher) FetchAzureSEVSNPVersion(_ context.Context, _ configapi.AzureSEVSNPVersionAPI) (configapi.AzureSEVSNPVersionAPI, error) {
|
||||
return configapi.AzureSEVSNPVersionAPI{
|
||||
AzureSEVSNPVersion: testCfg,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f fakeConfigFetcher) FetchAzureSEVSNPVersionLatest(_ context.Context) (configapi.AzureSEVSNPVersionAPI, error) {
|
||||
func (f stubAttestationFetcher) FetchAzureSEVSNPVersionLatest(_ context.Context, _ time.Time) (configapi.AzureSEVSNPVersionAPI, error) {
|
||||
return configapi.AzureSEVSNPVersionAPI{
|
||||
AzureSEVSNPVersion: testCfg,
|
||||
}, nil
|
||||
|
@ -18,6 +18,11 @@ import (
|
||||
sigsig "github.com/sigstore/sigstore/pkg/signature"
|
||||
)
|
||||
|
||||
// Verifier checks if the signature of content can be verified.
|
||||
type Verifier interface {
|
||||
VerifySignature(content, signature, publicKey []byte) error
|
||||
}
|
||||
|
||||
// CosignVerifier checks if the signature of content can be verified
|
||||
// using a cosign public key.
|
||||
type CosignVerifier struct{}
|
||||
|
@ -129,6 +129,9 @@ While this API should stay compatible with old release, extensive changes to our
|
||||
In this case a new API version will be used to retrieve the config in the updated format, e.g. `/constellation/v2/attestation/<ATTESTATION_VARIANT>/`.
|
||||
The old API will still receive updates for at least the next release cycle, during this time this API version will also return a deprecation warning when requesting `list`.
|
||||
|
||||
### Azure SEV-SNP
|
||||
IMPORTANT: Since the current version fetches from the Azure SEV-SNP report are not guaranteed to be globally rolled out at the time of the report, we introduce a minimum age (2 weeks) of the version to consider it a valid latest version.
|
||||
This validation is only enforced on the fetcher side! This means that the HTTP endpoints contain all versions, even those that do not yet have the minimum age.
|
||||
### AWS
|
||||
|
||||
AWS provides a way to precalculate launch-measurements for their firmware in SEV-SNP CVMs.
|
||||
|
Loading…
Reference in New Issue
Block a user