constellation/cli/internal/cmd/configgenerate_test.go
Moritz Sanft 913b09aeb8
Support SEV-SNP on GCP (#3011)
* terraform: enable creation of SEV-SNP VMs on GCP

* variant: add SEV-SNP attestation variant

* config: add SEV-SNP config options for GCP

* measurements: add GCP SEV-SNP measurements

* gcp: separate package for SEV-ES

* attestation: add GCP SEV-SNP attestation logic

* gcp: factor out common logic

* choose: add GCP SEV-SNP

* cli: add TF variable passthrough for GCP SEV-SNP variables

* cli: support GCP SEV-SNP for `constellation verify`

* Adjust usage of GCP SEV-SNP throughout codebase

* ci: add GCP SEV-SNP

* terraform-provider: support GCP SEV-SNP

* docs: add GCP SEV-SNP reference

* linter fixes

* gcp: only run test with TPM simulator

* gcp: remove nonsense test

* Update cli/internal/cmd/verify.go

Co-authored-by: Daniel Weiße <66256922+daniel-weisse@users.noreply.github.com>

* Update docs/docs/overview/clouds.md

Co-authored-by: Daniel Weiße <66256922+daniel-weisse@users.noreply.github.com>

* Update terraform-provider-constellation/internal/provider/attestation_data_source_test.go

Co-authored-by: Adrian Stobbe <stobbe.adrian@gmail.com>

* linter fixes

* terraform_provider: correctly pass down CC technology

* config: mark attestationconfigapi as unimplemented

* gcp: fix comments and typos

* snp: use nonce and PK hash in SNP report

* snp: ensure we never use ARK supplied by Issuer (#3025)

* Make sure SNP ARK is always loaded from config, or fetched from AMD KDS
* GCP: Set validator `reportData` correctly

---------

Signed-off-by: Daniel Weiße <dw@edgeless.systems>
Co-authored-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* attestationconfigapi: add GCP to uploading

* snp: use correct cert

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* terraform-provider: enable fetching of attestation config values for GCP SEV-SNP

* linter fixes

---------

Signed-off-by: Daniel Weiße <dw@edgeless.systems>
Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>
Co-authored-by: Daniel Weiße <66256922+daniel-weisse@users.noreply.github.com>
Co-authored-by: Adrian Stobbe <stobbe.adrian@gmail.com>
2024-04-16 18:13:47 +02:00

326 lines
8.2 KiB
Go

/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package cmd
import (
"fmt"
"strings"
"testing"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
"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/constellation/state"
"github.com/edgelesssys/constellation/v2/internal/file"
"github.com/edgelesssys/constellation/v2/internal/logger"
"github.com/edgelesssys/constellation/v2/internal/versions"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/mod/semver"
)
func TestParseKubernetesVersion(t *testing.T) {
testCases := map[string]struct {
version string
wantErr bool
}{
"default version": {
version: "",
},
"without v prefix": {
version: strings.TrimPrefix(string(versions.Default), "v"),
},
"K8s version without patch version": {
version: semver.MajorMinor(string(versions.Default)),
},
"K8s version with patch version": {
version: string(versions.Default),
},
"K8s version with invalid patch version": {
version: func() string {
s := string(versions.Default)
return s[:len(s)-1] + "99"
}(),
wantErr: true,
},
"outdated K8s version": {
version: "v1.0.0",
wantErr: true,
},
"no semver": {
version: "asdf",
wantErr: true,
},
"not supported": {
version: "1111",
wantErr: true,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
flags := newConfigGenerateCmd().Flags()
if tc.version != "" {
require.NoError(flags.Set("kubernetes", tc.version))
}
version, err := parseK8sFlag(flags)
if tc.wantErr {
assert.Error(err)
return
}
assert.NoError(err)
assert.Equal(versions.Default, version)
})
}
}
func TestConfigGenerateDefault(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
fileHandler := file.NewHandler(afero.NewMemMapFs())
cmd := newConfigGenerateCmd()
cg := &configGenerateCmd{
log: logger.NewTest(t),
flags: generateFlags{
attestationVariant: variant.Dummy{},
k8sVersion: versions.Default,
},
}
require.NoError(cg.configGenerate(cmd, fileHandler, cloudprovider.Unknown, ""))
var readConfig config.Config
err := fileHandler.ReadYAML(constants.ConfigFilename, &readConfig)
assert.NoError(err)
assert.Equal(*config.Default(), readConfig)
_, err = state.ReadFromFile(fileHandler, constants.StateFilename)
assert.NoError(err)
}
func TestConfigGenerateDefaultProviderSpecific(t *testing.T) {
testCases := map[string]struct {
provider cloudprovider.Provider
rawProvider string
}{
"aws": {
provider: cloudprovider.AWS,
},
"azure": {
provider: cloudprovider.Azure,
},
"gcp": {
provider: cloudprovider.GCP,
},
"openstack": {
provider: cloudprovider.OpenStack,
},
"stackit": {
provider: cloudprovider.OpenStack,
rawProvider: "stackit",
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
fileHandler := file.NewHandler(afero.NewMemMapFs())
cmd := newConfigGenerateCmd()
wantConf := config.Default().WithOpenStackProviderDefaults(cloudprovider.OpenStack, tc.rawProvider)
wantConf.RemoveProviderAndAttestationExcept(tc.provider)
cg := &configGenerateCmd{
log: logger.NewTest(t),
flags: generateFlags{
attestationVariant: variant.Dummy{},
k8sVersion: versions.Default,
},
}
require.NoError(cg.configGenerate(cmd, fileHandler, tc.provider, tc.rawProvider))
var readConfig config.Config
err := fileHandler.ReadYAML(constants.ConfigFilename, &readConfig)
assert.NoError(err)
assert.Equal(*wantConf, readConfig)
stateFile, err := state.ReadFromFile(fileHandler, constants.StateFilename)
assert.NoError(err)
switch tc.provider {
case cloudprovider.GCP:
assert.NotNil(stateFile.Infrastructure.GCP)
case cloudprovider.Azure:
assert.NotNil(stateFile.Infrastructure.Azure)
}
})
}
}
func TestConfigGenerateDefaultExists(t *testing.T) {
require := require.New(t)
fileHandler := file.NewHandler(afero.NewMemMapFs())
require.NoError(fileHandler.Write(constants.ConfigFilename, []byte("foobar"), file.OptNone))
cmd := newConfigGenerateCmd()
cg := &configGenerateCmd{
log: logger.NewTest(t),
flags: generateFlags{attestationVariant: variant.Dummy{}},
}
require.Error(cg.configGenerate(cmd, fileHandler, cloudprovider.Unknown, ""))
}
func TestNoValidProviderAttestationCombination(t *testing.T) {
assert := assert.New(t)
tests := []struct {
provider cloudprovider.Provider
attestation variant.Variant
}{
{cloudprovider.Azure, variant.AWSNitroTPM{}},
{cloudprovider.AWS, variant.AzureTrustedLaunch{}},
{cloudprovider.GCP, variant.AWSNitroTPM{}},
{cloudprovider.QEMU, variant.GCPSEVES{}},
{cloudprovider.OpenStack, variant.AWSNitroTPM{}},
}
for _, test := range tests {
t.Run("", func(_ *testing.T) {
_, err := createConfigWithAttestationVariant(test.provider, "", test.attestation)
assert.Error(err)
})
}
}
func TestValidProviderAttestationCombination(t *testing.T) {
defaultAttestation := config.Default().Attestation
tests := []struct {
provider cloudprovider.Provider
attestation variant.Variant
expected config.AttestationConfig
}{
{
cloudprovider.Azure,
variant.AzureTrustedLaunch{},
config.AttestationConfig{AzureTrustedLaunch: defaultAttestation.AzureTrustedLaunch},
},
{
cloudprovider.Azure,
variant.AzureSEVSNP{},
config.AttestationConfig{AzureSEVSNP: defaultAttestation.AzureSEVSNP},
},
{
cloudprovider.AWS,
variant.AWSSEVSNP{},
config.AttestationConfig{AWSSEVSNP: defaultAttestation.AWSSEVSNP},
},
{
cloudprovider.AWS,
variant.AWSNitroTPM{},
config.AttestationConfig{AWSNitroTPM: defaultAttestation.AWSNitroTPM},
},
{
cloudprovider.GCP,
variant.GCPSEVES{},
config.AttestationConfig{GCPSEVES: defaultAttestation.GCPSEVES},
},
{
cloudprovider.GCP,
variant.GCPSEVSNP{},
config.AttestationConfig{GCPSEVSNP: defaultAttestation.GCPSEVSNP},
},
{
cloudprovider.QEMU,
variant.QEMUVTPM{},
config.AttestationConfig{QEMUVTPM: defaultAttestation.QEMUVTPM},
},
{
cloudprovider.OpenStack,
variant.QEMUVTPM{},
config.AttestationConfig{QEMUVTPM: defaultAttestation.QEMUVTPM},
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("Provider:%s,Attestation:%s", test.provider, test.attestation), func(t *testing.T) {
sut, err := createConfigWithAttestationVariant(test.provider, "", test.attestation)
assert := assert.New(t)
assert.NoError(err)
assert.Equal(test.expected, sut.Attestation)
})
}
}
func TestParseAttestationFlag(t *testing.T) {
testCases := map[string]struct {
wantErr bool
attestationFlag string
wantVariant variant.Variant
}{
"invalid": {
wantErr: true,
attestationFlag: "unknown",
},
"AzureTrustedLaunch": {
attestationFlag: "azure-trustedlaunch",
wantVariant: variant.AzureTrustedLaunch{},
},
"AzureSEVSNP": {
attestationFlag: "azure-sev-snp",
wantVariant: variant.AzureSEVSNP{},
},
"AWSSEVSNP": {
attestationFlag: "aws-sev-snp",
wantVariant: variant.AWSSEVSNP{},
},
"AWSNitroTPM": {
attestationFlag: "aws-nitro-tpm",
wantVariant: variant.AWSNitroTPM{},
},
"GCPSEVES": {
attestationFlag: "gcp-sev-es",
wantVariant: variant.GCPSEVES{},
},
"GCPSEVSNP": {
attestationFlag: "gcp-sev-snp",
wantVariant: variant.GCPSEVSNP{},
},
"QEMUVTPM": {
attestationFlag: "qemu-vtpm",
wantVariant: variant.QEMUVTPM{},
},
"no flag": {
wantVariant: variant.Dummy{},
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
require := require.New(t)
assert := assert.New(t)
cmd := newConfigGenerateCmd()
if tc.attestationFlag != "" {
require.NoError(cmd.Flags().Set("attestation", tc.attestationFlag))
}
attestation, err := parseAttestationFlag(cmd.Flags())
if tc.wantErr {
assert.Error(err)
return
}
require.NoError(err)
assert.True(tc.wantVariant.Equal(attestation))
})
}
}