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:
Adrian Stobbe 2023-06-09 12:48:12 +02:00 committed by GitHub
parent 72e168e653
commit 3fde118b33
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 239 additions and 189 deletions

View File

@ -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

View File

@ -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)

View File

@ -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 {

View File

@ -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
}

View File

@ -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")
}

View File

@ -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
}

View File

@ -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)

View File

@ -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

View File

@ -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)
})
}
}

View File

@ -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
}

View File

@ -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

View File

@ -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{}

View File

@ -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.