2022-09-05 03:06:08 -04:00
|
|
|
/*
|
|
|
|
Copyright (c) Edgeless Systems GmbH
|
|
|
|
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
*/
|
|
|
|
|
2022-08-01 03:37:05 -04:00
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
2023-06-01 07:55:46 -04:00
|
|
|
"context"
|
2022-08-01 03:37:05 -04:00
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"testing"
|
|
|
|
|
2023-06-07 10:16:32 -04:00
|
|
|
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
|
|
|
|
"github.com/edgelesssys/constellation/v2/internal/api/versionsapi"
|
2023-11-28 11:30:11 -05:00
|
|
|
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
|
2023-11-14 04:03:01 -05:00
|
|
|
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
|
2022-09-21 07:47:57 -04:00
|
|
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
|
|
|
"github.com/edgelesssys/constellation/v2/internal/config"
|
|
|
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
|
|
|
"github.com/edgelesssys/constellation/v2/internal/file"
|
2023-01-04 04:46:29 -05:00
|
|
|
"github.com/edgelesssys/constellation/v2/internal/logger"
|
2022-08-01 03:37:05 -04:00
|
|
|
"github.com/spf13/afero"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
|
|
|
func urlMustParse(raw string) *url.URL {
|
|
|
|
parsed, _ := url.Parse(raw)
|
|
|
|
return parsed
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestParseFetchMeasurementsFlags(t *testing.T) {
|
|
|
|
testCases := map[string]struct {
|
|
|
|
urlFlag string
|
|
|
|
signatureURLFlag string
|
2023-01-31 05:45:31 -05:00
|
|
|
forceFlag bool
|
2023-10-16 09:05:29 -04:00
|
|
|
wantFlags fetchMeasurementsFlags
|
2022-08-01 03:37:05 -04:00
|
|
|
wantErr bool
|
|
|
|
}{
|
|
|
|
"default": {
|
2023-10-16 09:05:29 -04:00
|
|
|
wantFlags: fetchMeasurementsFlags{
|
2022-08-01 03:37:05 -04:00
|
|
|
measurementsURL: nil,
|
|
|
|
signatureURL: nil,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"url": {
|
|
|
|
urlFlag: "https://some.other.url/with/path",
|
|
|
|
signatureURLFlag: "https://some.other.url/with/path.sig",
|
2023-10-16 09:05:29 -04:00
|
|
|
wantFlags: fetchMeasurementsFlags{
|
2022-08-01 03:37:05 -04:00
|
|
|
measurementsURL: urlMustParse("https://some.other.url/with/path"),
|
|
|
|
signatureURL: urlMustParse("https://some.other.url/with/path.sig"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"broken url": {
|
|
|
|
urlFlag: "%notaurl%",
|
|
|
|
wantErr: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for name, tc := range testCases {
|
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
assert := assert.New(t)
|
|
|
|
require := require.New(t)
|
|
|
|
|
|
|
|
cmd := newConfigFetchMeasurementsCmd()
|
2023-08-04 07:53:51 -04:00
|
|
|
cmd.Flags().String("workspace", "", "") // register persistent flag manually
|
2023-10-16 09:05:29 -04:00
|
|
|
cmd.Flags().Bool("force", false, "")
|
|
|
|
cmd.Flags().Bool("debug", false, "")
|
|
|
|
cmd.Flags().String("tf-log", "NONE", "")
|
2022-08-01 03:37:05 -04:00
|
|
|
|
|
|
|
if tc.urlFlag != "" {
|
|
|
|
require.NoError(cmd.Flags().Set("url", tc.urlFlag))
|
|
|
|
}
|
|
|
|
if tc.signatureURLFlag != "" {
|
|
|
|
require.NoError(cmd.Flags().Set("signature-url", tc.signatureURLFlag))
|
|
|
|
}
|
2023-10-16 09:05:29 -04:00
|
|
|
var flags fetchMeasurementsFlags
|
|
|
|
err := flags.parse(cmd.Flags())
|
2022-08-01 03:37:05 -04:00
|
|
|
if tc.wantErr {
|
|
|
|
assert.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
require.NoError(err)
|
|
|
|
assert.Equal(tc.wantFlags, flags)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestUpdateURLs(t *testing.T) {
|
2023-08-01 10:48:13 -04:00
|
|
|
require := require.New(t)
|
|
|
|
|
|
|
|
ver, err := versionsapi.NewVersion("foo", "nightly", "v7.7.7", versionsapi.VersionKindImage)
|
|
|
|
require.NoError(err)
|
|
|
|
|
2022-08-01 03:37:05 -04:00
|
|
|
testCases := map[string]struct {
|
|
|
|
conf *config.Config
|
|
|
|
flags *fetchMeasurementsFlags
|
|
|
|
wantMeasurementsURL string
|
|
|
|
wantMeasurementsSigURL string
|
|
|
|
}{
|
|
|
|
"both values nil": {
|
|
|
|
conf: &config.Config{
|
2023-01-05 09:52:57 -05:00
|
|
|
Image: ver.ShortPath(),
|
2022-08-01 03:37:05 -04:00
|
|
|
Provider: config.ProviderConfig{
|
2022-11-22 12:47:08 -05:00
|
|
|
GCP: &config.GCPConfig{},
|
2022-08-01 03:37:05 -04:00
|
|
|
},
|
|
|
|
},
|
|
|
|
flags: &fetchMeasurementsFlags{},
|
2023-05-25 08:33:57 -04:00
|
|
|
wantMeasurementsURL: ver.ArtifactsURL(versionsapi.APIV2) + "/image/measurements.json",
|
|
|
|
wantMeasurementsSigURL: ver.ArtifactsURL(versionsapi.APIV2) + "/image/measurements.json.sig",
|
2022-08-01 03:37:05 -04:00
|
|
|
},
|
|
|
|
"both set by user": {
|
2023-02-03 05:05:42 -05:00
|
|
|
conf: &config.Config{
|
|
|
|
Image: ver.ShortPath(),
|
|
|
|
},
|
2022-08-01 03:37:05 -04:00
|
|
|
flags: &fetchMeasurementsFlags{
|
2022-11-24 04:57:58 -05:00
|
|
|
measurementsURL: urlMustParse("get.my/measurements.json"),
|
|
|
|
signatureURL: urlMustParse("get.my/measurements.json.sig"),
|
2022-08-01 03:37:05 -04:00
|
|
|
},
|
2022-11-24 04:57:58 -05:00
|
|
|
wantMeasurementsURL: "get.my/measurements.json",
|
|
|
|
wantMeasurementsSigURL: "get.my/measurements.json.sig",
|
2022-08-01 03:37:05 -04:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for name, tc := range testCases {
|
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
assert := assert.New(t)
|
|
|
|
|
2022-11-28 04:27:33 -05:00
|
|
|
err := tc.flags.updateURLs(tc.conf)
|
2022-08-01 03:37:05 -04:00
|
|
|
assert.NoError(err)
|
|
|
|
assert.Equal(tc.wantMeasurementsURL, tc.flags.measurementsURL.String())
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// roundTripFunc .
|
|
|
|
type roundTripFunc func(req *http.Request) *http.Response
|
|
|
|
|
|
|
|
// RoundTrip .
|
|
|
|
func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
|
|
return f(req), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// newTestClient returns *http.Client with Transport replaced to avoid making real calls.
|
|
|
|
func newTestClient(fn roundTripFunc) *http.Client {
|
|
|
|
return &http.Client{
|
|
|
|
Transport: fn,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestConfigFetchMeasurements(t *testing.T) {
|
2022-10-11 07:57:52 -04:00
|
|
|
testCases := map[string]struct {
|
2023-06-06 04:32:22 -04:00
|
|
|
insecureFlag bool
|
2023-11-28 11:30:11 -05:00
|
|
|
err error
|
2023-06-06 04:32:22 -04:00
|
|
|
wantErr bool
|
2022-10-11 07:57:52 -04:00
|
|
|
}{
|
2023-11-28 11:30:11 -05:00
|
|
|
"no error succeeds": {},
|
|
|
|
"failing rekor verify should not result in error": {
|
|
|
|
err: &measurements.RekorError{},
|
2022-10-11 07:57:52 -04:00
|
|
|
},
|
2023-11-28 11:30:11 -05:00
|
|
|
"error other than Rekor fails": {
|
|
|
|
err: assert.AnError,
|
2023-05-26 11:49:46 -04:00
|
|
|
wantErr: true,
|
|
|
|
},
|
2022-10-11 07:57:52 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
for name, tc := range testCases {
|
|
|
|
t.Run(name, func(t *testing.T) {
|
|
|
|
assert := assert.New(t)
|
|
|
|
require := require.New(t)
|
|
|
|
|
|
|
|
cmd := newConfigFetchMeasurementsCmd()
|
|
|
|
fileHandler := file.NewHandler(afero.NewMemMapFs())
|
|
|
|
|
2022-11-15 09:40:49 -05:00
|
|
|
gcpConfig := defaultConfigWithExpectedMeasurements(t, config.Default(), cloudprovider.GCP)
|
2022-11-28 04:27:33 -05:00
|
|
|
gcpConfig.Image = "v999.999.999"
|
2022-10-11 07:57:52 -04:00
|
|
|
|
|
|
|
err := fileHandler.WriteYAML(constants.ConfigFilename, gcpConfig, file.OptMkdirAll)
|
|
|
|
require.NoError(err)
|
2023-11-28 11:30:11 -05:00
|
|
|
fetcher := stubVerifyFetcher{err: tc.err}
|
|
|
|
cfm := &configFetchMeasurementsCmd{canFetchMeasurements: true, log: logger.NewTest(t), verifyFetcher: fetcher}
|
2023-10-16 09:05:29 -04:00
|
|
|
cfm.flags.insecure = tc.insecureFlag
|
|
|
|
cfm.flags.force = true
|
2022-10-11 07:57:52 -04:00
|
|
|
|
2023-11-28 11:30:11 -05:00
|
|
|
err = cfm.configFetchMeasurements(cmd, fileHandler, stubAttestationFetcher{})
|
2023-05-26 11:49:46 -04:00
|
|
|
if tc.wantErr {
|
|
|
|
assert.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
assert.NoError(err)
|
2022-10-11 07:57:52 -04:00
|
|
|
})
|
|
|
|
}
|
2022-08-01 03:37:05 -04:00
|
|
|
}
|
2023-06-01 07:55:46 -04:00
|
|
|
|
2023-11-28 11:30:11 -05:00
|
|
|
type stubVerifyFetcher struct {
|
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f stubVerifyFetcher) FetchAndVerifyMeasurements(_ context.Context, _ string, _ cloudprovider.Provider, _ variant.Variant, _ bool) (measurements.M, error) {
|
|
|
|
return nil, f.err
|
|
|
|
}
|
|
|
|
|
2023-06-06 04:32:22 -04:00
|
|
|
type stubAttestationFetcher struct{}
|
2023-06-01 07:55:46 -04:00
|
|
|
|
2023-11-14 04:03:01 -05:00
|
|
|
func (f stubAttestationFetcher) FetchSEVSNPVersionList(_ context.Context, _ attestationconfigapi.SEVSNPVersionList) (attestationconfigapi.SEVSNPVersionList, error) {
|
|
|
|
return attestationconfigapi.SEVSNPVersionList{}, nil
|
2023-06-01 07:55:46 -04:00
|
|
|
}
|
|
|
|
|
2023-11-14 04:03:01 -05:00
|
|
|
func (f stubAttestationFetcher) FetchSEVSNPVersion(_ context.Context, _ attestationconfigapi.SEVSNPVersionAPI) (attestationconfigapi.SEVSNPVersionAPI, error) {
|
|
|
|
return attestationconfigapi.SEVSNPVersionAPI{
|
|
|
|
SEVSNPVersion: testCfg,
|
2023-06-01 07:55:46 -04:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2023-11-14 04:03:01 -05:00
|
|
|
func (f stubAttestationFetcher) FetchSEVSNPVersionLatest(_ context.Context, _ variant.Variant) (attestationconfigapi.SEVSNPVersionAPI, error) {
|
|
|
|
return attestationconfigapi.SEVSNPVersionAPI{
|
|
|
|
SEVSNPVersion: testCfg,
|
2023-06-02 03:19:23 -04:00
|
|
|
}, nil
|
2023-06-01 07:55:46 -04:00
|
|
|
}
|
|
|
|
|
2023-11-14 04:03:01 -05:00
|
|
|
var testCfg = attestationconfigapi.SEVSNPVersion{
|
2023-06-01 07:55:46 -04:00
|
|
|
Microcode: 93,
|
|
|
|
TEE: 0,
|
|
|
|
SNP: 6,
|
|
|
|
Bootloader: 2,
|
|
|
|
}
|