AB#2350: Configurably enforce idkeydigest on Azure

* Add join-config entry for "enforceIdKeyDigest" bool
* Add join-config entry for "idkeydigest"
* Initially filled with TPM value from bootstrapper
* Add config entries for idkeydigest and enforceIdKeyDigest
* Extend azure attestation validator to check idkeydigest,
if configured.
* Update unittests
* Add logger to NewValidator for all CSPs
* Add csp to Updateable type

Co-authored-by: Thomas Tendyck <51411342+thomasten@users.noreply.github.com>
Co-authored-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
Otto Bittner 2022-08-29 16:41:09 +02:00
parent c84e44913b
commit 4adc19b7f5
31 changed files with 350 additions and 136 deletions

View File

@ -94,7 +94,7 @@ func main() {
} }
clusterInitJoiner = kubernetes.New( clusterInitJoiner = kubernetes.New(
"gcp", k8sapi.NewKubernetesUtil(), &k8sapi.CoreOSConfiguration{}, kubectl.New(), &gcpcloud.CloudControllerManager{}, "gcp", k8sapi.NewKubernetesUtil(), &k8sapi.CoreOSConfiguration{}, kubectl.New(), &gcpcloud.CloudControllerManager{},
&gcpcloud.CloudNodeManager{}, &gcpcloud.Autoscaler{}, metadata, pcrsJSON, &gcpcloud.CloudNodeManager{}, &gcpcloud.Autoscaler{}, metadata, pcrsJSON, nil,
) )
openTPM = vtpm.OpenVTPM openTPM = vtpm.OpenVTPM
fs = afero.NewOsFs() fs = afero.NewOsFs()
@ -108,6 +108,11 @@ func main() {
log.With(zap.Error(err)).Fatalf("Failed to get selected PCRs") log.With(zap.Error(err)).Fatalf("Failed to get selected PCRs")
} }
idKeyDigest, err := azure.GetIdKeyDigest(vtpm.OpenVTPM)
if err != nil {
log.With(zap.Error(err)).Fatalf("Failed to get idkeydigest")
}
issuer = azure.NewIssuer() issuer = azure.NewIssuer()
metadata, err := azurecloud.NewMetadata(ctx) metadata, err := azurecloud.NewMetadata(ctx)
@ -125,7 +130,7 @@ func main() {
} }
clusterInitJoiner = kubernetes.New( clusterInitJoiner = kubernetes.New(
"azure", k8sapi.NewKubernetesUtil(), &k8sapi.CoreOSConfiguration{}, kubectl.New(), azurecloud.NewCloudControllerManager(metadata), "azure", k8sapi.NewKubernetesUtil(), &k8sapi.CoreOSConfiguration{}, kubectl.New(), azurecloud.NewCloudControllerManager(metadata),
&azurecloud.CloudNodeManager{}, &azurecloud.Autoscaler{}, metadata, pcrsJSON, &azurecloud.CloudNodeManager{}, &azurecloud.Autoscaler{}, metadata, pcrsJSON, idKeyDigest,
) )
openTPM = vtpm.OpenVTPM openTPM = vtpm.OpenVTPM
@ -146,7 +151,7 @@ func main() {
} }
clusterInitJoiner = kubernetes.New( clusterInitJoiner = kubernetes.New(
"qemu", k8sapi.NewKubernetesUtil(), &k8sapi.CoreOSConfiguration{}, kubectl.New(), &qemucloud.CloudControllerManager{}, "qemu", k8sapi.NewKubernetesUtil(), &k8sapi.CoreOSConfiguration{}, kubectl.New(), &qemucloud.CloudControllerManager{},
&qemucloud.CloudNodeManager{}, &qemucloud.Autoscaler{}, metadata, pcrsJSON, &qemucloud.CloudNodeManager{}, &qemucloud.Autoscaler{}, metadata, pcrsJSON, nil,
) )
metadataAPI = metadata metadataAPI = metadata

View File

