mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-01-11 15:39:33 -05:00
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:
parent
c84e44913b
commit
4adc19b7f5
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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 (
|
||||||
|
@ -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 {
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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")
|
||||||
|
@ -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 {
|
||||||
|
@ -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
@ -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")
|
||||||
|
@ -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,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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{
|
||||||
|
@ -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."
|
||||||
|
@ -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"
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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"
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user