config: add attestation variant (#1413)

* Add attestation type to config (optional for now)

* Get attestation variant from config in CLI

* Set attestation variant for Constellation services in helm deployments

* Remove AzureCVM variable from helm deployments

---------

Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
Daniel Weiße 2023-03-14 11:46:27 +01:00 committed by GitHub
parent 8679988b6c
commit 6ea5588bdc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
44 changed files with 379 additions and 383 deletions

View file

@ -23,13 +23,9 @@ go_library(
"//cli/internal/libvirt",
"//cli/internal/terraform",
"//internal/atls",
"//internal/attestation/aws",
"//internal/attestation/azure/snp",
"//internal/attestation/azure/trustedlaunch",
"//internal/attestation/gcp",
"//internal/attestation/choose",
"//internal/attestation/idkeydigest",
"//internal/attestation/measurements",
"//internal/attestation/qemu",
"//internal/cloud/cloudprovider",
"//internal/cloud/gcpshared",
"//internal/compatibility",
@ -37,6 +33,7 @@ go_library(
"//internal/constants",
"//internal/kubernetes",
"//internal/kubernetes/kubectl",
"//internal/oid",
"//internal/versions",
"//internal/versions/components",
"//internal/versionsapi",
@ -83,6 +80,7 @@ go_test(
"//internal/config",
"//internal/constants",
"//internal/logger",
"//internal/oid",
"//internal/versions",
"//internal/versions/components",
"//operators/constellation-node-operator/api/v1alpha1",

View file

@ -14,46 +14,40 @@ import (
"fmt"
"github.com/edgelesssys/constellation/v2/internal/atls"
"github.com/edgelesssys/constellation/v2/internal/attestation/aws"
"github.com/edgelesssys/constellation/v2/internal/attestation/azure/snp"
"github.com/edgelesssys/constellation/v2/internal/attestation/azure/trustedlaunch"
"github.com/edgelesssys/constellation/v2/internal/attestation/gcp"
"github.com/edgelesssys/constellation/v2/internal/attestation/choose"
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/attestation/qemu"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/oid"
"github.com/spf13/cobra"
)
// Validator validates Platform Configuration Registers (PCRs).
type Validator struct {
provider cloudprovider.Provider
attestationVariant oid.Getter
pcrs measurements.M
idkeydigests idkeydigest.IDKeyDigests
enforceIDKeyDigest bool
azureCVM bool
validator atls.Validator
log debugLog
}
// NewValidator creates a new Validator.
func NewValidator(provider cloudprovider.Provider, conf *config.Config, log debugLog) (*Validator, error) {
func NewValidator(conf *config.Config, log debugLog) (*Validator, error) {
v := Validator{log: log}
if provider == cloudprovider.Unknown {
return nil, errors.New("unknown cloud provider")
variant, err := oid.FromString(conf.AttestationVariant)
if err != nil {
return nil, fmt.Errorf("parsing attestation variant: %w", err)
}
v.provider = provider
v.attestationVariant = variant // valid variant
if err := v.setPCRs(conf); err != nil {
return nil, err
}
if v.provider == cloudprovider.Azure {
v.azureCVM = *conf.Provider.Azure.ConfidentialVM
if v.azureCVM {
v.enforceIDKeyDigest = *conf.Provider.Azure.EnforceIDKeyDigest
v.idkeydigests = conf.Provider.Azure.IDKeyDigest
}
if v.attestationVariant.OID().Equal(oid.AzureSEVSNP{}.OID()) {
v.enforceIDKeyDigest = conf.EnforcesIDKeyDigest()
v.idkeydigests = conf.IDKeyDigests()
}
return &v, nil
@ -100,26 +94,26 @@ func (v *Validator) updatePCR(pcrIndex uint32, encoded string) error {
}
func (v *Validator) setPCRs(config *config.Config) error {
switch v.provider {
case cloudprovider.AWS:
switch v.attestationVariant {
case oid.AWSNitroTPM{}:
awsPCRs := config.Provider.AWS.Measurements
if len(awsPCRs) == 0 {
return errors.New("no expected measurement provided")
}
v.pcrs = awsPCRs
case cloudprovider.Azure:
case oid.AzureSEVSNP{}, oid.AzureTrustedLaunch{}:
azurePCRs := config.Provider.Azure.Measurements
if len(azurePCRs) == 0 {
return errors.New("no expected measurement provided")
}
v.pcrs = azurePCRs
case cloudprovider.GCP:
case oid.GCPSEVES{}:
gcpPCRs := config.Provider.GCP.Measurements
if len(gcpPCRs) == 0 {
return errors.New("no expected measurement provided")
}
v.pcrs = gcpPCRs
case cloudprovider.QEMU:
case oid.QEMUVTPM{}:
qemuPCRs := config.Provider.QEMU.Measurements
if len(qemuPCRs) == 0 {
return errors.New("no expected measurement provided")
@ -142,20 +136,9 @@ func (v *Validator) PCRS() measurements.M {
func (v *Validator) updateValidator(cmd *cobra.Command) {
log := warnLogger{cmd: cmd, log: v.log}
switch v.provider {
case cloudprovider.GCP:
v.validator = gcp.NewValidator(v.pcrs, log)
case cloudprovider.Azure:
if v.azureCVM {
v.validator = snp.NewValidator(v.pcrs, v.idkeydigests, v.enforceIDKeyDigest, log)
} else {
v.validator = trustedlaunch.NewValidator(v.pcrs, log)
}
case cloudprovider.AWS:
v.validator = aws.NewValidator(v.pcrs, log)
case cloudprovider.QEMU:
v.validator = qemu.NewValidator(v.pcrs, log)
}
// Use of a valid variant has been check in NewValidator so we may drop the error
v.validator, _ = choose.Validator(v.attestationVariant, v.pcrs, v.idkeydigests, v.enforceIDKeyDigest, log)
}
// warnLogger implements logging of warnings for validators.

View file

@ -19,11 +19,12 @@ import (
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/attestation/qemu"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/logger"
"github.com/edgelesssys/constellation/v2/internal/oid"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewValidator(t *testing.T) {
@ -37,47 +38,82 @@ func TestNewValidator(t *testing.T) {
}
testCases := map[string]struct {
provider cloudprovider.Provider
config *config.Config
pcrs measurements.M
enforceIDKeyDigest bool
digest idkeydigest.IDKeyDigests
azureCVM bool
wantErr bool
config *config.Config
wantErr bool
}{
"gcp": {
provider: cloudprovider.GCP,
pcrs: testPCRs,
config: &config.Config{
AttestationVariant: oid.GCPSEVES{}.String(),
Provider: config.ProviderConfig{
GCP: &config.GCPConfig{
Measurements: testPCRs,
},
},
},
},
"azure cvm": {
provider: cloudprovider.Azure,
pcrs: testPCRs,
azureCVM: true,
config: &config.Config{
AttestationVariant: oid.AzureSEVSNP{}.String(),
Provider: config.ProviderConfig{
Azure: &config.AzureConfig{
Measurements: testPCRs,
},
},
},
},
"azure trusted launch": {
provider: cloudprovider.Azure,
pcrs: testPCRs,
azureCVM: false,
config: &config.Config{
AttestationVariant: oid.AzureTrustedLaunch{}.String(),
Provider: config.ProviderConfig{
Azure: &config.AzureConfig{
Measurements: testPCRs,
},
},
},
},
"qemu": {
provider: cloudprovider.QEMU,
pcrs: testPCRs,
config: &config.Config{
AttestationVariant: oid.QEMUVTPM{}.String(),
Provider: config.ProviderConfig{
QEMU: &config.QEMUConfig{
Measurements: testPCRs,
},
},
},
},
"no pcrs provided": {
provider: cloudprovider.Azure,
pcrs: measurements.M{},
wantErr: true,
config: &config.Config{
AttestationVariant: oid.AzureSEVSNP{}.String(),
Provider: config.ProviderConfig{
Azure: &config.AzureConfig{
Measurements: measurements.M{},
},
},
},
wantErr: true,
},
"unknown provider": {
provider: cloudprovider.Unknown,
pcrs: testPCRs,
wantErr: true,
"unknown variant": {
config: &config.Config{
AttestationVariant: "unknown",
Provider: config.ProviderConfig{
QEMU: &config.QEMUConfig{
Measurements: testPCRs,
},
},
},
wantErr: true,
},
"set idkeydigest": {
provider: cloudprovider.Azure,
pcrs: testPCRs,
digest: idkeydigest.IDKeyDigests{[]byte("414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141")},
enforceIDKeyDigest: true,
config: &config.Config{
AttestationVariant: oid.AzureSEVSNP{}.String(),
Provider: config.ProviderConfig{
Azure: &config.AzureConfig{
Measurements: testPCRs,
IDKeyDigest: idkeydigest.IDKeyDigests{[]byte("414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141")},
EnforceIDKeyDigest: &[]bool{true}[0],
},
},
},
},
}
@ -85,25 +121,16 @@ func TestNewValidator(t *testing.T) {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
conf := &config.Config{Provider: config.ProviderConfig{}}
if tc.provider == cloudprovider.GCP {
conf.Provider.GCP = &config.GCPConfig{Measurements: tc.pcrs}
}
if tc.provider == cloudprovider.Azure {
conf.Provider.Azure = &config.AzureConfig{Measurements: tc.pcrs, EnforceIDKeyDigest: &tc.enforceIDKeyDigest, IDKeyDigest: tc.digest, ConfidentialVM: &tc.azureCVM}
}
if tc.provider == cloudprovider.QEMU {
conf.Provider.QEMU = &config.QEMUConfig{Measurements: tc.pcrs}
}
validators, err := NewValidator(tc.provider, conf, logger.NewTest(t))
validators, err := NewValidator(tc.config, logger.NewTest(t))
if tc.wantErr {
assert.Error(err)
} else {
assert.NoError(err)
assert.Equal(tc.pcrs, validators.pcrs)
assert.Equal(tc.provider, validators.provider)
assert.Equal(tc.config.GetMeasurements(), validators.pcrs)
variant, err := oid.FromString(tc.config.AttestationVariant)
require.NoError(t, err)
assert.Equal(variant, validators.attestationVariant)
}
})
}
@ -129,31 +156,29 @@ func TestValidatorV(t *testing.T) {
}
testCases := map[string]struct {
provider cloudprovider.Provider
pcrs measurements.M
wantVs atls.Validator
azureCVM bool
variant oid.Getter
pcrs measurements.M
wantVs atls.Validator
}{
"gcp": {
provider: cloudprovider.GCP,
pcrs: newTestPCRs(),
wantVs: gcp.NewValidator(newTestPCRs(), nil),
variant: oid.GCPSEVES{},
pcrs: newTestPCRs(),
wantVs: gcp.NewValidator(newTestPCRs(), nil),
},
"azure cvm": {
provider: cloudprovider.Azure,
pcrs: newTestPCRs(),
wantVs: snp.NewValidator(newTestPCRs(), idkeydigest.IDKeyDigests{}, false, nil),
azureCVM: true,
variant: oid.AzureSEVSNP{},
pcrs: newTestPCRs(),
wantVs: snp.NewValidator(newTestPCRs(), idkeydigest.IDKeyDigests{}, false, nil),
},
"azure trusted launch": {
provider: cloudprovider.Azure,
pcrs: newTestPCRs(),
wantVs: trustedlaunch.NewValidator(newTestPCRs(), nil),
variant: oid.AzureTrustedLaunch{},
pcrs: newTestPCRs(),
wantVs: trustedlaunch.NewValidator(newTestPCRs(), nil),
},
"qemu": {
provider: cloudprovider.QEMU,
pcrs: newTestPCRs(),
wantVs: qemu.NewValidator(newTestPCRs(), nil),
variant: oid.QEMUVTPM{},
pcrs: newTestPCRs(),
wantVs: qemu.NewValidator(newTestPCRs(), nil),
},
}
@ -161,7 +186,7 @@ func TestValidatorV(t *testing.T) {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
validators := &Validator{provider: tc.provider, pcrs: tc.pcrs, azureCVM: tc.azureCVM}
validators := &Validator{attestationVariant: tc.variant, pcrs: tc.pcrs}
resultValidator := validators.V(&cobra.Command{})
@ -206,53 +231,53 @@ func TestValidatorUpdateInitPCRs(t *testing.T) {
}
testCases := map[string]struct {
provider cloudprovider.Provider
variant oid.Getter
pcrs measurements.M
ownerID string
clusterID string
wantErr bool
}{
"gcp update owner ID": {
provider: cloudprovider.GCP,
pcrs: newTestPCRs(),
ownerID: one64,
variant: oid.GCPSEVES{},
pcrs: newTestPCRs(),
ownerID: one64,
},
"gcp update cluster ID": {
provider: cloudprovider.GCP,
variant: oid.GCPSEVES{},
pcrs: newTestPCRs(),
clusterID: one64,
},
"gcp update both": {
provider: cloudprovider.GCP,
variant: oid.GCPSEVES{},
pcrs: newTestPCRs(),
ownerID: one64,
clusterID: one64,
},
"azure update owner ID": {
provider: cloudprovider.Azure,
pcrs: newTestPCRs(),
ownerID: one64,
variant: oid.AzureSEVSNP{},
pcrs: newTestPCRs(),
ownerID: one64,
},
"azure update cluster ID": {
provider: cloudprovider.Azure,
variant: oid.AzureSEVSNP{},
pcrs: newTestPCRs(),
clusterID: one64,
},
"azure update both": {
provider: cloudprovider.Azure,
variant: oid.AzureSEVSNP{},
pcrs: newTestPCRs(),
ownerID: one64,
clusterID: one64,
},
"owner ID and cluster ID empty": {
provider: cloudprovider.GCP,
pcrs: newTestPCRs(),
variant: oid.GCPSEVES{},
pcrs: newTestPCRs(),
},
"invalid encoding": {
provider: cloudprovider.GCP,
pcrs: newTestPCRs(),
ownerID: "invalid",
wantErr: true,
variant: oid.GCPSEVES{},
pcrs: newTestPCRs(),
ownerID: "invalid",
wantErr: true,
},
}
@ -260,7 +285,7 @@ func TestValidatorUpdateInitPCRs(t *testing.T) {
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
validators := &Validator{provider: tc.provider, pcrs: tc.pcrs}
validators := &Validator{attestationVariant: tc.variant, pcrs: tc.pcrs}
err := validators.UpdateInitPCRs(tc.ownerID, tc.clusterID)
@ -392,8 +417,8 @@ func TestUpdatePCR(t *testing.T) {
}
validators := &Validator{
provider: cloudprovider.GCP,
pcrs: pcrs,
attestationVariant: oid.GCPSEVES{},
pcrs: pcrs,
}
err := validators.updatePCR(tc.pcrIndex, tc.encoded)