mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-05-04 23:35:11 -04:00
cli: new flag to set the attestation type for config generate
(#1769)
* add attestation flag to specify type in config
This commit is contained in:
parent
e7b7a544f0
commit
f99e06b63b
11 changed files with 336 additions and 42 deletions
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||
"github.com/edgelesssys/constellation/v2/internal/variant"
|
||||
"github.com/edgelesssys/constellation/v2/internal/versions"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/encoder"
|
||||
"github.com/spf13/afero"
|
||||
|
@ -36,13 +37,15 @@ func newConfigGenerateCmd() *cobra.Command {
|
|||
}
|
||||
cmd.Flags().StringP("file", "f", constants.ConfigFilename, "path to output file, or '-' for stdout")
|
||||
cmd.Flags().StringP("kubernetes", "k", semver.MajorMinor(config.Default().KubernetesVersion), "Kubernetes version to use in format MAJOR.MINOR")
|
||||
cmd.Flags().StringP("attestation", "a", "", fmt.Sprintf("attestation variant to use %s. If not specified, the default for the cloud provider is used", printFormattedSlice(variant.GetAvailableAttestationTypes())))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
type generateFlags struct {
|
||||
file string
|
||||
k8sVersion string
|
||||
file string
|
||||
k8sVersion string
|
||||
attestationVariant variant.Variant
|
||||
}
|
||||
|
||||
type configGenerateCmd struct {
|
||||
|
@ -69,7 +72,10 @@ func (cg *configGenerateCmd) configGenerate(cmd *cobra.Command, fileHandler file
|
|||
|
||||
cg.log.Debugf("Parsed flags as %v", flags)
|
||||
cg.log.Debugf("Using cloud provider %s", provider.String())
|
||||
conf := createConfig(provider)
|
||||
conf, err := createConfigWithAttestationType(provider, flags.attestationVariant)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating config: %w", err)
|
||||
}
|
||||
conf.KubernetesVersion = flags.k8sVersion
|
||||
if flags.file == "-" {
|
||||
content, err := encoder.NewEncoder(conf).Encode()
|
||||
|
@ -96,7 +102,7 @@ func (cg *configGenerateCmd) configGenerate(cmd *cobra.Command, fileHandler file
|
|||
}
|
||||
|
||||
// createConfig creates a config file for the given provider.
|
||||
func createConfig(provider cloudprovider.Provider) *config.Config {
|
||||
func createConfigWithAttestationType(provider cloudprovider.Provider, attestationVariant variant.Variant) (*config.Config, error) {
|
||||
conf := config.Default()
|
||||
conf.RemoveProviderExcept(provider)
|
||||
|
||||
|
@ -105,7 +111,25 @@ func createConfig(provider cloudprovider.Provider) *config.Config {
|
|||
conf.StateDiskSizeGB = 10
|
||||
}
|
||||
|
||||
return conf
|
||||
if provider == cloudprovider.Unknown {
|
||||
return conf, nil
|
||||
}
|
||||
if attestationVariant.Equal(variant.Dummy{}) {
|
||||
attestationVariant = variant.GetDefaultAttestation(provider)
|
||||
if attestationVariant.Equal(variant.Dummy{}) {
|
||||
return nil, fmt.Errorf("provider %s does not have a default attestation variant", provider)
|
||||
}
|
||||
} else if !variant.ValidProvider(provider, attestationVariant) {
|
||||
return nil, fmt.Errorf("provider %s does not support attestation type %s", provider, attestationVariant)
|
||||
}
|
||||
conf.SetAttestation(attestationVariant)
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
// createConfig creates a config file for the given provider.
|
||||
func createConfig(provider cloudprovider.Provider) *config.Config {
|
||||
res, _ := createConfigWithAttestationType(provider, variant.Dummy{})
|
||||
return res
|
||||
}
|
||||
|
||||
// supportedVersions prints the supported version without v prefix and without patch version.
|
||||
|
@ -135,13 +159,29 @@ func parseGenerateFlags(cmd *cobra.Command) (generateFlags, error) {
|
|||
return generateFlags{}, fmt.Errorf("resolving kuberentes version from flag: %w", err)
|
||||
}
|
||||
|
||||
attestationString, err := cmd.Flags().GetString("attestation")
|
||||
if err != nil {
|
||||
return generateFlags{}, fmt.Errorf("parsing attestation flag: %w", err)
|
||||
}
|
||||
|
||||
var attestationType variant.Variant
|
||||
// if no attestation type is specified, use the default for the cloud provider
|
||||
if attestationString == "" {
|
||||
attestationType = variant.Dummy{}
|
||||
} else {
|
||||
attestationType, err = variant.FromString(attestationString)
|
||||
if err != nil {
|
||||
return generateFlags{}, fmt.Errorf("invalid attestation variant: %s", attestationString)
|
||||
}
|
||||
}
|
||||
return generateFlags{
|
||||
file: file,
|
||||
k8sVersion: resolvedVersion,
|
||||
file: file,
|
||||
k8sVersion: resolvedVersion,
|
||||
attestationVariant: attestationType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// createCompletion handles the completion of the create command. It is frequently called
|
||||
// generateCompletion handles the completion of the create command. It is frequently called
|
||||
// while the user types arguments of the command to suggest completion.
|
||||
func generateCompletion(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) {
|
||||
switch len(args) {
|
||||
|
@ -167,3 +207,15 @@ func resolveK8sVersion(k8sVersion string) (string, error) {
|
|||
|
||||
return extendedVersion, nil
|
||||
}
|
||||
|
||||
func printFormattedSlice[T any](input []T) string {
|
||||
return fmt.Sprintf("{%s}", strings.Join(toString(input), "|"))
|
||||
}
|
||||
|
||||
func toString[T any](t []T) []string {
|
||||
var res []string
|
||||
for _, v := range t {
|
||||
res = append(res, fmt.Sprintf("%v", v))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ package cmd
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
|
@ -15,8 +16,10 @@ import (
|
|||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
"github.com/edgelesssys/constellation/v2/internal/variant"
|
||||
"github.com/edgelesssys/constellation/v2/internal/versions"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/mod/semver"
|
||||
|
@ -87,7 +90,7 @@ func TestConfigGenerateDefaultGCPSpecific(t *testing.T) {
|
|||
cmd := newConfigGenerateCmd()
|
||||
|
||||
wantConf := config.Default()
|
||||
wantConf.RemoveProviderExcept(cloudprovider.GCP)
|
||||
wantConf.RemoveProviderAndAttestationExcept(cloudprovider.GCP)
|
||||
|
||||
cg := &configGenerateCmd{log: logger.NewTest(t)}
|
||||
require.NoError(cg.configGenerate(cmd, fileHandler, cloudprovider.GCP))
|
||||
|
@ -139,3 +142,133 @@ func TestConfigGenerateStdOut(t *testing.T) {
|
|||
|
||||
assert.Equal(*config.Default(), readConfig)
|
||||
}
|
||||
|
||||
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{}},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run("", func(t *testing.T) {
|
||||
_, err := createConfigWithAttestationType(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.AWSNitroTPM{},
|
||||
config.AttestationConfig{AWSNitroTPM: defaultAttestation.AWSNitroTPM},
|
||||
},
|
||||
{
|
||||
cloudprovider.GCP,
|
||||
variant.GCPSEVES{},
|
||||
config.AttestationConfig{GCPSEVES: defaultAttestation.GCPSEVES},
|
||||
},
|
||||
|
||||
{
|
||||
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 := createConfigWithAttestationType(test.provider, test.attestation)
|
||||
assert := assert.New(t)
|
||||
assert.NoError(err)
|
||||
assert.Equal(test.expected, sut.Attestation)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttestationArgument(t *testing.T) {
|
||||
defaultAttestation := config.Default().Attestation
|
||||
tests := []struct {
|
||||
name string
|
||||
provider cloudprovider.Provider
|
||||
expectErr bool
|
||||
expectedCfg config.AttestationConfig
|
||||
setFlag func(*cobra.Command) error
|
||||
}{
|
||||
{
|
||||
name: "InvalidAttestationArgument",
|
||||
provider: cloudprovider.Unknown,
|
||||
expectErr: true,
|
||||
setFlag: func(cmd *cobra.Command) error {
|
||||
return cmd.Flags().Set("attestation", "unknown")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ValidAttestationArgument",
|
||||
provider: cloudprovider.Azure,
|
||||
expectErr: false,
|
||||
setFlag: func(cmd *cobra.Command) error {
|
||||
return cmd.Flags().Set("attestation", "azure-trustedlaunch")
|
||||
},
|
||||
expectedCfg: config.AttestationConfig{AzureTrustedLaunch: defaultAttestation.AzureTrustedLaunch},
|
||||
},
|
||||
{
|
||||
name: "WithoutAttestationArgument",
|
||||
provider: cloudprovider.Azure,
|
||||
expectErr: false,
|
||||
setFlag: func(cmd *cobra.Command) error {
|
||||
return nil
|
||||
},
|
||||
expectedCfg: config.AttestationConfig{AzureSEVSNP: defaultAttestation.AzureSEVSNP},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
require := assert.New(t)
|
||||
assert := assert.New(t)
|
||||
|
||||
cmd := newConfigGenerateCmd()
|
||||
require.NoError(test.setFlag(cmd))
|
||||
|
||||
fileHandler := file.NewHandler(afero.NewMemMapFs())
|
||||
|
||||
cg := &configGenerateCmd{log: logger.NewTest(t)}
|
||||
err := cg.configGenerate(cmd, fileHandler, test.provider)
|
||||
if test.expectErr {
|
||||
assert.Error(err)
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
var readConfig config.Config
|
||||
require.NoError(fileHandler.ReadYAML(constants.ConfigFilename, &readConfig))
|
||||
|
||||
assert.Equal(test.expectedCfg, readConfig.Attestation)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -429,7 +429,7 @@ func TestAttestation(t *testing.T) {
|
|||
|
||||
cfg := config.Default()
|
||||
cfg.Image = "image"
|
||||
cfg.RemoveProviderExcept(cloudprovider.QEMU)
|
||||
cfg.RemoveProviderAndAttestationExcept(cloudprovider.QEMU)
|
||||
cfg.Attestation.QEMUVTPM.Measurements[0] = measurements.WithAllBytes(0x00, measurements.Enforce, measurements.PCRMeasurementLength)
|
||||
cfg.Attestation.QEMUVTPM.Measurements[1] = measurements.WithAllBytes(0x11, measurements.Enforce, measurements.PCRMeasurementLength)
|
||||
cfg.Attestation.QEMUVTPM.Measurements[2] = measurements.WithAllBytes(0x22, measurements.Enforce, measurements.PCRMeasurementLength)
|
||||
|
@ -554,7 +554,7 @@ func defaultConfigWithExpectedMeasurements(t *testing.T, conf *config.Config, cs
|
|||
conf.Attestation.QEMUVTPM.Measurements[12] = measurements.WithAllBytes(0xcc, measurements.Enforce, measurements.PCRMeasurementLength)
|
||||
}
|
||||
|
||||
conf.RemoveProviderExcept(csp)
|
||||
conf.RemoveProviderAndAttestationExcept(csp)
|
||||
return conf
|
||||
}
|
||||
|
||||
|
|
|
@ -216,7 +216,7 @@ func (m *miniUpCmd) prepareConfig(cmd *cobra.Command, fileHandler file.Handler,
|
|||
|
||||
config := config.Default()
|
||||
config.Name = constants.MiniConstellationUID
|
||||
config.RemoveProviderExcept(cloudprovider.QEMU)
|
||||
config.RemoveProviderAndAttestationExcept(cloudprovider.QEMU)
|
||||
config.StateDiskSizeGB = 8
|
||||
|
||||
// only release images (e.g. v2.7.0) use the production NVRAM
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue