mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-10-01 01:36:09 -04:00
Add --insecure to config fetch-measurement (#1879)
* cli: add --insecure to fetch-measurements * cli: rename fake to stub * ci: upload measurements for debug images * fix cli docs
This commit is contained in:
parent
f7f11c32f8
commit
7c07e3be18
44
.github/actions/e2e_verify/action.yml
vendored
44
.github/actions/e2e_verify/action.yml
vendored
@ -32,51 +32,15 @@ runs:
|
||||
with:
|
||||
shortname: ${{ inputs.osImage }}
|
||||
|
||||
- name: Get attestation variant
|
||||
id: get-variant
|
||||
- name: Constellation fetch measurements
|
||||
shell: bash
|
||||
run: |
|
||||
# TODO(AB#3144): Refactor when API is update for attestation variants
|
||||
case ${{ inputs.cloudProvider }} in
|
||||
aws)
|
||||
echo ATTESTATION_VARIANT=awsNitroTPM >> $GITHUB_OUTPUT
|
||||
;;
|
||||
azure)
|
||||
echo ATTESTATION_VARIANT=azureSEVSNP >> $GITHUB_OUTPUT
|
||||
;;
|
||||
gcp)
|
||||
echo ATTESTATION_VARIANT=gcpSEVES >> $GITHUB_OUTPUT
|
||||
;;
|
||||
qemu)
|
||||
echo ATTESTATION_VARIANT=qemuVTPM >> $GITHUB_OUTPUT
|
||||
;;
|
||||
esac
|
||||
|
||||
- name: Fetch & write measurements
|
||||
shell: bash
|
||||
run: |
|
||||
ref=${{ steps.expand-version.outputs.ref }}
|
||||
stream=${{ steps.expand-version.outputs.stream }}
|
||||
version=${{ steps.expand-version.outputs.version }}
|
||||
verPath="ref/${ref}/stream/${stream}/${version}"
|
||||
MEASUREMENTS=$(curl -fsSL https://cdn.confidential.cloud/constellation/v1/${verPath}/image/csp/${{ inputs.cloudProvider }}/measurements.json | jq '.measurements' -r)
|
||||
for key in $(echo $MEASUREMENTS | jq 'keys[]' -r); do
|
||||
echo Updating $key to $(echo $MEASUREMENTS | jq ".\"$key\"" -r)
|
||||
if [[ $(yq '.version' constellation-conf.yaml) == "v2" ]]
|
||||
then
|
||||
yq -i ".provider.${{ inputs.cloudProvider }}.measurements.[$key] = $(echo $MEASUREMENTS | jq ".\"$key\"")" constellation-conf.yaml
|
||||
else
|
||||
yq -i ".attestation.${{ steps.get-variant.outputs.ATTESTATION_VARIANT }}.measurements.[$key] = $(echo $MEASUREMENTS | jq ".\"$key\"")" constellation-conf.yaml
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $(yq '.version' constellation-conf.yaml) == "v2" ]]
|
||||
if [[ ${{ steps.expand-version.outputs.stream }} == "debug" ]]
|
||||
then
|
||||
yq -i '.provider.${{ inputs.cloudProvider }}.measurements |= array_to_map' constellation-conf.yaml
|
||||
constellation config fetch-measurements --insecure
|
||||
else
|
||||
yq -i '.attestation.${{ steps.get-variant.outputs.ATTESTATION_VARIANT }}.measurements |= array_to_map' constellation-conf.yaml
|
||||
constellation config fetch-measurements
|
||||
fi
|
||||
cat constellation-conf.yaml
|
||||
|
||||
- name: Constellation verify
|
||||
shell: bash
|
||||
|
8
.github/workflows/build-os-image.yml
vendored
8
.github/workflows/build-os-image.yml
vendored
@ -717,7 +717,6 @@ jobs:
|
||||
upload-pcrs:
|
||||
name: "Sign & upload PCRs"
|
||||
needs: [build-settings, calculate-pcrs]
|
||||
if: inputs.stream != 'debug'
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
@ -759,6 +758,7 @@ jobs:
|
||||
echo "::endgroup::"
|
||||
|
||||
- name: Sign measurements
|
||||
if: inputs.stream != 'debug'
|
||||
shell: bash
|
||||
env:
|
||||
COSIGN_PUBLIC_KEY: ${{ inputs.isRelease && secrets.COSIGN_PUBLIC_KEY || secrets.COSIGN_DEV_PUBLIC_KEY }}
|
||||
@ -779,6 +779,12 @@ jobs:
|
||||
sig=$(rekor-cli get --uuid="${uuid}" --format=json | jq -r .Body.HashedRekordObj.signature.content)
|
||||
cosign verify-blob --key cosign.pub --signature <(echo "${sig}") "${{ github.workspace }}/measurements.json"
|
||||
|
||||
- name: Create stub signature file
|
||||
if: inputs.stream == 'debug'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "THOSE MEASUREMENTS BELONG TO A DEBUG IMAGE. THOSE ARE NOT SINGED BY ANY KEY." > "${{ github.workspace }}/measurements.json.sig"
|
||||
|
||||
- name: Upload measurements
|
||||
shell: bash
|
||||
run: |
|
||||
|
@ -36,6 +36,7 @@ func newConfigFetchMeasurementsCmd() *cobra.Command {
|
||||
}
|
||||
cmd.Flags().StringP("url", "u", "", "alternative URL to fetch measurements from")
|
||||
cmd.Flags().StringP("signature-url", "s", "", "alternative URL to fetch measurements' signature from")
|
||||
cmd.Flags().Bool("insecure", false, "skip the measurement signature verification")
|
||||
|
||||
return cmd
|
||||
}
|
||||
@ -43,6 +44,7 @@ func newConfigFetchMeasurementsCmd() *cobra.Command {
|
||||
type fetchMeasurementsFlags struct {
|
||||
measurementsURL *url.URL
|
||||
signatureURL *url.URL
|
||||
insecure bool
|
||||
configPath string
|
||||
force bool
|
||||
}
|
||||
@ -115,25 +117,44 @@ func (cfm *configFetchMeasurementsCmd) configFetchMeasurements(
|
||||
}
|
||||
|
||||
var fetchedMeasurements measurements.M
|
||||
hash, err := fetchedMeasurements.FetchAndVerify(
|
||||
ctx, client, cosign,
|
||||
flags.measurementsURL,
|
||||
flags.signatureURL,
|
||||
imageVersion,
|
||||
conf.GetProvider(),
|
||||
conf.GetAttestationConfig().GetVariant(),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
var hash string
|
||||
if flags.insecure {
|
||||
if err := fetchedMeasurements.FetchNoVerify(
|
||||
ctx,
|
||||
client,
|
||||
flags.measurementsURL,
|
||||
imageVersion,
|
||||
conf.GetProvider(),
|
||||
conf.GetAttestationConfig().GetVariant(),
|
||||
); err != nil {
|
||||
return fmt.Errorf("fetching measurements without verification: %w", err)
|
||||
}
|
||||
|
||||
cfm.log.Debugf("Fetched measurements without verification")
|
||||
} else {
|
||||
hash, err = fetchedMeasurements.FetchAndVerify(
|
||||
ctx,
|
||||
client,
|
||||
cosign,
|
||||
flags.measurementsURL,
|
||||
flags.signatureURL,
|
||||
imageVersion,
|
||||
conf.GetProvider(),
|
||||
conf.GetAttestationConfig().GetVariant(),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("fetching and verifying measurements: %w", err)
|
||||
}
|
||||
cfm.log.Debugf("Fetched and verified measurements, hash is %s", hash)
|
||||
if err := sigstore.VerifyWithRekor(cmd.Context(), imageVersion, rekor, hash); err != nil {
|
||||
cmd.PrintErrf("Ignoring Rekor related error: %v\n", err)
|
||||
cmd.PrintErrln("Make sure the downloaded measurements are trustworthy!")
|
||||
}
|
||||
|
||||
cfm.log.Debugf("Verified measurements with Rekor")
|
||||
}
|
||||
|
||||
cfm.log.Debugf("Fetched and verified measurements, hash is %s", hash)
|
||||
if err := sigstore.VerifyWithRekor(cmd.Context(), imageVersion, rekor, hash); err != nil {
|
||||
cmd.PrintErrf("Ignoring Rekor related error: %v\n", err)
|
||||
cmd.PrintErrln("Make sure the downloaded measurements are trustworthy!")
|
||||
}
|
||||
|
||||
cfm.log.Debugf("Verified measurements with Rekor, updating measurements in configuration")
|
||||
cfm.log.Debugf("Updating measurements in configuration")
|
||||
conf.UpdateMeasurements(fetchedMeasurements)
|
||||
if err := fileHandler.WriteYAML(flags.configPath, conf, file.OptOverwrite); err != nil {
|
||||
return err
|
||||
@ -170,6 +191,12 @@ func (cfm *configFetchMeasurementsCmd) parseFetchMeasurementsFlags(cmd *cobra.Co
|
||||
}
|
||||
cfm.log.Debugf("Parsed measurements signature URL as %v", measurementsSignatureURL)
|
||||
|
||||
insecure, err := cmd.Flags().GetBool("insecure")
|
||||
if err != nil {
|
||||
return &fetchMeasurementsFlags{}, fmt.Errorf("parsing insecure argument: %w", err)
|
||||
}
|
||||
cfm.log.Debugf("Insecure flag is %v", insecure)
|
||||
|
||||
config, err := cmd.Flags().GetString("config")
|
||||
if err != nil {
|
||||
return &fetchMeasurementsFlags{}, fmt.Errorf("parsing config path argument: %w", err)
|
||||
@ -184,6 +211,7 @@ func (cfm *configFetchMeasurementsCmd) parseFetchMeasurementsFlags(cmd *cobra.Co
|
||||
return &fetchMeasurementsFlags{
|
||||
measurementsURL: measurementsURL,
|
||||
signatureURL: measurementsSignatureURL,
|
||||
insecure: insecure,
|
||||
configPath: config,
|
||||
force: force,
|
||||
}, nil
|
||||
|
@ -9,11 +9,11 @@ package cmd
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfig"
|
||||
@ -233,31 +233,39 @@ func TestConfigFetchMeasurements(t *testing.T) {
|
||||
})
|
||||
|
||||
testCases := map[string]struct {
|
||||
cosign cosignVerifier
|
||||
rekor rekorVerifier
|
||||
wantErr bool
|
||||
cosign cosignVerifier
|
||||
rekor rekorVerifier
|
||||
insecureFlag bool
|
||||
wantErr bool
|
||||
}{
|
||||
"success": {
|
||||
cosign: &stubCosignVerifier{},
|
||||
rekor: singleUUIDVerifier(),
|
||||
},
|
||||
"success without cosign": {
|
||||
insecureFlag: true,
|
||||
cosign: &stubCosignVerifier{
|
||||
verifyError: assert.AnError,
|
||||
},
|
||||
rekor: singleUUIDVerifier(),
|
||||
},
|
||||
"failing search should not result in error": {
|
||||
cosign: &stubCosignVerifier{},
|
||||
rekor: &stubRekorVerifier{
|
||||
SearchByHashUUIDs: []string{},
|
||||
SearchByHashError: errors.New("some error"),
|
||||
SearchByHashError: assert.AnError,
|
||||
},
|
||||
},
|
||||
"failing verify should not result in error": {
|
||||
cosign: &stubCosignVerifier{},
|
||||
rekor: &stubRekorVerifier{
|
||||
SearchByHashUUIDs: []string{"11111111111111111111111111111111111111111111111111111111111111111111111111111111"},
|
||||
VerifyEntryError: errors.New("some error"),
|
||||
VerifyEntryError: assert.AnError,
|
||||
},
|
||||
},
|
||||
"signature verification failure": {
|
||||
cosign: &stubCosignVerifier{
|
||||
verifyError: errors.New("some error"),
|
||||
verifyError: assert.AnError,
|
||||
},
|
||||
rekor: singleUUIDVerifier(),
|
||||
wantErr: true,
|
||||
@ -272,6 +280,7 @@ func TestConfigFetchMeasurements(t *testing.T) {
|
||||
cmd := newConfigFetchMeasurementsCmd()
|
||||
cmd.Flags().String("config", constants.ConfigFilename, "") // register persistent flag manually
|
||||
cmd.Flags().Bool("force", true, "") // register persistent flag manually
|
||||
require.NoError(cmd.Flags().Set("insecure", strconv.FormatBool(tc.insecureFlag)))
|
||||
fileHandler := file.NewHandler(afero.NewMemMapFs())
|
||||
|
||||
gcpConfig := defaultConfigWithExpectedMeasurements(t, config.Default(), cloudprovider.GCP)
|
||||
@ -281,7 +290,7 @@ func TestConfigFetchMeasurements(t *testing.T) {
|
||||
require.NoError(err)
|
||||
cfm := &configFetchMeasurementsCmd{canFetchMeasurements: true, log: logger.NewTest(t)}
|
||||
|
||||
err = cfm.configFetchMeasurements(cmd, tc.cosign, tc.rekor, fileHandler, fakeAttestationFetcher{}, client)
|
||||
err = cfm.configFetchMeasurements(cmd, tc.cosign, tc.rekor, fileHandler, stubAttestationFetcher{}, client)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
@ -291,21 +300,21 @@ func TestConfigFetchMeasurements(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type fakeAttestationFetcher struct{}
|
||||
type stubAttestationFetcher struct{}
|
||||
|
||||
func (f fakeAttestationFetcher) FetchAzureSEVSNPVersionList(_ context.Context, _ attestationconfig.AzureSEVSNPVersionList) (attestationconfig.AzureSEVSNPVersionList, error) {
|
||||
func (f stubAttestationFetcher) FetchAzureSEVSNPVersionList(_ context.Context, _ attestationconfig.AzureSEVSNPVersionList) (attestationconfig.AzureSEVSNPVersionList, error) {
|
||||
return attestationconfig.AzureSEVSNPVersionList(
|
||||
[]string{},
|
||||
), nil
|
||||
}
|
||||
|
||||
func (f fakeAttestationFetcher) FetchAzureSEVSNPVersion(_ context.Context, _ attestationconfig.AzureSEVSNPVersionAPI) (attestationconfig.AzureSEVSNPVersionAPI, error) {
|
||||
func (f stubAttestationFetcher) FetchAzureSEVSNPVersion(_ context.Context, _ attestationconfig.AzureSEVSNPVersionAPI) (attestationconfig.AzureSEVSNPVersionAPI, error) {
|
||||
return attestationconfig.AzureSEVSNPVersionAPI{
|
||||
AzureSEVSNPVersion: testCfg,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f fakeAttestationFetcher) FetchAzureSEVSNPVersionLatest(_ context.Context) (attestationconfig.AzureSEVSNPVersionAPI, error) {
|
||||
func (f stubAttestationFetcher) FetchAzureSEVSNPVersionLatest(_ context.Context) (attestationconfig.AzureSEVSNPVersionAPI, error) {
|
||||
return attestationconfig.AzureSEVSNPVersionAPI{
|
||||
AzureSEVSNPVersion: testCfg,
|
||||
}, nil
|
||||
|
@ -202,7 +202,7 @@ func TestCreate(t *testing.T) {
|
||||
|
||||
fileHandler := file.NewHandler(tc.setupFs(require, tc.provider))
|
||||
c := &createCmd{log: logger.NewTest(t)}
|
||||
err := c.create(cmd, tc.creator, fileHandler, &nopSpinner{}, fakeAttestationFetcher{})
|
||||
err := c.create(cmd, tc.creator, fileHandler, &nopSpinner{}, stubAttestationFetcher{})
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
|
@ -175,7 +175,7 @@ func TestInitialize(t *testing.T) {
|
||||
defer cancel()
|
||||
cmd.SetContext(ctx)
|
||||
i := &initCmd{log: logger.NewTest(t), spinner: &nopSpinner{}}
|
||||
err := i.initialize(cmd, newDialer, fileHandler, &stubLicenseClient{}, fakeAttestationFetcher{})
|
||||
err := i.initialize(cmd, newDialer, fileHandler, &stubLicenseClient{}, stubAttestationFetcher{})
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
@ -519,7 +519,7 @@ func TestAttestation(t *testing.T) {
|
||||
cmd.SetContext(ctx)
|
||||
|
||||
i := &initCmd{log: logger.NewTest(t), spinner: &nopSpinner{}}
|
||||
err := i.initialize(cmd, newDialer, fileHandler, &stubLicenseClient{}, fakeAttestationFetcher{})
|
||||
err := i.initialize(cmd, newDialer, fileHandler, &stubLicenseClient{}, stubAttestationFetcher{})
|
||||
assert.Error(err)
|
||||
// make sure the error is actually a TLS handshake error
|
||||
assert.Contains(err.Error(), "transport: authentication handshake failed")
|
||||
|
@ -163,7 +163,7 @@ func TestRecover(t *testing.T) {
|
||||
))
|
||||
|
||||
newDialer := func(atls.Validator) *dialer.Dialer { return nil }
|
||||
r := &recoverCmd{log: logger.NewTest(t), configFetcher: fakeAttestationFetcher{}}
|
||||
r := &recoverCmd{log: logger.NewTest(t), configFetcher: stubAttestationFetcher{}}
|
||||
err := r.recover(cmd, fileHandler, time.Millisecond, tc.doer, newDialer)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
|
@ -142,7 +142,7 @@ func TestUpgradeApply(t *testing.T) {
|
||||
require.NoError(handler.WriteYAML(constants.ConfigFilename, cfg))
|
||||
require.NoError(handler.WriteJSON(constants.ClusterIDsFileName, clusterid.File{}))
|
||||
|
||||
upgrader := upgradeApplyCmd{upgrader: tc.upgrader, log: logger.NewTest(t), imageFetcher: tc.fetcher, configFetcher: fakeAttestationFetcher{}}
|
||||
upgrader := upgradeApplyCmd{upgrader: tc.upgrader, log: logger.NewTest(t), imageFetcher: tc.fetcher, configFetcher: stubAttestationFetcher{}}
|
||||
err := upgrader.upgradeApply(cmd, handler)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
|
@ -271,7 +271,7 @@ func TestUpgradeCheck(t *testing.T) {
|
||||
|
||||
cmd := newUpgradeCheckCmd()
|
||||
|
||||
err := checkCmd.upgradeCheck(cmd, fileHandler, fakeAttestationFetcher{}, tc.flags)
|
||||
err := checkCmd.upgradeCheck(cmd, fileHandler, stubAttestationFetcher{}, tc.flags)
|
||||
if tc.wantError {
|
||||
assert.Error(err)
|
||||
return
|
||||
|
@ -190,7 +190,7 @@ func TestVerify(t *testing.T) {
|
||||
}
|
||||
|
||||
v := &verifyCmd{log: logger.NewTest(t)}
|
||||
err := v.verify(cmd, fileHandler, tc.protoClient, tc.formatter, fakeAttestationFetcher{})
|
||||
err := v.verify(cmd, fileHandler, tc.protoClient, tc.formatter, stubAttestationFetcher{})
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
|
@ -108,6 +108,7 @@ constellation config fetch-measurements [flags]
|
||||
|
||||
```
|
||||
-h, --help help for fetch-measurements
|
||||
--insecure skip the measurement signature verification
|
||||
-s, --signature-url string alternative URL to fetch measurements' signature from
|
||||
-u, --url string alternative URL to fetch measurements from
|
||||
```
|
||||
|
Loading…
Reference in New Issue
Block a user