mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-01-14 08:59:49 -05:00
fdaa5aab3c
* Remove signature checks from unittests. Would need to export signature from client/fetcher (unwanted). Can't figure out a better way. e2e test completes in ~4sec and runs automatically. So seems like a acceptable tradeoff. * list object is now signed, but not verified. If we start to verify the list we will have to adapt the e2e test to restore the previous list. Otherwise there could be conflicts between dev and release keys.
105 lines
3.9 KiB
Go
105 lines
3.9 KiB
Go
/*
|
|
Copyright (c) Edgeless Systems GmbH
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
|
|
package attestationconfigapi
|
|
|
|
import (
|
|
"context"
|
|
"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, 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.
|
|
func NewFetcher() Fetcher {
|
|
return NewFetcherWithClient(apifetcher.NewHTTPClient())
|
|
}
|
|
|
|
// NewFetcherWithClient returns a new fetcher with custom http client.
|
|
func NewFetcherWithClient(client apifetcher.HTTPClient) Fetcher {
|
|
verifier, err := sigstore.NewCosignVerifier([]byte(cosignPublicKey))
|
|
if err != nil {
|
|
// This relies on an embedded public key. If this key can not be validated, there is no way to recover from this.
|
|
panic(fmt.Errorf("creating cosign verifier: %w", err))
|
|
}
|
|
return newFetcherWithClientAndVerifier(client, verifier)
|
|
}
|
|
|
|
func newFetcherWithClientAndVerifier(client apifetcher.HTTPClient, cosignVerifier sigstore.Verifier) Fetcher {
|
|
return &fetcher{client, cosignVerifier}
|
|
}
|
|
|
|
// FetchAzureSEVSNPVersionList fetches the version list information from the config API.
|
|
func (f *fetcher) FetchAzureSEVSNPVersionList(ctx context.Context, attestation AzureSEVSNPVersionList) (AzureSEVSNPVersionList, error) {
|
|
// TODO (derpsteb): Replace with FetchAndVerify once we move to v2 of the config API.
|
|
return apifetcher.Fetch(ctx, f.HTTPClient, attestation)
|
|
}
|
|
|
|
// FetchAzureSEVSNPVersion fetches the version information from the config API.
|
|
func (f *fetcher) FetchAzureSEVSNPVersion(ctx context.Context, azureVersion AzureSEVSNPVersionAPI) (AzureSEVSNPVersionAPI, error) {
|
|
fetchedVersion, err := apifetcher.FetchAndVerify(ctx, f.HTTPClient, azureVersion, f.verifier)
|
|
if err != nil {
|
|
return fetchedVersion, fmt.Errorf("fetch version %s: %w", fetchedVersion.Version, err)
|
|
}
|
|
|
|
return fetchedVersion, nil
|
|
}
|
|
|
|
// FetchAzureSEVSNPVersionLatest returns the latest versions of the given type.
|
|
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)
|
|
}
|
|
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
|
|
}
|
|
|
|
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(VersionFormat, 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")
|
|
}
|