@ -15,7 +15,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. // 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( func (c *clusterFake) InitCluster(
context.Context, []string, string, string, []byte, []uint32, context.Context, []string, string, string, []byte, []uint32, bool,
resources.KMSConfig, map[string]string, []byte, *logger.Logger, resources.KMSConfig, map[string]string, []byte, *logger.Logger,
) ([]byte, error) { ) ([]byte, error) {
return []byte{}, nil return []byte{}, nil

View File

@ -37,6 +37,7 @@ type InitRequest struct {
Salt []byte `protobuf:"bytes,10,opt,name=salt,proto3" json:"salt,omitempty"` Salt []byte `protobuf:"bytes,10,opt,name=salt,proto3" json:"salt,omitempty"`
HelmDeployments []byte `protobuf:"bytes,11,opt,name=helm_deployments,json=helmDeployments,proto3" json:"helm_deployments,omitempty"` HelmDeployments []byte `protobuf:"bytes,11,opt,name=helm_deployments,json=helmDeployments,proto3" json:"helm_deployments,omitempty"`
EnforcedPcrs []uint32 `protobuf:"varint,12,rep,packed,name=enforced_pcrs,json=enforcedPcrs,proto3" json:"enforced_pcrs,omitempty"` EnforcedPcrs []uint32 `protobuf:"varint,12,rep,packed,name=enforced_pcrs,json=enforcedPcrs,proto3" json:"enforced_pcrs,omitempty"`
EnforceIdkeydigest bool `protobuf:"varint,13,opt,name=enforce_idkeydigest,json=enforceIdkeydigest,proto3" json:"enforce_idkeydigest,omitempty"`
} }
func (x *InitRequest) Reset() { func (x *InitRequest) Reset() {
@ -155,6 +156,13 @@ func (x *InitRequest) GetEnforcedPcrs() []uint32 {
return nil return nil
} }
func (x *InitRequest) GetEnforceIdkeydigest() bool {
if x != nil {
return x.EnforceIdkeydigest
}
return false
}
type InitResponse struct { type InitResponse struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -277,7 +285,7 @@ var File_init_proto protoreflect.FileDescriptor
var file_init_proto_rawDesc = []byte{ var file_init_proto_rawDesc = []byte{
0x0a, 0x0a, 0x69, 0x6e, 0x69, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x69, 0x6e, 0x0a, 0x0a, 0x69, 0x6e, 0x69, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x69, 0x6e,
0x69, 0x74, 0x22, 0x85, 0x04, 0x0a, 0x0b, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x69, 0x74, 0x22, 0xb6, 0x04, 0x0a, 0x0b, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x12, 0x36, 0x0a, 0x17, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x69, 0x6e, 0x73, 0x74, 0x12, 0x36, 0x0a, 0x17, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x69, 0x6e,
0x67, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x67, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20,
0x03, 0x28, 0x09, 0x52, 0x15, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x03, 0x28, 0x09, 0x52, 0x15, 0x61, 0x75, 0x74, 0x6f, 0x73, 0x63, 0x61, 0x6c, 0x69, 0x6e, 0x67,
@ -309,26 +317,29 @@ var file_init_proto_rawDesc = []byte{
0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x68, 0x65, 0x6c, 0x6d, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x68, 0x65, 0x6c, 0x6d, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79,
0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65,
0x64, 0x5f, 0x70, 0x63, 0x72, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x65, 0x6e, 0x64, 0x5f, 0x70, 0x63, 0x72, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x0c, 0x65, 0x6e,
0x66, 0x6f, 0x72, 0x63, 0x65, 0x64, 0x50, 0x63, 0x72, 0x73, 0x22, 0x68, 0x0a, 0x0c, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x64, 0x50, 0x63, 0x72, 0x73, 0x12, 0x2f, 0x0a, 0x13, 0x65, 0x6e,
0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x6b, 0x75, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x6b, 0x65, 0x79, 0x64, 0x69, 0x67, 0x65, 0x73,
0x62, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65,
0x6b, 0x75, 0x62, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x77, 0x49, 0x64, 0x6b, 0x65, 0x79, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x22, 0x68, 0x0a, 0x0c, 0x49,
0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6f, 0x77, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x6b,
0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x75, 0x62, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52,
0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x0a, 0x6b, 0x75, 0x62, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x6f,
0x65, 0x72, 0x49, 0x64, 0x22, 0x47, 0x0a, 0x0a, 0x53, 0x53, 0x48, 0x55, 0x73, 0x65, 0x72, 0x4b, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6f,
0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x77, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65,
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73,
0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, 0x47, 0x0a, 0x0a, 0x53, 0x53, 0x48, 0x55, 0x73, 0x65, 0x72,
0x28, 0x09, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x32, 0x34, 0x0a, 0x4b, 0x65, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18,
0x03, 0x41, 0x50, 0x49, 0x12, 0x2d, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x11, 0x2e, 0x69, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12,
0x6e, 0x69, 0x74, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20,
0x12, 0x2e, 0x69, 0x6e, 0x69, 0x74, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x32, 0x34,
0x6e, 0x73, 0x65, 0x42, 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x0a, 0x03, 0x41, 0x50, 0x49, 0x12, 0x2d, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x11, 0x2e,
0x6d, 0x2f, 0x65, 0x64, 0x67, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x73, 0x79, 0x73, 0x2f, 0x63, 0x6f, 0x69, 0x6e, 0x69, 0x74, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x6e, 0x73, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x62, 0x6f, 0x6f, 0x74, 0x1a, 0x12, 0x2e, 0x69, 0x6e, 0x69, 0x74, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70,
0x73, 0x74, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x2f, 0x69, 0x6e, 0x69, 0x74, 0x70, 0x72, 0x6f, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x6f, 0x6d, 0x2f, 0x65, 0x64, 0x67, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x73, 0x79, 0x73, 0x2f, 0x63,
0x6f, 0x6e, 0x73, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x62, 0x6f, 0x6f,
0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x2f, 0x69, 0x6e, 0x69, 0x74, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (

View File

@ -21,6 +21,7 @@ message InitRequest {
bytes salt = 10; bytes salt = 10;
bytes helm_deployments = 11; bytes helm_deployments = 11;
repeated uint32 enforced_pcrs = 12; repeated uint32 enforced_pcrs = 12;
bool enforce_idkeydigest = 13;
} }
message InitResponse { message InitResponse {

View File

@ -120,6 +120,7 @@ func (s *Server) Init(ctx context.Context, req *initproto.InitRequest) (*initpro
req.KubernetesVersion, req.KubernetesVersion,
measurementSalt, measurementSalt,
req.EnforcedPcrs, req.EnforcedPcrs,
req.EnforceIdkeydigest,
resources.KMSConfig{ resources.KMSConfig{
MasterSecret: req.MasterSecret, MasterSecret: req.MasterSecret,
Salt: req.Salt, Salt: req.Salt,
@ -203,6 +204,7 @@ type ClusterInitializer interface {
k8sVersion string, k8sVersion string,
measurementSalt []byte, measurementSalt []byte,
enforcedPcrs []uint32, enforcedPcrs []uint32,
enforceIdKeyDigest bool,
kmsConfig resources.KMSConfig, kmsConfig resources.KMSConfig,
sshUserKeys map[string]string, sshUserKeys map[string]string,
helmDeployments []byte, helmDeployments []byte,

View File

@ -283,7 +283,7 @@ type stubClusterInitializer struct {
} }
func (i *stubClusterInitializer) InitCluster( func (i *stubClusterInitializer) InitCluster(
context.Context, []string, string, string, []byte, []uint32, context.Context, []string, string, string, []byte, []uint32, bool,
resources.KMSConfig, map[string]string, []byte, *logger.Logger, resources.KMSConfig, map[string]string, []byte, *logger.Logger,
) ([]byte, error) { ) ([]byte, error) {
return i.initClusterKubeconfig, i.initClusterErr return i.initClusterKubeconfig, i.initClusterErr

View File

@ -3,6 +3,7 @@ package resources
import ( import (
"fmt" "fmt"
"github.com/edgelesssys/constellation/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/internal/constants" "github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/kubernetes" "github.com/edgelesssys/constellation/internal/kubernetes"
"github.com/edgelesssys/constellation/internal/versions" "github.com/edgelesssys/constellation/internal/versions"
@ -23,7 +24,16 @@ type joinServiceDaemonset struct {
} }
// NewJoinServiceDaemonset returns a daemonset for the join service. // NewJoinServiceDaemonset returns a daemonset for the join service.
func NewJoinServiceDaemonset(csp, measurementsJSON, enforcedPCRsJSON string, measurementSalt []byte) *joinServiceDaemonset { func NewJoinServiceDaemonset(csp, measurementsJSON, enforcedPCRsJSON, initialIdKeyDigest, enforceIdKeyDigest string, measurementSalt []byte) *joinServiceDaemonset {
joinConfigData := map[string]string{
constants.MeasurementsFilename: measurementsJSON,
constants.EnforcedPCRsFilename: enforcedPCRsJSON,
}
if cloudprovider.FromString(csp) == cloudprovider.Azure {
joinConfigData[constants.EnforceIdKeyDigestFilename] = enforceIdKeyDigest
joinConfigData[constants.IdKeyDigestFilename] = initialIdKeyDigest
}
return &joinServiceDaemonset{ return &joinServiceDaemonset{
ClusterRole: rbac.ClusterRole{ ClusterRole: rbac.ClusterRole{
TypeMeta: meta.TypeMeta{ TypeMeta: meta.TypeMeta{
@ -240,10 +250,7 @@ func NewJoinServiceDaemonset(csp, measurementsJSON, enforcedPCRsJSON string, mea
Name: constants.JoinConfigMap, Name: constants.JoinConfigMap,
Namespace: constants.ConstellationNamespace, Namespace: constants.ConstellationNamespace,
}, },
Data: map[string]string{ Data: joinConfigData,
constants.MeasurementsFilename: measurementsJSON,
constants.EnforcedPCRsFilename: enforcedPCRsJSON,
},
BinaryData: map[string][]byte{ BinaryData: map[string][]byte{
constants.MeasurementSaltFilename: measurementSalt, constants.MeasurementSaltFilename: measurementSalt,
}, },

View File

@ -9,7 +9,7 @@ import (
) )
func TestNewJoinServiceDaemonset(t *testing.T) { func TestNewJoinServiceDaemonset(t *testing.T) {
deployment := NewJoinServiceDaemonset("csp", "measurementsJSON", "enforcedPCRsJSON", []byte{0x0, 0x1, 0x2}) deployment := NewJoinServiceDaemonset("csp", "measurementsJSON", "enforcedPCRsJSON", "deadbeef", "true", []byte{0x0, 0x1, 0x2})
deploymentYAML, err := deployment.Marshal() deploymentYAML, err := deployment.Marshal()
require.NoError(t, err) require.NoError(t, err)

View File

@ -2,9 +2,11 @@ package kubernetes
import ( import (
"context" "context"
"encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net" "net"
"strconv"
"strings" "strings"
"github.com/edgelesssys/constellation/bootstrapper/internal/kubernetes/k8sapi" "github.com/edgelesssys/constellation/bootstrapper/internal/kubernetes/k8sapi"
@ -45,12 +47,13 @@ type KubeWrapper struct {
clusterAutoscaler ClusterAutoscaler clusterAutoscaler ClusterAutoscaler
providerMetadata ProviderMetadata providerMetadata ProviderMetadata
initialMeasurementsJSON []byte initialMeasurementsJSON []byte
initialIdKeyDigest []byte
getIPAddr func() (string, error) getIPAddr func() (string, error)
} }
// New creates a new KubeWrapper with real values. // New creates a new KubeWrapper with real values.
func New(cloudProvider string, clusterUtil clusterUtil, configProvider configurationProvider, client k8sapi.Client, cloudControllerManager CloudControllerManager, func New(cloudProvider string, clusterUtil clusterUtil, configProvider configurationProvider, client k8sapi.Client, cloudControllerManager CloudControllerManager,
cloudNodeManager CloudNodeManager, clusterAutoscaler ClusterAutoscaler, providerMetadata ProviderMetadata, initialMeasurementsJSON []byte, cloudNodeManager CloudNodeManager, clusterAutoscaler ClusterAutoscaler, providerMetadata ProviderMetadata, initialMeasurementsJSON, initialIdKeyDigest []byte,
) *KubeWrapper { ) *KubeWrapper {
return &KubeWrapper{ return &KubeWrapper{
cloudProvider: cloudProvider, cloudProvider: cloudProvider,
@ -63,6 +66,7 @@ func New(cloudProvider string, clusterUtil clusterUtil, configProvider configura
clusterAutoscaler: clusterAutoscaler, clusterAutoscaler: clusterAutoscaler,
providerMetadata: providerMetadata, providerMetadata: providerMetadata,
initialMeasurementsJSON: initialMeasurementsJSON, initialMeasurementsJSON: initialMeasurementsJSON,
initialIdKeyDigest: initialIdKeyDigest,
getIPAddr: getIPAddr, getIPAddr: getIPAddr,
} }
} }
@ -70,7 +74,7 @@ func New(cloudProvider string, clusterUtil clusterUtil, configProvider configura
// InitCluster initializes a new Kubernetes cluster and applies pod network provider. // InitCluster initializes a new Kubernetes cluster and applies pod network provider.
func (k *KubeWrapper) InitCluster( func (k *KubeWrapper) InitCluster(
ctx context.Context, autoscalingNodeGroups []string, cloudServiceAccountURI, versionString string, measurementSalt []byte, ctx context.Context, autoscalingNodeGroups []string, cloudServiceAccountURI, versionString string, measurementSalt []byte,
enforcedPCRs []uint32, kmsConfig resources.KMSConfig, sshUsers map[string]string, helmDeployments []byte, log *logger.Logger, enforcedPCRs []uint32, enforceIdKeyDigest bool, kmsConfig resources.KMSConfig, sshUsers map[string]string, helmDeployments []byte, log *logger.Logger,
) ([]byte, error) { ) ([]byte, error) {
k8sVersion, err := versions.NewValidK8sVersion(versionString) k8sVersion, err := versions.NewValidK8sVersion(versionString)
if err != nil { if err != nil {
@ -174,7 +178,7 @@ func (k *KubeWrapper) InitCluster(
return nil, fmt.Errorf("setting up kms: %w", err) return nil, fmt.Errorf("setting up kms: %w", err)
} }
if err := k.setupJoinService(k.cloudProvider, k.initialMeasurementsJSON, measurementSalt, enforcedPCRs); err != nil { if err := k.setupJoinService(k.cloudProvider, k.initialMeasurementsJSON, measurementSalt, enforcedPCRs, k.initialIdKeyDigest, enforceIdKeyDigest); err != nil {
return nil, fmt.Errorf("setting up join service failed: %w", err) return nil, fmt.Errorf("setting up join service failed: %w", err)
} }
@ -305,7 +309,7 @@ func (k *KubeWrapper) GetKubeconfig() ([]byte, error) {
} }
func (k *KubeWrapper) setupJoinService( func (k *KubeWrapper) setupJoinService(
csp string, measurementsJSON, measurementSalt []byte, enforcedPCRs []uint32, csp string, measurementsJSON, measurementSalt []byte, enforcedPCRs []uint32, initialIdKeyDigest []byte, enforceIdKeyDigest bool,
) error { ) error {
enforcedPCRsJSON, err := json.Marshal(enforcedPCRs) enforcedPCRsJSON, err := json.Marshal(enforcedPCRs)
if err != nil { if err != nil {
@ -313,7 +317,7 @@ func (k *KubeWrapper) setupJoinService(
} }
joinConfiguration := resources.NewJoinServiceDaemonset( joinConfiguration := resources.NewJoinServiceDaemonset(
csp, string(measurementsJSON), string(enforcedPCRsJSON), measurementSalt, csp, string(measurementsJSON), string(enforcedPCRsJSON), hex.EncodeToString(initialIdKeyDigest), strconv.FormatBool(enforceIdKeyDigest), measurementSalt,
) )
return k.clusterUtil.SetupJoinService(k.client, joinConfiguration) return k.clusterUtil.SetupJoinService(k.client, joinConfiguration)

View File

@ -302,7 +302,7 @@ func TestInitCluster(t *testing.T) {
_, err := kube.InitCluster( _, err := kube.InitCluster(
context.Background(), autoscalingNodeGroups, serviceAccountURI, string(tc.k8sVersion), context.Background(), autoscalingNodeGroups, serviceAccountURI, string(tc.k8sVersion),
nil, nil, resources.KMSConfig{MasterSecret: masterSecret}, nil, nil, logger.NewTest(t), nil, nil, false, resources.KMSConfig{MasterSecret: masterSecret}, nil, nil, logger.NewTest(t),
) )
if tc.wantErr { if tc.wantErr {

View File

@ -3,6 +3,7 @@ package cloudcmd
import ( import (
"crypto/sha256" "crypto/sha256"
"encoding/base64" "encoding/base64"
"encoding/hex"
"errors" "errors"
"fmt" "fmt"
@ -17,10 +18,12 @@ import (
) )
type Validator struct { type Validator struct {
provider cloudprovider.Provider provider cloudprovider.Provider
pcrs map[uint32][]byte pcrs map[uint32][]byte
enforcedPCRs []uint32 enforcedPCRs []uint32
validator atls.Validator idkeydigest []byte
enforceIdKeyDigest bool
validator atls.Validator
} }
func NewValidator(provider cloudprovider.Provider, config *config.Config) (*Validator, error) { func NewValidator(provider cloudprovider.Provider, config *config.Config) (*Validator, error) {
@ -32,6 +35,16 @@ func NewValidator(provider cloudprovider.Provider, config *config.Config) (*Vali
if err := v.setPCRs(config); err != nil { if err := v.setPCRs(config); err != nil {
return nil, err return nil, err
} }
if v.provider == cloudprovider.Azure {
idkeydigest, err := hex.DecodeString(config.Provider.Azure.IdKeyDigest)
if err != nil {
return nil, fmt.Errorf("bad config: decoding idkeydigest from config: %w", err)
}
v.enforceIdKeyDigest = *config.Provider.Azure.EnforceIdKeyDigest
v.idkeydigest = idkeydigest
}
return &v, nil return &v, nil
} }
@ -116,15 +129,15 @@ func (v *Validator) PCRS() map[uint32][]byte {
} }
func (v *Validator) updateValidator(cmd *cobra.Command) { func (v *Validator) updateValidator(cmd *cobra.Command) {
log := warnLogger{cmd: cmd}
switch v.provider { switch v.provider {
case cloudprovider.GCP: case cloudprovider.GCP:
v.validator = gcp.NewValidator(v.pcrs, v.enforcedPCRs) v.validator = gcp.NewValidator(v.pcrs, v.enforcedPCRs, log)
case cloudprovider.Azure: case cloudprovider.Azure:
v.validator = azure.NewValidator(v.pcrs, v.enforcedPCRs) v.validator = azure.NewValidator(v.pcrs, v.enforcedPCRs, v.idkeydigest, v.enforceIdKeyDigest, log)
case cloudprovider.QEMU: case cloudprovider.QEMU:
v.validator = qemu.NewValidator(v.pcrs, v.enforcedPCRs) v.validator = qemu.NewValidator(v.pcrs, v.enforcedPCRs, log)
} }
v.validator.AddLogger(warnLogger{cmd: cmd})
} }
func (v *Validator) checkPCRs(pcrs map[uint32][]byte, enforcedPCRs []uint32) error { func (v *Validator) checkPCRs(pcrs map[uint32][]byte, enforcedPCRs []uint32) error {

View File

@ -29,10 +29,12 @@ func TestNewValidator(t *testing.T) {
} }
testCases := map[string]struct { testCases := map[string]struct {
provider cloudprovider.Provider provider cloudprovider.Provider
config *config.Config config *config.Config
pcrs map[uint32][]byte pcrs map[uint32][]byte
wantErr bool enforceIdKeyDigest bool
idkeydigest string
wantErr bool
}{ }{
"gcp": { "gcp": {
provider: cloudprovider.GCP, provider: cloudprovider.GCP,
@ -61,6 +63,19 @@ func TestNewValidator(t *testing.T) {
pcrs: testPCRs, pcrs: testPCRs,
wantErr: true, wantErr: true,
}, },
"set idkeydigest": {
provider: cloudprovider.Azure,
pcrs: testPCRs,
idkeydigest: "414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141",
enforceIdKeyDigest: true,
},
"invalid idkeydigest": {
provider: cloudprovider.Azure,
pcrs: testPCRs,
idkeydigest: "41414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414",
enforceIdKeyDigest: true,
wantErr: true,
},
} }
for name, tc := range testCases { for name, tc := range testCases {
@ -74,7 +89,7 @@ func TestNewValidator(t *testing.T) {
} }
if tc.provider == cloudprovider.Azure { if tc.provider == cloudprovider.Azure {
measurements := config.Measurements(tc.pcrs) measurements := config.Measurements(tc.pcrs)
conf.Provider.Azure = &config.AzureConfig{Measurements: measurements} conf.Provider.Azure = &config.AzureConfig{Measurements: measurements, EnforceIdKeyDigest: &tc.enforceIdKeyDigest, IdKeyDigest: tc.idkeydigest}
} }
if tc.provider == cloudprovider.QEMU { if tc.provider == cloudprovider.QEMU {
measurements := config.Measurements(tc.pcrs) measurements := config.Measurements(tc.pcrs)
@ -96,6 +111,7 @@ func TestNewValidator(t *testing.T) {
func TestValidatorV(t *testing.T) { func TestValidatorV(t *testing.T) {
zero := []byte("00000000000000000000000000000000") zero := []byte("00000000000000000000000000000000")
newTestPCRs := func() map[uint32][]byte { newTestPCRs := func() map[uint32][]byte {
return map[uint32][]byte{ return map[uint32][]byte{
0: zero, 0: zero,
@ -122,17 +138,17 @@ func TestValidatorV(t *testing.T) {
"gcp": { "gcp": {
provider: cloudprovider.GCP, provider: cloudprovider.GCP,
pcrs: newTestPCRs(), pcrs: newTestPCRs(),
wantVs: gcp.NewValidator(newTestPCRs(), nil), wantVs: gcp.NewValidator(newTestPCRs(), nil, nil),
}, },
"azure": { "azure": {
provider: cloudprovider.Azure, provider: cloudprovider.Azure,
pcrs: newTestPCRs(), pcrs: newTestPCRs(),
wantVs: azure.NewValidator(newTestPCRs(), nil), wantVs: azure.NewValidator(newTestPCRs(), nil, nil, false, nil),
}, },
"qemu": { "qemu": {
provider: cloudprovider.QEMU, provider: cloudprovider.QEMU,
pcrs: newTestPCRs(), pcrs: newTestPCRs(),
wantVs: qemu.NewValidator(newTestPCRs(), nil), wantVs: qemu.NewValidator(newTestPCRs(), nil, nil),
}, },
} }

View File

@ -140,6 +140,7 @@ func initialize(cmd *cobra.Command, newDialer func(validator *cloudcmd.Validator
SshUserKeys: ssh.ToProtoSlice(sshUsers), SshUserKeys: ssh.ToProtoSlice(sshUsers),
HelmDeployments: helmDeployments, HelmDeployments: helmDeployments,
EnforcedPcrs: getEnforcedMeasurements(provider, config), EnforcedPcrs: getEnforcedMeasurements(provider, config),
EnforceIdkeydigest: getEnforceIdKeyDigest(provider, config),
} }
resp, err := initCall(cmd.Context(), newDialer(validator), flags.endpoint, req) resp, err := initCall(cmd.Context(), newDialer(validator), flags.endpoint, req)
if err != nil { if err != nil {
@ -236,6 +237,15 @@ func getEnforcedMeasurements(provider cloudprovider.Provider, config *config.Con
} }
} }
func getEnforceIdKeyDigest(provider cloudprovider.Provider, config *config.Config) bool {
switch provider {
case cloudprovider.Azure:
return *config.Provider.Azure.EnforceIdKeyDigest
default:
return false
}
}
// evalFlagArgs gets the flag values and does preprocessing of these values like // evalFlagArgs gets the flag values and does preprocessing of these values like
// reading the content from file path flags and deriving other values from flag combinations. // reading the content from file path flags and deriving other values from flag combinations.
func evalFlagArgs(cmd *cobra.Command, fileHandler file.Handler) (initFlags, error) { func evalFlagArgs(cmd *cobra.Command, fileHandler file.Handler) (initFlags, error) {

View File

@ -14,7 +14,6 @@ import (
"github.com/edgelesssys/constellation/bootstrapper/initproto" "github.com/edgelesssys/constellation/bootstrapper/initproto"
"github.com/edgelesssys/constellation/cli/internal/cloudcmd" "github.com/edgelesssys/constellation/cli/internal/cloudcmd"
"github.com/edgelesssys/constellation/internal/attestation/vtpm"
"github.com/edgelesssys/constellation/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/internal/cloud/cloudtypes" "github.com/edgelesssys/constellation/internal/cloud/cloudtypes"
"github.com/edgelesssys/constellation/internal/config" "github.com/edgelesssys/constellation/internal/config"
@ -505,8 +504,6 @@ func (v *testValidator) Validate(attDoc []byte, nonce []byte) ([]byte, error) {
return attestation.UserData, nil return attestation.UserData, nil
} }
func (v *testValidator) AddLogger(vtpm.WarnLogger) {}
type testIssuer struct { type testIssuer struct {
oid.Getter oid.Getter
pcrs map[uint32][]byte pcrs map[uint32][]byte

View File

@ -17,7 +17,6 @@ import (
"math/big" "math/big"
"time" "time"
"github.com/edgelesssys/constellation/internal/attestation/vtpm"
"github.com/edgelesssys/constellation/internal/crypto" "github.com/edgelesssys/constellation/internal/crypto"
"github.com/edgelesssys/constellation/internal/oid" "github.com/edgelesssys/constellation/internal/oid"
) )
@ -72,7 +71,6 @@ type Issuer interface {
type Validator interface { type Validator interface {
oid.Getter oid.Getter
Validate(attDoc []byte, nonce []byte) ([]byte, error) Validate(attDoc []byte, nonce []byte) ([]byte, error)
AddLogger(log vtpm.WarnLogger)
} }
// getATLSConfigForClientFunc returns a config setup function that is called once for every client connecting to the server. // getATLSConfigForClientFunc returns a config setup function that is called once for every client connecting to the server.
@ -367,9 +365,6 @@ func NewFakeValidators(oid oid.Getter) []Validator {
return []Validator{NewFakeValidator(oid)} return []Validator{NewFakeValidator(oid)}
} }
// AddLogger is a nop for FakeValidator.
func (v FakeValidator) AddLogger(log vtpm.WarnLogger) {}
// Validate unmarshals the attestation document and verifies the nonce. // Validate unmarshals the attestation document and verifies the nonce.
func (v FakeValidator) Validate(attDoc []byte, nonce []byte) ([]byte, error) { func (v FakeValidator) Validate(attDoc []byte, nonce []byte) ([]byte, error) {
var doc FakeAttestationDoc var doc FakeAttestationDoc

View File

@ -21,7 +21,7 @@ func TestAttestation(t *testing.T) {
require := require.New(t) require := require.New(t)
issuer := NewIssuer() issuer := NewIssuer()
validator := NewValidator(map[uint32][]byte{}, nil) // TODO: check for list of expected Azure PCRs validator := NewValidator(map[uint32][]byte{}, nil, nil, false, nil) // TODO: check for list of expected Azure PCRs
nonce := []byte{2, 3, 4} nonce := []byte{2, 3, 4}
challenge := []byte("Constellation") challenge := []byte("Constellation")

View File

@ -18,6 +18,7 @@ const (
lenHclHeader = 0x20 lenHclHeader = 0x20
lenSnpReport = 0x4a0 lenSnpReport = 0x4a0
lenSnpReportRuntimeDataPadding = 0x14 lenSnpReportRuntimeDataPadding = 0x14
tpmReportIdx = 0x01400001
) )
// Issuer for Azure TPM attestation. // Issuer for Azure TPM attestation.
@ -41,6 +42,27 @@ func NewIssuer() *Issuer {
} }
} }
// GetIdKeyDigest reads the idkeydigest from the snp report saved in the TPM's non-volatile memory.
func GetIdKeyDigest(open vtpm.TPMOpenFunc) ([]byte, error) {
tpm, err := open()
if err != nil {
return nil, err
}
defer tpm.Close()
reportRaw, err := tpm2.NVReadEx(tpm, tpmReportIdx, tpm2.HandleOwner, "", 0)
if err != nil {
return nil, fmt.Errorf("reading idx %x from TMP: %w", tpmReportIdx, err)
}
report, err := newSNPReportFromBytes(reportRaw)
if err != nil {
return nil, fmt.Errorf("creating snp report: %w", err)
}
return report.IdKeyDigest[:], nil
}
func hclAkTemplate() tpm2.Public { func hclAkTemplate() tpm2.Public {
akFlags := tpm2.FlagFixedTPM | tpm2.FlagFixedParent | tpm2.FlagSensitiveDataOrigin | tpm2.FlagUserWithAuth | tpm2.FlagNoDA | tpm2.FlagRestricted | tpm2.FlagSign akFlags := tpm2.FlagFixedTPM | tpm2.FlagFixedParent | tpm2.FlagSensitiveDataOrigin | tpm2.FlagUserWithAuth | tpm2.FlagNoDA | tpm2.FlagRestricted | tpm2.FlagSign
return tpm2.Public{ return tpm2.Public{
@ -113,7 +135,7 @@ func getSNPAttestation(reportGetter tpmReportGetter, imdsAPI imdsApi) func(tpm i
type tpmReport struct{} type tpmReport struct{}
func (s *tpmReport) get(tpm io.ReadWriteCloser) ([]byte, error) { func (s *tpmReport) get(tpm io.ReadWriteCloser) ([]byte, error) {
return tpm2.NVReadEx(tpm, 0x01400001, tpm2.HandleOwner, "", 0) return tpm2.NVReadEx(tpm, tpmReportIdx, tpm2.HandleOwner, "", 0)
} }
type tpmReportGetter interface { type tpmReportGetter interface {

View File

@ -29,14 +29,15 @@ type Validator struct {
} }
// NewValidator initializes a new Azure validator with the provided PCR values. // NewValidator initializes a new Azure validator with the provided PCR values.
func NewValidator(pcrs map[uint32][]byte, enforcedPCRs []uint32) *Validator { func NewValidator(pcrs map[uint32][]byte, enforcedPCRs []uint32, idkeydigest []byte, enforceIdKeyDigest bool, log vtpm.WarnLogger) *Validator {
return &Validator{ return &Validator{
Validator: vtpm.NewValidator( Validator: vtpm.NewValidator(
pcrs, pcrs,
enforcedPCRs, enforcedPCRs,
trustedKeyFromSNP(&azureInstanceInfo{}), trustedKeyFromSNP(&azureInstanceInfo{}, idkeydigest, enforceIdKeyDigest, log),
validateAzureCVM, validateAzureCVM,
vtpm.VerifyPKCS1v15, vtpm.VerifyPKCS1v15,
log,
), ),
} }
} }
@ -77,9 +78,21 @@ func (e *vcekError) Error() string {
return fmt.Sprintf("validating VCEK: %v", e.innerError) return fmt.Sprintf("validating VCEK: %v", e.innerError)
} }
type idkeyError struct {
expectedValue []byte
}
func (e *idkeyError) Unwrap() error {
return nil
}
func (e *idkeyError) Error() string {
return fmt.Sprintf("configured idkeydigest does not match reported idkeydigest: %x", e.expectedValue)
}
// trustedKeyFromSNP establishes trust in the given public key. // trustedKeyFromSNP establishes trust in the given public key.
// It does so by verifying the SNP attestation statement in instanceInfo. // It does so by verifying the SNP attestation statement in instanceInfo.
func trustedKeyFromSNP(hclAk HCLAkValidator) func(akPub, instanceInfoRaw []byte) (crypto.PublicKey, error) { func trustedKeyFromSNP(hclAk HCLAkValidator, idkeydigest []byte, enforceIdKeyDigest bool, log vtpm.WarnLogger) func(akPub, instanceInfoRaw []byte) (crypto.PublicKey, error) {
return func(akPub, instanceInfoRaw []byte) (crypto.PublicKey, error) { return func(akPub, instanceInfoRaw []byte) (crypto.PublicKey, error) {
var instanceInfo azureInstanceInfo var instanceInfo azureInstanceInfo
if err := json.Unmarshal(instanceInfoRaw, &instanceInfo); err != nil { if err := json.Unmarshal(instanceInfoRaw, &instanceInfo); err != nil {
@ -96,7 +109,7 @@ func trustedKeyFromSNP(hclAk HCLAkValidator) func(akPub, instanceInfoRaw []byte)
return nil, fmt.Errorf("validating VCEK: %w", err) return nil, fmt.Errorf("validating VCEK: %w", err)
} }
if err = validateSNPReport(vcek, report); err != nil { if err = validateSNPReport(vcek, idkeydigest, enforceIdKeyDigest, report, log); err != nil {
return nil, fmt.Errorf("validating SNP report: %w", err) return nil, fmt.Errorf("validating SNP report: %w", err)
} }
@ -163,7 +176,7 @@ func validateVCEK(vcekRaw []byte, certChain []byte) (*x509.Certificate, error) {
return vcek, nil return vcek, nil
} }
func validateSNPReport(cert *x509.Certificate, report snpAttestationReport) error { func validateSNPReport(cert *x509.Certificate, expectedIdKeyDigest []byte, enforceIdKeyDigest bool, report snpAttestationReport, log vtpm.WarnLogger) error {
sig_r := report.Signature.R[:] sig_r := report.Signature.R[:]
sig_s := report.Signature.S[:] sig_s := report.Signature.S[:]
@ -189,6 +202,15 @@ func validateSNPReport(cert *x509.Certificate, report snpAttestationReport) erro
return &signatureError{err} return &signatureError{err}
} }
if !bytes.Equal(expectedIdKeyDigest, report.IdKeyDigest[:]) {
if enforceIdKeyDigest {
return &idkeyError{report.IdKeyDigest[:]}
}
if log != nil {
log.Warnf("Encountered different than configured idkeydigest value: %x.\n", report.IdKeyDigest[:])
}
}
return nil return nil
} }

File diff suppressed because one or more lines are too long

View File

@ -22,7 +22,7 @@ func TestAttestation(t *testing.T) {
PCR0 := []byte{0x0f, 0x35, 0xc2, 0x14, 0x60, 0x8d, 0x93, 0xc7, 0xa6, 0xe6, 0x8a, 0xe7, 0x35, 0x9b, 0x4a, 0x8b, 0xe5, 0xa0, 0xe9, 0x9e, 0xea, 0x91, 0x07, 0xec, 0xe4, 0x27, 0xc4, 0xde, 0xa4, 0xe4, 0x39, 0xcf} PCR0 := []byte{0x0f, 0x35, 0xc2, 0x14, 0x60, 0x8d, 0x93, 0xc7, 0xa6, 0xe6, 0x8a, 0xe7, 0x35, 0x9b, 0x4a, 0x8b, 0xe5, 0xa0, 0xe9, 0x9e, 0xea, 0x91, 0x07, 0xec, 0xe4, 0x27, 0xc4, 0xde, 0xa4, 0xe4, 0x39, 0xcf}
issuer := NewIssuer() issuer := NewIssuer()
validator := NewValidator(map[uint32][]byte{0: PCR0}, nil) validator := NewValidator(map[uint32][]byte{0: PCR0}, nil, nil)
nonce := []byte{2, 3, 4} nonce := []byte{2, 3, 4}
challenge := []byte("Constellation") challenge := []byte("Constellation")

View File

@ -28,7 +28,7 @@ type Validator struct {
} }
// NewValidator initializes a new GCP validator with the provided PCR values. // NewValidator initializes a new GCP validator with the provided PCR values.
func NewValidator(pcrs map[uint32][]byte, enforcedPCRs []uint32) *Validator { func NewValidator(pcrs map[uint32][]byte, enforcedPCRs []uint32, log vtpm.WarnLogger) *Validator {
return &Validator{ return &Validator{
Validator: vtpm.NewValidator( Validator: vtpm.NewValidator(
pcrs, pcrs,
@ -36,6 +36,7 @@ func NewValidator(pcrs map[uint32][]byte, enforcedPCRs []uint32) *Validator {
trustedKeyFromGCEAPI(newInstanceClient), trustedKeyFromGCEAPI(newInstanceClient),
gceNonHostInfoEvent, gceNonHostInfoEvent,
vtpm.VerifyPKCS1v15, vtpm.VerifyPKCS1v15,
log,
), ),
} }
} }

View File

@ -15,7 +15,7 @@ type Validator struct {
} }
// NewValidator initializes a new qemu validator with the provided PCR values. // NewValidator initializes a new qemu validator with the provided PCR values.
func NewValidator(pcrs map[uint32][]byte, enforcedPCRs []uint32) *Validator { func NewValidator(pcrs map[uint32][]byte, enforcedPCRs []uint32, log vtpm.WarnLogger) *Validator {
return &Validator{ return &Validator{
Validator: vtpm.NewValidator( Validator: vtpm.NewValidator(
pcrs, pcrs,
@ -23,6 +23,7 @@ func NewValidator(pcrs map[uint32][]byte, enforcedPCRs []uint32) *Validator {
unconditionalTrust, unconditionalTrust,
func(attestation vtpm.AttestationDocument) error { return nil }, func(attestation vtpm.AttestationDocument) error { return nil },
vtpm.VerifyPKCS1v15, vtpm.VerifyPKCS1v15,
log,
), ),
} }
} }

View File

@ -138,7 +138,7 @@ type Validator struct {
// NewValidator returns a new Validator. // NewValidator returns a new Validator.
func NewValidator(expectedPCRs map[uint32][]byte, enforcedPCRs []uint32, func NewValidator(expectedPCRs map[uint32][]byte, enforcedPCRs []uint32,
getTrustedKey GetTPMTrustedAttestationPublicKey, validateCVM ValidateCVM, verifyUserData VerifyUserData, getTrustedKey GetTPMTrustedAttestationPublicKey, validateCVM ValidateCVM, verifyUserData VerifyUserData, log WarnLogger,
) *Validator { ) *Validator {
// Convert the enforced PCR list to a map for convenient and fast lookup // Convert the enforced PCR list to a map for convenient and fast lookup
enforcedMap := make(map[uint32]struct{}) enforcedMap := make(map[uint32]struct{})
@ -152,14 +152,10 @@ func NewValidator(expectedPCRs map[uint32][]byte, enforcedPCRs []uint32,
getTrustedKey: getTrustedKey, getTrustedKey: getTrustedKey,
validateCVM: validateCVM, validateCVM: validateCVM,
verifyUserData: verifyUserData, verifyUserData: verifyUserData,
log: log,
} }
} }
// AddLogger adds a logger to the validator.
func (v *Validator) AddLogger(log WarnLogger) {
v.log = log
}
// Validate a TPM based attestation. // Validate a TPM based attestation.
func (v *Validator) Validate(attDocRaw []byte, nonce []byte) ([]byte, error) { func (v *Validator) Validate(attDocRaw []byte, nonce []byte) ([]byte, error) {
var attDoc AttestationDocument var attDoc AttestationDocument

View File

@ -62,9 +62,10 @@ func TestValidate(t *testing.T) {
0: {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 0: {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
1: {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 1: {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
} }
warnLog := &testWarnLog{}
issuer := NewIssuer(newSimTPMWithEventLog, tpmclient.AttestationKeyRSA, fakeGetInstanceInfo) issuer := NewIssuer(newSimTPMWithEventLog, tpmclient.AttestationKeyRSA, fakeGetInstanceInfo)
validator := NewValidator(testExpectedPCRs, []uint32{0, 1}, fakeGetTrustedKey, fakeValidateCVM, VerifyPKCS1v15) validator := NewValidator(testExpectedPCRs, []uint32{0, 1}, fakeGetTrustedKey, fakeValidateCVM, VerifyPKCS1v15, warnLog)
nonce := []byte{1, 2, 3, 4} nonce := []byte{1, 2, 3, 4}
challenge := []byte("Constellation") challenge := []byte("Constellation")
@ -82,7 +83,6 @@ func TestValidate(t *testing.T) {
require.NoError(err) require.NoError(err)
require.Equal(challenge, out) require.Equal(challenge, out)
warnLog := &testWarnLog{}
enforcedPCRs := []uint32{0, 1} enforcedPCRs := []uint32{0, 1}
expectedPCRs := map[uint32][]byte{ expectedPCRs := map[uint32][]byte{
0: {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 0: {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
@ -98,8 +98,8 @@ func TestValidate(t *testing.T) {
fakeGetTrustedKey, fakeGetTrustedKey,
fakeValidateCVM, fakeValidateCVM,
VerifyPKCS1v15, VerifyPKCS1v15,
warnLog,
) )
warningValidator.AddLogger(warnLog)
out, err = warningValidator.Validate(attDocRaw, nonce) out, err = warningValidator.Validate(attDocRaw, nonce)
require.NoError(err) require.NoError(err)
assert.Equal(t, challenge, out) assert.Equal(t, challenge, out)
@ -112,13 +112,13 @@ func TestValidate(t *testing.T) {
wantErr bool wantErr bool
}{ }{
"invalid nonce": { "invalid nonce": {
validator: NewValidator(testExpectedPCRs, []uint32{0, 1}, fakeGetTrustedKey, fakeValidateCVM, VerifyPKCS1v15), validator: NewValidator(testExpectedPCRs, []uint32{0, 1}, fakeGetTrustedKey, fakeValidateCVM, VerifyPKCS1v15, warnLog),
attDoc: mustMarshalAttestation(attDoc, require), attDoc: mustMarshalAttestation(attDoc, require),
nonce: []byte{4, 3, 2, 1}, nonce: []byte{4, 3, 2, 1},
wantErr: true, wantErr: true,
}, },
"invalid signature": { "invalid signature": {
validator: NewValidator(testExpectedPCRs, []uint32{0, 1}, fakeGetTrustedKey, fakeValidateCVM, VerifyPKCS1v15), validator: NewValidator(testExpectedPCRs, []uint32{0, 1}, fakeGetTrustedKey, fakeValidateCVM, VerifyPKCS1v15, warnLog),
attDoc: mustMarshalAttestation(AttestationDocument{ attDoc: mustMarshalAttestation(AttestationDocument{
Attestation: attDoc.Attestation, Attestation: attDoc.Attestation,
InstanceInfo: attDoc.InstanceInfo, InstanceInfo: attDoc.InstanceInfo,
@ -135,7 +135,7 @@ func TestValidate(t *testing.T) {
func(akPub, instanceInfo []byte) (crypto.PublicKey, error) { func(akPub, instanceInfo []byte) (crypto.PublicKey, error) {
return nil, errors.New("untrusted") return nil, errors.New("untrusted")
}, },
fakeValidateCVM, VerifyPKCS1v15), fakeValidateCVM, VerifyPKCS1v15, warnLog),
attDoc: mustMarshalAttestation(attDoc, require), attDoc: mustMarshalAttestation(attDoc, require),
nonce: nonce, nonce: nonce,
wantErr: true, wantErr: true,
@ -148,7 +148,7 @@ func TestValidate(t *testing.T) {
func(attestation AttestationDocument) error { func(attestation AttestationDocument) error {
return errors.New("untrusted") return errors.New("untrusted")
}, },
VerifyPKCS1v15), VerifyPKCS1v15, warnLog),
attDoc: mustMarshalAttestation(attDoc, require), attDoc: mustMarshalAttestation(attDoc, require),
nonce: nonce, nonce: nonce,
wantErr: true, wantErr: true,
@ -161,13 +161,13 @@ func TestValidate(t *testing.T) {
[]uint32{0}, []uint32{0},
fakeGetTrustedKey, fakeGetTrustedKey,
fakeValidateCVM, fakeValidateCVM,
VerifyPKCS1v15), VerifyPKCS1v15, warnLog),
attDoc: mustMarshalAttestation(attDoc, require), attDoc: mustMarshalAttestation(attDoc, require),
nonce: nonce, nonce: nonce,
wantErr: true, wantErr: true,
}, },
"no sha256 quote": { "no sha256 quote": {
validator: NewValidator(testExpectedPCRs, []uint32{0, 1}, fakeGetTrustedKey, fakeValidateCVM, VerifyPKCS1v15), validator: NewValidator(testExpectedPCRs, []uint32{0, 1}, fakeGetTrustedKey, fakeValidateCVM, VerifyPKCS1v15, warnLog),
attDoc: mustMarshalAttestation(AttestationDocument{ attDoc: mustMarshalAttestation(AttestationDocument{
Attestation: &attest.Attestation{ Attestation: &attest.Attestation{
AkPub: attDoc.Attestation.AkPub, AkPub: attDoc.Attestation.AkPub,
@ -185,7 +185,7 @@ func TestValidate(t *testing.T) {
wantErr: true, wantErr: true,
}, },
"invalid attestation document": { "invalid attestation document": {
validator: NewValidator(testExpectedPCRs, []uint32{0, 1}, fakeGetTrustedKey, fakeValidateCVM, VerifyPKCS1v15), validator: NewValidator(testExpectedPCRs, []uint32{0, 1}, fakeGetTrustedKey, fakeValidateCVM, VerifyPKCS1v15, warnLog),
attDoc: []byte("invalid attestation"), attDoc: []byte("invalid attestation"),
nonce: nonce, nonce: nonce,
wantErr: true, wantErr: true,

View File

@ -167,6 +167,12 @@ type AzureConfig struct {
// List of values that should be enforced to be equal to the ones from the measurement list. Any non-equal values not in this list will only result in a warning. // List of values that should be enforced to be equal to the ones from the measurement list. Any non-equal values not in this list will only result in a warning.
EnforcedMeasurements []uint32 `yaml:"enforcedMeasurements"` EnforcedMeasurements []uint32 `yaml:"enforcedMeasurements"`
// description: | // 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"`
// description: |
// Enforce the specified idKeyDigest value during remote attestation.
EnforceIdKeyDigest *bool `yaml:"enforceIdKeyDigest" validate:"required"`
// description: |
// Use VMs with security type Confidential VM. If set to false, Trusted Launch VMs will be used instead. See: https://docs.microsoft.com/en-us/azure/confidential-computing/confidential-vm-overview // Use VMs with security type Confidential VM. If set to false, Trusted Launch VMs will be used instead. See: https://docs.microsoft.com/en-us/azure/confidential-computing/confidential-vm-overview
ConfidentialVM *bool `yaml:"confidentialVM" validate:"required"` ConfidentialVM *bool `yaml:"confidentialVM" validate:"required"`
} }
@ -247,7 +253,6 @@ func Default() *Config {
}, },
}, },
Provider: ProviderConfig{ Provider: ProviderConfig{
// TODO remove our subscriptions from the default config
Azure: &AzureConfig{ Azure: &AzureConfig{
SubscriptionID: "", SubscriptionID: "",
TenantID: "", TenantID: "",
@ -258,6 +263,8 @@ func Default() *Config {
StateDiskType: "Premium_LRS", StateDiskType: "Premium_LRS",
Measurements: copyPCRMap(azurePCRs), Measurements: copyPCRMap(azurePCRs),
EnforcedMeasurements: []uint32{8, 9, 11, 12}, EnforcedMeasurements: []uint32{8, 9, 11, 12},
IdKeyDigest: "57486a447ec0f1958002a22a06b7673b9fd27d11e1c6527498056054c5fa92d23c50f9de44072760fe2b6fb89740b696",
EnforceIdKeyDigest: func() *bool { b := true; return &b }(),
ConfidentialVM: func() *bool { b := true; return &b }(), ConfidentialVM: func() *bool { b := true; return &b }(),
}, },
GCP: &GCPConfig{ GCP: &GCPConfig{

View File

@ -199,7 +199,7 @@ func init() {
FieldName: "azure", FieldName: "azure",
}, },
} }
AzureConfigDoc.Fields = make([]encoder.Doc, 12) AzureConfigDoc.Fields = make([]encoder.Doc, 14)
AzureConfigDoc.Fields[0].Name = "subscription" AzureConfigDoc.Fields[0].Name = "subscription"
AzureConfigDoc.Fields[0].Type = "string" AzureConfigDoc.Fields[0].Type = "string"
AzureConfigDoc.Fields[0].Note = "" AzureConfigDoc.Fields[0].Note = ""
@ -255,11 +255,21 @@ func init() {
AzureConfigDoc.Fields[10].Note = "" AzureConfigDoc.Fields[10].Note = ""
AzureConfigDoc.Fields[10].Description = "List of values that should be enforced to be equal to the ones from the measurement list. Any non-equal values not in this list will only result in a warning." AzureConfigDoc.Fields[10].Description = "List of values that should be enforced to be equal to the ones from the measurement list. Any non-equal values not in this list will only result in a warning."
AzureConfigDoc.Fields[10].Comments[encoder.LineComment] = "List of values that should be enforced to be equal to the ones from the measurement list. Any non-equal values not in this list will only result in a warning." AzureConfigDoc.Fields[10].Comments[encoder.LineComment] = "List of values that should be enforced to be equal to the ones from the measurement list. Any non-equal values not in this list will only result in a warning."
AzureConfigDoc.Fields[11].Name = "confidentialVM" AzureConfigDoc.Fields[11].Name = "idKeyDigest"
AzureConfigDoc.Fields[11].Type = "bool" AzureConfigDoc.Fields[11].Type = "string"
AzureConfigDoc.Fields[11].Note = "" AzureConfigDoc.Fields[11].Note = ""
AzureConfigDoc.Fields[11].Description = "Use VMs with security type Confidential VM. If set to false, Trusted Launch VMs will be used instead. See: https://docs.microsoft.com/en-us/azure/confidential-computing/confidential-vm-overview" AzureConfigDoc.Fields[11].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[11].Comments[encoder.LineComment] = "Use VMs with security type Confidential VM. If set to false, Trusted Launch VMs will be used instead. See: https://docs.microsoft.com/en-us/azure/confidential-computing/confidential-vm-overview" AzureConfigDoc.Fields[11].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].Name = "enforceIdKeyDigest"
AzureConfigDoc.Fields[12].Type = "bool"
AzureConfigDoc.Fields[12].Note = ""
AzureConfigDoc.Fields[12].Description = "Enforce the specified idKeyDigest value during remote attestation."
AzureConfigDoc.Fields[12].Comments[encoder.LineComment] = "Enforce the specified idKeyDigest value during remote attestation."
AzureConfigDoc.Fields[13].Name = "confidentialVM"
AzureConfigDoc.Fields[13].Type = "bool"
AzureConfigDoc.Fields[13].Note = ""
AzureConfigDoc.Fields[13].Description = "Use VMs with security type Confidential VM. If set to false, Trusted Launch VMs will be used instead. See: https://docs.microsoft.com/en-us/azure/confidential-computing/confidential-vm-overview"
AzureConfigDoc.Fields[13].Comments[encoder.LineComment] = "Use VMs with security type Confidential VM. If set to false, Trusted Launch VMs will be used instead. See: https://docs.microsoft.com/en-us/azure/confidential-computing/confidential-vm-overview"
GCPConfigDoc.Type = "GCPConfig" GCPConfigDoc.Type = "GCPConfig"
GCPConfigDoc.Comments[encoder.LineComment] = "GCPConfig are GCP specific configuration values used by the CLI." GCPConfigDoc.Comments[encoder.LineComment] = "GCPConfig are GCP specific configuration values used by the CLI."

View File

@ -74,6 +74,10 @@ const (
MeasurementSaltFilename = "measurementSalt" MeasurementSaltFilename = "measurementSalt"
// MeasurementSecretFilename is the filename of the secret used in creation of the clusterID. // MeasurementSecretFilename is the filename of the secret used in creation of the clusterID.
MeasurementSecretFilename = "measurementSecret" MeasurementSecretFilename = "measurementSecret"
// IdKeyDigestFilename is the name of the file holding the currently enforced idkeydigest.
IdKeyDigestFilename = "idkeydigest"
// EnforceIdKeyDigestFilename is the name of the file configuring whether idkeydigest is enforced or not.
EnforceIdKeyDigestFilename = "enforceIdKeyDigest"
// K8sVersion is the filename of the mapped "k8s-version" configMap file. // K8sVersion is the filename of the mapped "k8s-version" configMap file.
K8sVersion = "k8s-version" K8sVersion = "k8s-version"

View File

@ -11,7 +11,6 @@ import (
"github.com/edgelesssys/constellation/bootstrapper/initproto" "github.com/edgelesssys/constellation/bootstrapper/initproto"
"github.com/edgelesssys/constellation/internal/atls" "github.com/edgelesssys/constellation/internal/atls"
"github.com/edgelesssys/constellation/internal/attestation/vtpm"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uber.org/goleak" "go.uber.org/goleak"
@ -101,8 +100,6 @@ func (v fakeValidator) Validate(attDoc []byte, nonce []byte) ([]byte, error) {
return doc.UserData, v.err return doc.UserData, v.err
} }
func (v fakeValidator) AddLogger(vtpm.WarnLogger) {}
type fakeOID asn1.ObjectIdentifier type fakeOID asn1.ObjectIdentifier
func (o fakeOID) OID() asn1.ObjectIdentifier { func (o fakeOID) OID() asn1.ObjectIdentifier {

View File

@ -30,10 +30,10 @@ func IsSupportedK8sVersion(version string) bool {
const ( const (
// Constellation images. // Constellation images.
// These images are built in a way that they support all versions currently listed in VersionConfigs. // These images are built in a way that they support all versions currently listed in VersionConfigs.
JoinImage = "ghcr.io/edgelesssys/constellation/join-service:v0.0.1" JoinImage = "ghcr.io/edgelesssys/constellation/join-service:v0.0.1-0.20220831112436-10766c6049b8"
AccessManagerImage = "ghcr.io/edgelesssys/constellation/access-manager:v0.0.1" AccessManagerImage = "ghcr.io/edgelesssys/constellation/access-manager:v0.0.1"
KmsImage = "ghcr.io/edgelesssys/constellation/kmsserver:v0.0.1" KmsImage = "ghcr.io/edgelesssys/constellation/kmsserver:v0.0.1"
VerificationImage = "ghcr.io/edgelesssys/constellation/verification-service:v0.0.1" VerificationImage = "ghcr.io/edgelesssys/constellation/verification-service:v0.0.1-0.20220831112436-10766c6049b8"
GcpGuestImage = "ghcr.io/edgelesssys/gcp-guest-agent:20220713.00" GcpGuestImage = "ghcr.io/edgelesssys/gcp-guest-agent:20220713.00"
NodeOperatorCatalogImage = "ghcr.io/edgelesssys/constellation/node-operator-catalog" NodeOperatorCatalogImage = "ghcr.io/edgelesssys/constellation/node-operator-catalog"
NodeOperatorVersion = "v0.0.1" NodeOperatorVersion = "v0.0.1"

View File

@ -2,8 +2,10 @@ package watcher
import ( import (
"encoding/asn1" "encoding/asn1"
"encoding/hex"
"fmt" "fmt"
"path/filepath" "path/filepath"
"strconv"
"sync" "sync"
"github.com/edgelesssys/constellation/internal/atls" "github.com/edgelesssys/constellation/internal/atls"
@ -22,6 +24,7 @@ type Updatable struct {
mux sync.Mutex mux sync.Mutex
newValidator newValidatorFunc newValidator newValidatorFunc
fileHandler file.Handler fileHandler file.Handler
csp cloudprovider.Provider
atls.Validator atls.Validator
} }
@ -30,11 +33,17 @@ func NewValidator(log *logger.Logger, csp string, fileHandler file.Handler) (*Up
var newValidator newValidatorFunc var newValidator newValidatorFunc
switch cloudprovider.FromString(csp) { switch cloudprovider.FromString(csp) {
case cloudprovider.Azure: case cloudprovider.Azure:
newValidator = func(m map[uint32][]byte, e []uint32) atls.Validator { return azure.NewValidator(m, e) } newValidator = func(m map[uint32][]byte, e []uint32, idkeydigest []byte, enforceIdKeyDigest bool, log *logger.Logger) atls.Validator {
return azure.NewValidator(m, e, idkeydigest, enforceIdKeyDigest, log)
}
case cloudprovider.GCP: case cloudprovider.GCP:
newValidator = func(m map[uint32][]byte, e []uint32) atls.Validator { return gcp.NewValidator(m, e) } newValidator = func(m map[uint32][]byte, e []uint32, _ []byte, _ bool, log *logger.Logger) atls.Validator {
return gcp.NewValidator(m, e, log)
}
case cloudprovider.QEMU: case cloudprovider.QEMU:
newValidator = func(m map[uint32][]byte, e []uint32) atls.Validator { return qemu.NewValidator(m, e) } newValidator = func(m map[uint32][]byte, e []uint32, _ []byte, _ bool, log *logger.Logger) atls.Validator {
return qemu.NewValidator(m, e, log)
}
default: default:
return nil, fmt.Errorf("unknown cloud service provider: %q", csp) return nil, fmt.Errorf("unknown cloud service provider: %q", csp)
} }
@ -43,6 +52,7 @@ func NewValidator(log *logger.Logger, csp string, fileHandler file.Handler) (*Up
log: log, log: log,
newValidator: newValidator, newValidator: newValidator,
fileHandler: fileHandler, fileHandler: fileHandler,
csp: cloudprovider.FromString(csp),
} }
if err := u.Update(); err != nil { if err := u.Update(); err != nil {
@ -82,10 +92,35 @@ func (u *Updatable) Update() error {
} }
u.log.Debugf("Enforced PCRs: %v", enforced) u.log.Debugf("Enforced PCRs: %v", enforced)
u.Validator = u.newValidator(measurements, enforced) var idkeydigest []byte
u.Validator.AddLogger(u.log) var enforceIdKeyDigest bool
if u.csp == cloudprovider.Azure {
u.log.Infof("Updating encforceIdKeyDigest value")
enforceRaw, err := u.fileHandler.Read(filepath.Join(constants.ServiceBasePath, constants.EnforceIdKeyDigestFilename))
if err != nil {
return err
}
enforceIdKeyDigest, err = strconv.ParseBool(string(enforceRaw))
if err != nil {
return fmt.Errorf("parsing content of EnforceIdKeyDigestFilename: %s: %w", enforceRaw, err)
}
u.log.Debugf("New encforceIdKeyDigest value: %v", enforceIdKeyDigest)
u.log.Infof("Updating expected idkeydigest")
idkeydigestRaw, err := u.fileHandler.Read(filepath.Join(constants.ServiceBasePath, constants.IdKeyDigestFilename))
if err != nil {
return err
}
idkeydigest, err = hex.DecodeString(string(idkeydigestRaw))
if err != nil {
return fmt.Errorf("parsing hexstring: %s: %w", idkeydigestRaw, err)
}
u.log.Debugf("New idkeydigest: %x", idkeydigest)
}
u.Validator = u.newValidator(measurements, enforced, idkeydigest, enforceIdKeyDigest, u.log)
return nil return nil
} }
type newValidatorFunc func(measurements map[uint32][]byte, enforcedPCRs []uint32) atls.Validator type newValidatorFunc func(measurements map[uint32][]byte, enforcedPCRs []uint32, idkeydigest []byte, enforceIdKeyDigest bool, log *logger.Logger) atls.Validator

View File

@ -14,7 +14,6 @@ import (
"testing" "testing"
"github.com/edgelesssys/constellation/internal/atls" "github.com/edgelesssys/constellation/internal/atls"
"github.com/edgelesssys/constellation/internal/attestation/vtpm"
"github.com/edgelesssys/constellation/internal/constants" "github.com/edgelesssys/constellation/internal/constants"
"github.com/edgelesssys/constellation/internal/file" "github.com/edgelesssys/constellation/internal/file"
"github.com/edgelesssys/constellation/internal/logger" "github.com/edgelesssys/constellation/internal/logger"
@ -78,6 +77,14 @@ func TestNewUpdateableValidator(t *testing.T) {
filepath.Join(constants.ServiceBasePath, constants.EnforcedPCRsFilename), filepath.Join(constants.ServiceBasePath, constants.EnforcedPCRsFilename),
[]uint32{11}, []uint32{11},
)) ))
require.NoError(handler.Write(
filepath.Join(constants.ServiceBasePath, constants.IdKeyDigestFilename),
[]byte{},
))
require.NoError(handler.Write(
filepath.Join(constants.ServiceBasePath, constants.EnforceIdKeyDigestFilename),
[]byte("false"),
))
} }
_, err := NewValidator( _, err := NewValidator(
@ -99,7 +106,7 @@ func TestUpdate(t *testing.T) {
require := require.New(t) require := require.New(t)
oid := fakeOID{1, 3, 9900, 1} oid := fakeOID{1, 3, 9900, 1}
newValidator := func(m map[uint32][]byte, e []uint32) atls.Validator { newValidator := func(m map[uint32][]byte, e []uint32, idkeydigest []byte, enforceIdKeyDigest bool, _ *logger.Logger) atls.Validator {
return fakeValidator{fakeOID: oid} return fakeValidator{fakeOID: oid}
} }
handler := file.NewHandler(afero.NewMemMapFs()) handler := file.NewHandler(afero.NewMemMapFs())
@ -126,6 +133,14 @@ func TestUpdate(t *testing.T) {
filepath.Join(constants.ServiceBasePath, constants.EnforcedPCRsFilename), filepath.Join(constants.ServiceBasePath, constants.EnforcedPCRsFilename),
[]uint32{11}, []uint32{11},
)) ))
require.NoError(handler.Write(
filepath.Join(constants.ServiceBasePath, constants.IdKeyDigestFilename),
[]byte{},
))
require.NoError(handler.Write(
filepath.Join(constants.ServiceBasePath, constants.EnforceIdKeyDigestFilename),
[]byte("false"),
))
// call update once to initialize the server's validator // call update once to initialize the server's validator
require.NoError(validator.Update()) require.NoError(validator.Update())
@ -169,7 +184,7 @@ func TestUpdateConcurrency(t *testing.T) {
validator := &Updatable{ validator := &Updatable{
log: logger.NewTest(t), log: logger.NewTest(t),
fileHandler: handler, fileHandler: handler,
newValidator: func(m map[uint32][]byte, e []uint32) atls.Validator { newValidator: func(m map[uint32][]byte, e []uint32, idkeydigest []byte, enforceIdKeyDigest bool, _ *logger.Logger) atls.Validator {
return fakeValidator{fakeOID: fakeOID{1, 3, 9900, 1}} return fakeValidator{fakeOID: fakeOID{1, 3, 9900, 1}}
}, },
} }
@ -184,6 +199,14 @@ func TestUpdateConcurrency(t *testing.T) {
filepath.Join(constants.ServiceBasePath, constants.EnforcedPCRsFilename), filepath.Join(constants.ServiceBasePath, constants.EnforcedPCRsFilename),
[]uint32{11}, []uint32{11},
)) ))
require.NoError(handler.Write(
filepath.Join(constants.ServiceBasePath, constants.IdKeyDigestFilename),
[]byte{},
))
require.NoError(handler.Write(
filepath.Join(constants.ServiceBasePath, constants.EnforceIdKeyDigestFilename),
[]byte("false"),
))
var wg sync.WaitGroup var wg sync.WaitGroup
@ -232,8 +255,6 @@ func (v fakeValidator) Validate(attDoc []byte, nonce []byte) ([]byte, error) {
return doc.UserData, v.err return doc.UserData, v.err
} }
func (v fakeValidator) AddLogger(logger vtpm.WarnLogger) {}
type fakeOID asn1.ObjectIdentifier type fakeOID asn1.ObjectIdentifier
func (o fakeOID) OID() asn1.ObjectIdentifier { func (o fakeOID) OID() asn1.ObjectIdentifier {