mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-10-01 01:36:09 -04:00
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:
parent
8679988b6c
commit
6ea5588bdc
@ -21,7 +21,7 @@ type clusterFake struct{}
|
||||
|
||||
// InitCluster fakes bootstrapping a new cluster with the current node being the master, returning the arguments required to join the cluster.
|
||||
func (c *clusterFake) InitCluster(
|
||||
context.Context, string, string, string, []byte, []uint32, bool, bool,
|
||||
context.Context, string, string, string, []byte, []uint32, bool,
|
||||
[]byte, bool, components.Components, *logger.Logger,
|
||||
) ([]byte, error) {
|
||||
return []byte{}, nil
|
||||
|
@ -11,7 +11,6 @@ go_library(
|
||||
"//bootstrapper/internal/diskencryption",
|
||||
"//internal/atls",
|
||||
"//internal/attestation",
|
||||
"//internal/attestation/azure/snp",
|
||||
"//internal/crypto",
|
||||
"//internal/file",
|
||||
"//internal/grpc/atlscredentials",
|
||||
|
@ -29,7 +29,6 @@ import (
|
||||
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/diskencryption"
|
||||
"github.com/edgelesssys/constellation/v2/internal/atls"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/azure/snp"
|
||||
"github.com/edgelesssys/constellation/v2/internal/crypto"
|
||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||
"github.com/edgelesssys/constellation/v2/internal/grpc/atlscredentials"
|
||||
@ -168,9 +167,6 @@ func (s *Server) Init(ctx context.Context, req *initproto.InitRequest) (*initpro
|
||||
return nil, status.Errorf(codes.Internal, "persisting node state: %s", err)
|
||||
}
|
||||
|
||||
// Check if we are running on a CVM
|
||||
_, isCVM := s.issuer.(*snp.Issuer)
|
||||
|
||||
clusterName := req.ClusterName
|
||||
if clusterName == "" {
|
||||
clusterName = "constellation"
|
||||
@ -183,7 +179,6 @@ func (s *Server) Init(ctx context.Context, req *initproto.InitRequest) (*initpro
|
||||
measurementSalt,
|
||||
req.EnforcedPcrs,
|
||||
req.EnforceIdkeydigest,
|
||||
isCVM,
|
||||
req.HelmDeployments,
|
||||
req.ConformanceMode,
|
||||
components.NewComponentsFromInitProto(req.KubernetesComponents),
|
||||
@ -260,7 +255,6 @@ type ClusterInitializer interface {
|
||||
measurementSalt []byte,
|
||||
enforcedPcrs []uint32,
|
||||
enforceIDKeyDigest bool,
|
||||
azureCVM bool,
|
||||
helmDeployments []byte,
|
||||
conformanceMode bool,
|
||||
kubernetesComponents components.Components,
|
||||
|
@ -320,7 +320,7 @@ type stubClusterInitializer struct {
|
||||
}
|
||||
|
||||
func (i *stubClusterInitializer) InitCluster(
|
||||
context.Context, string, string, string, []byte, []uint32, bool, bool,
|
||||
context.Context, string, string, string, []byte, []uint32, bool,
|
||||
[]byte, bool, components.Components, *logger.Logger,
|
||||
) ([]byte, error) {
|
||||
return i.initClusterKubeconfig, i.initClusterErr
|
||||
|
@ -15,7 +15,6 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -82,7 +81,7 @@ func New(cloudProvider string, clusterUtil clusterUtil, configProvider configura
|
||||
// InitCluster initializes a new Kubernetes cluster and applies pod network provider.
|
||||
func (k *KubeWrapper) InitCluster(
|
||||
ctx context.Context, cloudServiceAccountURI, versionString, clusterName string,
|
||||
measurementSalt []byte, enforcedPCRs []uint32, enforceIDKeyDigest bool, azureCVM bool,
|
||||
measurementSalt []byte, enforcedPCRs []uint32, enforceIDKeyDigest bool,
|
||||
helmReleasesRaw []byte, conformanceMode bool, kubernetesComponents components.Components, log *logger.Logger,
|
||||
) ([]byte, error) {
|
||||
log.With(zap.String("version", versionString)).Infof("Installing Kubernetes components")
|
||||
@ -222,7 +221,7 @@ func (k *KubeWrapper) InitCluster(
|
||||
return nil, fmt.Errorf("installing constellation-services: %w", err)
|
||||
}
|
||||
|
||||
if err := k.setupInternalConfigMap(ctx, strconv.FormatBool(azureCVM)); err != nil {
|
||||
if err := k.setupInternalConfigMap(ctx); err != nil {
|
||||
return nil, fmt.Errorf("failed to setup internal ConfigMap: %w", err)
|
||||
}
|
||||
|
||||
@ -319,7 +318,7 @@ func (k *KubeWrapper) setupK8sComponentsConfigMap(ctx context.Context, component
|
||||
}
|
||||
|
||||
// setupInternalConfigMap applies a ConfigMap (cf. server-side apply) to store information that is not supposed to be user-editable.
|
||||
func (k *KubeWrapper) setupInternalConfigMap(ctx context.Context, azureCVM string) error {
|
||||
func (k *KubeWrapper) setupInternalConfigMap(ctx context.Context) error {
|
||||
config := corev1.ConfigMap{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
@ -329,9 +328,7 @@ func (k *KubeWrapper) setupInternalConfigMap(ctx context.Context, azureCVM strin
|
||||
Name: constants.InternalConfigMap,
|
||||
Namespace: "kube-system",
|
||||
},
|
||||
Data: map[string]string{
|
||||
constants.AzureCVM: azureCVM,
|
||||
},
|
||||
Data: map[string]string{},
|
||||
}
|
||||
|
||||
// We do not use the client's Apply method here since we are handling a kubernetes-native type.
|
||||
|
@ -216,7 +216,7 @@ func TestInitCluster(t *testing.T) {
|
||||
|
||||
_, err := kube.InitCluster(
|
||||
context.Background(), serviceAccountURI, string(tc.k8sVersion), "kubernetes",
|
||||
nil, nil, false, true, []byte("{}"), false, nil, logger.NewTest(t),
|
||||
nil, nil, false, []byte("{}"), false, nil, logger.NewTest(t),
|
||||
)
|
||||
|
||||
if tc.wantErr {
|
||||
|
@ -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",
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
||||
|
@ -59,6 +59,7 @@ go_library(
|
||||
"//internal/kubernetes/kubectl",
|
||||
"//internal/license",
|
||||
"//internal/logger",
|
||||
"//internal/oid",
|
||||
"//internal/retry",
|
||||
"//internal/sigstore",
|
||||
"//internal/versions",
|
||||
|
@ -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/oid"
|
||||
"github.com/edgelesssys/constellation/v2/internal/versions"
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/encoder"
|
||||
"github.com/spf13/afero"
|
||||
@ -105,6 +106,18 @@ func createConfig(provider cloudprovider.Provider) *config.Config {
|
||||
conf.StateDiskSizeGB = 10
|
||||
}
|
||||
|
||||
// TODO(AB#2976): Replace hardcoded values with user input
|
||||
switch provider {
|
||||
case cloudprovider.AWS:
|
||||
conf.AttestationVariant = oid.AWSNitroTPM{}.String()
|
||||
case cloudprovider.Azure:
|
||||
conf.AttestationVariant = oid.AzureSEVSNP{}.String()
|
||||
case cloudprovider.GCP:
|
||||
conf.AttestationVariant = oid.GCPSEVES{}.String()
|
||||
case cloudprovider.QEMU:
|
||||
conf.AttestationVariant = oid.QEMUVTPM{}.String()
|
||||
}
|
||||
|
||||
return conf
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ 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/oid"
|
||||
"github.com/edgelesssys/constellation/v2/internal/versions"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -92,6 +93,9 @@ func TestConfigGenerateDefaultGCPSpecific(t *testing.T) {
|
||||
cg := &configGenerateCmd{log: logger.NewTest(t)}
|
||||
require.NoError(cg.configGenerate(cmd, fileHandler, cloudprovider.GCP))
|
||||
|
||||
// TODO(AB#2976): Remove this once attestation variants are dynamically created
|
||||
wantConf.AttestationVariant = oid.GCPSEVES{}.String()
|
||||
|
||||
var readConfig config.Config
|
||||
err := fileHandler.ReadYAML(constants.ConfigFilename, &readConfig)
|
||||
assert.NoError(err)
|
||||
|
@ -135,7 +135,7 @@ func (i *initCmd) initialize(cmd *cobra.Command, newDialer func(validator *cloud
|
||||
cmd.PrintErrf("License check failed: %v", err)
|
||||
}
|
||||
i.log.Debugf("Checked license")
|
||||
validator, err := cloudcmd.NewValidator(provider, conf, i.log)
|
||||
validator, err := cloudcmd.NewValidator(conf, i.log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -436,6 +436,7 @@ func TestAttestation(t *testing.T) {
|
||||
|
||||
cfg := config.Default()
|
||||
cfg.Image = "image"
|
||||
cfg.AttestationVariant = oid.QEMUVTPM{}.String()
|
||||
cfg.RemoveProviderExcept(cloudprovider.QEMU)
|
||||
cfg.Provider.QEMU.Measurements[0] = measurements.WithAllBytes(0x00, false)
|
||||
cfg.Provider.QEMU.Measurements[1] = measurements.WithAllBytes(0x11, false)
|
||||
@ -529,6 +530,7 @@ func defaultConfigWithExpectedMeasurements(t *testing.T, conf *config.Config, cs
|
||||
|
||||
switch csp {
|
||||
case cloudprovider.Azure:
|
||||
conf.AttestationVariant = oid.AzureSEVSNP{}.String()
|
||||
conf.Provider.Azure.SubscriptionID = "01234567-0123-0123-0123-0123456789ab"
|
||||
conf.Provider.Azure.TenantID = "01234567-0123-0123-0123-0123456789ab"
|
||||
conf.Provider.Azure.Location = "test-location"
|
||||
@ -540,6 +542,7 @@ func defaultConfigWithExpectedMeasurements(t *testing.T, conf *config.Config, cs
|
||||
conf.Provider.Azure.Measurements[9] = measurements.WithAllBytes(0x11, false)
|
||||
conf.Provider.Azure.Measurements[12] = measurements.WithAllBytes(0xcc, false)
|
||||
case cloudprovider.GCP:
|
||||
conf.AttestationVariant = oid.GCPSEVES{}.String()
|
||||
conf.Provider.GCP.Region = "test-region"
|
||||
conf.Provider.GCP.Project = "test-project"
|
||||
conf.Provider.GCP.Zone = "test-zone"
|
||||
@ -548,6 +551,7 @@ func defaultConfigWithExpectedMeasurements(t *testing.T, conf *config.Config, cs
|
||||
conf.Provider.GCP.Measurements[9] = measurements.WithAllBytes(0x11, false)
|
||||
conf.Provider.GCP.Measurements[12] = measurements.WithAllBytes(0xcc, false)
|
||||
case cloudprovider.QEMU:
|
||||
conf.AttestationVariant = oid.QEMUVTPM{}.String()
|
||||
conf.Provider.QEMU.Measurements[4] = measurements.WithAllBytes(0x44, false)
|
||||
conf.Provider.QEMU.Measurements[9] = measurements.WithAllBytes(0x11, false)
|
||||
conf.Provider.QEMU.Measurements[12] = measurements.WithAllBytes(0xcc, false)
|
||||
|
@ -95,7 +95,7 @@ func (r *recoverCmd) recover(
|
||||
interval = 20 * time.Second // Azure LB takes a while to remove unhealthy instances
|
||||
}
|
||||
|
||||
validator, err := cloudcmd.NewValidator(provider, conf, r.log)
|
||||
validator, err := cloudcmd.NewValidator(conf, r.log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ func (v *verifyCmd) verify(cmd *cobra.Command, fileHandler file.Handler, verifyC
|
||||
|
||||
provider := conf.GetProvider()
|
||||
v.log.Debugf("Creating aTLS Validator for %s", provider)
|
||||
validators, err := cloudcmd.NewValidator(provider, conf, v.log)
|
||||
validators, err := cloudcmd.NewValidator(conf, v.log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -357,6 +357,7 @@ go_test(
|
||||
"//internal/deploy/helm",
|
||||
"//internal/file",
|
||||
"//internal/logger",
|
||||
"//internal/oid",
|
||||
"@com_github_pkg_errors//:errors",
|
||||
"@com_github_spf13_afero//:afero",
|
||||
"@com_github_stretchr_testify//assert",
|
||||
|
@ -39,6 +39,7 @@ spec:
|
||||
args:
|
||||
- --cloud-provider={{ .Values.csp }}
|
||||
- --key-service-endpoint=key-service.{{ .Release.Namespace }}:{{ .Values.global.keyServicePort }}
|
||||
- --attestation-variant={{ .Values.attestationVariant }}
|
||||
volumeMounts:
|
||||
- mountPath: {{ .Values.global.serviceBasePath | quote }}
|
||||
name: config
|
||||
|
@ -28,13 +28,19 @@
|
||||
"description": "Salt used to generate node measurements",
|
||||
"type": "string",
|
||||
"examples": ["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"]
|
||||
},
|
||||
"attestationVariant": {
|
||||
"description": "Attestation variant to use for aTLS connections.",
|
||||
"type": "string",
|
||||
"examples": ["azure-sev-snp", "azure-trusted-launch", "gcp-sev-es"]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"csp",
|
||||
"measurements",
|
||||
"measurementSalt",
|
||||
"image"
|
||||
"image",
|
||||
"attestationVariant"
|
||||
],
|
||||
"if": {
|
||||
"properties": { "csp": { "const": "azure" } },
|
||||
|
@ -1,3 +1,4 @@
|
||||
csp: "gcp"
|
||||
attestationVariant: ""
|
||||
joinServicePort: 9090
|
||||
joinServiceNodePort: 30090
|
||||
|
@ -17,7 +17,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
- --cloud-provider={{ .Values.csp }}
|
||||
- --attestation-variant={{ .Values.attestationVariant }}
|
||||
image: {{ .Values.image | quote }}
|
||||
name: verification-service
|
||||
ports:
|
||||
|
@ -1,10 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft-07/schema#",
|
||||
"properties": {
|
||||
"csp": {
|
||||
"description": "CSP to which the chart is deployed.",
|
||||
"enum": ["Azure", "GCP", "AWS", "QEMU"]
|
||||
},
|
||||
"image": {
|
||||
"description": "Container image to use for the spawned pods.",
|
||||
"type": "string",
|
||||
@ -13,12 +9,17 @@
|
||||
"loadBalancerIP": {
|
||||
"description": "IP of the k8s LB service",
|
||||
"type": "string"
|
||||
},
|
||||
"attestationVariant": {
|
||||
"description": "Attestation variant to use for aTLS connections.",
|
||||
"type": "string",
|
||||
"examples": ["azure-sev-snp", "azure-trusted-launch", "gcp-sev-es"]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"csp",
|
||||
"image",
|
||||
"loadBalancerIP"
|
||||
"loadBalancerIP",
|
||||
"attestationVariant"
|
||||
],
|
||||
"title": "Values",
|
||||
"type": "object"
|
||||
|
@ -1,3 +1,5 @@
|
||||
image: ""
|
||||
attestationVariant: ""
|
||||
httpContainerPort: 8080
|
||||
grpcContainerPort: 9090
|
||||
httpNodePort: 30080
|
||||
|
@ -415,7 +415,6 @@ func (i *ChartLoader) loadConstellationServicesValues() (map[string]any, error)
|
||||
"image": i.autoscalerImage,
|
||||
},
|
||||
"verification-service": map[string]any{
|
||||
"csp": i.csp.String(),
|
||||
"image": i.verificationServiceImage,
|
||||
},
|
||||
"gcp-guest-agent": map[string]any{
|
||||
@ -491,6 +490,18 @@ func extendConstellationServicesValues(in map[string]any, config *config.Config,
|
||||
keyServiceValues["masterSecret"] = base64.StdEncoding.EncodeToString(masterSecret)
|
||||
keyServiceValues["salt"] = base64.StdEncoding.EncodeToString(salt)
|
||||
|
||||
joinServiceVals, ok := in["join-service"].(map[string]any)
|
||||
if !ok {
|
||||
return errors.New("invalid join-service values")
|
||||
}
|
||||
joinServiceVals["attestationVariant"] = config.AttestationVariant
|
||||
|
||||
verifyServiceVals, ok := in["verification-service"].(map[string]any)
|
||||
if !ok {
|
||||
return errors.New("invalid verification-service values")
|
||||
}
|
||||
verifyServiceVals["attestationVariant"] = config.AttestationVariant
|
||||
|
||||
csp := config.GetProvider()
|
||||
switch csp {
|
||||
case cloudprovider.Azure:
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||
"github.com/edgelesssys/constellation/v2/internal/deploy/helm"
|
||||
"github.com/edgelesssys/constellation/v2/internal/oid"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -59,25 +60,34 @@ func TestConstellationServices(t *testing.T) {
|
||||
cnmImage string
|
||||
}{
|
||||
"GCP": {
|
||||
config: &config.Config{Provider: config.ProviderConfig{GCP: &config.GCPConfig{
|
||||
DeployCSIDriver: func() *bool { b := true; return &b }(),
|
||||
}}},
|
||||
config: &config.Config{
|
||||
AttestationVariant: oid.GCPSEVES{}.String(),
|
||||
Provider: config.ProviderConfig{GCP: &config.GCPConfig{
|
||||
DeployCSIDriver: toPtr(true),
|
||||
}},
|
||||
},
|
||||
enforceIDKeyDigest: false,
|
||||
valuesModifier: prepareGCPValues,
|
||||
ccmImage: "ccmImageForGCP",
|
||||
},
|
||||
"Azure": {
|
||||
config: &config.Config{Provider: config.ProviderConfig{Azure: &config.AzureConfig{
|
||||
DeployCSIDriver: func() *bool { b := true; return &b }(),
|
||||
EnforceIDKeyDigest: func() *bool { b := true; return &b }(),
|
||||
}}},
|
||||
config: &config.Config{
|
||||
AttestationVariant: oid.AzureSEVSNP{}.String(),
|
||||
Provider: config.ProviderConfig{Azure: &config.AzureConfig{
|
||||
DeployCSIDriver: toPtr(true),
|
||||
EnforceIDKeyDigest: toPtr(true),
|
||||
}},
|
||||
},
|
||||
enforceIDKeyDigest: true,
|
||||
valuesModifier: prepareAzureValues,
|
||||
ccmImage: "ccmImageForAzure",
|
||||
cnmImage: "cnmImageForAzure",
|
||||
},
|
||||
"QEMU": {
|
||||
config: &config.Config{Provider: config.ProviderConfig{QEMU: &config.QEMUConfig{}}},
|
||||
config: &config.Config{
|
||||
AttestationVariant: oid.QEMUVTPM{}.String(),
|
||||
Provider: config.ProviderConfig{QEMU: &config.QEMUConfig{}},
|
||||
},
|
||||
enforceIDKeyDigest: false,
|
||||
valuesModifier: prepareQEMUValues,
|
||||
},
|
||||
@ -430,3 +440,7 @@ func prepareQEMUValues(values map[string]any) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func toPtr[T any](v T) *T {
|
||||
return &v
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ spec:
|
||||
args:
|
||||
- --cloud-provider=Azure
|
||||
- --key-service-endpoint=key-service.testNamespace:9000
|
||||
- --attestation-variant=azure-sev-snp
|
||||
volumeMounts:
|
||||
- mountPath: /var/config
|
||||
name: config
|
||||
|
@ -17,7 +17,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
- --cloud-provider=Azure
|
||||
- --attestation-variant=azure-sev-snp
|
||||
image: verificationImage
|
||||
name: verification-service
|
||||
ports:
|
||||
|
@ -39,6 +39,7 @@ spec:
|
||||
args:
|
||||
- --cloud-provider=GCP
|
||||
- --key-service-endpoint=key-service.testNamespace:9000
|
||||
- --attestation-variant=gcp-sev-es
|
||||
volumeMounts:
|
||||
- mountPath: /var/config
|
||||
name: config
|
||||
|
@ -17,7 +17,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
- --cloud-provider=GCP
|
||||
- --attestation-variant=gcp-sev-es
|
||||
image: verificationImage
|
||||
name: verification-service
|
||||
ports:
|
||||
|
@ -39,6 +39,7 @@ spec:
|
||||
args:
|
||||
- --cloud-provider=QEMU
|
||||
- --key-service-endpoint=key-service.testNamespace:9000
|
||||
- --attestation-variant=qemu-vtpm
|
||||
volumeMounts:
|
||||
- mountPath: /var/config
|
||||
name: config
|
||||
|
@ -17,7 +17,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
- --cloud-provider=QEMU
|
||||
- --attestation-variant=qemu-vtpm
|
||||
image: verificationImage
|
||||
name: verification-service
|
||||
ports:
|
||||
|
@ -22,6 +22,7 @@ go_library(
|
||||
"//internal/config/instancetypes",
|
||||
"//internal/constants",
|
||||
"//internal/file",
|
||||
"//internal/oid",
|
||||
"//internal/versions",
|
||||
"//internal/versionsapi",
|
||||
"@com_github_go_playground_locales//en",
|
||||
@ -48,6 +49,7 @@ go_test(
|
||||
"//internal/config/instancetypes",
|
||||
"//internal/constants",
|
||||
"//internal/file",
|
||||
"//internal/oid",
|
||||
"@com_github_go_playground_locales//en",
|
||||
"@com_github_go_playground_universal_translator//:universal-translator",
|
||||
"@com_github_go_playground_validator_v10//:validator",
|
||||
|
@ -77,6 +77,9 @@ type Config struct {
|
||||
// DON'T USE IN PRODUCTION: enable debug mode and use debug images. For usage, see: https://github.com/edgelesssys/constellation/blob/main/debugd/README.md
|
||||
DebugCluster *bool `yaml:"debugCluster" validate:"required"`
|
||||
// description: |
|
||||
// Attestation variant used to verify the integrity of a node.
|
||||
AttestationVariant string `yaml:"attestationVariant" validate:"valid_attestation_variant"` // TODO: v2.8: Mark required
|
||||
// description: |
|
||||
// Supported cloud providers and their specific configurations.
|
||||
Provider ProviderConfig `yaml:"provider" validate:"dive"`
|
||||
// description: |
|
||||
@ -573,10 +576,15 @@ func (c *Config) Validate(force bool) error {
|
||||
if err := validate.RegisterTranslation("version_compatibility", trans, registerVersionCompatibilityError, translateVersionCompatibilityError); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validate.RegisterTranslation("valid_name", trans, registerValidateNameError, c.translateValidateNameError); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validate.RegisterTranslation("valid_attestation_variant", trans, registerValidAttestVariantError, c.translateValidAttestVariantError); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validate.RegisterValidation("valid_name", c.validateName); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -613,6 +621,10 @@ func (c *Config) Validate(force bool) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validate.RegisterValidation("valid_attestation_variant", c.validAttestVariant); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Register provider validation
|
||||
validate.RegisterStructValidation(validateProvider, ProviderConfig{})
|
||||
|
||||
|
@ -25,7 +25,7 @@ func init() {
|
||||
ConfigDoc.Type = "Config"
|
||||
ConfigDoc.Comments[encoder.LineComment] = "Config defines configuration used by CLI."
|
||||
ConfigDoc.Description = "Config defines configuration used by CLI."
|
||||
ConfigDoc.Fields = make([]encoder.Doc, 9)
|
||||
ConfigDoc.Fields = make([]encoder.Doc, 10)
|
||||
ConfigDoc.Fields[0].Name = "version"
|
||||
ConfigDoc.Fields[0].Type = "string"
|
||||
ConfigDoc.Fields[0].Note = ""
|
||||
@ -61,18 +61,23 @@ func init() {
|
||||
ConfigDoc.Fields[6].Note = ""
|
||||
ConfigDoc.Fields[6].Description = "DON'T USE IN PRODUCTION: enable debug mode and use debug images. For usage, see: https://github.com/edgelesssys/constellation/blob/main/debugd/README.md"
|
||||
ConfigDoc.Fields[6].Comments[encoder.LineComment] = "DON'T USE IN PRODUCTION: enable debug mode and use debug images. For usage, see: https://github.com/edgelesssys/constellation/blob/main/debugd/README.md"
|
||||
ConfigDoc.Fields[7].Name = "provider"
|
||||
ConfigDoc.Fields[7].Type = "ProviderConfig"
|
||||
ConfigDoc.Fields[7].Note = ""
|
||||
ConfigDoc.Fields[7].Description = "Supported cloud providers and their specific configurations."
|
||||
ConfigDoc.Fields[7].Comments[encoder.LineComment] = "Supported cloud providers and their specific configurations."
|
||||
ConfigDoc.Fields[8].Name = "upgrade"
|
||||
ConfigDoc.Fields[8].Type = "UpgradeConfig"
|
||||
ConfigDoc.Fields[7].Name = "attestationVariant"
|
||||
ConfigDoc.Fields[7].Type = "string"
|
||||
ConfigDoc.Fields[7].Note = "TODO: v2.8: Mark required\n"
|
||||
ConfigDoc.Fields[7].Description = "Attestation variant used to verify the integrity of a node."
|
||||
ConfigDoc.Fields[7].Comments[encoder.LineComment] = "Attestation variant used to verify the integrity of a node."
|
||||
ConfigDoc.Fields[8].Name = "provider"
|
||||
ConfigDoc.Fields[8].Type = "ProviderConfig"
|
||||
ConfigDoc.Fields[8].Note = ""
|
||||
ConfigDoc.Fields[8].Description = "Configuration to apply during constellation upgrade."
|
||||
ConfigDoc.Fields[8].Comments[encoder.LineComment] = "Configuration to apply during constellation upgrade."
|
||||
ConfigDoc.Fields[8].Description = "Supported cloud providers and their specific configurations."
|
||||
ConfigDoc.Fields[8].Comments[encoder.LineComment] = "Supported cloud providers and their specific configurations."
|
||||
ConfigDoc.Fields[9].Name = "upgrade"
|
||||
ConfigDoc.Fields[9].Type = "UpgradeConfig"
|
||||
ConfigDoc.Fields[9].Note = ""
|
||||
ConfigDoc.Fields[9].Description = "Configuration to apply during constellation upgrade."
|
||||
ConfigDoc.Fields[9].Comments[encoder.LineComment] = "Configuration to apply during constellation upgrade."
|
||||
|
||||
ConfigDoc.Fields[8].AddExample("", UpgradeConfig{Image: "", Measurements: Measurements{}})
|
||||
ConfigDoc.Fields[9].AddExample("", UpgradeConfig{Image: "", Measurements: Measurements{}})
|
||||
|
||||
UpgradeConfigDoc.Type = "UpgradeConfig"
|
||||
UpgradeConfigDoc.Comments[encoder.LineComment] = "UpgradeConfig defines configuration used during constellation upgrade."
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
"github.com/edgelesssys/constellation/v2/internal/config/instancetypes"
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||
"github.com/edgelesssys/constellation/v2/internal/oid"
|
||||
"github.com/go-playground/locales/en"
|
||||
ut "github.com/go-playground/universal-translator"
|
||||
"github.com/go-playground/validator/v10"
|
||||
@ -122,6 +123,7 @@ func TestNewWithDefaultOptions(t *testing.T) {
|
||||
c := Default()
|
||||
c.RemoveProviderExcept(cloudprovider.Azure)
|
||||
c.Image = "v" + constants.VersionInfo()
|
||||
c.AttestationVariant = oid.AzureSEVSNP{}.String()
|
||||
c.Provider.Azure.SubscriptionID = "f4278079-288c-4766-a98c-ab9d5dba01a5"
|
||||
c.Provider.Azure.TenantID = "d4ff9d63-6d6d-4042-8f6a-21e804add5aa"
|
||||
c.Provider.Azure.Location = "westus"
|
||||
@ -141,6 +143,7 @@ func TestNewWithDefaultOptions(t *testing.T) {
|
||||
c := Default()
|
||||
c.RemoveProviderExcept(cloudprovider.Azure)
|
||||
c.Image = "v" + constants.VersionInfo()
|
||||
c.AttestationVariant = oid.AzureSEVSNP{}.String()
|
||||
c.Provider.Azure.SubscriptionID = "f4278079-288c-4766-a98c-ab9d5dba01a5"
|
||||
c.Provider.Azure.TenantID = "d4ff9d63-6d6d-4042-8f6a-21e804add5aa"
|
||||
c.Provider.Azure.Location = "westus"
|
||||
@ -232,6 +235,7 @@ func TestValidate(t *testing.T) {
|
||||
cnf: func() *Config {
|
||||
cnf := Default()
|
||||
cnf.Image = "v" + constants.VersionInfo()
|
||||
cnf.AttestationVariant = oid.AzureSEVSNP{}.String()
|
||||
az := cnf.Provider.Azure
|
||||
az.SubscriptionID = "01234567-0123-0123-0123-0123456789ab"
|
||||
az.TenantID = "01234567-0123-0123-0123-0123456789ab"
|
||||
@ -261,6 +265,7 @@ func TestValidate(t *testing.T) {
|
||||
cnf: func() *Config {
|
||||
cnf := Default()
|
||||
cnf.Image = "v" + constants.VersionInfo()
|
||||
cnf.AttestationVariant = oid.GCPSEVES{}.String()
|
||||
gcp := cnf.Provider.GCP
|
||||
gcp.Region = "test-region"
|
||||
gcp.Project = "test-project"
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"github.com/edgelesssys/constellation/v2/internal/compatibility"
|
||||
"github.com/edgelesssys/constellation/v2/internal/config/instancetypes"
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
"github.com/edgelesssys/constellation/v2/internal/oid"
|
||||
"github.com/edgelesssys/constellation/v2/internal/versions"
|
||||
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
|
||||
ut "github.com/go-playground/universal-translator"
|
||||
@ -466,3 +467,55 @@ func (c *Config) validateName(fl validator.FieldLevel) bool {
|
||||
}
|
||||
return len(fl.Field().String()) <= constants.ConstellationNameLength
|
||||
}
|
||||
|
||||
func registerValidAttestVariantError(ut ut.Translator) error {
|
||||
return ut.Add("valid_attestation_variant", `"{0}" is not a valid attestation variant for CSP {1}`, true)
|
||||
}
|
||||
|
||||
func (c *Config) translateValidAttestVariantError(ut ut.Translator, fe validator.FieldError) string {
|
||||
csp := c.GetProvider()
|
||||
t, _ := ut.T("valid_attestation_variant", c.AttestationVariant, csp.String())
|
||||
return t
|
||||
}
|
||||
|
||||
func (c *Config) validAttestVariant(fl validator.FieldLevel) bool {
|
||||
// TODO: v2.8: remove variant fallback and make variant a required field
|
||||
c.addMissingVariant()
|
||||
|
||||
variant, err := oid.FromString(c.AttestationVariant)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// make sure the variant is valid for the chosen CSP
|
||||
switch variant {
|
||||
case oid.AWSNitroTPM{}:
|
||||
return c.Provider.AWS != nil
|
||||
case oid.AzureSEVSNP{}, oid.AzureTrustedLaunch{}:
|
||||
return c.Provider.Azure != nil
|
||||
case oid.GCPSEVES{}:
|
||||
return c.Provider.GCP != nil
|
||||
case oid.QEMUVTPM{}:
|
||||
return c.Provider.QEMU != nil
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) addMissingVariant() {
|
||||
if c.AttestationVariant != "" {
|
||||
return
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "WARNING: the config key `attestationVariant` is not set. This key will be required in the next version.")
|
||||
|
||||
switch c.GetProvider() {
|
||||
case cloudprovider.AWS:
|
||||
c.AttestationVariant = oid.AWSNitroTPM{}.String()
|
||||
case cloudprovider.Azure:
|
||||
c.AttestationVariant = oid.AzureTrustedLaunch{}.String()
|
||||
case cloudprovider.GCP:
|
||||
c.AttestationVariant = oid.GCPSEVES{}.String()
|
||||
case cloudprovider.QEMU:
|
||||
c.AttestationVariant = oid.QEMUVTPM{}.String()
|
||||
}
|
||||
}
|
||||
|
@ -108,8 +108,6 @@ const (
|
||||
ServiceBasePath = "/var/config"
|
||||
// MeasurementsFilename is the filename of CC measurements.
|
||||
MeasurementsFilename = "measurements"
|
||||
// EnforcedPCRsFilename is the filename for a list PCRs that are required to pass attestation.
|
||||
EnforcedPCRsFilename = "enforcedPCRs"
|
||||
// MeasurementSaltFilename is the filename of the salt used in creation of the clusterID.
|
||||
MeasurementSaltFilename = "measurementSalt"
|
||||
// MeasurementSecretFilename is the filename of the secret used in creation of the clusterID.
|
||||
@ -118,8 +116,6 @@ const (
|
||||
IDKeyDigestFilename = "idkeydigests"
|
||||
// EnforceIDKeyDigestFilename is the name of the file configuring whether idkeydigest is enforced or not.
|
||||
EnforceIDKeyDigestFilename = "enforceIdKeyDigest"
|
||||
// AzureCVM is the name of the file indicating whether the cluster is expected to run on CVMs or not.
|
||||
AzureCVM = "azureCVM"
|
||||
|
||||
// K8sVersionFieldName is the name of the of the key holding the wanted Kubernetes version.
|
||||
K8sVersionFieldName = "cluster-version"
|
||||
|
@ -11,17 +11,13 @@ go_library(
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = [
|
||||
"//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/constants",
|
||||
"//internal/file",
|
||||
"//internal/logger",
|
||||
"//internal/oid",
|
||||
"@com_github_fsnotify_fsnotify//:fsnotify",
|
||||
"@org_uber_go_zap//:zap",
|
||||
],
|
||||
@ -38,9 +34,11 @@ go_test(
|
||||
"//internal/atls",
|
||||
"//internal/attestation/idkeydigest",
|
||||
"//internal/attestation/measurements",
|
||||
"//internal/cloud/cloudprovider",
|
||||
"//internal/constants",
|
||||
"//internal/file",
|
||||
"//internal/logger",
|
||||
"//internal/oid",
|
||||
"@com_github_fsnotify_fsnotify//:fsnotify",
|
||||
"@com_github_spf13_afero//:afero",
|
||||
"@com_github_stretchr_testify//assert",
|
||||
|
@ -9,74 +9,36 @@ package watcher
|
||||
import (
|
||||
"encoding/asn1"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"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/constants"
|
||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
"github.com/edgelesssys/constellation/v2/internal/oid"
|
||||
)
|
||||
|
||||
// Updatable implements an updatable atls.Validator.
|
||||
type Updatable struct {
|
||||
log *logger.Logger
|
||||
mux sync.Mutex
|
||||
newValidator newValidatorFunc
|
||||
fileHandler file.Handler
|
||||
csp cloudprovider.Provider
|
||||
azureCVM bool
|
||||
log *logger.Logger
|
||||
mux sync.Mutex
|
||||
fileHandler file.Handler
|
||||
variant oid.Getter
|
||||
atls.Validator
|
||||
}
|
||||
|
||||
// NewValidator initializes a new updatable validator.
|
||||
func NewValidator(log *logger.Logger, csp string, fileHandler file.Handler, azureCVM bool) (*Updatable, error) {
|
||||
var newValidator newValidatorFunc
|
||||
switch cloudprovider.FromString(csp) {
|
||||
case cloudprovider.AWS:
|
||||
newValidator = func(m measurements.M, _ idkeydigest.IDKeyDigests, _ bool, log *logger.Logger) atls.Validator {
|
||||
return aws.NewValidator(m, log)
|
||||
}
|
||||
case cloudprovider.Azure:
|
||||
if azureCVM {
|
||||
newValidator = func(m measurements.M, idkeydigest idkeydigest.IDKeyDigests, enforceIdKeyDigest bool, log *logger.Logger) atls.Validator {
|
||||
return snp.NewValidator(m, idkeydigest, enforceIdKeyDigest, log)
|
||||
}
|
||||
} else {
|
||||
newValidator = func(m measurements.M, idkeydigest idkeydigest.IDKeyDigests, enforceIdKeyDigest bool, log *logger.Logger) atls.Validator {
|
||||
return trustedlaunch.NewValidator(m, log)
|
||||
}
|
||||
}
|
||||
case cloudprovider.GCP:
|
||||
newValidator = func(m measurements.M, _ idkeydigest.IDKeyDigests, _ bool, log *logger.Logger) atls.Validator {
|
||||
return gcp.NewValidator(m, log)
|
||||
}
|
||||
case cloudprovider.QEMU:
|
||||
newValidator = func(m measurements.M, _ idkeydigest.IDKeyDigests, _ bool, log *logger.Logger) atls.Validator {
|
||||
return qemu.NewValidator(m, log)
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown cloud service provider: %q", csp)
|
||||
}
|
||||
|
||||
func NewValidator(log *logger.Logger, variant oid.Getter, fileHandler file.Handler) (*Updatable, error) {
|
||||
u := &Updatable{
|
||||
log: log,
|
||||
newValidator: newValidator,
|
||||
fileHandler: fileHandler,
|
||||
csp: cloudprovider.FromString(csp),
|
||||
azureCVM: azureCVM,
|
||||
log: log,
|
||||
fileHandler: fileHandler,
|
||||
variant: variant,
|
||||
}
|
||||
|
||||
if err := u.Update(); err != nil {
|
||||
@ -112,22 +74,9 @@ func (u *Updatable) Update() error {
|
||||
}
|
||||
u.log.Debugf("New measurements: %+v", measurements)
|
||||
|
||||
// handle legacy measurement format, where expected measurements and enforced measurements were stored in separate data structures
|
||||
// TODO: remove with v2.4.0
|
||||
var enforced []uint32
|
||||
if err := u.fileHandler.ReadJSON(filepath.Join(constants.ServiceBasePath, constants.EnforcedPCRsFilename), &enforced); err == nil {
|
||||
u.log.Debugf("Detected legacy format. Loading enforced PCRs...")
|
||||
if err := measurements.SetEnforced(enforced); err != nil {
|
||||
return err
|
||||
}
|
||||
u.log.Debugf("Merged measurements with enforced values: %+v", measurements)
|
||||
} else if !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
|
||||
var digest idkeydigest.IDKeyDigests
|
||||
var enforceIDKeyDigest bool
|
||||
if u.csp == cloudprovider.Azure && u.azureCVM {
|
||||
if u.variant.OID().Equal(oid.AzureSEVSNP{}.OID()) {
|
||||
u.log.Infof("Updating encforceIdKeyDigest value")
|
||||
enforceRaw, err := u.fileHandler.Read(filepath.Join(constants.ServiceBasePath, constants.EnforceIDKeyDigestFilename))
|
||||
if err != nil {
|
||||
@ -150,9 +99,11 @@ func (u *Updatable) Update() error {
|
||||
u.log.Debugf("New idkeydigest: %v", digest)
|
||||
}
|
||||
|
||||
u.Validator = u.newValidator(measurements, digest, enforceIDKeyDigest, u.log)
|
||||
validator, err := choose.Validator(u.variant, measurements, digest, enforceIDKeyDigest, u.log)
|
||||
if err != nil {
|
||||
return fmt.Errorf("updating validator: %w", err)
|
||||
}
|
||||
u.Validator = validator
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type newValidatorFunc func(measurements measurements.M, idkeydigest idkeydigest.IDKeyDigests, enforceIdKeyDigest bool, log *logger.Logger) atls.Validator
|
||||
|
@ -7,11 +7,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
package watcher
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/asn1"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@ -22,9 +20,11 @@ import (
|
||||
"github.com/edgelesssys/constellation/v2/internal/atls"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"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/oid"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -40,29 +40,29 @@ func TestMain(m *testing.M) {
|
||||
|
||||
func TestNewUpdateableValidator(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
provider string
|
||||
variant oid.Getter
|
||||
writeFile bool
|
||||
wantErr bool
|
||||
}{
|
||||
"azure": {
|
||||
provider: "azure",
|
||||
variant: oid.AzureSEVSNP{},
|
||||
writeFile: true,
|
||||
},
|
||||
"gcp": {
|
||||
provider: "gcp",
|
||||
variant: oid.GCPSEVES{},
|
||||
writeFile: true,
|
||||
},
|
||||
"qemu": {
|
||||
provider: "qemu",
|
||||
variant: oid.QEMUVTPM{},
|
||||
writeFile: true,
|
||||
},
|
||||
"no file": {
|
||||
provider: "azure",
|
||||
variant: oid.AzureSEVSNP{},
|
||||
writeFile: false,
|
||||
wantErr: true,
|
||||
},
|
||||
"invalid provider": {
|
||||
provider: "invalid",
|
||||
variant: fakeOID{1, 3, 9900, 9999, 9999},
|
||||
writeFile: true,
|
||||
wantErr: true,
|
||||
},
|
||||
@ -77,33 +77,24 @@ func TestNewUpdateableValidator(t *testing.T) {
|
||||
if tc.writeFile {
|
||||
require.NoError(handler.WriteJSON(
|
||||
filepath.Join(constants.ServiceBasePath, constants.MeasurementsFilename),
|
||||
map[uint32][]byte{
|
||||
11: {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
|
||||
},
|
||||
))
|
||||
require.NoError(handler.WriteJSON(
|
||||
filepath.Join(constants.ServiceBasePath, constants.EnforcedPCRsFilename),
|
||||
[]uint32{11},
|
||||
measurements.M{11: measurements.WithAllBytes(0x00, false)},
|
||||
))
|
||||
keyDigest, err := json.Marshal(idkeydigest.DefaultsFor(cloudprovider.Azure))
|
||||
require.NoError(err)
|
||||
require.NoError(handler.Write(
|
||||
filepath.Join(constants.ServiceBasePath, constants.IDKeyDigestFilename),
|
||||
[]byte{},
|
||||
keyDigest,
|
||||
))
|
||||
require.NoError(handler.Write(
|
||||
filepath.Join(constants.ServiceBasePath, constants.EnforceIDKeyDigestFilename),
|
||||
[]byte("false"),
|
||||
))
|
||||
require.NoError(handler.Write(
|
||||
filepath.Join(constants.ServiceBasePath, constants.AzureCVM),
|
||||
[]byte("true"),
|
||||
))
|
||||
}
|
||||
|
||||
_, err := NewValidator(
|
||||
logger.NewTest(t),
|
||||
tc.provider,
|
||||
tc.variant,
|
||||
handler,
|
||||
false,
|
||||
)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
@ -118,26 +109,13 @@ func TestUpdate(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
// we need safe access for overwriting the fake validator OID
|
||||
oid := fakeOID{1, 3, 9900, 1}
|
||||
var oidLock sync.Mutex
|
||||
updatedOID := func(newOID fakeOID) {
|
||||
oidLock.Lock()
|
||||
defer oidLock.Unlock()
|
||||
oid = newOID
|
||||
}
|
||||
newValidator := func(m measurements.M, digest idkeydigest.IDKeyDigests, enforceIdKeyDigest bool, _ *logger.Logger) atls.Validator {
|
||||
oidLock.Lock()
|
||||
defer oidLock.Unlock()
|
||||
return fakeValidator{fakeOID: oid}
|
||||
}
|
||||
handler := file.NewHandler(afero.NewMemMapFs())
|
||||
|
||||
// create server
|
||||
validator := &Updatable{
|
||||
log: logger.NewTest(t),
|
||||
newValidator: newValidator,
|
||||
fileHandler: handler,
|
||||
log: logger.NewTest(t),
|
||||
variant: oid.Dummy{},
|
||||
fileHandler: handler,
|
||||
}
|
||||
|
||||
// Update should fail if the file does not exist
|
||||
@ -156,10 +134,6 @@ func TestUpdate(t *testing.T) {
|
||||
filepath.Join(constants.ServiceBasePath, constants.EnforceIDKeyDigestFilename),
|
||||
[]byte("false"),
|
||||
))
|
||||
require.NoError(handler.Write(
|
||||
filepath.Join(constants.ServiceBasePath, constants.AzureCVM),
|
||||
[]byte("true"),
|
||||
))
|
||||
|
||||
// call update once to initialize the server's validator
|
||||
require.NoError(validator.Update())
|
||||
@ -175,7 +149,7 @@ func TestUpdate(t *testing.T) {
|
||||
defer server.Close()
|
||||
|
||||
// test connection to server
|
||||
clientOID := fakeOID{1, 3, 9900, 1}
|
||||
clientOID := oid.Dummy{}
|
||||
resp, err := testConnection(require, server.URL, clientOID)
|
||||
require.NoError(err)
|
||||
defer resp.Body.Close()
|
||||
@ -184,7 +158,7 @@ func TestUpdate(t *testing.T) {
|
||||
assert.EqualValues("hello", body)
|
||||
|
||||
// update the server's validator
|
||||
updatedOID(fakeOID{1, 3, 9900, 2})
|
||||
validator.variant = oid.QEMUVTPM{}
|
||||
require.NoError(validator.Update())
|
||||
|
||||
// client connection should fail now, since the server's validator expects a different OID from the client
|
||||
@ -193,23 +167,6 @@ func TestUpdate(t *testing.T) {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
assert.Error(err)
|
||||
|
||||
// update should work for legacy measurement format
|
||||
// TODO: remove with v2.4.0
|
||||
require.NoError(handler.WriteJSON(
|
||||
filepath.Join(constants.ServiceBasePath, constants.MeasurementsFilename),
|
||||
map[uint32][]byte{
|
||||
11: bytes.Repeat([]byte{0x0}, 32),
|
||||
12: bytes.Repeat([]byte{0x1}, 32),
|
||||
},
|
||||
file.OptOverwrite,
|
||||
))
|
||||
require.NoError(handler.WriteJSON(
|
||||
filepath.Join(constants.ServiceBasePath, constants.EnforcedPCRsFilename),
|
||||
[]uint32{11},
|
||||
))
|
||||
|
||||
assert.NoError(validator.Update())
|
||||
}
|
||||
|
||||
func TestOIDConcurrency(t *testing.T) {
|
||||
@ -226,14 +183,11 @@ func TestOIDConcurrency(t *testing.T) {
|
||||
[]byte{},
|
||||
))
|
||||
|
||||
newValidator := func(m measurements.M, digest idkeydigest.IDKeyDigests, enforceIdKeyDigest bool, _ *logger.Logger) atls.Validator {
|
||||
return fakeValidator{fakeOID: fakeOID{1, 3, 9900, 1}}
|
||||
}
|
||||
// create server
|
||||
validator := &Updatable{
|
||||
log: logger.NewTest(t),
|
||||
newValidator: newValidator,
|
||||
fileHandler: handler,
|
||||
log: logger.NewTest(t),
|
||||
variant: oid.Dummy{},
|
||||
fileHandler: handler,
|
||||
}
|
||||
|
||||
// call update once to initialize the server's validator
|
||||
@ -262,21 +216,13 @@ func TestUpdateConcurrency(t *testing.T) {
|
||||
validator := &Updatable{
|
||||
log: logger.NewTest(t),
|
||||
fileHandler: handler,
|
||||
newValidator: func(m measurements.M, digest idkeydigest.IDKeyDigests, enforceIdKeyDigest bool, _ *logger.Logger) atls.Validator {
|
||||
return fakeValidator{fakeOID: fakeOID{1, 3, 9900, 1}}
|
||||
},
|
||||
variant: oid.Dummy{},
|
||||
}
|
||||
require.NoError(handler.WriteJSON(
|
||||
filepath.Join(constants.ServiceBasePath, constants.MeasurementsFilename),
|
||||
map[uint32][]byte{
|
||||
11: {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
|
||||
},
|
||||
measurements.M{11: measurements.WithAllBytes(0x00, false)},
|
||||
file.OptNone,
|
||||
))
|
||||
require.NoError(handler.WriteJSON(
|
||||
filepath.Join(constants.ServiceBasePath, constants.EnforcedPCRsFilename),
|
||||
[]uint32{11},
|
||||
))
|
||||
require.NoError(handler.Write(
|
||||
filepath.Join(constants.ServiceBasePath, constants.IDKeyDigestFilename),
|
||||
[]byte{},
|
||||
@ -285,10 +231,6 @@ func TestUpdateConcurrency(t *testing.T) {
|
||||
filepath.Join(constants.ServiceBasePath, constants.EnforceIDKeyDigestFilename),
|
||||
[]byte("false"),
|
||||
))
|
||||
require.NoError(handler.Write(
|
||||
filepath.Join(constants.ServiceBasePath, constants.AzureCVM),
|
||||
[]byte("true"),
|
||||
))
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
@ -303,8 +245,8 @@ func TestUpdateConcurrency(t *testing.T) {
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func testConnection(require *require.Assertions, url string, oid fakeOID) (*http.Response, error) {
|
||||
clientConfig, err := atls.CreateAttestationClientTLSConfig(fakeIssuer{fakeOID: oid}, nil)
|
||||
func testConnection(require *require.Assertions, url string, oid oid.Getter) (*http.Response, error) {
|
||||
clientConfig, err := atls.CreateAttestationClientTLSConfig(fakeIssuer{oid}, nil)
|
||||
require.NoError(err)
|
||||
client := http.Client{Transport: &http.Transport{TLSClientConfig: clientConfig}}
|
||||
|
||||
@ -314,29 +256,13 @@ func testConnection(require *require.Assertions, url string, oid fakeOID) (*http
|
||||
}
|
||||
|
||||
type fakeIssuer struct {
|
||||
fakeOID
|
||||
oid.Getter
|
||||
}
|
||||
|
||||
func (fakeIssuer) Issue(userData []byte, nonce []byte) ([]byte, error) {
|
||||
return json.Marshal(fakeDoc{UserData: userData, Nonce: nonce})
|
||||
}
|
||||
|
||||
type fakeValidator struct {
|
||||
fakeOID
|
||||
err error
|
||||
}
|
||||
|
||||
func (v fakeValidator) Validate(attDoc []byte, nonce []byte) ([]byte, error) {
|
||||
var doc fakeDoc
|
||||
if err := json.Unmarshal(attDoc, &doc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !bytes.Equal(doc.Nonce, nonce) {
|
||||
return nil, errors.New("invalid nonce")
|
||||
}
|
||||
return doc.UserData, v.err
|
||||
}
|
||||
|
||||
type fakeOID asn1.ObjectIdentifier
|
||||
|
||||
func (o fakeOID) OID() asn1.ObjectIdentifier {
|
||||
|
@ -18,6 +18,7 @@ go_library(
|
||||
"//internal/file",
|
||||
"//internal/grpc/atlscredentials",
|
||||
"//internal/logger",
|
||||
"//internal/oid",
|
||||
"//internal/watcher",
|
||||
"//joinservice/internal/kms",
|
||||
"//joinservice/internal/kubeadm",
|
||||
|
@ -27,6 +27,7 @@ import (
|
||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||
"github.com/edgelesssys/constellation/v2/internal/grpc/atlscredentials"
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
"github.com/edgelesssys/constellation/v2/internal/oid"
|
||||
"github.com/edgelesssys/constellation/v2/internal/watcher"
|
||||
"github.com/edgelesssys/constellation/v2/joinservice/internal/kms"
|
||||
"github.com/edgelesssys/constellation/v2/joinservice/internal/kubeadm"
|
||||
@ -42,25 +43,24 @@ const vpcIPTimeout = 30 * time.Second
|
||||
func main() {
|
||||
provider := flag.String("cloud-provider", "", "cloud service provider this binary is running on")
|
||||
keyServiceEndpoint := flag.String("key-service-endpoint", "", "endpoint of Constellations key management service")
|
||||
attestationVariant := flag.String("attestation-variant", "", "attestation variant to use for aTLS connections")
|
||||
verbosity := flag.Int("v", 0, logger.CmdLineVerbosityDescription)
|
||||
flag.Parse()
|
||||
|
||||
log := logger.New(logger.JSONLog, logger.VerbosityFromInt(*verbosity))
|
||||
log.With(zap.String("version", constants.VersionInfo()), zap.String("cloudProvider", *provider)).
|
||||
Infof("Constellation Node Join Service")
|
||||
log.With(
|
||||
zap.String("version", constants.VersionInfo()),
|
||||
zap.String("cloudProvider", *provider),
|
||||
zap.String("attestationVariant", *attestationVariant),
|
||||
).Infof("Constellation Node Join Service")
|
||||
|
||||
handler := file.NewHandler(afero.NewOsFs())
|
||||
|
||||
cvmRaw, err := handler.Read(filepath.Join(constants.ServiceBasePath, constants.AzureCVM))
|
||||
variant, err := oid.FromString(*attestationVariant)
|
||||
if err != nil {
|
||||
log.With(zap.Error(err)).Fatalf("Failed to get azureCVM from config map")
|
||||
log.With(zap.Error(err)).Fatalf("Failed to parse attestation variant")
|
||||
}
|
||||
azureCVM, err := strconv.ParseBool(string(cvmRaw))
|
||||
if err != nil {
|
||||
log.With(zap.Error(err)).Fatalf("Failed to parse content of AzureCVM: %s", cvmRaw)
|
||||
}
|
||||
|
||||
validator, err := watcher.NewValidator(log.Named("validator"), *provider, handler, azureCVM)
|
||||
validator, err := watcher.NewValidator(log.Named("validator"), variant, handler)
|
||||
if err != nil {
|
||||
flag.Usage()
|
||||
log.With(zap.Error(err)).Fatalf("Failed to create validator")
|
||||
|
@ -6,13 +6,10 @@ go_library(
|
||||
importpath = "github.com/edgelesssys/constellation/v2/verify/cmd",
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
"//internal/attestation/aws",
|
||||
"//internal/attestation/azure/snp",
|
||||
"//internal/attestation/gcp",
|
||||
"//internal/attestation/qemu",
|
||||
"//internal/cloud/cloudprovider",
|
||||
"//internal/attestation/choose",
|
||||
"//internal/constants",
|
||||
"//internal/logger",
|
||||
"//internal/oid",
|
||||
"//verify/server",
|
||||
"@org_uber_go_zap//:zap",
|
||||
],
|
||||
|
@ -11,39 +11,31 @@ import (
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/aws"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/azure/snp"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/gcp"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/qemu"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/choose"
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
"github.com/edgelesssys/constellation/v2/internal/oid"
|
||||
"github.com/edgelesssys/constellation/v2/verify/server"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
provider := flag.String("cloud-provider", "", "cloud service provider this binary is running on")
|
||||
attestationVariant := flag.String("attestation-variant", "", "attestation variant to use for aTLS connections")
|
||||
verbosity := flag.Int("v", 0, logger.CmdLineVerbosityDescription)
|
||||
|
||||
flag.Parse()
|
||||
log := logger.New(logger.JSONLog, logger.VerbosityFromInt(*verbosity))
|
||||
|
||||
log.With(zap.String("version", constants.VersionInfo()), zap.String("cloudProvider", *provider)).
|
||||
log.With(zap.String("version", constants.VersionInfo()), zap.String("attestationVariant", *attestationVariant)).
|
||||
Infof("Constellation Verification Service")
|
||||
|
||||
var issuer server.AttestationIssuer
|
||||
switch cloudprovider.FromString(*provider) {
|
||||
case cloudprovider.AWS:
|
||||
issuer = aws.NewIssuer(log)
|
||||
case cloudprovider.GCP:
|
||||
issuer = gcp.NewIssuer(log)
|
||||
case cloudprovider.Azure:
|
||||
issuer = snp.NewIssuer(log) // TODO: dynamic selection
|
||||
case cloudprovider.QEMU:
|
||||
issuer = qemu.NewIssuer(log)
|
||||
default:
|
||||
log.With(zap.String("cloudProvider", *provider)).Fatalf("Unknown cloud provider")
|
||||
variant, err := oid.FromString(*attestationVariant)
|
||||
if err != nil {
|
||||
log.With(zap.Error(err)).Fatalf("Failed to parse attestation variant")
|
||||
}
|
||||
issuer, err := choose.Issuer(variant, log.Named("issuer"))
|
||||
if err != nil {
|
||||
log.With(zap.Error(err)).Fatalf("Failed to create issuer")
|
||||
}
|
||||
|
||||
server := server.New(log.Named("server"), issuer)
|
||||
|
Loading…
Reference in New Issue
Block a user