mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-12-24 23:19:39 -05:00
azure: allow a set of idkeydigest values (#991)
This commit is contained in:
parent
a3db3c8424
commit
632090c21b
@ -14,7 +14,6 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/helm"
|
||||
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/initserver"
|
||||
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes"
|
||||
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes/k8sapi"
|
||||
kubewaiter "github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes/kubeWaiter"
|
||||
@ -32,7 +31,6 @@ import (
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
gcpcloud "github.com/edgelesssys/constellation/v2/internal/cloud/gcp"
|
||||
qemucloud "github.com/edgelesssys/constellation/v2/internal/cloud/qemu"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/vmtype"
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||
"github.com/edgelesssys/constellation/v2/internal/kubernetes/kubectl"
|
||||
@ -68,7 +66,7 @@ func main() {
|
||||
var clusterInitJoiner clusterInitJoiner
|
||||
var metadataAPI metadataAPI
|
||||
var cloudLogger logging.CloudLogger
|
||||
var issuer initserver.IssuerWrapper
|
||||
var issuer atls.Issuer
|
||||
var openTPM vtpm.TPMOpenFunc
|
||||
var fs afero.Fs
|
||||
|
||||
@ -84,7 +82,7 @@ func main() {
|
||||
log.With(zap.Error(err)).Fatalf("Failed to get selected PCRs")
|
||||
}
|
||||
|
||||
issuer = initserver.NewIssuerWrapper(aws.NewIssuer(), vmtype.Unknown, nil)
|
||||
issuer = aws.NewIssuer()
|
||||
|
||||
metadata, err := awscloud.New(ctx)
|
||||
if err != nil {
|
||||
@ -110,7 +108,7 @@ func main() {
|
||||
log.With(zap.Error(err)).Fatalf("Failed to get selected PCRs")
|
||||
}
|
||||
|
||||
issuer = initserver.NewIssuerWrapper(gcp.NewIssuer(), vmtype.Unknown, nil)
|
||||
issuer = gcp.NewIssuer()
|
||||
|
||||
metadata, err := gcpcloud.New(ctx)
|
||||
if err != nil {
|
||||
@ -138,11 +136,11 @@ func main() {
|
||||
log.With(zap.Error(err)).Fatalf("Failed to get selected PCRs")
|
||||
}
|
||||
|
||||
if idkeydigest, err := snp.GetIDKeyDigest(vtpm.OpenVTPM); err == nil {
|
||||
issuer = initserver.NewIssuerWrapper(snp.NewIssuer(), vmtype.AzureCVM, idkeydigest)
|
||||
if _, err := snp.GetIDKeyDigest(vtpm.OpenVTPM); err == nil {
|
||||
issuer = snp.NewIssuer()
|
||||
} else {
|
||||
// assume we are running in a trusted-launch VM
|
||||
issuer = initserver.NewIssuerWrapper(trustedlaunch.NewIssuer(), vmtype.AzureTrustedLaunch, idkeydigest)
|
||||
issuer = trustedlaunch.NewIssuer()
|
||||
}
|
||||
|
||||
metadata, err := azurecloud.New(ctx)
|
||||
@ -168,7 +166,7 @@ func main() {
|
||||
log.With(zap.Error(err)).Fatalf("Failed to get selected PCRs")
|
||||
}
|
||||
|
||||
issuer = initserver.NewIssuerWrapper(qemu.NewIssuer(), vmtype.Unknown, nil)
|
||||
issuer = qemu.NewIssuer()
|
||||
|
||||
cloudLogger = qemucloud.NewLogger()
|
||||
metadata := qemucloud.New()
|
||||
@ -181,7 +179,7 @@ func main() {
|
||||
openTPM = vtpm.OpenVTPM
|
||||
fs = afero.NewOsFs()
|
||||
default:
|
||||
issuer = initserver.NewIssuerWrapper(atls.NewFakeIssuer(oid.Dummy{}), vmtype.Unknown, nil)
|
||||
issuer = atls.NewFakeIssuer(oid.Dummy{})
|
||||
clusterInitJoiner = &clusterFake{}
|
||||
metadataAPI = &providerMetadataFake{}
|
||||
cloudLogger = &logging.NopLogger{}
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/joinclient"
|
||||
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/logging"
|
||||
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/nodelock"
|
||||
"github.com/edgelesssys/constellation/v2/internal/atls"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||
@ -24,7 +25,7 @@ import (
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func run(issuerWrapper initserver.IssuerWrapper, tpm vtpm.TPMOpenFunc, fileHandler file.Handler,
|
||||
func run(issuer atls.Issuer, tpm vtpm.TPMOpenFunc, fileHandler file.Handler,
|
||||
kube clusterInitJoiner, metadata metadataAPI,
|
||||
bindIP, bindPort string, log *logger.Logger,
|
||||
cloudLogger logging.CloudLogger,
|
||||
@ -56,12 +57,12 @@ func run(issuerWrapper initserver.IssuerWrapper, tpm vtpm.TPMOpenFunc, fileHandl
|
||||
}
|
||||
|
||||
nodeLock := nodelock.New(tpm)
|
||||
initServer, err := initserver.New(context.Background(), nodeLock, kube, issuerWrapper, fileHandler, metadata, log)
|
||||
initServer, err := initserver.New(context.Background(), nodeLock, kube, issuer, fileHandler, metadata, log)
|
||||
if err != nil {
|
||||
log.With(zap.Error(err)).Fatalf("Failed to create init server")
|
||||
}
|
||||
|
||||
dialer := dialer.New(issuerWrapper, nil, &net.Dialer{})
|
||||
dialer := dialer.New(issuer, nil, &net.Dialer{})
|
||||
joinClient := joinclient.New(nodeLock, dialer, kube, metadata, log)
|
||||
|
||||
cleaner := clean.New().With(initServer).With(joinClient)
|
||||
|
@ -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, []byte, []uint32, bool, []byte, bool,
|
||||
context.Context, string, string, []byte, []uint32, bool, bool,
|
||||
[]byte, bool, components.Components, *logger.Logger,
|
||||
) ([]byte, error) {
|
||||
return []byte{}, nil
|
||||
|
@ -17,7 +17,7 @@ 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/cloud/vmtype"
|
||||
"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"
|
||||
@ -38,13 +38,13 @@ import (
|
||||
// The server handles initialization calls from the CLI and initializes the
|
||||
// Kubernetes cluster.
|
||||
type Server struct {
|
||||
nodeLock locker
|
||||
initializer ClusterInitializer
|
||||
disk encryptedDisk
|
||||
fileHandler file.Handler
|
||||
grpcServer serveStopper
|
||||
cleaner cleaner
|
||||
issuerWrapper IssuerWrapper
|
||||
nodeLock locker
|
||||
initializer ClusterInitializer
|
||||
disk encryptedDisk
|
||||
fileHandler file.Handler
|
||||
grpcServer serveStopper
|
||||
cleaner cleaner
|
||||
issuer atls.Issuer
|
||||
|
||||
initSecretHash []byte
|
||||
|
||||
@ -54,7 +54,7 @@ type Server struct {
|
||||
}
|
||||
|
||||
// New creates a new initialization server.
|
||||
func New(ctx context.Context, lock locker, kube ClusterInitializer, issuerWrapper IssuerWrapper, fh file.Handler, metadata MetadataAPI, log *logger.Logger) (*Server, error) {
|
||||
func New(ctx context.Context, lock locker, kube ClusterInitializer, issuer atls.Issuer, fh file.Handler, metadata MetadataAPI, log *logger.Logger) (*Server, error) {
|
||||
log = log.Named("initServer")
|
||||
|
||||
initSecretHash, err := metadata.InitSecretHash(ctx)
|
||||
@ -70,13 +70,13 @@ func New(ctx context.Context, lock locker, kube ClusterInitializer, issuerWrappe
|
||||
disk: diskencryption.New(),
|
||||
initializer: kube,
|
||||
fileHandler: fh,
|
||||
issuerWrapper: issuerWrapper,
|
||||
issuer: issuer,
|
||||
log: log,
|
||||
initSecretHash: initSecretHash,
|
||||
}
|
||||
|
||||
grpcServer := grpc.NewServer(
|
||||
grpc.Creds(atlscredentials.New(issuerWrapper, nil)),
|
||||
grpc.Creds(atlscredentials.New(issuer, nil)),
|
||||
grpc.KeepaliveParams(keepalive.ServerParameters{Time: 15 * time.Second}),
|
||||
log.Named("gRPC").GetServerUnaryInterceptor(),
|
||||
)
|
||||
@ -140,14 +140,16 @@ 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)
|
||||
|
||||
kubeconfig, err := s.initializer.InitCluster(ctx,
|
||||
req.CloudServiceAccountUri,
|
||||
req.KubernetesVersion,
|
||||
measurementSalt,
|
||||
req.EnforcedPcrs,
|
||||
req.EnforceIdkeydigest,
|
||||
s.issuerWrapper.IDKeyDigest(),
|
||||
s.issuerWrapper.VMType() == vmtype.AzureCVM,
|
||||
isCVM,
|
||||
req.HelmDeployments,
|
||||
req.ConformanceMode,
|
||||
components.NewComponentsFromInitProto(req.KubernetesComponents),
|
||||
@ -193,33 +195,6 @@ func (s *Server) setupDisk(masterSecret, salt []byte) error {
|
||||
return s.disk.UpdatePassphrase(string(diskKey))
|
||||
}
|
||||
|
||||
// IssuerWrapper adds VM type context to an issuer to distinguish between
|
||||
// confidential and trusted launch VMs.
|
||||
type IssuerWrapper struct {
|
||||
atls.Issuer
|
||||
vmType vmtype.VMType
|
||||
idkeydigest []byte
|
||||
}
|
||||
|
||||
// NewIssuerWrapper creates a new issuer with VM type context.
|
||||
func NewIssuerWrapper(issuer atls.Issuer, vmType vmtype.VMType, idkeydigest []byte) IssuerWrapper {
|
||||
return IssuerWrapper{
|
||||
Issuer: issuer,
|
||||
vmType: vmType,
|
||||
idkeydigest: idkeydigest,
|
||||
}
|
||||
}
|
||||
|
||||
// VMType returns the VM type.
|
||||
func (i *IssuerWrapper) VMType() vmtype.VMType {
|
||||
return i.vmType
|
||||
}
|
||||
|
||||
// IDKeyDigest returns the ID key digest.
|
||||
func (i *IssuerWrapper) IDKeyDigest() []byte {
|
||||
return i.idkeydigest
|
||||
}
|
||||
|
||||
func deriveMeasurementValues(masterSecret, hkdfSalt []byte) (salt, clusterID []byte, err error) {
|
||||
salt, err = crypto.GenerateRandomBytes(crypto.RNGLengthDefault)
|
||||
if err != nil {
|
||||
@ -247,7 +222,6 @@ type ClusterInitializer interface {
|
||||
measurementSalt []byte,
|
||||
enforcedPcrs []uint32,
|
||||
enforceIDKeyDigest bool,
|
||||
idKeyDigest []byte,
|
||||
azureCVM bool,
|
||||
helmDeployments []byte,
|
||||
conformanceMode bool,
|
||||
|
@ -16,9 +16,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/bootstrapper/initproto"
|
||||
"github.com/edgelesssys/constellation/v2/internal/atls"
|
||||
"github.com/edgelesssys/constellation/v2/internal/crypto/testvector"
|
||||
"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/components"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -55,7 +57,7 @@ func TestNew(t *testing.T) {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
server, err := New(context.TODO(), newFakeLock(), &stubClusterInitializer{}, IssuerWrapper{}, fh, &tc.metadata, logger.NewTest(t))
|
||||
server, err := New(context.TODO(), newFakeLock(), &stubClusterInitializer{}, atls.NewFakeIssuer(oid.Dummy{}), fh, &tc.metadata, logger.NewTest(t))
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
@ -301,7 +303,7 @@ type stubClusterInitializer struct {
|
||||
}
|
||||
|
||||
func (i *stubClusterInitializer) InitCluster(
|
||||
context.Context, string, string, []byte, []uint32, bool, []byte, bool,
|
||||
context.Context, string, string, []byte, []uint32, bool, bool,
|
||||
[]byte, bool, components.Components, *logger.Logger,
|
||||
) ([]byte, error) {
|
||||
return i.initClusterKubeconfig, i.initClusterErr
|
||||
|
@ -9,7 +9,6 @@ package kubernetes
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@ -89,7 +88,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 string, measurementSalt []byte, enforcedPCRs []uint32,
|
||||
enforceIDKeyDigest bool, idKeyDigest []byte, azureCVM bool,
|
||||
enforceIDKeyDigest bool, azureCVM bool,
|
||||
helmReleasesRaw []byte, conformanceMode bool, kubernetesComponents components.Components, log *logger.Logger,
|
||||
) ([]byte, error) {
|
||||
log.With(zap.String("version", versionString)).Infof("Installing Kubernetes components")
|
||||
@ -216,7 +215,6 @@ func (k *KubeWrapper) InitCluster(
|
||||
}
|
||||
serviceConfig := constellationServicesConfig{
|
||||
initialMeasurementsJSON: measurementsJSON,
|
||||
idkeydigest: idKeyDigest,
|
||||
measurementSalt: measurementSalt,
|
||||
subnetworkPodCIDR: subnetworkPodCIDR,
|
||||
cloudServiceAccountURI: cloudServiceAccountURI,
|
||||
@ -484,12 +482,6 @@ func (k *KubeWrapper) setupExtraVals(ctx context.Context, serviceConfig constell
|
||||
"subnetworkPodCIDR": serviceConfig.subnetworkPodCIDR,
|
||||
}
|
||||
|
||||
joinVals, ok := extraVals["join-service"].(map[string]any)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid join-service values")
|
||||
}
|
||||
joinVals["idkeydigest"] = hex.EncodeToString(serviceConfig.idkeydigest)
|
||||
|
||||
subscriptionID, resourceGroup, err := azureshared.BasicsFromProviderID(instance.ProviderID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -532,7 +524,6 @@ type ccmConfigGetter interface {
|
||||
|
||||
type constellationServicesConfig struct {
|
||||
initialMeasurementsJSON []byte
|
||||
idkeydigest []byte
|
||||
measurementSalt []byte
|
||||
subnetworkPodCIDR string
|
||||
cloudServiceAccountURI string
|
||||
|
@ -257,7 +257,7 @@ func TestInitCluster(t *testing.T) {
|
||||
|
||||
_, err := kube.InitCluster(
|
||||
context.Background(), serviceAccountURI, string(tc.k8sVersion),
|
||||
nil, nil, false, nil, true, []byte("{}"), false, nil, logger.NewTest(t),
|
||||
nil, nil, false, true, []byte("{}"), false, nil, logger.NewTest(t),
|
||||
)
|
||||
|
||||
if tc.wantErr {
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"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/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"
|
||||
@ -30,7 +31,7 @@ import (
|
||||
type Validator struct {
|
||||
provider cloudprovider.Provider
|
||||
pcrs measurements.M
|
||||
idkeydigest []byte
|
||||
idkeydigests idkeydigest.IDKeyDigests
|
||||
enforceIDKeyDigest bool
|
||||
azureCVM bool
|
||||
validator atls.Validator
|
||||
@ -50,12 +51,8 @@ func NewValidator(provider cloudprovider.Provider, conf *config.Config) (*Valida
|
||||
if v.provider == cloudprovider.Azure {
|
||||
v.azureCVM = *conf.Provider.Azure.ConfidentialVM
|
||||
if v.azureCVM {
|
||||
idkeydigest, err := hex.DecodeString(conf.Provider.Azure.IDKeyDigest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("bad config: decoding idkeydigest from config: %w", err)
|
||||
}
|
||||
v.enforceIDKeyDigest = *conf.Provider.Azure.EnforceIDKeyDigest
|
||||
v.idkeydigest = idkeydigest
|
||||
v.idkeydigests = conf.Provider.Azure.IDKeyDigests
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,7 +150,7 @@ func (v *Validator) updateValidator(cmd *cobra.Command) {
|
||||
v.validator = gcp.NewValidator(v.pcrs, log)
|
||||
case cloudprovider.Azure:
|
||||
if v.azureCVM {
|
||||
v.validator = snp.NewValidator(v.pcrs, v.idkeydigest, v.enforceIDKeyDigest, log)
|
||||
v.validator = snp.NewValidator(v.pcrs, v.idkeydigests, v.enforceIDKeyDigest, log)
|
||||
} else {
|
||||
v.validator = trustedlaunch.NewValidator(v.pcrs, log)
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
"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/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"
|
||||
@ -39,7 +40,7 @@ func TestNewValidator(t *testing.T) {
|
||||
config *config.Config
|
||||
pcrs measurements.M
|
||||
enforceIDKeyDigest bool
|
||||
idKeyDigest string
|
||||
digest idkeydigest.IDKeyDigests
|
||||
azureCVM bool
|
||||
wantErr bool
|
||||
}{
|
||||
@ -74,17 +75,9 @@ func TestNewValidator(t *testing.T) {
|
||||
"set idkeydigest": {
|
||||
provider: cloudprovider.Azure,
|
||||
pcrs: testPCRs,
|
||||
idKeyDigest: "414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141",
|
||||
digest: idkeydigest.IDKeyDigests{[]byte("414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141")},
|
||||
enforceIDKeyDigest: true,
|
||||
},
|
||||
"invalid idkeydigest": {
|
||||
provider: cloudprovider.Azure,
|
||||
pcrs: testPCRs,
|
||||
idKeyDigest: "41414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414",
|
||||
enforceIDKeyDigest: true,
|
||||
azureCVM: true,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
@ -96,7 +89,7 @@ func TestNewValidator(t *testing.T) {
|
||||
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.idKeyDigest, ConfidentialVM: &tc.azureCVM}
|
||||
conf.Provider.Azure = &config.AzureConfig{Measurements: tc.pcrs, EnforceIDKeyDigest: &tc.enforceIDKeyDigest, IDKeyDigests: tc.digest, ConfidentialVM: &tc.azureCVM}
|
||||
}
|
||||
if tc.provider == cloudprovider.QEMU {
|
||||
conf.Provider.QEMU = &config.QEMUConfig{Measurements: tc.pcrs}
|
||||
@ -148,7 +141,7 @@ func TestValidatorV(t *testing.T) {
|
||||
"azure cvm": {
|
||||
provider: cloudprovider.Azure,
|
||||
pcrs: newTestPCRs(),
|
||||
wantVs: snp.NewValidator(newTestPCRs(), nil, false, nil),
|
||||
wantVs: snp.NewValidator(newTestPCRs(), idkeydigest.IDKeyDigests{}, false, nil),
|
||||
azureCVM: true,
|
||||
},
|
||||
"azure trusted launch": {
|
||||
|
@ -9,7 +9,7 @@ data:
|
||||
{{- if eq .Values.csp "Azure" }}
|
||||
{{/* ConfigMap.data is of type map[string]string. quote will not quote a quoted string. */}}
|
||||
enforceIdKeyDigest: {{ .Values.enforceIdKeyDigest | quote }}
|
||||
idkeydigest: {{ .Values.idkeydigest | quote }}
|
||||
idkeydigests: {{ .Values.idkeydigests | mustToJson }}
|
||||
{{- end }}
|
||||
binaryData:
|
||||
measurementSalt: {{ .Values.measurementSalt }}
|
||||
|
@ -14,10 +14,10 @@
|
||||
"description": "Whether or not idkeydigest should be enforced during attestation on azure.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"idkeydigest": {
|
||||
"description": "Expected idkeydigest value for Azure SNP attestation.",
|
||||
"idkeydigests": {
|
||||
"description": "List of expected idkeydigest values for Azure SNP attestation.",
|
||||
"type": "string",
|
||||
"examples": ["57486a447ec0f1958002a22a06b7673b9fd27d11e1c6527498056054c5fa92d23c50f9de44072760fe2b6fb89740b696"]
|
||||
"examples": ["[\"57486a447ec0f1958002a22a06b7673b9fd27d11e1c6527498056054c5fa92d23c50f9de44072760fe2b6fb89740b696\", \"0356215882a825279a85b300b0b742931d113bf7e32dde2e50ffde7ec743ca491ecdd7f336dc28a6e0b2bb57af7a44a3\"]"]
|
||||
},
|
||||
"image": {
|
||||
"description": "Container image to use for the spawned pods.",
|
||||
@ -40,7 +40,7 @@
|
||||
"properties": { "csp": { "const": "azure" } },
|
||||
"required": ["csp"]
|
||||
},
|
||||
"then": { "required": ["enforceIdKeyDigest", "idkeydigest"] },
|
||||
"then": { "required": ["enforceIdKeyDigest", "idkeydigests"] },
|
||||
"title": "Values",
|
||||
"type": "object"
|
||||
}
|
||||
|
@ -405,6 +405,12 @@ func (i *ChartLoader) loadConstellationServicesValues(config *config.Config, mas
|
||||
}
|
||||
joinServiceVals["enforceIdKeyDigest"] = config.EnforcesIDKeyDigest()
|
||||
|
||||
marshalledDigests, err := json.Marshal(config.IDKeyDigests())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("marshalling id key digests: %w", err)
|
||||
}
|
||||
joinServiceVals["idkeydigests"] = string(marshalledDigests)
|
||||
|
||||
ccmVals, ok := values["ccm"].(map[string]any)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid ccm values")
|
||||
|
@ -356,7 +356,7 @@ func prepareAzureValues(values map[string]any) error {
|
||||
if !ok {
|
||||
return errors.New("missing 'join-service' key")
|
||||
}
|
||||
joinVals["idkeydigest"] = "baaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaad"
|
||||
joinVals["idkeydigests"] = "[\"baaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaad\", \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"]"
|
||||
m := measurements.M{1: measurements.WithAllBytes(0xAA, false)}
|
||||
mJSON, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
|
@ -6,6 +6,6 @@ metadata:
|
||||
data:
|
||||
measurements: "{\"1\":{\"expected\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"warnOnly\":false}}"
|
||||
enforceIdKeyDigest: "true"
|
||||
idkeydigest: "baaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaad"
|
||||
idkeydigests: "[\"baaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaad\", \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"]"
|
||||
binaryData:
|
||||
measurementSalt: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
|
@ -9,6 +9,8 @@ package snp
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
|
||||
)
|
||||
|
||||
type signatureError struct {
|
||||
@ -48,7 +50,8 @@ func (e *vcekError) Error() string {
|
||||
}
|
||||
|
||||
type idKeyError struct {
|
||||
expectedValue []byte
|
||||
encounteredValue []byte
|
||||
expectedValues idkeydigest.IDKeyDigests
|
||||
}
|
||||
|
||||
func (e *idKeyError) Unwrap() error {
|
||||
@ -56,7 +59,7 @@ func (e *idKeyError) Unwrap() error {
|
||||
}
|
||||
|
||||
func (e *idKeyError) Error() string {
|
||||
return fmt.Sprintf("configured idkeydigest does not match reported idkeydigest: %x", e.expectedValue)
|
||||
return fmt.Sprintf("configured idkeydigests %x doesn't contain reported idkeydigest %x", e.expectedValues, e.encounteredValue)
|
||||
}
|
||||
|
||||
type versionError struct {
|
||||
|
@ -19,6 +19,7 @@ import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
|
||||
internalCrypto "github.com/edgelesssys/constellation/v2/internal/crypto"
|
||||
@ -42,11 +43,11 @@ type Validator struct {
|
||||
}
|
||||
|
||||
// NewValidator initializes a new Azure validator with the provided PCR values.
|
||||
func NewValidator(pcrs measurements.M, idKeyDigest []byte, enforceIDKeyDigest bool, log vtpm.AttestationLogger) *Validator {
|
||||
func NewValidator(pcrs measurements.M, idKeyDigests idkeydigest.IDKeyDigests, enforceIDKeyDigest bool, log vtpm.AttestationLogger) *Validator {
|
||||
return &Validator{
|
||||
Validator: vtpm.NewValidator(
|
||||
pcrs,
|
||||
getTrustedKey(&azureInstanceInfo{}, idKeyDigest, enforceIDKeyDigest, log),
|
||||
getTrustedKey(&azureInstanceInfo{}, idKeyDigests, enforceIDKeyDigest, log),
|
||||
validateCVM,
|
||||
vtpm.VerifyPKCS1v15,
|
||||
log,
|
||||
@ -77,7 +78,7 @@ func reverseEndian(b []byte) {
|
||||
// getTrustedKey establishes trust in the given public key.
|
||||
// It does so by verifying the SNP attestation statement in instanceInfo.
|
||||
func getTrustedKey(
|
||||
hclAk HCLAkValidator, idKeyDigest []byte, enforceIDKeyDigest bool, log vtpm.AttestationLogger,
|
||||
hclAk HCLAkValidator, idKeyDigest idkeydigest.IDKeyDigests, enforceIDKeyDigest bool, log vtpm.AttestationLogger,
|
||||
) func(akPub, instanceInfoRaw []byte) (crypto.PublicKey, error) {
|
||||
return func(akPub, instanceInfoRaw []byte) (crypto.PublicKey, error) {
|
||||
var instanceInfo azureInstanceInfo
|
||||
@ -95,7 +96,7 @@ func getTrustedKey(
|
||||
return nil, fmt.Errorf("validating VCEK: %w", err)
|
||||
}
|
||||
|
||||
if err = validateSNPReport(vcek, idKeyDigest, enforceIDKeyDigest, report, log); err != nil {
|
||||
if err := validateSNPReport(vcek, idKeyDigest, enforceIDKeyDigest, report, log); err != nil {
|
||||
return nil, fmt.Errorf("validating SNP report: %w", err)
|
||||
}
|
||||
|
||||
@ -143,7 +144,7 @@ func validateVCEK(vcekRaw []byte, certChain []byte) (*x509.Certificate, error) {
|
||||
}
|
||||
|
||||
func validateSNPReport(
|
||||
cert *x509.Certificate, expectedIDKeyDigest []byte, enforceIDKeyDigest bool,
|
||||
cert *x509.Certificate, expectedIDKeyDigests idkeydigest.IDKeyDigests, enforceIDKeyDigest bool,
|
||||
report snpAttestationReport, log vtpm.AttestationLogger,
|
||||
) error {
|
||||
if report.Policy.Debug() {
|
||||
@ -189,12 +190,20 @@ func validateSNPReport(
|
||||
return &signatureError{err}
|
||||
}
|
||||
|
||||
if !bytes.Equal(expectedIDKeyDigest, report.IDKeyDigest[:]) {
|
||||
hasExpectedIDKeyDigest := false
|
||||
for _, digest := range expectedIDKeyDigests {
|
||||
if bytes.Equal(digest, report.IDKeyDigest[:]) {
|
||||
hasExpectedIDKeyDigest = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !hasExpectedIDKeyDigest {
|
||||
if enforceIDKeyDigest {
|
||||
return &idKeyError{report.IDKeyDigest[:]}
|
||||
return &idKeyError{report.IDKeyDigest[:], expectedIDKeyDigests}
|
||||
}
|
||||
if log != nil {
|
||||
log.Warnf("Encountered different than configured IDKeyDigest value: %x", report.IDKeyDigest[:])
|
||||
log.Warnf("configured idkeydigests %x doesn't contain reported idkeydigest %x", expectedIDKeyDigests, report.IDKeyDigest[:])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/simulator"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
|
||||
"github.com/google/go-tpm-tools/client"
|
||||
@ -41,14 +42,16 @@ func TestTrustedKeyFromSNP(t *testing.T) {
|
||||
defaultRuntimeData := "7b226b657973223a5b7b226b6964223a2248434c416b507562222c226b65795f6f7073223a5b22656e6372797074225d2c226b7479223a22525341222c2265223a2241514142222c226e223a22747946717641414166324746656c6b5737566352684a6e4132597659364c6a427a65554e3276614d5a6e5a74685f74466e574d6b4b35415874757379434e656c337569703356475a7a54617a3558327447566a4772732d4d56486361703951647771555856573367394f515f74456269786378372d78626c554a516b474551666e626253646e5049326c764c7a4f73315a5f30766a65444178765351726d616773366e592d634a4157482d706744564a79487470735553735f5142576b6c617a44736f3557486d6e4d743973394d75696c57586f7830525379586e55656151796859316a753752545363526e5658754e7936377a5f454a6e774d393264727746623841556430534a5f396f687645596c34615a52444543476f3056726a635348552d4a474a6575574335566844425235454f6f4356424267716539653833765f6c4a784933574c65326f7653495a49497a416d625351227d5d2c22766d2d636f6e66696775726174696f6e223a7b22636f6e736f6c652d656e61626c6564223a747275652c2263757272656e742d74696d65223a313636313435353339312c227365637572652d626f6f74223a66616c73652c2274706d2d656e61626c6564223a747275652c22766d556e697175654964223a2242364339384333422d344543372d344441362d424432462d374439384432304437423735227d7d"
|
||||
defaultVCEK := "-----BEGIN CERTIFICATE-----\nMIIFTDCCAvugAwIBAgIBADBGBgkqhkiG9w0BAQowOaAPMA0GCWCGSAFlAwQCAgUA\noRwwGgYJKoZIhvcNAQEIMA0GCWCGSAFlAwQCAgUAogMCATCjAwIBATB7MRQwEgYD\nVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDASBgNVBAcMC1NhbnRhIENs\nYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5jZWQgTWljcm8gRGV2aWNl\nczESMBAGA1UEAwwJU0VWLU1pbGFuMB4XDTIyMDYyOTE2MzEzMFoXDTI5MDYyOTE2\nMzEzMFowejEUMBIGA1UECwwLRW5naW5lZXJpbmcxCzAJBgNVBAYTAlVTMRQwEgYD\nVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExHzAdBgNVBAoMFkFkdmFuY2Vk\nIE1pY3JvIERldmljZXMxETAPBgNVBAMMCFNFVi1WQ0VLMHYwEAYHKoZIzj0CAQYF\nK4EEACIDYgAEhPX8Cl9uA7PxqNGzeqamJNYJLx/VFE/s3+8qOWtaztKNcn1PaAI4\nndE+yaVfMHsiA8CLTylumpWXcVBHPYV9kPEVrtozhvrrT5Oii9OpZPYHJ7/WPVmM\nJ3K8/Iz3AshTo4IBFjCCARIwEAYJKwYBBAGceAEBBAMCAQAwFwYJKwYBBAGceAEC\nBAoWCE1pbGFuLUIwMBEGCisGAQQBnHgBAwEEAwIBAjARBgorBgEEAZx4AQMCBAMC\nAQAwEQYKKwYBBAGceAEDBAQDAgEAMBEGCisGAQQBnHgBAwUEAwIBADARBgorBgEE\nAZx4AQMGBAMCAQAwEQYKKwYBBAGceAEDBwQDAgEAMBEGCisGAQQBnHgBAwMEAwIB\nBjARBgorBgEEAZx4AQMIBAMCAV0wTQYJKwYBBAGceAEEBECeRKrvAs/Kb926ymac\nbP0p4auNl+vJOYVxKKy7E7h0DfMUNtNOhuX4rgzf6zoOGF20beysF2zHfXYcIqG5\n3PJbMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0B\nAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBA4ICAQBXXzX8w+z06JNKLVAa9vyE\njC69c7uvfTPScqLzOCV+S8yZ7Ibpn6gdRcgn5s3F7uerVs9/8mq+rDpMzLTVxLei\nYAW9jDS9VdEgfUp3GzzL1g3zsWNZPpWuAu0Cw1V7KnQ9kiGsJMRKerx8QLrm+aAH\nOiob4XHl2naUx9aILzCLbNgLBdh6Tw2XkGj8NB9O7kNQoINEz6U+cAJL5LWzuoYt\nW1IJkYUEMydvLImFHeFIFtB2wI4mTSuCjtb/pBUeRdvDm5dmY/VPvh+CkvCeXNze\nHPZ8vcQ+ZZNS44O9rMnSUOtRFZb3ow3atXsx53Gy9rp41Bd0OZgSMrnHH74lDQX0\nkkNP+UrRYs66q0gJaSZglzkWfHLtAGfuRh9XyBh4kBgHcjF1Qh6frTpotX9t+0V/\nQZv3KjPVMsGaUN407WHEoAl6qX6TSS/An2EdXgqbhXS5O81gzatWDTcT2D3VJG1N\nHYtkh1J5WmrFTphc7OhxmVk7l3UkWPyS8Oi8be2y8Q4x0wgviZn5eOa/djpHoarW\nLS91KKPZXGyXlj49TlCjbl4RfyKYOd/HqgAYYdtqBe84AyJQRvuD5gWmdBzagncb\nyKjs6tYr74aAGnAqulp+yqvrzb7teUQmCMkROfzFjYZmLByqw6UGRdHgCf8hOzmO\nch4hf9cHRLAUJpqynRmb+g==\n-----END CERTIFICATE-----\n"
|
||||
defaultCertChain := "-----BEGIN CERTIFICATE-----\nMIIGiTCCBDigAwIBAgIDAQABMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC\nBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS\nBgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg\nQ2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp\nY2VzMRIwEAYDVQQDDAlBUkstTWlsYW4wHhcNMjAxMDIyMTgyNDIwWhcNNDUxMDIy\nMTgyNDIwWjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS\nBgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j\nZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJU0VWLU1pbGFuMIICIjANBgkqhkiG\n9w0BAQEFAAOCAg8AMIICCgKCAgEAnU2drrNTfbhNQIllf+W2y+ROCbSzId1aKZft\n2T9zjZQOzjGccl17i1mIKWl7NTcB0VYXt3JxZSzOZjsjLNVAEN2MGj9TiedL+Qew\nKZX0JmQEuYjm+WKksLtxgdLp9E7EZNwNDqV1r0qRP5tB8OWkyQbIdLeu4aCz7j/S\nl1FkBytev9sbFGzt7cwnjzi9m7noqsk+uRVBp3+In35QPdcj8YflEmnHBNvuUDJh\nLCJMW8KOjP6++Phbs3iCitJcANEtW4qTNFoKW3CHlbcSCjTM8KsNbUx3A8ek5EVL\njZWH1pt9E3TfpR6XyfQKnY6kl5aEIPwdW3eFYaqCFPrIo9pQT6WuDSP4JCYJbZne\nKKIbZjzXkJt3NQG32EukYImBb9SCkm9+fS5LZFg9ojzubMX3+NkBoSXI7OPvnHMx\njup9mw5se6QUV7GqpCA2TNypolmuQ+cAaxV7JqHE8dl9pWf+Y3arb+9iiFCwFt4l\nAlJw5D0CTRTC1Y5YWFDBCrA/vGnmTnqG8C+jjUAS7cjjR8q4OPhyDmJRPnaC/ZG5\nuP0K0z6GoO/3uen9wqshCuHegLTpOeHEJRKrQFr4PVIwVOB0+ebO5FgoyOw43nyF\nD5UKBDxEB4BKo/0uAiKHLRvvgLbORbU8KARIs1EoqEjmF8UtrmQWV2hUjwzqwvHF\nei8rPxMCAwEAAaOBozCBoDAdBgNVHQ4EFgQUO8ZuGCrD/T1iZEib47dHLLT8v/gw\nHwYDVR0jBBgwFoAUhawa0UP3yKxV1MUdQUir1XhK1FMwEgYDVR0TAQH/BAgwBgEB\n/wIBADAOBgNVHQ8BAf8EBAMCAQQwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cHM6Ly9r\nZHNpbnRmLmFtZC5jb20vdmNlay92MS9NaWxhbi9jcmwwRgYJKoZIhvcNAQEKMDmg\nDzANBglghkgBZQMEAgIFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgIFAKID\nAgEwowMCAQEDggIBAIgeUQScAf3lDYqgWU1VtlDbmIN8S2dC5kmQzsZ/HtAjQnLE\nPI1jh3gJbLxL6gf3K8jxctzOWnkYcbdfMOOr28KT35IaAR20rekKRFptTHhe+DFr\n3AFzZLDD7cWK29/GpPitPJDKCvI7A4Ug06rk7J0zBe1fz/qe4i2/F12rvfwCGYhc\nRxPy7QF3q8fR6GCJdB1UQ5SlwCjFxD4uezURztIlIAjMkt7DFvKRh+2zK+5plVGG\nFsjDJtMz2ud9y0pvOE4j3dH5IW9jGxaSGStqNrabnnpF236ETr1/a43b8FFKL5QN\nmt8Vr9xnXRpznqCRvqjr+kVrb6dlfuTlliXeQTMlBoRWFJORL8AcBJxGZ4K2mXft\nl1jU5TLeh5KXL9NW7a/qAOIUs2FiOhqrtzAhJRg9Ij8QkQ9Pk+cKGzw6El3T3kFr\nEg6zkxmvMuabZOsdKfRkWfhH2ZKcTlDfmH1H0zq0Q2bG3uvaVdiCtFY1LlWyB38J\nS2fNsR/Py6t5brEJCFNvzaDky6KeC4ion/cVgUai7zzS3bGQWzKDKU35SqNU2WkP\nI8xCZ00WtIiKKFnXWUQxvlKmmgZBIYPe01zD0N8atFxmWiSnfJl690B9rJpNR/fI\najxCW3Seiws6r1Zm+tCuVbMiNtpS9ThjNX4uve5thyfE2DgoxRFvY1CsoF5M\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIGYzCCBBKgAwIBAgIDAQAAMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC\nBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS\nBgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg\nQ2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp\nY2VzMRIwEAYDVQQDDAlBUkstTWlsYW4wHhcNMjAxMDIyMTcyMzA1WhcNNDUxMDIy\nMTcyMzA1WjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS\nBgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j\nZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJQVJLLU1pbGFuMIICIjANBgkqhkiG\n9w0BAQEFAAOCAg8AMIICCgKCAgEA0Ld52RJOdeiJlqK2JdsVmD7FktuotWwX1fNg\nW41XY9Xz1HEhSUmhLz9Cu9DHRlvgJSNxbeYYsnJfvyjx1MfU0V5tkKiU1EesNFta\n1kTA0szNisdYc9isqk7mXT5+KfGRbfc4V/9zRIcE8jlHN61S1ju8X93+6dxDUrG2\nSzxqJ4BhqyYmUDruPXJSX4vUc01P7j98MpqOS95rORdGHeI52Naz5m2B+O+vjsC0\n60d37jY9LFeuOP4Meri8qgfi2S5kKqg/aF6aPtuAZQVR7u3KFYXP59XmJgtcog05\ngmI0T/OitLhuzVvpZcLph0odh/1IPXqx3+MnjD97A7fXpqGd/y8KxX7jksTEzAOg\nbKAeam3lm+3yKIcTYMlsRMXPcjNbIvmsBykD//xSniusuHBkgnlENEWx1UcbQQrs\n+gVDkuVPhsnzIRNgYvM48Y+7LGiJYnrmE8xcrexekBxrva2V9TJQqnN3Q53kt5vi\nQi3+gCfmkwC0F0tirIZbLkXPrPwzZ0M9eNxhIySb2npJfgnqz55I0u33wh4r0ZNQ\neTGfw03MBUtyuzGesGkcw+loqMaq1qR4tjGbPYxCvpCq7+OgpCCoMNit2uLo9M18\nfHz10lOMT8nWAUvRZFzteXCm+7PHdYPlmQwUw3LvenJ/ILXoQPHfbkH0CyPfhl1j\nWhJFZasCAwEAAaN+MHwwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSFrBrRQ/fI\nrFXUxR1BSKvVeErUUzAPBgNVHRMBAf8EBTADAQH/MDoGA1UdHwQzMDEwL6AtoCuG\nKWh0dHBzOi8va2RzaW50Zi5hbWQuY29tL3ZjZWsvdjEvTWlsYW4vY3JsMEYGCSqG\nSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZI\nAWUDBAICBQCiAwIBMKMDAgEBA4ICAQC6m0kDp6zv4Ojfgy+zleehsx6ol0ocgVel\nETobpx+EuCsqVFRPK1jZ1sp/lyd9+0fQ0r66n7kagRk4Ca39g66WGTJMeJdqYriw\nSTjjDCKVPSesWXYPVAyDhmP5n2v+BYipZWhpvqpaiO+EGK5IBP+578QeW/sSokrK\ndHaLAxG2LhZxj9aF73fqC7OAJZ5aPonw4RE299FVarh1Tx2eT3wSgkDgutCTB1Yq\nzT5DuwvAe+co2CIVIzMDamYuSFjPN0BCgojl7V+bTou7dMsqIu/TW/rPCX9/EUcp\nKGKqPQ3P+N9r1hjEFY1plBg93t53OOo49GNI+V1zvXPLI6xIFVsh+mto2RtgEX/e\npmMKTNN6psW88qg7c1hTWtN6MbRuQ0vm+O+/2tKBF2h8THb94OvvHHoFDpbCELlq\nHnIYhxy0YKXGyaW1NjfULxrrmxVW4wcn5E8GddmvNa6yYm8scJagEi13mhGu4Jqh\n3QU3sf8iUSUr09xQDwHtOQUVIqx4maBZPBtSMf+qUDtjXSSq8lfWcd8bLr9mdsUn\nJZJ0+tuPMKmBnSH860llKk+VpVQsgqbzDIvOLvD6W1Umq25boxCYJ+TuBoa4s+HH\nCViAvgT9kf/rBq1d+ivj6skkHxuzcxbk1xv6ZGxrteJxVH7KlX7YRdZ6eARKwLe4\nAFZEAwoKCQ==\n-----END CERTIFICATE-----\n"
|
||||
defaultIDKeyDigest := "57e229e0ffe5fa92d0faddff6cae0e61c926fc9ef9afd20a8b8cfcf7129db9338cbe5bf3f6987733a2bf65d06dc38fc1"
|
||||
defaultIDKeyDigestOld, err := hex.DecodeString("57e229e0ffe5fa92d0faddff6cae0e61c926fc9ef9afd20a8b8cfcf7129db9338cbe5bf3f6987733a2bf65d06dc38fc1")
|
||||
require.NoError(err)
|
||||
defaultIDKeyDigest := idkeydigest.NewIDKeyDigests([][]byte{defaultIDKeyDigestOld})
|
||||
|
||||
testCases := map[string]struct {
|
||||
report string
|
||||
runtimeData string
|
||||
vcek string
|
||||
certChain string
|
||||
idkeydigest string
|
||||
idkeydigest idkeydigest.IDKeyDigests
|
||||
enforceIDKeyDigest bool
|
||||
wantErr bool
|
||||
assertCorrectError func(error)
|
||||
@ -118,7 +121,7 @@ func TestTrustedKeyFromSNP(t *testing.T) {
|
||||
runtimeData: defaultRuntimeData,
|
||||
vcek: defaultVCEK,
|
||||
certChain: defaultCertChain,
|
||||
idkeydigest: "67e229e0ffe5fa92d0faddff6cae0e61c926fc9ef9afd20a8b8cfcf7129db9338cbe5bf3f6987733a2bf65d06dc38fc1",
|
||||
idkeydigest: idkeydigest.IDKeyDigests{[]byte{0x00}},
|
||||
enforceIDKeyDigest: true,
|
||||
wantErr: true,
|
||||
assertCorrectError: func(err error) {
|
||||
@ -131,7 +134,7 @@ func TestTrustedKeyFromSNP(t *testing.T) {
|
||||
runtimeData: defaultRuntimeData,
|
||||
vcek: defaultVCEK,
|
||||
certChain: defaultCertChain,
|
||||
idkeydigest: "",
|
||||
idkeydigest: idkeydigest.IDKeyDigests{[]byte{0x00}},
|
||||
},
|
||||
"unsupported microcode version": {
|
||||
report: "02000000020000001f0003000000000001000000000000000000000000000000020000000000000000000000000000000000000001000000020000000000065d010000000000000000000000000000000ccc0895ef2f2c3b8c8568f5a2bb65ff5bf9387a09359742ad41e686cacfd38b00000000000000000000000000000000000000000000000000000000000000005677f1de87289e7ad2c7e99c805d0468b1a9ccd83f0d245afa5242d405da4d5725852f8c6550564870e5f3206dfb1841000000000000000000000000000000000000000000000000000000000000000057e229e0ffe5fa92d0faddff6cae0e61c926fc9ef9afd20a8b8cfcf7129db9338cbe5bf3f6987733a2bf65d06dc38fc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f7240b24a1babe2ece844c4f792bcd9844bf6907d14aeea00156310b9538daffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff020000000000065d0000000000000000000000000000000000000000000000009e44aaef02cfca6fddbaca669c6cfd29e1ab8d97ebc939857128acbb13b8740df31436d34e86e5f8ae0cdfeb3a0e185db46decac176cc77d761c22a1b9dcf25b020000000000065c0133010001330100020000000000065d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bcb7dc15abff884802e774b39adba8e6ff7efcf05e115c91588e657065151056a320f70c788d0e3619391052922e422b000000000000000000000000000000000000000000000000e8dbf581140443bbc681c50eca8639a76ef6cab34e0780cbca977e2e2a03f8b864fd4e9774b0f8055511567e031e59bf00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c02000001000000020000000100000048020000",
|
||||
@ -198,10 +201,7 @@ func TestTrustedKeyFromSNP(t *testing.T) {
|
||||
assert.Error(err)
|
||||
}
|
||||
|
||||
idkeydigest, err := hex.DecodeString(tc.idkeydigest)
|
||||
assert.NoError(err)
|
||||
|
||||
key, err := getTrustedKey(&instanceInfo, idkeydigest, tc.enforceIDKeyDigest, nil)(akPub, statement)
|
||||
key, err := getTrustedKey(&instanceInfo, tc.idkeydigest, tc.enforceIDKeyDigest, nil)(akPub, statement)
|
||||
if tc.wantErr {
|
||||
tc.assertCorrectError(err)
|
||||
} else {
|
||||
|
117
internal/attestation/idkeydigest/idkeydigest.go
Normal file
117
internal/attestation/idkeydigest/idkeydigest.go
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package idkeydigest
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"go.uber.org/multierr"
|
||||
)
|
||||
|
||||
// IDKeyDigests is a list of trusted digest values for the ID key.
|
||||
type IDKeyDigests [][]byte
|
||||
|
||||
type encodedIDKeyDigests []string
|
||||
|
||||
// encodedDigestLength is the length of a digest in hex encoding.
|
||||
const encodedDigestLength = 2 * 48
|
||||
|
||||
// NewIDKeyDigests creates a new IDKeyDigests from a list of digests.
|
||||
func NewIDKeyDigests(digests [][]byte) IDKeyDigests {
|
||||
idKeyDigests := make(IDKeyDigests, len(digests))
|
||||
copy(idKeyDigests, digests)
|
||||
return idKeyDigests
|
||||
}
|
||||
|
||||
// DefaultsFor returns the default IDKeyDigests for the given cloud provider.
|
||||
func DefaultsFor(csp cloudprovider.Provider) IDKeyDigests {
|
||||
switch csp {
|
||||
case cloudprovider.Azure:
|
||||
return IDKeyDigests{
|
||||
{0x57, 0x48, 0x6a, 0x44, 0x7e, 0xc0, 0xf1, 0x95, 0x80, 0x02, 0xa2, 0x2a, 0x06, 0xb7, 0x67, 0x3b, 0x9f, 0xd2, 0x7d, 0x11, 0xe1, 0xc6, 0x52, 0x74, 0x98, 0x05, 0x60, 0x54, 0xc5, 0xfa, 0x92, 0xd2, 0x3c, 0x50, 0xf9, 0xde, 0x44, 0x07, 0x27, 0x60, 0xfe, 0x2b, 0x6f, 0xb8, 0x97, 0x40, 0xb6, 0x96},
|
||||
{0x03, 0x56, 0x21, 0x58, 0x82, 0xa8, 0x25, 0x27, 0x9a, 0x85, 0xb3, 0x00, 0xb0, 0xb7, 0x42, 0x93, 0x1d, 0x11, 0x3b, 0xf7, 0xe3, 0x2d, 0xde, 0x2e, 0x50, 0xff, 0xde, 0x7e, 0xc7, 0x43, 0xca, 0x49, 0x1e, 0xcd, 0xd7, 0xf3, 0x36, 0xdc, 0x28, 0xa6, 0xe0, 0xb2, 0xbb, 0x57, 0xaf, 0x7a, 0x44, 0xa3},
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalYAML implements the yaml.Marshaler interface.
|
||||
func (d IDKeyDigests) MarshalYAML() (any, error) {
|
||||
encodedIDKeyDigests := []string{}
|
||||
for _, digest := range d {
|
||||
encodedIDKeyDigests = append(encodedIDKeyDigests, hex.EncodeToString(digest))
|
||||
}
|
||||
return encodedIDKeyDigests, nil
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (d *IDKeyDigests) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
var encodedDigests encodedIDKeyDigests
|
||||
if err := unmarshal(&encodedDigests); err != nil {
|
||||
// Unmarshalling failed, IDKeyDigests might be a simple string instead of IDKeyDigests struct.
|
||||
var unmarshalledString string
|
||||
if legacyErr := unmarshal(&unmarshalledString); legacyErr != nil {
|
||||
return multierr.Append(
|
||||
err,
|
||||
fmt.Errorf("trying legacy format: %w", legacyErr),
|
||||
)
|
||||
}
|
||||
encodedDigests = append(encodedDigests, unmarshalledString)
|
||||
}
|
||||
if err := d.unmarshal(encodedDigests); err != nil {
|
||||
return fmt.Errorf("unmarshalling yaml: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
func (d IDKeyDigests) MarshalJSON() ([]byte, error) {
|
||||
encodedIDKeyDigests := []string{}
|
||||
for _, digest := range d {
|
||||
encodedIDKeyDigests = append(encodedIDKeyDigests, hex.EncodeToString(digest))
|
||||
}
|
||||
return json.Marshal(encodedIDKeyDigests)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||
func (d *IDKeyDigests) UnmarshalJSON(b []byte) error {
|
||||
var encodedDigests encodedIDKeyDigests
|
||||
if err := json.Unmarshal(b, &encodedDigests); err != nil {
|
||||
// Unmarshalling failed, IDKeyDigests might be a simple string instead of IDKeyDigests struct.
|
||||
var unmarshalledString string
|
||||
if legacyErr := json.Unmarshal(b, &unmarshalledString); legacyErr != nil {
|
||||
return multierr.Append(
|
||||
err,
|
||||
fmt.Errorf("trying legacy format: %w", legacyErr),
|
||||
)
|
||||
}
|
||||
encodedDigests = []string{unmarshalledString}
|
||||
}
|
||||
if err := d.unmarshal(encodedDigests); err != nil {
|
||||
return fmt.Errorf("unmarshalling json: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// unmarshal is a helper function for unmarshalling encodedIDKeyDigests into IDKeyDigests.
|
||||
func (d *IDKeyDigests) unmarshal(encodedDigests encodedIDKeyDigests) error {
|
||||
for _, encodedDigest := range encodedDigests {
|
||||
if len(encodedDigest) != encodedDigestLength {
|
||||
return fmt.Errorf("invalid digest length: %d", len(encodedDigest))
|
||||
}
|
||||
digest, err := hex.DecodeString(encodedDigest)
|
||||
if err != nil {
|
||||
return fmt.Errorf("decoding digest: %w", err)
|
||||
}
|
||||
*d = append(*d, digest)
|
||||
}
|
||||
return nil
|
||||
}
|
119
internal/attestation/idkeydigest/idkeydigest_test.go
Normal file
119
internal/attestation/idkeydigest/idkeydigest_test.go
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package idkeydigest
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func TestMarshal(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
dgst IDKeyDigests
|
||||
wantYAML string
|
||||
wantJSON string
|
||||
}{
|
||||
"digest": {
|
||||
dgst: IDKeyDigests{{0x01, 0x02, 0x03, 0x04}, {0xff, 0xff, 0xff, 0xff}},
|
||||
wantJSON: `["01020304","ffffffff"]`,
|
||||
wantYAML: `
|
||||
- "01020304"
|
||||
- "ffffffff"`,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
{
|
||||
// YAML
|
||||
yaml, err := yaml.Marshal(tc.dgst)
|
||||
require.NoError(err)
|
||||
|
||||
assert.YAMLEq(tc.wantYAML, string(yaml))
|
||||
}
|
||||
|
||||
{
|
||||
// JSON
|
||||
json, err := json.Marshal(tc.dgst)
|
||||
require.NoError(err)
|
||||
|
||||
assert.JSONEq(tc.wantJSON, string(json))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshal(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
yaml string
|
||||
json string
|
||||
wantDgst IDKeyDigests
|
||||
wantErr bool
|
||||
}{
|
||||
"digest struct": {
|
||||
json: `["57486a447ec0f1958002a22a06b7673b9fd27d11e1c6527498056054c5fa92d23c50f9de44072760fe2b6fb89740b696","0356215882a825279a85b300b0b742931d113bf7e32dde2e50ffde7ec743ca491ecdd7f336dc28a6e0b2bb57af7a44a3"]`,
|
||||
yaml: `
|
||||
- "57486a447ec0f1958002a22a06b7673b9fd27d11e1c6527498056054c5fa92d23c50f9de44072760fe2b6fb89740b696"
|
||||
- "0356215882a825279a85b300b0b742931d113bf7e32dde2e50ffde7ec743ca491ecdd7f336dc28a6e0b2bb57af7a44a3"`,
|
||||
wantDgst: IDKeyDigests{
|
||||
{0x57, 0x48, 0x6a, 0x44, 0x7e, 0xc0, 0xf1, 0x95, 0x80, 0x02, 0xa2, 0x2a, 0x06, 0xb7, 0x67, 0x3b, 0x9f, 0xd2, 0x7d, 0x11, 0xe1, 0xc6, 0x52, 0x74, 0x98, 0x05, 0x60, 0x54, 0xc5, 0xfa, 0x92, 0xd2, 0x3c, 0x50, 0xf9, 0xde, 0x44, 0x07, 0x27, 0x60, 0xfe, 0x2b, 0x6f, 0xb8, 0x97, 0x40, 0xb6, 0x96},
|
||||
{0x03, 0x56, 0x21, 0x58, 0x82, 0xa8, 0x25, 0x27, 0x9a, 0x85, 0xb3, 0x00, 0xb0, 0xb7, 0x42, 0x93, 0x1d, 0x11, 0x3b, 0xf7, 0xe3, 0x2d, 0xde, 0x2e, 0x50, 0xff, 0xde, 0x7e, 0xc7, 0x43, 0xca, 0x49, 0x1e, 0xcd, 0xd7, 0xf3, 0x36, 0xdc, 0x28, 0xa6, 0xe0, 0xb2, 0xbb, 0x57, 0xaf, 0x7a, 0x44, 0xa3},
|
||||
},
|
||||
},
|
||||
"legacy digest as string": {
|
||||
json: `"57486a447ec0f1958002a22a06b7673b9fd27d11e1c6527498056054c5fa92d23c50f9de44072760fe2b6fb89740b696"`,
|
||||
yaml: `"57486a447ec0f1958002a22a06b7673b9fd27d11e1c6527498056054c5fa92d23c50f9de44072760fe2b6fb89740b696"`,
|
||||
wantDgst: IDKeyDigests{{0x57, 0x48, 0x6a, 0x44, 0x7e, 0xc0, 0xf1, 0x95, 0x80, 0x02, 0xa2, 0x2a, 0x06, 0xb7, 0x67, 0x3b, 0x9f, 0xd2, 0x7d, 0x11, 0xe1, 0xc6, 0x52, 0x74, 0x98, 0x05, 0x60, 0x54, 0xc5, 0xfa, 0x92, 0xd2, 0x3c, 0x50, 0xf9, 0xde, 0x44, 0x07, 0x27, 0x60, 0xfe, 0x2b, 0x6f, 0xb8, 0x97, 0x40, 0xb6, 0x96}},
|
||||
},
|
||||
"invalid length": {
|
||||
json: `"010203"`,
|
||||
yaml: `"010203"`,
|
||||
wantDgst: IDKeyDigests{{}},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
{
|
||||
// YAML
|
||||
var dgst IDKeyDigests
|
||||
err := yaml.Unmarshal([]byte(tc.yaml), &dgst)
|
||||
if tc.wantErr {
|
||||
require.Error(err)
|
||||
} else {
|
||||
require.NoError(err)
|
||||
|
||||
assert.Equal(tc.wantDgst, dgst)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// JSON
|
||||
var dgst IDKeyDigests
|
||||
err := json.Unmarshal([]byte(tc.json), &dgst)
|
||||
if tc.wantErr {
|
||||
require.Error(err)
|
||||
} else {
|
||||
require.NoError(err)
|
||||
|
||||
assert.Equal(tc.wantDgst, dgst)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package vmtype
|
||||
|
||||
import "strings"
|
||||
|
||||
//go:generate stringer -type=VMType
|
||||
|
||||
// VMType describes different vm types we support. Introduced for Azure SNP / Trusted Launch attestation.
|
||||
type VMType uint32
|
||||
|
||||
const (
|
||||
// Unknown is the default value for VMType and should not be used.
|
||||
Unknown VMType = iota
|
||||
// AzureCVM is an Azure Confidential Virtual Machine (CVM).
|
||||
AzureCVM
|
||||
// AzureTrustedLaunch is an Azure Trusted Launch VM.
|
||||
AzureTrustedLaunch
|
||||
)
|
||||
|
||||
// FromString returns a VMType from a string.
|
||||
func FromString(s string) VMType {
|
||||
s = strings.ToLower(s)
|
||||
switch s {
|
||||
case "azurecvm":
|
||||
return AzureCVM
|
||||
case "azuretrustedlaunch":
|
||||
return AzureTrustedLaunch
|
||||
default:
|
||||
return Unknown
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
// Code generated by "stringer -type=VMType"; DO NOT EDIT.
|
||||
|
||||
package vmtype
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[Unknown-0]
|
||||
_ = x[AzureCVM-1]
|
||||
_ = x[AzureTrustedLaunch-2]
|
||||
}
|
||||
|
||||
const _VMType_name = "UnknownAzureCVMAzureTrustedLaunch"
|
||||
|
||||
var _VMType_index = [...]uint8{0, 7, 15, 33}
|
||||
|
||||
func (i VMType) String() string {
|
||||
if i >= VMType(len(_VMType_index)-1) {
|
||||
return "VMType(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _VMType_name[_VMType_index[i]:_VMType_index[i+1]]
|
||||
}
|
@ -17,6 +17,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"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"
|
||||
@ -33,6 +34,10 @@ import (
|
||||
// types in other packages.
|
||||
type Measurements = measurements.M
|
||||
|
||||
// Digests is a required alias since docgen is not able to work with
|
||||
// types in other packages.
|
||||
type Digests = idkeydigest.IDKeyDigests
|
||||
|
||||
const (
|
||||
// Version2 is the second version number for Constellation config file.
|
||||
Version2 = "v2"
|
||||
@ -161,8 +166,8 @@ type AzureConfig struct {
|
||||
// Enable secure boot for VMs. If enabled, the OS image has to include a virtual machine guest state (VMGS) blob.
|
||||
SecureBoot *bool `yaml:"secureBoot" validate:"required"`
|
||||
// description: |
|
||||
// Expected value for the field 'idkeydigest' in the AMD SEV-SNP attestation report. Only usable with ConfidentialVMs. See 4.6 and 7.3 in: https://www.amd.com/system/files/TechDocs/56860.pdf
|
||||
IDKeyDigest string `yaml:"idKeyDigest" validate:"required_if=EnforceIdKeyDigest true,omitempty,hexadecimal,len=96"`
|
||||
// List of accepted values for the field 'idkeydigest' in the AMD SEV-SNP attestation report. Only usable with ConfidentialVMs. See 4.6 and 7.3 in: https://www.amd.com/system/files/TechDocs/56860.pdf
|
||||
IDKeyDigests Digests `yaml:"idKeyDigests" validate:"required_if=EnforceIdKeyDigest true,omitempty"`
|
||||
// description: |
|
||||
// Enforce the specified idKeyDigest value during remote attestation.
|
||||
EnforceIDKeyDigest *bool `yaml:"enforceIdKeyDigest" validate:"required"`
|
||||
@ -255,7 +260,7 @@ func Default() *Config {
|
||||
InstanceType: "Standard_DC4as_v5",
|
||||
StateDiskType: "Premium_LRS",
|
||||
DeployCSIDriver: func() *bool { b := true; return &b }(),
|
||||
IDKeyDigest: "57486a447ec0f1958002a22a06b7673b9fd27d11e1c6527498056054c5fa92d23c50f9de44072760fe2b6fb89740b696",
|
||||
IDKeyDigests: idkeydigest.DefaultsFor(cloudprovider.Azure),
|
||||
EnforceIDKeyDigest: func() *bool { b := true; return &b }(),
|
||||
ConfidentialVM: func() *bool { b := true; return &b }(),
|
||||
SecureBoot: func() *bool { b := false; return &b }(),
|
||||
@ -428,6 +433,14 @@ func (c *Config) EnforcedPCRs() []uint32 {
|
||||
}
|
||||
}
|
||||
|
||||
// IDKeyDigests returns the ID Key Digests for the configured cloud provider.
|
||||
func (c *Config) IDKeyDigests() idkeydigest.IDKeyDigests {
|
||||
if c.Provider.Azure != nil {
|
||||
return c.Provider.Azure.IDKeyDigests
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeployCSIDriver returns whether the CSI driver should be deployed for a given cloud provider.
|
||||
func (c *Config) DeployCSIDriver() bool {
|
||||
return c.Provider.Azure != nil && c.Provider.Azure.DeployCSIDriver != nil && *c.Provider.Azure.DeployCSIDriver ||
|
||||
|
@ -238,11 +238,11 @@ func init() {
|
||||
AzureConfigDoc.Fields[11].Note = ""
|
||||
AzureConfigDoc.Fields[11].Description = "Enable secure boot for VMs. If enabled, the OS image has to include a virtual machine guest state (VMGS) blob."
|
||||
AzureConfigDoc.Fields[11].Comments[encoder.LineComment] = "Enable secure boot for VMs. If enabled, the OS image has to include a virtual machine guest state (VMGS) blob."
|
||||
AzureConfigDoc.Fields[12].Name = "idKeyDigest"
|
||||
AzureConfigDoc.Fields[12].Type = "string"
|
||||
AzureConfigDoc.Fields[12].Name = "idKeyDigests"
|
||||
AzureConfigDoc.Fields[12].Type = "Digests"
|
||||
AzureConfigDoc.Fields[12].Note = ""
|
||||
AzureConfigDoc.Fields[12].Description = "Expected value for the field 'idkeydigest' in the AMD SEV-SNP attestation report. Only usable with ConfidentialVMs. See 4.6 and 7.3 in: https://www.amd.com/system/files/TechDocs/56860.pdf"
|
||||
AzureConfigDoc.Fields[12].Comments[encoder.LineComment] = "Expected value for the field 'idkeydigest' in the AMD SEV-SNP attestation report. Only usable with ConfidentialVMs. See 4.6 and 7.3 in: https://www.amd.com/system/files/TechDocs/56860.pdf"
|
||||
AzureConfigDoc.Fields[12].Description = "List of accepted values for the field 'idkeydigest' in the AMD SEV-SNP attestation report. Only usable with ConfidentialVMs. See 4.6 and 7.3 in: https://www.amd.com/system/files/TechDocs/56860.pdf"
|
||||
AzureConfigDoc.Fields[12].Comments[encoder.LineComment] = "List of accepted values for the field 'idkeydigest' in the AMD SEV-SNP attestation report. Only usable with ConfidentialVMs. See 4.6 and 7.3 in: https://www.amd.com/system/files/TechDocs/56860.pdf"
|
||||
AzureConfigDoc.Fields[13].Name = "enforceIdKeyDigest"
|
||||
AzureConfigDoc.Fields[13].Type = "bool"
|
||||
AzureConfigDoc.Fields[13].Note = ""
|
||||
|
@ -111,7 +111,7 @@ const (
|
||||
// MeasurementSecretFilename is the filename of the secret used in creation of the clusterID.
|
||||
MeasurementSecretFilename = "measurementSecret"
|
||||
// IDKeyDigestFilename is the name of the file holding the currently enforced idkeydigest.
|
||||
IDKeyDigestFilename = "idkeydigest"
|
||||
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.
|
||||
|
@ -8,7 +8,7 @@ package watcher
|
||||
|
||||
import (
|
||||
"encoding/asn1"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
@ -21,6 +21,7 @@ import (
|
||||
"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/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"
|
||||
@ -45,25 +46,25 @@ func NewValidator(log *logger.Logger, csp string, fileHandler file.Handler, azur
|
||||
var newValidator newValidatorFunc
|
||||
switch cloudprovider.FromString(csp) {
|
||||
case cloudprovider.AWS:
|
||||
newValidator = func(m measurements.M, _ []byte, _ bool, log *logger.Logger) atls.Validator {
|
||||
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 []byte, enforceIdKeyDigest bool, log *logger.Logger) atls.Validator {
|
||||
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 []byte, enforceIdKeyDigest bool, log *logger.Logger) atls.Validator {
|
||||
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, _ []byte, _ bool, log *logger.Logger) atls.Validator {
|
||||
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, _ []byte, _ bool, log *logger.Logger) atls.Validator {
|
||||
newValidator = func(m measurements.M, _ idkeydigest.IDKeyDigests, _ bool, log *logger.Logger) atls.Validator {
|
||||
return qemu.NewValidator(m, log)
|
||||
}
|
||||
default:
|
||||
@ -124,7 +125,7 @@ func (u *Updatable) Update() error {
|
||||
return err
|
||||
}
|
||||
|
||||
var idkeydigest []byte
|
||||
var digest idkeydigest.IDKeyDigests
|
||||
var enforceIDKeyDigest bool
|
||||
if u.csp == cloudprovider.Azure && u.azureCVM {
|
||||
u.log.Infof("Updating encforceIdKeyDigest value")
|
||||
@ -143,16 +144,15 @@ func (u *Updatable) Update() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
idkeydigest, err = hex.DecodeString(string(idkeydigestRaw))
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing hexstring: %s: %w", idkeydigestRaw, err)
|
||||
if err = json.Unmarshal(idkeydigestRaw, &digest); err != nil {
|
||||
return fmt.Errorf("unmarshaling content of IDKeyDigestFilename: %s: %w", idkeydigestRaw, err)
|
||||
}
|
||||
u.log.Debugf("New idkeydigest: %x", idkeydigest)
|
||||
u.log.Debugf("New idkeydigest: %v", digest)
|
||||
}
|
||||
|
||||
u.Validator = u.newValidator(measurements, idkeydigest, enforceIDKeyDigest, u.log)
|
||||
u.Validator = u.newValidator(measurements, digest, enforceIDKeyDigest, u.log)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type newValidatorFunc func(measurements measurements.M, idkeydigest []byte, enforceIdKeyDigest bool, log *logger.Logger) atls.Validator
|
||||
type newValidatorFunc func(measurements measurements.M, idkeydigest idkeydigest.IDKeyDigests, enforceIdKeyDigest bool, log *logger.Logger) atls.Validator
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"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/constants"
|
||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||
@ -125,7 +126,7 @@ func TestUpdate(t *testing.T) {
|
||||
defer oidLock.Unlock()
|
||||
oid = newOID
|
||||
}
|
||||
newValidator := func(m measurements.M, idkeydigest []byte, enforceIdKeyDigest bool, _ *logger.Logger) atls.Validator {
|
||||
newValidator := func(m measurements.M, digest idkeydigest.IDKeyDigests, enforceIdKeyDigest bool, _ *logger.Logger) atls.Validator {
|
||||
oidLock.Lock()
|
||||
defer oidLock.Unlock()
|
||||
return fakeValidator{fakeOID: oid}
|
||||
@ -225,7 +226,7 @@ func TestOIDConcurrency(t *testing.T) {
|
||||
[]byte{},
|
||||
))
|
||||
|
||||
newValidator := func(m measurements.M, idkeydigest []byte, enforceIdKeyDigest bool, _ *logger.Logger) atls.Validator {
|
||||
newValidator := func(m measurements.M, digest idkeydigest.IDKeyDigests, enforceIdKeyDigest bool, _ *logger.Logger) atls.Validator {
|
||||
return fakeValidator{fakeOID: fakeOID{1, 3, 9900, 1}}
|
||||
}
|
||||
// create server
|
||||
@ -261,7 +262,7 @@ func TestUpdateConcurrency(t *testing.T) {
|
||||
validator := &Updatable{
|
||||
log: logger.NewTest(t),
|
||||
fileHandler: handler,
|
||||
newValidator: func(m measurements.M, idkeydigest []byte, enforceIdKeyDigest bool, _ *logger.Logger) atls.Validator {
|
||||
newValidator: func(m measurements.M, digest idkeydigest.IDKeyDigests, enforceIdKeyDigest bool, _ *logger.Logger) atls.Validator {
|
||||
return fakeValidator{fakeOID: fakeOID{1, 3, 9900, 1}}
|
||||
},
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user