mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-04-20 15:35:55 -04:00
attestation: add option for MAA fallback to verify azure's snp-sev id key digest (#1257)
* Convert enforceIDKeyDigest setting to enum * Use MAA fallback in Azure SNP attestation * Only create MAA provider if MAA fallback is enabled --------- Signed-off-by: Daniel Weiße <dw@edgeless.systems> Co-authored-by: Thomas Tendyck <tt@edgeless.systems>
This commit is contained in:
parent
9a9688583d
commit
5a0234b3f2
@ -1718,6 +1718,15 @@ def go_dependencies():
|
||||
sum = "h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=",
|
||||
version = "v1.1.0",
|
||||
)
|
||||
go_repository(
|
||||
name = "com_github_edgelesssys_go_azguestattestation",
|
||||
build_file_generation = "on",
|
||||
build_file_proto_mode = "disable_global",
|
||||
importpath = "github.com/edgelesssys/go-azguestattestation",
|
||||
sum = "h1:J9k1gV8YA5beC6jANKQy5O7UtaKS3ueuanxUan5Y5NU=",
|
||||
version = "v0.0.0-20230303085714-62ede861d33f",
|
||||
)
|
||||
|
||||
go_repository(
|
||||
name = "com_github_edsrzf_mmap_go",
|
||||
build_file_generation = "on",
|
||||
@ -2724,8 +2733,9 @@ def go_dependencies():
|
||||
build_file_generation = "on",
|
||||
build_file_proto_mode = "disable_global",
|
||||
importpath = "github.com/google/go-tpm",
|
||||
sum = "h1:P/ZFNBZYXRxc+z7i5uyd8VP7MaDteuLZInzrH2idRGo=",
|
||||
version = "v0.3.3",
|
||||
replace = "github.com/thomasten/go-tpm",
|
||||
sum = "h1:4/WcA7Q05f5to+jVERKcZjU5NJ89DiRHP3UUZHLfAtw=",
|
||||
version = "v0.0.0-20230222180349-bb3cc5560299",
|
||||
)
|
||||
go_repository(
|
||||
name = "com_github_google_go_tpm_tools",
|
||||
|
@ -35,9 +35,9 @@ type InitRequest struct {
|
||||
KubernetesVersion string `protobuf:"bytes,8,opt,name=kubernetes_version,json=kubernetesVersion,proto3" json:"kubernetes_version,omitempty"`
|
||||
// repeated SSHUserKey ssh_user_keys = 9; removed
|
||||
// bytes salt = 10; removed
|
||||
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"`
|
||||
EnforceIdkeydigest bool `protobuf:"varint,13,opt,name=enforce_idkeydigest,json=enforceIdkeydigest,proto3" json:"enforce_idkeydigest,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"`
|
||||
// bool enforce_idkeydigest = 13; removed
|
||||
ConformanceMode bool `protobuf:"varint,14,opt,name=conformance_mode,json=conformanceMode,proto3" json:"conformance_mode,omitempty"`
|
||||
KubernetesComponents []*KubernetesComponent `protobuf:"bytes,15,rep,name=kubernetes_components,json=kubernetesComponents,proto3" json:"kubernetes_components,omitempty"`
|
||||
InitSecret []byte `protobuf:"bytes,16,opt,name=init_secret,json=initSecret,proto3" json:"init_secret,omitempty"`
|
||||
@ -118,13 +118,6 @@ func (x *InitRequest) GetEnforcedPcrs() []uint32 {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *InitRequest) GetEnforceIdkeydigest() bool {
|
||||
if x != nil {
|
||||
return x.EnforceIdkeydigest
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *InitRequest) GetConformanceMode() bool {
|
||||
if x != nil {
|
||||
return x.ConformanceMode
|
||||
@ -291,7 +284,7 @@ var File_init_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_init_proto_rawDesc = []byte{
|
||||
0x0a, 0x0a, 0x69, 0x6e, 0x69, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x69, 0x6e,
|
||||
0x69, 0x74, 0x22, 0xf1, 0x03, 0x0a, 0x0b, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x69, 0x74, 0x22, 0xc0, 0x03, 0x0a, 0x0b, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x6b, 0x6d, 0x73, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x06, 0x6b, 0x6d, 0x73, 0x55, 0x72, 0x69, 0x12, 0x1f, 0x0a, 0x0b, 0x73,
|
||||
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09,
|
||||
@ -307,44 +300,41 @@ var file_init_proto_rawDesc = []byte{
|
||||
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, 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, 0x12, 0x2f, 0x0a, 0x13, 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63,
|
||||
0x65, 0x5f, 0x69, 0x64, 0x6b, 0x65, 0x79, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x0d, 0x20,
|
||||
0x01, 0x28, 0x08, 0x52, 0x12, 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x49, 0x64, 0x6b, 0x65,
|
||||
0x79, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x6f,
|
||||
0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28,
|
||||
0x08, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x4d, 0x6f,
|
||||
0x64, 0x65, 0x12, 0x4e, 0x0a, 0x15, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73,
|
||||
0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28,
|
||||
0x0b, 0x32, 0x19, 0x2e, 0x69, 0x6e, 0x69, 0x74, 0x2e, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65,
|
||||
0x74, 0x65, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x52, 0x14, 0x6b, 0x75,
|
||||
0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e,
|
||||
0x74, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x69, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65,
|
||||
0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x69, 0x6e, 0x69, 0x74, 0x53, 0x65, 0x63,
|
||||
0x72, 0x65, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74,
|
||||
0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x68, 0x0a, 0x0c, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x6b, 0x75, 0x62, 0x65, 0x63, 0x6f,
|
||||
0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6b, 0x75, 0x62, 0x65,
|
||||
0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f,
|
||||
0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x49,
|
||||
0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18,
|
||||
0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64,
|
||||
0x22, 0x78, 0x0a, 0x13, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, 0x6f,
|
||||
0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73,
|
||||
0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a,
|
||||
0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x50, 0x61, 0x74, 0x68,
|
||||
0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28,
|
||||
0x08, 0x52, 0x07, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x32, 0x34, 0x0a, 0x03, 0x41, 0x50,
|
||||
0x49, 0x12, 0x2d, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x11, 0x2e, 0x69, 0x6e, 0x69, 0x74,
|
||||
0x2e, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x69,
|
||||
0x6e, 0x69, 0x74, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 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, 0x76, 0x32, 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,
|
||||
0x65, 0x64, 0x50, 0x63, 0x72, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x6f, 0x72,
|
||||
0x6d, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08,
|
||||
0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x4d, 0x6f, 0x64,
|
||||
0x65, 0x12, 0x4e, 0x0a, 0x15, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x5f,
|
||||
0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b,
|
||||
0x32, 0x19, 0x2e, 0x69, 0x6e, 0x69, 0x74, 0x2e, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74,
|
||||
0x65, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x52, 0x14, 0x6b, 0x75, 0x62,
|
||||
0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74,
|
||||
0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x69, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74,
|
||||
0x18, 0x10, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x69, 0x6e, 0x69, 0x74, 0x53, 0x65, 0x63, 0x72,
|
||||
0x65, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61,
|
||||
0x6d, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65,
|
||||
0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x68, 0x0a, 0x0c, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x6b, 0x75, 0x62, 0x65, 0x63, 0x6f, 0x6e,
|
||||
0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6b, 0x75, 0x62, 0x65, 0x63,
|
||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69,
|
||||
0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x49, 0x64,
|
||||
0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x03,
|
||||
0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22,
|
||||
0x78, 0x0a, 0x13, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x6d,
|
||||
0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, 0x0c,
|
||||
0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12,
|
||||
0x18, 0x0a, 0x07, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08,
|
||||
0x52, 0x07, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x32, 0x34, 0x0a, 0x03, 0x41, 0x50, 0x49,
|
||||
0x12, 0x2d, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x11, 0x2e, 0x69, 0x6e, 0x69, 0x74, 0x2e,
|
||||
0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x69, 0x6e,
|
||||
0x69, 0x74, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42,
|
||||
0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 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, 0x76, 0x32, 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 (
|
||||
|
@ -21,7 +21,7 @@ message InitRequest {
|
||||
// bytes salt = 10; removed
|
||||
bytes helm_deployments = 11;
|
||||
repeated uint32 enforced_pcrs = 12;
|
||||
bool enforce_idkeydigest = 13;
|
||||
// bool enforce_idkeydigest = 13; removed
|
||||
bool conformance_mode = 14;
|
||||
repeated KubernetesComponent kubernetes_components = 15;
|
||||
bytes init_secret = 16;
|
||||
|
@ -81,9 +81,8 @@ func New(cloudProvider string, clusterUtil clusterUtil, configProvider configura
|
||||
|
||||
// InitCluster initializes a new Kubernetes cluster and applies pod network provider.
|
||||
func (k *KubeWrapper) InitCluster(
|
||||
ctx context.Context, cloudServiceAccountURI, versionString, clusterName string,
|
||||
measurementSalt []byte, enforcedPCRs []uint32, helmReleasesRaw []byte, conformanceMode bool,
|
||||
kubernetesComponents components.Components, log *logger.Logger,
|
||||
ctx context.Context, cloudServiceAccountURI, versionString, clusterName string, measurementSalt []byte, enforcedPCRs []uint32,
|
||||
helmReleasesRaw []byte, conformanceMode bool, kubernetesComponents components.Components, log *logger.Logger,
|
||||
) ([]byte, error) {
|
||||
log.With(zap.String("version", versionString)).Infof("Installing Kubernetes components")
|
||||
if err := k.clusterUtil.InstallComponents(ctx, kubernetesComponents); err != nil {
|
||||
|
@ -27,6 +27,7 @@ import (
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/image"
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/libvirt"
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
@ -206,7 +207,7 @@ func (c *Creator) createAzure(ctx context.Context, cl terraformClient, config *c
|
||||
ImageID: image,
|
||||
ConfidentialVM: *config.Provider.Azure.ConfidentialVM,
|
||||
SecureBoot: *config.Provider.Azure.SecureBoot,
|
||||
CreateMAA: *config.Provider.Azure.EnforceIDKeyDigest,
|
||||
CreateMAA: config.Provider.Azure.EnforceIDKeyDigest == idkeydigest.MAAFallback,
|
||||
Debug: config.IsDebugCluster(),
|
||||
}
|
||||
|
||||
|
@ -26,14 +26,13 @@ import (
|
||||
type Validator struct {
|
||||
attestationVariant oid.Getter
|
||||
pcrs measurements.M
|
||||
idkeydigests idkeydigest.IDKeyDigests
|
||||
enforceIDKeyDigest bool
|
||||
idKeyConfig idkeydigest.Config
|
||||
validator atls.Validator
|
||||
log debugLog
|
||||
}
|
||||
|
||||
// NewValidator creates a new Validator.
|
||||
func NewValidator(conf *config.Config, log debugLog) (*Validator, error) {
|
||||
func NewValidator(conf *config.Config, maaURL string, log debugLog) (*Validator, error) {
|
||||
v := Validator{log: log}
|
||||
variant, err := oid.FromString(conf.AttestationVariant)
|
||||
if err != nil {
|
||||
@ -46,8 +45,11 @@ func NewValidator(conf *config.Config, log debugLog) (*Validator, error) {
|
||||
}
|
||||
|
||||
if v.attestationVariant.OID().Equal(oid.AzureSEVSNP{}.OID()) {
|
||||
v.enforceIDKeyDigest = conf.EnforcesIDKeyDigest()
|
||||
v.idkeydigests = conf.IDKeyDigests()
|
||||
v.idKeyConfig = idkeydigest.Config{
|
||||
IDKeyDigests: conf.Provider.Azure.IDKeyDigest,
|
||||
EnforcementPolicy: conf.IDKeyDigestPolicy(),
|
||||
MAAURL: maaURL,
|
||||
}
|
||||
}
|
||||
|
||||
return &v, nil
|
||||
@ -138,7 +140,7 @@ func (v *Validator) updateValidator(cmd *cobra.Command) {
|
||||
log := warnLogger{cmd: cmd, log: v.log}
|
||||
|
||||
// Use of a valid variant has been check in NewValidator so we may drop the error
|
||||
v.validator, _ = choose.Validator(v.attestationVariant, v.pcrs, v.idkeydigests, v.enforceIDKeyDigest, log)
|
||||
v.validator, _ = choose.Validator(v.attestationVariant, v.pcrs, v.idKeyConfig, log)
|
||||
}
|
||||
|
||||
// warnLogger implements logging of warnings for validators.
|
||||
|
@ -110,7 +110,7 @@ func TestNewValidator(t *testing.T) {
|
||||
Azure: &config.AzureConfig{
|
||||
Measurements: testPCRs,
|
||||
IDKeyDigest: idkeydigest.IDKeyDigests{[]byte("414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141")},
|
||||
EnforceIDKeyDigest: &[]bool{true}[0],
|
||||
EnforceIDKeyDigest: idkeydigest.StrictChecking,
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -121,7 +121,7 @@ func TestNewValidator(t *testing.T) {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
validators, err := NewValidator(tc.config, logger.NewTest(t))
|
||||
validators, err := NewValidator(tc.config, "https://192.0.2.1:8080/maa", logger.NewTest(t))
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
@ -168,7 +168,11 @@ func TestValidatorV(t *testing.T) {
|
||||
"azure cvm": {
|
||||
variant: oid.AzureSEVSNP{},
|
||||
pcrs: newTestPCRs(),
|
||||
wantVs: snp.NewValidator(newTestPCRs(), idkeydigest.IDKeyDigests{}, false, nil),
|
||||
wantVs: snp.NewValidator(
|
||||
newTestPCRs(),
|
||||
idkeydigest.Config{IDKeyDigests: idkeydigest.IDKeyDigests{}, EnforcementPolicy: idkeydigest.WarnOnly},
|
||||
nil,
|
||||
),
|
||||
},
|
||||
"azure trusted launch": {
|
||||
variant: oid.AzureTrustedLaunch{},
|
||||
|
@ -43,6 +43,7 @@ go_library(
|
||||
"//cli/internal/terraform",
|
||||
"//disk-mapper/recoverproto",
|
||||
"//internal/atls",
|
||||
"//internal/attestation/idkeydigest",
|
||||
"//internal/attestation/measurements",
|
||||
"//internal/cloud/azureshared",
|
||||
"//internal/cloud/cloudprovider",
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/terraform"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||
"github.com/edgelesssys/constellation/v2/internal/constants"
|
||||
@ -97,8 +98,8 @@ func (c *createCmd) create(cmd *cobra.Command, creator cloudCreator, fileHandler
|
||||
if conf.IsAzureNonCVM() {
|
||||
cmd.PrintErrln("Disabling Confidential VMs is insecure. Use only for evaluation purposes.")
|
||||
printedAWarning = true
|
||||
if conf.EnforcesIDKeyDigest() {
|
||||
cmd.PrintErrln("Your config asks for enforcing the idkeydigest. This is only available on Confidential VMs. It will not be enforced.")
|
||||
if conf.IDKeyDigestPolicy() == idkeydigest.StrictChecking || conf.IDKeyDigestPolicy() == idkeydigest.MAAFallback {
|
||||
cmd.PrintErrln("Your config asks for validating the idkeydigest. This is only available on Confidential VMs. It will not be enforced.")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,7 +137,9 @@ func (i *initCmd) initialize(cmd *cobra.Command, newDialer func(validator *cloud
|
||||
cmd.PrintErrf("License check failed: %v", err)
|
||||
}
|
||||
i.log.Debugf("Checked license")
|
||||
validator, err := cloudcmd.NewValidator(conf, i.log)
|
||||
|
||||
i.log.Debugf("Creating aTLS Validator for %s", conf.AttestationVariant)
|
||||
validator, err := cloudcmd.NewValidator(conf, idFile.AttestationURL, i.log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -153,7 +155,7 @@ func (i *initCmd) initialize(cmd *cobra.Command, newDialer func(validator *cloud
|
||||
}
|
||||
helmLoader := helm.NewLoader(provider, k8sVersion)
|
||||
i.log.Debugf("Created new Helm loader")
|
||||
helmDeployments, err := helmLoader.Load(conf, flags.conformance, masterSecret.Key, masterSecret.Salt)
|
||||
helmDeployments, err := helmLoader.Load(conf, flags.conformance, masterSecret.Key, masterSecret.Salt, idFile.AttestationURL)
|
||||
i.log.Debugf("Loaded Helm deployments")
|
||||
if err != nil {
|
||||
return fmt.Errorf("loading Helm charts: %w", err)
|
||||
@ -172,7 +174,6 @@ func (i *initCmd) initialize(cmd *cobra.Command, newDialer func(validator *cloud
|
||||
KubernetesComponents: versions.VersionConfigs[k8sVersion].KubernetesComponents.ToInitProto(),
|
||||
HelmDeployments: helmDeployments,
|
||||
EnforcedPcrs: conf.EnforcedPCRs(),
|
||||
EnforceIdkeydigest: conf.EnforcesIDKeyDigest(),
|
||||
ConformanceMode: flags.conformance,
|
||||
InitSecret: idFile.InitSecret,
|
||||
ClusterName: clusterName,
|
||||
@ -426,17 +427,6 @@ func (i *initCmd) readOrGenerateMasterSecret(outWriter io.Writer, fileHandler fi
|
||||
return secret, nil
|
||||
}
|
||||
|
||||
func readIPFromIDFile(fileHandler file.Handler) (string, error) {
|
||||
var idFile clusterid.File
|
||||
if err := fileHandler.ReadJSON(constants.ClusterIDsFileName, &idFile); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if idFile.IP == "" {
|
||||
return "", fmt.Errorf("missing IP address in %q", constants.ClusterIDsFileName)
|
||||
}
|
||||
return idFile.IP, nil
|
||||
}
|
||||
|
||||
func (i *initCmd) getMarshaledServiceAccountURI(provider cloudprovider.Provider, config *config.Config, fileHandler file.Handler) (string, error) {
|
||||
i.log.Debugf("Getting service account URI")
|
||||
switch provider {
|
||||
|
@ -16,6 +16,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
|
||||
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
|
||||
"github.com/edgelesssys/constellation/v2/disk-mapper/recoverproto"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||
@ -95,7 +96,8 @@ func (r *recoverCmd) recover(
|
||||
interval = 20 * time.Second // Azure LB takes a while to remove unhealthy instances
|
||||
}
|
||||
|
||||
validator, err := cloudcmd.NewValidator(conf, r.log)
|
||||
r.log.Debugf("Creating aTLS Validator for %s", conf.AttestationVariant)
|
||||
validator, err := cloudcmd.NewValidator(conf, flags.maaURL, r.log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -208,20 +210,23 @@ type recoverFlags struct {
|
||||
endpoint string
|
||||
secretPath string
|
||||
configPath string
|
||||
maaURL string
|
||||
force bool
|
||||
}
|
||||
|
||||
func (r *recoverCmd) parseRecoverFlags(cmd *cobra.Command, fileHandler file.Handler) (recoverFlags, error) {
|
||||
var idFile clusterid.File
|
||||
if err := fileHandler.ReadJSON(constants.ClusterIDsFileName, &idFile); err != nil && !errors.Is(err, afero.ErrFileNotFound) {
|
||||
return recoverFlags{}, err
|
||||
}
|
||||
|
||||
endpoint, err := cmd.Flags().GetString("endpoint")
|
||||
r.log.Debugf("Endpoint flag is %s", endpoint)
|
||||
if err != nil {
|
||||
return recoverFlags{}, fmt.Errorf("parsing endpoint argument: %w", err)
|
||||
}
|
||||
if endpoint == "" {
|
||||
endpoint, err = readIPFromIDFile(fileHandler)
|
||||
if err != nil {
|
||||
return recoverFlags{}, fmt.Errorf("getting recovery endpoint: %w", err)
|
||||
}
|
||||
endpoint = idFile.IP
|
||||
}
|
||||
endpoint, err = addPortIfMissing(endpoint, constants.RecoveryPort)
|
||||
if err != nil {
|
||||
@ -248,6 +253,7 @@ func (r *recoverCmd) parseRecoverFlags(cmd *cobra.Command, fileHandler file.Hand
|
||||
endpoint: endpoint,
|
||||
secretPath: masterSecretPath,
|
||||
configPath: configPath,
|
||||
maaURL: idFile.AttestationURL,
|
||||
force: force,
|
||||
}, nil
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -84,9 +83,8 @@ func (v *verifyCmd) verify(cmd *cobra.Command, fileHandler file.Handler, verifyC
|
||||
return err
|
||||
}
|
||||
|
||||
provider := conf.GetProvider()
|
||||
v.log.Debugf("Creating aTLS Validator for %s", provider)
|
||||
validators, err := cloudcmd.NewValidator(conf, v.log)
|
||||
v.log.Debugf("Creating aTLS Validator for %s", conf.AttestationVariant)
|
||||
validators, err := cloudcmd.NewValidator(conf, flags.maaURL, v.log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -143,24 +141,24 @@ func (v *verifyCmd) parseVerifyFlags(cmd *cobra.Command, fileHandler file.Handle
|
||||
}
|
||||
v.log.Debugf("Flag 'force' set to %t", force)
|
||||
|
||||
var idFile clusterid.File
|
||||
if err := fileHandler.ReadJSON(constants.ClusterIDsFileName, &idFile); err != nil && !errors.Is(err, afero.ErrFileNotFound) {
|
||||
return verifyFlags{}, fmt.Errorf("reading cluster ID file: %w", err)
|
||||
}
|
||||
|
||||
// Get empty values from ID file
|
||||
emptyEndpoint := endpoint == ""
|
||||
emptyIDs := ownerID == "" && clusterID == ""
|
||||
if emptyEndpoint || emptyIDs {
|
||||
v.log.Debugf("Trying to supplement empty flag values from %q", constants.ClusterIDsFileName)
|
||||
var idFile clusterid.File
|
||||
if err := fileHandler.ReadJSON(constants.ClusterIDsFileName, &idFile); err == nil {
|
||||
if emptyEndpoint {
|
||||
cmd.Printf("Using endpoint from %q. Specify --node-endpoint to override this.\n", constants.ClusterIDsFileName)
|
||||
endpoint = idFile.IP
|
||||
}
|
||||
if emptyIDs {
|
||||
cmd.Printf("Using ID from %q. Specify --cluster-id to override this.\n", constants.ClusterIDsFileName)
|
||||
ownerID = idFile.OwnerID
|
||||
clusterID = idFile.ClusterID
|
||||
}
|
||||
} else if !errors.Is(err, fs.ErrNotExist) {
|
||||
return verifyFlags{}, fmt.Errorf("reading cluster ID file: %w", err)
|
||||
if emptyEndpoint {
|
||||
cmd.Printf("Using endpoint from %q. Specify --node-endpoint to override this.\n", constants.ClusterIDsFileName)
|
||||
endpoint = idFile.IP
|
||||
}
|
||||
if emptyIDs {
|
||||
cmd.Printf("Using ID from %q. Specify --cluster-id to override this.\n", constants.ClusterIDsFileName)
|
||||
ownerID = idFile.OwnerID
|
||||
clusterID = idFile.ClusterID
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,6 +176,7 @@ func (v *verifyCmd) parseVerifyFlags(cmd *cobra.Command, fileHandler file.Handle
|
||||
configPath: configPath,
|
||||
ownerID: ownerID,
|
||||
clusterID: clusterID,
|
||||
maaURL: idFile.AttestationURL,
|
||||
force: force,
|
||||
}, nil
|
||||
}
|
||||
@ -187,6 +186,7 @@ type verifyFlags struct {
|
||||
ownerID string
|
||||
clusterID string
|
||||
configPath string
|
||||
maaURL string
|
||||
force bool
|
||||
}
|
||||
|
||||
|
@ -319,6 +319,7 @@ go_library(
|
||||
importpath = "github.com/edgelesssys/constellation/v2/cli/internal/helm",
|
||||
visibility = ["//cli:__subpackages__"],
|
||||
deps = [
|
||||
"//internal/attestation/idkeydigest",
|
||||
"//internal/cloud/cloudprovider",
|
||||
"//internal/compatibility",
|
||||
"//internal/config",
|
||||
@ -352,6 +353,7 @@ go_test(
|
||||
data = glob(["testdata/**"]),
|
||||
embed = [":helm"],
|
||||
deps = [
|
||||
"//internal/attestation/idkeydigest",
|
||||
"//internal/attestation/measurements",
|
||||
"//internal/cloud/cloudprovider",
|
||||
"//internal/compatibility",
|
||||
|
@ -7,9 +7,7 @@ data:
|
||||
{{/* mustToJson is required so the json-strings passed from go are of type string in the rendered yaml. */}}
|
||||
measurements: {{ .Values.measurements | mustToJson }}
|
||||
{{- if eq .Values.csp "Azure" }}
|
||||
{{/* ConfigMap.data is of type map[string]string. quote will not quote a quoted string. */}}
|
||||
enforceIdKeyDigest: {{ .Values.enforceIdKeyDigest | quote }}
|
||||
idkeydigests: {{ .Values.idkeydigests | mustToJson }}
|
||||
idKeyConfig: {{ .Values.idKeyConfig | mustToJson }}
|
||||
{{- end }}
|
||||
binaryData:
|
||||
measurementSalt: {{ .Values.measurementSalt }}
|
||||
|
@ -11,13 +11,13 @@
|
||||
"examples": ["{'1':{'expected':'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA','warnOnly':true},'15':{'expected':'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=','warnOnly':true}}"]
|
||||
},
|
||||
"enforceIdKeyDigest": {
|
||||
"description": "Whether or not idkeydigest should be enforced during attestation on azure.",
|
||||
"type": "boolean"
|
||||
"description": "ID Key Digest enforcement policy.",
|
||||
"enum": ["StrictChecking", "MAAFallback", "WarnOnly"]
|
||||
},
|
||||
"idkeydigests": {
|
||||
"description": "List of expected idkeydigest values for Azure SNP attestation.",
|
||||
"idKeyConfig": {
|
||||
"description": "Configuration for validating the ID Key Digest of the SEV-SNP attestation.",
|
||||
"type": "string",
|
||||
"examples": ["[\"57486a447ec0f1958002a22a06b7673b9fd27d11e1c6527498056054c5fa92d23c50f9de44072760fe2b6fb89740b696\", \"0356215882a825279a85b300b0b742931d113bf7e32dde2e50ffde7ec743ca491ecdd7f336dc28a6e0b2bb57af7a44a3\"]"]
|
||||
"examples": ["{'EnforcementPolicy': 'MAAFallback', 'MAAURL': 'https://192.0.2.1:8080/maa', 'IDKeyDigests': ['57486a447ec0f1958002a22a06b7673b9fd27d11e1c6527498056054c5fa92d23c50f9de44072760fe2b6fb89740b696', '0356215882a825279a85b300b0b742931d113bf7e32dde2e50ffde7ec743ca491ecdd7f336dc28a6e0b2bb57af7a44a3'}"]
|
||||
},
|
||||
"image": {
|
||||
"description": "Container image to use for the spawned pods.",
|
||||
@ -46,7 +46,7 @@
|
||||
"properties": { "csp": { "const": "azure" } },
|
||||
"required": ["csp"]
|
||||
},
|
||||
"then": { "required": ["enforceIdKeyDigest", "idkeydigests"] },
|
||||
"then": { "required": ["idKeyConfig"] },
|
||||
"title": "Values",
|
||||
"type": "object"
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
csp: "gcp"
|
||||
attestationVariant: ""
|
||||
measurements: ""
|
||||
idKeyConfig: ""
|
||||
measurementSalt: ""
|
||||
joinServicePort: 9090
|
||||
joinServiceNodePort: 30090
|
||||
|
@ -17,6 +17,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/compatibility"
|
||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||
@ -107,7 +108,7 @@ func AvailableServiceVersions() (string, error) {
|
||||
}
|
||||
|
||||
// Load the embedded helm charts.
|
||||
func (i *ChartLoader) Load(config *config.Config, conformanceMode bool, masterSecret, salt []byte) ([]byte, error) {
|
||||
func (i *ChartLoader) Load(config *config.Config, conformanceMode bool, masterSecret, salt []byte, maaURL string) ([]byte, error) {
|
||||
ciliumRelease, err := i.loadRelease(ciliumInfo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("loading cilium: %w", err)
|
||||
@ -128,7 +129,7 @@ func (i *ChartLoader) Load(config *config.Config, conformanceMode bool, masterSe
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("loading constellation-services: %w", err)
|
||||
}
|
||||
if err := extendConstellationServicesValues(conServicesRelease.Values, config, masterSecret, salt); err != nil {
|
||||
if err := extendConstellationServicesValues(conServicesRelease.Values, config, masterSecret, salt, maaURL); err != nil {
|
||||
return nil, fmt.Errorf("extending constellation-services values: %w", err)
|
||||
}
|
||||
|
||||
@ -467,7 +468,9 @@ func (i *ChartLoader) loadConstellationServicesValues() (map[string]any, error)
|
||||
// reuse user input from the init step. However, we can't rely on reuse-values, because
|
||||
// during upgrades all values need to be set locally as they might have changed.
|
||||
// Also, the charts are not rendered correctly without all of these values.
|
||||
func extendConstellationServicesValues(in map[string]any, config *config.Config, masterSecret, salt []byte) error {
|
||||
func extendConstellationServicesValues(
|
||||
in map[string]any, config *config.Config, masterSecret, salt []byte, maaURL string,
|
||||
) error {
|
||||
keyServiceValues, ok := in["key-service"].(map[string]any)
|
||||
if !ok {
|
||||
return errors.New("missing 'key-service' key")
|
||||
@ -494,13 +497,17 @@ func extendConstellationServicesValues(in map[string]any, config *config.Config,
|
||||
if !ok {
|
||||
return errors.New("invalid join-service values")
|
||||
}
|
||||
joinServiceVals["enforceIdKeyDigest"] = config.EnforcesIDKeyDigest()
|
||||
|
||||
marshalledDigests, err := json.Marshal(config.IDKeyDigests())
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshalling id key digests: %w", err)
|
||||
idKeyCfg := idkeydigest.Config{
|
||||
IDKeyDigests: config.IDKeyDigests(),
|
||||
EnforcementPolicy: config.IDKeyDigestPolicy(),
|
||||
MAAURL: maaURL,
|
||||
}
|
||||
joinServiceVals["idkeydigests"] = string(marshalledDigests)
|
||||
marshalledCfg, err := json.Marshal(idKeyCfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshalling id key digest config: %w", err)
|
||||
}
|
||||
joinServiceVals["idKeyConfig"] = string(marshalledCfg)
|
||||
|
||||
in["azure"] = map[string]any{
|
||||
"deployCSIDriver": config.DeployCSIDriver(),
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
"github.com/edgelesssys/constellation/v2/internal/config"
|
||||
@ -38,7 +39,7 @@ func TestLoad(t *testing.T) {
|
||||
|
||||
config := &config.Config{Provider: config.ProviderConfig{GCP: &config.GCPConfig{}}}
|
||||
chartLoader := ChartLoader{csp: config.GetProvider()}
|
||||
release, err := chartLoader.Load(config, true, []byte("secret"), []byte("salt"))
|
||||
release, err := chartLoader.Load(config, true, []byte("secret"), []byte("salt"), "https://192.0.2.1:8080/maa")
|
||||
require.NoError(err)
|
||||
|
||||
var helmReleases helm.Releases
|
||||
@ -72,7 +73,11 @@ func TestConstellationServices(t *testing.T) {
|
||||
AttestationVariant: oid.AzureSEVSNP{}.String(),
|
||||
Provider: config.ProviderConfig{Azure: &config.AzureConfig{
|
||||
DeployCSIDriver: toPtr(true),
|
||||
EnforceIDKeyDigest: toPtr(true),
|
||||
EnforceIDKeyDigest: idkeydigest.StrictChecking,
|
||||
IDKeyDigest: [][]byte{
|
||||
{0xba, 0xaa, 0xaa, 0xad, 0xba, 0xaa, 0xaa, 0xad, 0xba, 0xaa, 0xaa, 0xad, 0xba, 0xaa, 0xaa, 0xad, 0xba, 0xaa, 0xaa, 0xad, 0xba, 0xaa, 0xaa, 0xad, 0xba, 0xaa, 0xaa, 0xad, 0xba, 0xaa, 0xaa, 0xad},
|
||||
{0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa},
|
||||
},
|
||||
}},
|
||||
},
|
||||
enforceIDKeyDigest: true,
|
||||
@ -127,7 +132,7 @@ func TestConstellationServices(t *testing.T) {
|
||||
require.NoError(err)
|
||||
values, err := chartLoader.loadConstellationServicesValues()
|
||||
require.NoError(err)
|
||||
err = extendConstellationServicesValues(values, tc.config, []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"))
|
||||
err = extendConstellationServicesValues(values, tc.config, []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), "https://192.0.2.1:8080/maa")
|
||||
require.NoError(err)
|
||||
|
||||
options := chartutil.ReleaseOptions{
|
||||
|
@ -5,7 +5,6 @@ metadata:
|
||||
namespace: testNamespace
|
||||
data:
|
||||
measurements: "{\"1\":{\"expected\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"warnOnly\":false}}"
|
||||
enforceIdKeyDigest: "true"
|
||||
idkeydigests: "[\"baaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaad\", \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"]"
|
||||
idKeyConfig: "{\"idKeyDigests\":[\"baaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaad\",\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"],\"enforcementPolicy\":\"StrictChecking\",\"maaURL\":\"https://192.0.2.1:8080/maa\"}"
|
||||
binaryData:
|
||||
measurementSalt: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
|
3
go.mod
3
go.mod
@ -33,6 +33,7 @@ replace (
|
||||
|
||||
replace (
|
||||
github.com/edgelesssys/constellation/v2/operators/constellation-node-operator/v2/api => ./operators/constellation-node-operator/api
|
||||
github.com/google/go-tpm => github.com/thomasten/go-tpm v0.0.0-20230222180349-bb3cc5560299
|
||||
github.com/google/go-tpm-tools => github.com/daniel-weisse/go-tpm-tools v0.0.0-20230105122812-f7474d459dfc
|
||||
)
|
||||
|
||||
@ -68,6 +69,7 @@ require (
|
||||
github.com/coreos/go-systemd/v22 v22.5.0
|
||||
github.com/docker/docker v20.10.22+incompatible
|
||||
github.com/edgelesssys/constellation/v2/operators/constellation-node-operator/v2/api v0.0.0
|
||||
github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f
|
||||
github.com/fsnotify/fsnotify v1.6.0
|
||||
github.com/go-playground/locales v0.14.1
|
||||
github.com/go-playground/universal-translator v0.18.1
|
||||
@ -187,6 +189,7 @@ require (
|
||||
github.com/go-chi/chi v4.1.2+incompatible // indirect
|
||||
github.com/go-errors/errors v1.4.2 // indirect
|
||||
github.com/go-gorp/gorp/v3 v3.0.2 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
github.com/go-openapi/analysis v0.21.4 // indirect
|
||||
github.com/go-openapi/errors v0.20.3 // indirect
|
||||
|
9
go.sum
9
go.sum
@ -405,6 +405,8 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f h1:J9k1gV8YA5beC6jANKQy5O7UtaKS3ueuanxUan5Y5NU=
|
||||
github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f/go.mod h1:hX9gZBSvliJcBEAyrJDh7990hLRg/Is+6PBpDZWSMoc=
|
||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc=
|
||||
github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE=
|
||||
@ -473,6 +475,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
|
||||
github.com/go-gorp/gorp/v3 v3.0.2 h1:ULqJXIekoqMx29FI5ekXXFoH1dT2Vc8UhnRzBg+Emz4=
|
||||
github.com/go-gorp/gorp/v3 v3.0.2/go.mod h1:BJ3q1ejpV8cVALtcXvXaXyTOlMmJhWDxTmncaR6rwBY=
|
||||
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
|
||||
@ -677,8 +681,6 @@ github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVgg
|
||||
github.com/google/go-replayers/httpreplay v0.1.0/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no=
|
||||
github.com/google/go-sev-guest v0.4.1 h1:IjxtGAvzR+zSyAqMc1FWfYKCg1cwPkBly9+Xog3YMZc=
|
||||
github.com/google/go-sev-guest v0.4.1/go.mod h1:UEi9uwoPbLdKGl1QHaq1G8pfCbQ4QP0swWX4J0k6r+Q=
|
||||
github.com/google/go-tpm v0.3.3 h1:P/ZFNBZYXRxc+z7i5uyd8VP7MaDteuLZInzrH2idRGo=
|
||||
github.com/google/go-tpm v0.3.3/go.mod h1:9Hyn3rgnzWF9XBWVk6ml6A6hNkbWjNFlDQL51BeghL4=
|
||||
github.com/google/go-tspi v0.2.1-0.20190423175329-115dea689aad/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI=
|
||||
github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus=
|
||||
github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI=
|
||||
@ -1319,6 +1321,8 @@ github.com/tent/canonical-json-go v0.0.0-20130607151641-96e4ba3a7613 h1:iGnD/q91
|
||||
github.com/tent/canonical-json-go v0.0.0-20130607151641-96e4ba3a7613/go.mod h1:g6AnIpDSYMcphz193otpSIzN+11Rs+AAIIC6rm1enug=
|
||||
github.com/theupdateframework/go-tuf v0.5.2 h1:habfDzTmpbzBLIFGWa2ZpVhYvFBoK0C1onC3a4zuPRA=
|
||||
github.com/theupdateframework/go-tuf v0.5.2/go.mod h1:SyMV5kg5n4uEclsyxXJZI2UxPFJNDc4Y+r7wv+MlvTA=
|
||||
github.com/thomasten/go-tpm v0.0.0-20230222180349-bb3cc5560299 h1:4/WcA7Q05f5to+jVERKcZjU5NJ89DiRHP3UUZHLfAtw=
|
||||
github.com/thomasten/go-tpm v0.0.0-20230222180349-bb3cc5560299/go.mod h1:9Hyn3rgnzWF9XBWVk6ml6A6hNkbWjNFlDQL51BeghL4=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0=
|
||||
@ -1483,6 +1487,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
|
@ -50,10 +50,14 @@ require (
|
||||
require (
|
||||
cloud.google.com/go/compute v1.18.0 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
code.cloudfoundry.org/clock v0.0.0-20180518195852-02e53af36e6c // indirect
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights v1.0.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.1.0 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.28 // indirect
|
||||
@ -114,6 +118,7 @@ require (
|
||||
github.com/docker/go-metrics v0.0.1 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/edgelesssys/constellation/v2/operators/constellation-node-operator/v2/api v0.0.0 // indirect
|
||||
github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
|
||||
@ -124,6 +129,7 @@ require (
|
||||
github.com/go-git/gcfg v1.5.0 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.4.1 // indirect
|
||||
github.com/go-gorp/gorp/v3 v3.0.2 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
github.com/go-openapi/analysis v0.21.4 // indirect
|
||||
github.com/go-openapi/errors v0.20.3 // indirect
|
||||
@ -139,6 +145,7 @@ require (
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.11.2 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/gofrs/uuid v4.2.0+incompatible // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
@ -197,6 +204,7 @@ require (
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/microsoft/ApplicationInsights-Go v0.4.4 // indirect
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
|
25
hack/go.sum
25
hack/go.sum
@ -63,6 +63,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
cloud.google.com/go/trace v0.1.0/go.mod h1:wxEwsoeRVPbeSkt7ZC9nWCgmoKQRAoySN7XHW2AmI7g=
|
||||
code.cloudfoundry.org/clock v0.0.0-20180518195852-02e53af36e6c h1:5eeuG0BHx1+DHeT3AP+ISKZ2ht1UjGhm581ljqYpVeQ=
|
||||
code.cloudfoundry.org/clock v0.0.0-20180518195852-02e53af36e6c/go.mod h1:QD9Lzhd/ux6eNQVUDVRJX/RKTigpewimNYBi7ivZKY8=
|
||||
code.gitea.io/sdk/gitea v0.11.3/go.mod h1:z3uwDV/b9Ls47NGukYM9XhnHtqPh/J+t40lsUrR6JDY=
|
||||
contrib.go.opencensus.io/exporter/aws v0.0.0-20181029163544-2befc13012d0/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA=
|
||||
contrib.go.opencensus.io/exporter/ocagent v0.5.0/go.mod h1:ImxhfLRpxoYiSq891pBrLVhN+qmP8BTVvdH2YLs7Gl0=
|
||||
@ -85,6 +87,16 @@ github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.1 h1:T8quHYlUGyb/oqtSTwqlC
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.1/go.mod h1:gLa1CL2RNE4s7M3yopJ/p0iq5DdY6Yv5ZUt9MTRZOQM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 h1:+5VZ72z0Qan5Bog5C+ZkgSqUbeVUd9wgtHOrIKuc5b8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights v1.0.0 h1:BpGGvzarSyE7kQF1x1hptUcGmNzZEE3yYI+uqBSNRxk=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights v1.0.0/go.mod h1:1ijUM40peD7YK5MFEJja2wjjp4eimFNWv0NXoY3nsZM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v3 v3.0.1 h1:H3g2mkmu105ON0c/Gqx3Bm+bzoIijLom8LmV9Gjn7X0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.1.0 h1:Vjq3Uy3JAU1DTxbA+uX6BegIhgO2pyFltbfbmDa9KdI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.1.0/go.mod h1:Q3u+T/qw3Kb1Wf3DFKiFwEZlyaAyPb4yBgWm9wq7yh8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.0.0 h1:lMW1lD/17LUA5z1XTURo7LcVG2ICBPlyMHjIUrcFZNQ=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 h1:QM6sE5k2ZT/vI5BEe0r7mqjsUSnhVBFbOsVkEuaEfiA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.1.0 h1:mk57wRUA8fyjFxVcPPGv4shLcWDXPFYokTJL9zJxQtE=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.1.0/go.mod h1:mU96hbp8qJDA9OzTV1Ji7wCyPyaqC5kI6ZPsZfJ8sE4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 h1:ECsQtyERDVz3NP3kvDOTLvbQhqWp/x9EsGKtb4ogUr8=
|
||||
github.com/Azure/azure-service-bus-go v0.9.1/go.mod h1:yzBx6/BUGfjfeqbRZny9AQIbIe3AcV9WZbAdpkoXOa0=
|
||||
github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
@ -102,6 +114,7 @@ github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSY
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU=
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk=
|
||||
github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac=
|
||||
github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
|
||||
github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=
|
||||
@ -361,6 +374,8 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f h1:J9k1gV8YA5beC6jANKQy5O7UtaKS3ueuanxUan5Y5NU=
|
||||
github.com/edgelesssys/go-azguestattestation v0.0.0-20230303085714-62ede861d33f/go.mod h1:hX9gZBSvliJcBEAyrJDh7990hLRg/Is+6PBpDZWSMoc=
|
||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc=
|
||||
github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE=
|
||||
@ -436,6 +451,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
|
||||
github.com/go-gorp/gorp/v3 v3.0.2 h1:ULqJXIekoqMx29FI5ekXXFoH1dT2Vc8UhnRzBg+Emz4=
|
||||
github.com/go-gorp/gorp/v3 v3.0.2/go.mod h1:BJ3q1ejpV8cVALtcXvXaXyTOlMmJhWDxTmncaR6rwBY=
|
||||
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
|
||||
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
|
||||
@ -541,6 +558,9 @@ github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZ
|
||||
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/godror/godror v0.24.2/go.mod h1:wZv/9vPiUib6tkoDl+AZ/QLf5YZgMravZ7jxH2eQWAE=
|
||||
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
|
||||
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
@ -961,6 +981,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/microsoft/ApplicationInsights-Go v0.4.4 h1:G4+H9WNs6ygSCe6sUyxRc2U81TI5Es90b2t/MwX5KqY=
|
||||
github.com/microsoft/ApplicationInsights-Go v0.4.4/go.mod h1:fKRUseBqkw6bDiXTs3ESTiU/4YTIHsQS4W3fP2ieF4U=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
@ -1037,6 +1059,7 @@ github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FW
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.10.3 h1:OoxbjfXVZyod1fmWYhI7SEyaD8B00ynP3T+D5GiyHOY=
|
||||
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo/v2 v2.6.0 h1:9t9b9vRUbFq3C4qKFCGkVuq/fIHji802N1nrtkh1mNc=
|
||||
@ -1270,6 +1293,7 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tedsuo/ifrit v0.0.0-20180802180643-bea94bb476cc/go.mod h1:eyZnKCc955uh98WQvzOm0dgAeLnf2O0Rz0LPoC5ze+0=
|
||||
github.com/tent/canonical-json-go v0.0.0-20130607151641-96e4ba3a7613 h1:iGnD/q9160NWqKZZ5vY4p0dMiYMRknzctfSkqA4nBDw=
|
||||
github.com/tent/canonical-json-go v0.0.0-20130607151641-96e4ba3a7613/go.mod h1:g6AnIpDSYMcphz193otpSIzN+11Rs+AAIIC6rm1enug=
|
||||
github.com/theupdateframework/go-tuf v0.5.2 h1:habfDzTmpbzBLIFGWa2ZpVhYvFBoK0C1onC3a4zuPRA=
|
||||
@ -1440,6 +1464,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
|
@ -43,6 +43,7 @@ go_test(
|
||||
"@com_github_aws_aws_sdk_go_v2_service_ec2//types",
|
||||
"@com_github_aws_smithy_go//middleware",
|
||||
"@com_github_google_go_tpm_tools//client",
|
||||
"@com_github_google_go_tpm_tools//proto/attest",
|
||||
"@com_github_stretchr_testify//assert",
|
||||
"@com_github_stretchr_testify//require",
|
||||
],
|
||||
|
@ -52,8 +52,8 @@ func getAttestationKey(tpm io.ReadWriter) (*tpmclient.Key, error) {
|
||||
|
||||
// getInstanceInfo returns information about the current instance using the aws Metadata SDK.
|
||||
// The returned bytes will be written into the attestation document.
|
||||
func getInstanceInfo(client awsMetaData) func(tpm io.ReadWriteCloser) ([]byte, error) {
|
||||
return func(io.ReadWriteCloser) ([]byte, error) {
|
||||
func getInstanceInfo(client awsMetaData) func(context.Context, io.ReadWriteCloser, []byte) ([]byte, error) {
|
||||
return func(context.Context, io.ReadWriteCloser, []byte) ([]byte, error) {
|
||||
ec2InstanceIdentityOutput, err := client.GetInstanceIdentityDocument(context.Background(), &imds.GetInstanceIdentityDocumentInput{})
|
||||
if err != nil {
|
||||
return nil, errors.New("unable to fetch instance identity document")
|
||||
|
@ -91,7 +91,7 @@ func TestGetInstanceInfo(t *testing.T) {
|
||||
instanceInfoFunc := getInstanceInfo(&tc.client)
|
||||
assert.NotNil(instanceInfoFunc)
|
||||
|
||||
info, err := instanceInfoFunc(tpm)
|
||||
info, err := instanceInfoFunc(context.Background(), tpm, nil)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
assert.Nil(info)
|
||||
|
@ -44,9 +44,9 @@ func NewValidator(pcrs measurements.M, log vtpm.AttestationLogger) *Validator {
|
||||
|
||||
// getTrustedKeys return the public area of the provides attestation key.
|
||||
// Normally, here the trust of this key should be verified, but currently AWS does not provide this feature.
|
||||
func getTrustedKey(akPub []byte, _ []byte) (crypto.PublicKey, error) {
|
||||
func getTrustedKey(_ context.Context, attDoc vtpm.AttestationDocument, _ []byte) (crypto.PublicKey, error) {
|
||||
// Copied from https://github.com/edgelesssys/constellation/blob/main/internal/attestation/qemu/validator.go
|
||||
pubArea, err := tpm2.DecodePublic(akPub)
|
||||
pubArea, err := tpm2.DecodePublic(attDoc.Attestation.AkPub)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -16,23 +16,24 @@ import (
|
||||
"github.com/aws/aws-sdk-go-v2/service/ec2"
|
||||
"github.com/aws/aws-sdk-go-v2/service/ec2/types"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
|
||||
"github.com/google/go-tpm-tools/proto/attest"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGeTrustedKey(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
attDoc []byte
|
||||
nonce []byte
|
||||
akPub []byte
|
||||
info []byte
|
||||
wantErr bool
|
||||
}{
|
||||
"nul byte docs": {
|
||||
attDoc: []byte{0x00, 0x00, 0x00, 0x00},
|
||||
nonce: []byte{0x00, 0x00, 0x00, 0x00},
|
||||
akPub: []byte{0x00, 0x00, 0x00, 0x00},
|
||||
info: []byte{0x00, 0x00, 0x00, 0x00},
|
||||
wantErr: true,
|
||||
},
|
||||
"nil": {
|
||||
attDoc: nil,
|
||||
nonce: nil,
|
||||
akPub: nil,
|
||||
info: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
@ -40,7 +41,16 @@ func TestGeTrustedKey(t *testing.T) {
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
out, err := getTrustedKey(tc.attDoc, tc.nonce)
|
||||
out, err := getTrustedKey(
|
||||
context.Background(),
|
||||
vtpm.AttestationDocument{
|
||||
Attestation: &attest.Attestation{
|
||||
AkPub: tc.akPub,
|
||||
},
|
||||
InstanceInfo: tc.info,
|
||||
},
|
||||
nil,
|
||||
)
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
|
@ -7,6 +7,7 @@ go_library(
|
||||
"errors.go",
|
||||
"imds.go",
|
||||
"issuer.go",
|
||||
"maa.go",
|
||||
"snp.go",
|
||||
"validator.go",
|
||||
],
|
||||
@ -16,8 +17,10 @@ go_library(
|
||||
"//internal/attestation/idkeydigest",
|
||||
"//internal/attestation/measurements",
|
||||
"//internal/attestation/vtpm",
|
||||
"//internal/cloud/azure",
|
||||
"//internal/crypto",
|
||||
"//internal/oid",
|
||||
"@com_github_edgelesssys_go_azguestattestation//maa",
|
||||
"@com_github_google_go_tpm//tpm2",
|
||||
"@com_github_google_go_tpm_tools//client",
|
||||
"@com_github_google_go_tpm_tools//proto/attest",
|
||||
@ -41,8 +44,11 @@ go_test(
|
||||
"//internal/attestation/idkeydigest",
|
||||
"//internal/attestation/simulator",
|
||||
"//internal/attestation/vtpm",
|
||||
"//internal/logger",
|
||||
"@com_github_edgelesssys_go_azguestattestation//maa",
|
||||
"@com_github_google_go_tpm//tpm2",
|
||||
"@com_github_google_go_tpm_tools//client",
|
||||
"@com_github_google_go_tpm_tools//proto/attest",
|
||||
"@com_github_stretchr_testify//assert",
|
||||
"@com_github_stretchr_testify//require",
|
||||
],
|
||||
|
@ -59,7 +59,7 @@ func (e *idKeyError) Unwrap() error {
|
||||
}
|
||||
|
||||
func (e *idKeyError) Error() string {
|
||||
return fmt.Sprintf("configured idkeydigests %x doesn't contain reported idkeydigest %x", e.expectedValues, e.encounteredValue)
|
||||
return fmt.Sprintf("configured idkeydigests %x don't contain reported idkeydigest %x", e.expectedValues, e.encounteredValue)
|
||||
}
|
||||
|
||||
type versionError struct {
|
||||
|
@ -8,42 +8,28 @@ package snp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"fmt"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/azure"
|
||||
)
|
||||
|
||||
// Modified version of bootstrapper/cloudprovider/azure/imds.go
|
||||
|
||||
const (
|
||||
imdsVcekURL = "http://169.254.169.254/metadata/THIM/amd/certification"
|
||||
)
|
||||
const tagMAAURL = "constellation-maa-url"
|
||||
|
||||
type imdsClient struct {
|
||||
client *http.Client
|
||||
imdsClient *azure.IMDSClient
|
||||
}
|
||||
|
||||
// Retrieve retrieves instance metadata from the azure imds API.
|
||||
func (c imdsClient) getVcek(ctx context.Context) (vcekResponse, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, imdsVcekURL, http.NoBody)
|
||||
func newIMDSClient() *imdsClient {
|
||||
return &imdsClient{
|
||||
imdsClient: azure.NewIMDSClient(),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *imdsClient) getMAAURL(ctx context.Context) (string, error) {
|
||||
tags, err := c.imdsClient.Tags(ctx)
|
||||
if err != nil {
|
||||
return vcekResponse{}, err
|
||||
}
|
||||
req.Header.Add("Metadata", "True")
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return vcekResponse{}, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var res vcekResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&res); err != nil {
|
||||
return vcekResponse{}, err
|
||||
return "", fmt.Errorf("getting tags: %w", err)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
type vcekResponse struct {
|
||||
VcekCert string
|
||||
CertificateChain string
|
||||
return tags[tagMAAURL], nil
|
||||
}
|
||||
|
@ -7,106 +7,76 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
package snp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
|
||||
"github.com/edgelesssys/constellation/v2/internal/oid"
|
||||
"github.com/edgelesssys/go-azguestattestation/maa"
|
||||
tpmclient "github.com/google/go-tpm-tools/client"
|
||||
"github.com/google/go-tpm/tpm2"
|
||||
)
|
||||
|
||||
const (
|
||||
lenHclHeader = 0x20
|
||||
lenSnpReport = 0x4a0
|
||||
lenSnpReportRuntimeDataPadding = 0x14
|
||||
tpmReportIdx = 0x01400001
|
||||
tpmAkIdx = 0x81000003
|
||||
)
|
||||
|
||||
// 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 TPM: %w", tpmReportIdx, err)
|
||||
}
|
||||
|
||||
report, err := newSNPReportFromBytes(reportRaw[lenHclHeader:])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating snp report: %w", err)
|
||||
}
|
||||
|
||||
return report.IDKeyDigest[:], nil
|
||||
}
|
||||
const tpmAkIdx = 0x81000003
|
||||
|
||||
// Issuer for Azure TPM attestation.
|
||||
type Issuer struct {
|
||||
oid.AzureSEVSNP
|
||||
*vtpm.Issuer
|
||||
|
||||
imds imdsAPI
|
||||
maa maaTokenCreator
|
||||
}
|
||||
|
||||
// NewIssuer initializes a new Azure Issuer.
|
||||
func NewIssuer(log vtpm.AttestationLogger) *Issuer {
|
||||
imdsAPI := imdsClient{
|
||||
client: &http.Client{Transport: &http.Transport{Proxy: nil}},
|
||||
i := &Issuer{
|
||||
imds: newIMDSClient(),
|
||||
maa: newMAAClient(),
|
||||
}
|
||||
|
||||
return &Issuer{
|
||||
Issuer: vtpm.NewIssuer(
|
||||
vtpm.OpenVTPM,
|
||||
getAttestationKey,
|
||||
getInstanceInfo(&tpmReport{}, imdsAPI),
|
||||
log,
|
||||
),
|
||||
}
|
||||
i.Issuer = vtpm.NewIssuer(
|
||||
vtpm.OpenVTPM,
|
||||
getAttestationKey,
|
||||
i.getInstanceInfo,
|
||||
log,
|
||||
)
|
||||
return i
|
||||
}
|
||||
|
||||
// getInstanceInfo loads and returns the SEV-SNP attestation report [1] and the
|
||||
// AMD VCEK certificate chain.
|
||||
// The attestation report is loaded from the TPM, the certificate chain is queried
|
||||
// from the cloud metadata API.
|
||||
// [1] https://github.com/AMDESE/sev-guest/blob/main/include/attestation.h
|
||||
func getInstanceInfo(reportGetter tpmReportGetter, imdsapi imdsAPI) func(tpm io.ReadWriteCloser) ([]byte, error) {
|
||||
return func(tpm io.ReadWriteCloser) ([]byte, error) {
|
||||
hclReport, err := reportGetter.get(tpm)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading report from TPM: %w", err)
|
||||
}
|
||||
if len(hclReport) < lenHclHeader+lenSnpReport+lenSnpReportRuntimeDataPadding {
|
||||
return nil, fmt.Errorf("report read from TPM is shorter then expected: %x", hclReport)
|
||||
}
|
||||
hclReport = hclReport[lenHclHeader:]
|
||||
|
||||
runtimeData, _, _ := bytes.Cut(hclReport[lenSnpReport+lenSnpReportRuntimeDataPadding:], []byte{0})
|
||||
|
||||
vcekResponse, err := imdsapi.getVcek(context.TODO())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getVcekFromIMDS: %w", err)
|
||||
}
|
||||
|
||||
instanceInfo := azureInstanceInfo{
|
||||
Vcek: []byte(vcekResponse.VcekCert),
|
||||
CertChain: []byte(vcekResponse.CertificateChain),
|
||||
AttestationReport: hclReport[:0x4a0],
|
||||
RuntimeData: runtimeData,
|
||||
}
|
||||
statement, err := json.Marshal(instanceInfo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("marshalling AzureInstanceInfo: %w", err)
|
||||
}
|
||||
|
||||
return statement, nil
|
||||
func (i *Issuer) getInstanceInfo(ctx context.Context, tpm io.ReadWriteCloser, userData []byte) ([]byte, error) {
|
||||
params, err := i.maa.newParameters(ctx, userData, tpm)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting system parameters: %w", err)
|
||||
}
|
||||
|
||||
var maaToken string
|
||||
|
||||
maaURL, err := i.imds.getMAAURL(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("retrieving MAA URL from IMDS API: %w", err)
|
||||
}
|
||||
if maaURL != "" {
|
||||
maaToken, err = i.maa.createToken(ctx, tpm, maaURL, userData, params)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating MAA token: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
instanceInfo := azureInstanceInfo{
|
||||
Vcek: params.VcekCert,
|
||||
CertChain: params.VcekChain,
|
||||
AttestationReport: params.SNPReport,
|
||||
RuntimeData: params.RuntimeData,
|
||||
MAAToken: maaToken,
|
||||
}
|
||||
statement, err := json.Marshal(instanceInfo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("marshalling AzureInstanceInfo: %w", err)
|
||||
}
|
||||
|
||||
return statement, nil
|
||||
}
|
||||
|
||||
// getAttestationKey reads the attestation key put into the TPM during early boot.
|
||||
@ -119,16 +89,11 @@ func getAttestationKey(tpm io.ReadWriter) (*tpmclient.Key, error) {
|
||||
return ak, nil
|
||||
}
|
||||
|
||||
type tpmReport struct{}
|
||||
|
||||
func (s *tpmReport) get(tpm io.ReadWriteCloser) ([]byte, error) {
|
||||
return tpm2.NVReadEx(tpm, tpmReportIdx, tpm2.HandleOwner, "", 0)
|
||||
}
|
||||
|
||||
type tpmReportGetter interface {
|
||||
get(tpm io.ReadWriteCloser) ([]byte, error)
|
||||
}
|
||||
|
||||
type imdsAPI interface {
|
||||
getVcek(ctx context.Context) (vcekResponse, error)
|
||||
getMAAURL(ctx context.Context) (string, error)
|
||||
}
|
||||
|
||||
type maaTokenCreator interface {
|
||||
newParameters(context.Context, []byte, io.ReadWriter) (maa.Parameters, error)
|
||||
createToken(context.Context, io.ReadWriter, string, []byte, maa.Parameters) (string, error)
|
||||
}
|
||||
|
@ -8,13 +8,13 @@ package snp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/simulator"
|
||||
"github.com/edgelesssys/go-azguestattestation/maa"
|
||||
tpmclient "github.com/google/go-tpm-tools/client"
|
||||
"github.com/google/go-tpm/tpm2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -23,27 +23,34 @@ import (
|
||||
|
||||
func TestGetSNPAttestation(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
report string
|
||||
vcek string
|
||||
certChain string
|
||||
maaURL string
|
||||
maaToken string
|
||||
apiError error
|
||||
tokenErr error
|
||||
paramsErr error
|
||||
wantErr bool
|
||||
}{
|
||||
"success": {
|
||||
report: "48434c41010000001c070000020000000000000000000000000000000000000002000000020000001f0003000000000001000000000000000000000000000000020000000000000000000000000000000000000001000000020000000000065d010000000000000000000000000000000ccc0895ef2f2c3b8c8568f5a2bb65ff5bf9387a09359742ad41e686cacfd38b00000000000000000000000000000000000000000000000000000000000000005677f1de87289e7ad2c7e99c805d0468b1a9ccd83f0d245afa5242d405da4d5725852f8c6550564870e5f3206dfb1841000000000000000000000000000000000000000000000000000000000000000057e229e0ffe5fa92d0faddff6cae0e61c926fc9ef9afd20a8b8cfcf7129db9338cbe5bf3f6987733a2bf65d06dc38fc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f7240b24a1babe2ece844c4f792bcd9844bf6907d14aeea00156310b9538daffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff020000000000065d0000000000000000000000000000000000000000000000009e44aaef02cfca6fddbaca669c6cfd29e1ab8d97ebc939857128acbb13b8740df31436d34e86e5f8ae0cdfeb3a0e185db46decac176cc77d761c22a1b9dcf25b020000000000065d0133010001330100020000000000065d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bcb7dc15abff884802e774b39adba8e6ff7efcf05e115c91588e657065151056a320f70c788d0e3619391052922e422b000000000000000000000000000000000000000000000000e8dbf581140443bbc681c50eca8639a76ef6cab34e0780cbca977e2e2a03f8b864fd4e9774b0f8055511567e031e59bf00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c020000010000000200000001000000480200007b226b657973223a5b7b226b6964223a2248434c416b507562222c226b65795f6f7073223a5b22656e6372797074225d2c226b7479223a22525341222c2265223a2241514142222c226e223a22747946717641414166324746656c6b5737566352684a6e4132597659364c6a427a65554e3276614d5a6e5a74685f74466e574d6b4b35415874757379434e656c337569703356475a7a54617a3558327447566a4772732d4d56486361703951647771555856573367394f515f74456269786378372d78626c554a516b474551666e626253646e5049326c764c7a4f73315a5f30766a65444178765351726d616773366e592d634a4157482d706744564a79487470735553735f5142576b6c617a44736f3557486d6e4d743973394d75696c57586f7830525379586e55656151796859316a753752545363526e5658754e7936377a5f454a6e774d393264727746623841556430534a5f396f687645596c34615a52444543476f3056726a635348552d4a474a6575574335566844425235454f6f4356424267716539653833765f6c4a784933574c65326f7653495a49497a416d625351227d5d2c22766d2d636f6e66696775726174696f6e223a7b22636f6e736f6c652d656e61626c6564223a747275652c2263757272656e742d74696d65223a313636313435353339312c227365637572652d626f6f74223a66616c73652c2274706d2d656e61626c6564223a747275652c22766d556e697175654964223a2242364339384333422d344543372d344441362d424432462d374439384432304437423735227d7d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
vcek: "someVCEKCert",
|
||||
certChain: "ASKARK",
|
||||
wantErr: false,
|
||||
"success without maa": {
|
||||
wantErr: false,
|
||||
},
|
||||
"report too short": {
|
||||
report: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
wantErr: true,
|
||||
"success with maa": {
|
||||
maaURL: "maaurl",
|
||||
maaToken: "maatoken",
|
||||
wantErr: false,
|
||||
},
|
||||
"vcek api fails": {
|
||||
report: "48434c41010000001c070000020000000000000000000000000000000000000002000000020000001f0003000000000001000000000000000000000000000000020000000000000000000000000000000000000001000000020000000000065d010000000000000000000000000000000ccc0895ef2f2c3b8c8568f5a2bb65ff5bf9387a09359742ad41e686cacfd38b00000000000000000000000000000000000000000000000000000000000000005677f1de87289e7ad2c7e99c805d0468b1a9ccd83f0d245afa5242d405da4d5725852f8c6550564870e5f3206dfb1841000000000000000000000000000000000000000000000000000000000000000057e229e0ffe5fa92d0faddff6cae0e61c926fc9ef9afd20a8b8cfcf7129db9338cbe5bf3f6987733a2bf65d06dc38fc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f7240b24a1babe2ece844c4f792bcd9844bf6907d14aeea00156310b9538daffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff020000000000065d0000000000000000000000000000000000000000000000009e44aaef02cfca6fddbaca669c6cfd29e1ab8d97ebc939857128acbb13b8740df31436d34e86e5f8ae0cdfeb3a0e185db46decac176cc77d761c22a1b9dcf25b020000000000065d0133010001330100020000000000065d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bcb7dc15abff884802e774b39adba8e6ff7efcf05e115c91588e657065151056a320f70c788d0e3619391052922e422b000000000000000000000000000000000000000000000000e8dbf581140443bbc681c50eca8639a76ef6cab34e0780cbca977e2e2a03f8b864fd4e9774b0f8055511567e031e59bf00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c020000010000000200000001000000480200007b226b657973223a5b7b226b6964223a2248434c416b507562222c226b65795f6f7073223a5b22656e6372797074225d2c226b7479223a22525341222c2265223a2241514142222c226e223a22747946717641414166324746656c6b5737566352684a6e4132597659364c6a427a65554e3276614d5a6e5a74685f74466e574d6b4b35415874757379434e656c337569703356475a7a54617a3558327447566a4772732d4d56486361703951647771555856573367394f515f74456269786378372d78626c554a516b474551666e626253646e5049326c764c7a4f73315a5f30766a65444178765351726d616773366e592d634a4157482d706744564a79487470735553735f5142576b6c617a44736f3557486d6e4d743973394d75696c57586f7830525379586e55656151796859316a753752545363526e5658754e7936377a5f454a6e774d393264727746623841556430534a5f396f687645596c34615a52444543476f3056726a635348552d4a474a6575574335566844425235454f6f4356424267716539653833765f6c4a784933574c65326f7653495a49497a416d625351227d5d2c22766d2d636f6e66696775726174696f6e223a7b22636f6e736f6c652d656e61626c6564223a747275652c2263757272656e742d74696d65223a313636313435353339312c227365637572652d626f6f74223a66616c73652c2274706d2d656e61626c6564223a747275652c22766d556e697175654964223a2242364339384333422d344543372d344441362d424432462d374439384432304437423735227d7d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"api fails": {
|
||||
apiError: errors.New(""),
|
||||
wantErr: true,
|
||||
},
|
||||
"createToken fails": {
|
||||
maaURL: "maaurl",
|
||||
tokenErr: errors.New(""),
|
||||
wantErr: true,
|
||||
},
|
||||
"newParameters fails": {
|
||||
paramsErr: errors.New(""),
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
@ -51,40 +58,55 @@ func TestGetSNPAttestation(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
tpm, err := simulator.OpenSimulatedTPM()
|
||||
require.NoError(err)
|
||||
defer tpm.Close()
|
||||
|
||||
imdsClient := stubImdsClient{
|
||||
vcek: tc.vcek,
|
||||
certChain: tc.certChain,
|
||||
apiError: tc.apiError,
|
||||
}
|
||||
reportDecoded, err := hex.DecodeString(tc.report)
|
||||
assert.NoError(err)
|
||||
snpAttestationReport := stubTpmReport{
|
||||
tpmContent: reportDecoded,
|
||||
err: nil,
|
||||
maaURL: tc.maaURL,
|
||||
apiError: tc.apiError,
|
||||
}
|
||||
|
||||
attestationJSON, err := getInstanceInfo(&snpAttestationReport, imdsClient)(tpm)
|
||||
params := maa.Parameters{
|
||||
SNPReport: []byte("snpreport"),
|
||||
RuntimeData: []byte("runtimedata"),
|
||||
VcekCert: []byte("vcekcert"),
|
||||
VcekChain: []byte("vcekchain"),
|
||||
}
|
||||
|
||||
maa := &stubMaaTokenCreator{
|
||||
token: tc.maaToken,
|
||||
tokenErr: tc.tokenErr,
|
||||
params: params,
|
||||
paramsErr: tc.paramsErr,
|
||||
}
|
||||
|
||||
issuer := Issuer{
|
||||
imds: imdsClient,
|
||||
maa: maa,
|
||||
}
|
||||
|
||||
data := []byte("data")
|
||||
|
||||
attestationJSON, err := issuer.getInstanceInfo(context.Background(), nil, data)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
assert.NoError(err)
|
||||
require.NoError(err)
|
||||
|
||||
assert.Equal(data, maa.gotParamsData)
|
||||
if tc.maaURL == "" {
|
||||
assert.Empty(maa.gotTokenData)
|
||||
} else {
|
||||
assert.Equal(data, maa.gotTokenData)
|
||||
}
|
||||
|
||||
var instanceInfo azureInstanceInfo
|
||||
err = json.Unmarshal(attestationJSON, &instanceInfo)
|
||||
assert.NoError(err)
|
||||
require.NoError(err)
|
||||
|
||||
if tc.wantErr {
|
||||
assert.NotEqualValues(tc.report[0x20:], instanceInfo.AttestationReport)
|
||||
} else {
|
||||
assert.EqualValues(reportDecoded[0x20:0x4a0+0x20], instanceInfo.AttestationReport)
|
||||
assert.Equal(tc.vcek, string(instanceInfo.Vcek))
|
||||
assert.Equal(tc.certChain, string(instanceInfo.CertChain))
|
||||
}
|
||||
assert.Equal(params.VcekCert, instanceInfo.Vcek)
|
||||
assert.Equal(params.VcekChain, instanceInfo.CertChain)
|
||||
assert.Equal(params.SNPReport, instanceInfo.AttestationReport)
|
||||
assert.Equal(params.RuntimeData, instanceInfo.RuntimeData)
|
||||
assert.Equal(tc.maaToken, instanceInfo.MAAToken)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -126,30 +148,30 @@ func TestGetHCLAttestationKey(t *testing.T) {
|
||||
}
|
||||
|
||||
type stubImdsClient struct {
|
||||
vcek string
|
||||
certChain string
|
||||
apiError error
|
||||
maaURL string
|
||||
apiError error
|
||||
}
|
||||
|
||||
func (c stubImdsClient) getVcek(_ context.Context) (vcekResponse, error) {
|
||||
if c.apiError != nil {
|
||||
return vcekResponse{}, c.apiError
|
||||
}
|
||||
|
||||
return vcekResponse{
|
||||
VcekCert: c.vcek,
|
||||
CertificateChain: c.certChain,
|
||||
}, nil
|
||||
func (c stubImdsClient) getMAAURL(_ context.Context) (string, error) {
|
||||
return c.maaURL, c.apiError
|
||||
}
|
||||
|
||||
type stubTpmReport struct {
|
||||
tpmContent []byte
|
||||
err error
|
||||
type stubMaaTokenCreator struct {
|
||||
token string
|
||||
tokenErr error
|
||||
gotTokenData []byte
|
||||
|
||||
params maa.Parameters
|
||||
paramsErr error
|
||||
gotParamsData []byte
|
||||
}
|
||||
|
||||
func (s *stubTpmReport) get(_ io.ReadWriteCloser) ([]byte, error) {
|
||||
if s.err != nil {
|
||||
return nil, s.err
|
||||
}
|
||||
return s.tpmContent, nil
|
||||
func (s *stubMaaTokenCreator) newParameters(_ context.Context, data []byte, _ io.ReadWriter) (maa.Parameters, error) {
|
||||
s.gotParamsData = data
|
||||
return s.params, s.paramsErr
|
||||
}
|
||||
|
||||
func (s *stubMaaTokenCreator) createToken(_ context.Context, _ io.ReadWriter, _ string, data []byte, _ maa.Parameters) (string, error) {
|
||||
s.gotTokenData = data
|
||||
return s.token, s.tokenErr
|
||||
}
|
||||
|
80
internal/attestation/azure/snp/maa.go
Normal file
80
internal/attestation/azure/snp/maa.go
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
Copyright (c) Edgeless Systems GmbH
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package snp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/edgelesssys/go-azguestattestation/maa"
|
||||
)
|
||||
|
||||
type maaClient struct {
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
func newMAAClient() *maaClient {
|
||||
return &maaClient{
|
||||
client: &http.Client{Transport: &http.Transport{Proxy: nil}},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *maaClient) newParameters(ctx context.Context, nonce []byte, tpmHandle io.ReadWriter) (maa.Parameters, error) {
|
||||
return maa.NewParameters(ctx, nonce, m.client, tpmHandle)
|
||||
}
|
||||
|
||||
func (m *maaClient) createToken(
|
||||
ctx context.Context, tpm io.ReadWriter, maaURL string, data []byte, params maa.Parameters,
|
||||
) (string, error) {
|
||||
tokenEnc, err := maa.GetEncryptedToken(ctx, params, data, maaURL, m.client)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("getting encrypted token: %w", err)
|
||||
}
|
||||
|
||||
token, err := maa.DecryptToken(tokenEnc, tpm)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("decrypting token: %w", err)
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (m *maaClient) validateToken(ctx context.Context, maaURL, token string, extraData []byte) error {
|
||||
keySet, err := maa.GetKeySet(ctx, maaURL, m.client)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting key set from MAA: %w", err)
|
||||
}
|
||||
claims, err := maa.ValidateToken(token, keySet)
|
||||
if err != nil {
|
||||
return fmt.Errorf("validating token: %w", err)
|
||||
}
|
||||
return m.validateClaims(claims, extraData)
|
||||
}
|
||||
|
||||
func (m *maaClient) validateClaims(claims map[string]interface{}, extraData []byte) error {
|
||||
runtime, ok := claims["x-ms-runtime"].(map[string]interface{})
|
||||
if !ok {
|
||||
return errors.New("invalid claims: missing x-ms-runtime")
|
||||
}
|
||||
payload, ok := runtime["client-payload"].(map[string]interface{})
|
||||
if !ok {
|
||||
return errors.New("invalid claims: missing client-payload")
|
||||
}
|
||||
nonce, ok := payload["nonce"].(string)
|
||||
if !ok {
|
||||
return errors.New("invalid claims: missing nonce")
|
||||
}
|
||||
|
||||
if nonce != base64.StdEncoding.EncodeToString(extraData) {
|
||||
return errors.New("invalid claims: nonce does not match extra data")
|
||||
}
|
||||
return nil
|
||||
}
|
@ -8,6 +8,7 @@ package snp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
@ -41,18 +42,36 @@ const (
|
||||
type Validator struct {
|
||||
oid.AzureSEVSNP
|
||||
*vtpm.Validator
|
||||
hclValidator hclAkValidator
|
||||
maa maaValidator
|
||||
|
||||
idKeyDigests idkeydigest.IDKeyDigests
|
||||
enforceIDKeyDigest idkeydigest.EnforceIDKeyDigest
|
||||
maaURL string
|
||||
|
||||
log vtpm.AttestationLogger
|
||||
}
|
||||
|
||||
// NewValidator initializes a new Azure validator with the provided PCR values.
|
||||
func NewValidator(pcrs measurements.M, idKeyDigests idkeydigest.IDKeyDigests, enforceIDKeyDigest bool, log vtpm.AttestationLogger) *Validator {
|
||||
return &Validator{
|
||||
Validator: vtpm.NewValidator(
|
||||
pcrs,
|
||||
getTrustedKey(&azureInstanceInfo{}, idKeyDigests, enforceIDKeyDigest, log),
|
||||
validateCVM,
|
||||
log,
|
||||
),
|
||||
func NewValidator(pcrs measurements.M, idKeyConf idkeydigest.Config, log vtpm.AttestationLogger) *Validator {
|
||||
if log == nil {
|
||||
log = nopAttestationLogger{}
|
||||
}
|
||||
v := &Validator{
|
||||
hclValidator: &azureInstanceInfo{},
|
||||
maa: newMAAClient(),
|
||||
idKeyDigests: idKeyConf.IDKeyDigests,
|
||||
enforceIDKeyDigest: idKeyConf.EnforcementPolicy,
|
||||
maaURL: idKeyConf.MAAURL,
|
||||
log: log,
|
||||
}
|
||||
v.Validator = vtpm.NewValidator(
|
||||
pcrs,
|
||||
v.getTrustedKey,
|
||||
validateCVM,
|
||||
log,
|
||||
)
|
||||
return v
|
||||
}
|
||||
|
||||
// validateCVM is a stub, since SEV-SNP attestation is already verified in trustedKeyFromSNP().
|
||||
@ -77,40 +96,36 @@ func reverseEndian(b []byte) {
|
||||
|
||||
// getTrustedKey establishes trust in the given public key.
|
||||
// It does so by verifying the SNP attestation statement in instanceInfo.
|
||||
func getTrustedKey(
|
||||
hclAk HCLAkValidator, idKeyDigest idkeydigest.IDKeyDigests, enforceIDKeyDigest bool, log vtpm.AttestationLogger,
|
||||
) func(akPub, instanceInfoRaw []byte) (crypto.PublicKey, error) {
|
||||
return func(akPub, instanceInfoRaw []byte) (crypto.PublicKey, error) {
|
||||
var instanceInfo azureInstanceInfo
|
||||
if err := json.Unmarshal(instanceInfoRaw, &instanceInfo); err != nil {
|
||||
return nil, fmt.Errorf("unmarshalling instanceInfoRaw: %w", err)
|
||||
}
|
||||
|
||||
report, err := newSNPReportFromBytes(instanceInfo.AttestationReport)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing attestation report: %w", err)
|
||||
}
|
||||
|
||||
vcek, err := validateVCEK(instanceInfo.Vcek, instanceInfo.CertChain)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("validating VCEK: %w", err)
|
||||
}
|
||||
|
||||
if err := validateSNPReport(vcek, idKeyDigest, enforceIDKeyDigest, report, log); err != nil {
|
||||
return nil, fmt.Errorf("validating SNP report: %w", err)
|
||||
}
|
||||
|
||||
pubArea, err := tpm2.DecodePublic(akPub)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = hclAk.validateAk(instanceInfo.RuntimeData, report.ReportData[:], pubArea.RSAParameters); err != nil {
|
||||
return nil, fmt.Errorf("validating HCLAkPub: %w", err)
|
||||
}
|
||||
|
||||
return pubArea.Key()
|
||||
func (v *Validator) getTrustedKey(ctx context.Context, attDoc vtpm.AttestationDocument, extraData []byte) (crypto.PublicKey, error) {
|
||||
var instanceInfo azureInstanceInfo
|
||||
if err := json.Unmarshal(attDoc.InstanceInfo, &instanceInfo); err != nil {
|
||||
return nil, fmt.Errorf("unmarshalling instanceInfoRaw: %w", err)
|
||||
}
|
||||
|
||||
report, err := newSNPReportFromBytes(instanceInfo.AttestationReport)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing attestation report: %w", err)
|
||||
}
|
||||
|
||||
vcek, err := validateVCEK(instanceInfo.Vcek, instanceInfo.CertChain)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("validating VCEK: %w", err)
|
||||
}
|
||||
|
||||
if err := v.validateSNPReport(ctx, vcek, report, instanceInfo.MAAToken, extraData); err != nil {
|
||||
return nil, fmt.Errorf("validating SNP report: %w", err)
|
||||
}
|
||||
|
||||
pubArea, err := tpm2.DecodePublic(attDoc.Attestation.AkPub)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = v.hclValidator.validateAk(instanceInfo.RuntimeData, report.ReportData[:], pubArea.RSAParameters); err != nil {
|
||||
return nil, fmt.Errorf("validating HCLAkPub: %w", err)
|
||||
}
|
||||
|
||||
return pubArea.Key()
|
||||
}
|
||||
|
||||
// validateVCEK takes the PEM-encoded X509 certificate VCEK, ASK and ARK and verifies the integrity of the chain.
|
||||
@ -143,9 +158,8 @@ func validateVCEK(vcekRaw []byte, certChain []byte) (*x509.Certificate, error) {
|
||||
return vcek, nil
|
||||
}
|
||||
|
||||
func validateSNPReport(
|
||||
cert *x509.Certificate, expectedIDKeyDigests idkeydigest.IDKeyDigests, enforceIDKeyDigest bool,
|
||||
report snpAttestationReport, log vtpm.AttestationLogger,
|
||||
func (v *Validator) validateSNPReport(
|
||||
ctx context.Context, cert *x509.Certificate, report snpAttestationReport, maaToken string, extraData []byte,
|
||||
) error {
|
||||
if report.Policy.Debug() {
|
||||
return errDebugEnabled
|
||||
@ -191,7 +205,7 @@ func validateSNPReport(
|
||||
}
|
||||
|
||||
hasExpectedIDKeyDigest := false
|
||||
for _, digest := range expectedIDKeyDigests {
|
||||
for _, digest := range v.idKeyDigests {
|
||||
if bytes.Equal(digest, report.IDKeyDigest[:]) {
|
||||
hasExpectedIDKeyDigest = true
|
||||
break
|
||||
@ -199,11 +213,14 @@ func validateSNPReport(
|
||||
}
|
||||
|
||||
if !hasExpectedIDKeyDigest {
|
||||
if enforceIDKeyDigest {
|
||||
return &idKeyError{report.IDKeyDigest[:], expectedIDKeyDigests}
|
||||
}
|
||||
if log != nil {
|
||||
log.Warnf("configured idkeydigests %x doesn't contain reported idkeydigest %x", expectedIDKeyDigests, report.IDKeyDigest[:])
|
||||
switch v.enforceIDKeyDigest {
|
||||
case idkeydigest.MAAFallback:
|
||||
v.log.Infof("configured idkeydigests %x don't contain reported idkeydigest %x, falling back to MAA validation", v.idKeyDigests, report.IDKeyDigest[:])
|
||||
return v.maa.validateToken(ctx, v.maaURL, maaToken, extraData)
|
||||
case idkeydigest.WarnOnly:
|
||||
v.log.Warnf("configured idkeydigests %x don't contain reported idkeydigest %x", v.idKeyDigests, report.IDKeyDigest[:])
|
||||
default:
|
||||
return &idKeyError{report.IDKeyDigest[:], v.idKeyDigests}
|
||||
}
|
||||
}
|
||||
|
||||
@ -270,6 +287,7 @@ type azureInstanceInfo struct {
|
||||
CertChain []byte
|
||||
AttestationReport []byte
|
||||
RuntimeData []byte
|
||||
MAAToken string
|
||||
}
|
||||
|
||||
// validateAk validates that the attestation key from the TPM is trustworthy. The steps are:
|
||||
@ -320,10 +338,10 @@ func (a *azureInstanceInfo) validateAk(runtimeDataRaw []byte, reportData []byte,
|
||||
return nil
|
||||
}
|
||||
|
||||
// HCLAkValidator validates an attestation key issued by the Host Compatibility Layer (HCL).
|
||||
// hclAkValidator validates an attestation key issued by the Host Compatibility Layer (HCL).
|
||||
// The HCL is written by Azure, and sits between the Hypervisor and CVM OS.
|
||||
// The HCL runs in the protected context of the CVM.
|
||||
type HCLAkValidator interface {
|
||||
type hclAkValidator interface {
|
||||
validateAk(runtimeDataRaw []byte, reportData []byte, rsaParameters *tpm2.RSAParams) error
|
||||
}
|
||||
|
||||
@ -413,3 +431,16 @@ type akPub struct {
|
||||
type runtimeData struct {
|
||||
Keys []akPub
|
||||
}
|
||||
|
||||
// nopAttestationLogger is a no-op implementation of AttestationLogger.
|
||||
type nopAttestationLogger struct{}
|
||||
|
||||
// Infof is a no-op.
|
||||
func (nopAttestationLogger) Infof(string, ...interface{}) {}
|
||||
|
||||
// Warnf is a no-op.
|
||||
func (nopAttestationLogger) Warnf(string, ...interface{}) {}
|
||||
|
||||
type maaValidator interface {
|
||||
validateToken(ctx context.Context, maaURL string, token string, extraData []byte) error
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -33,10 +33,12 @@ go_test(
|
||||
deps = [
|
||||
"//internal/attestation/measurements",
|
||||
"//internal/attestation/simulator",
|
||||
"//internal/attestation/vtpm",
|
||||
"//internal/crypto",
|
||||
"//internal/logger",
|
||||
"@com_github_google_go_tpm//tpm2",
|
||||
"@com_github_google_go_tpm_tools//client",
|
||||
"@com_github_google_go_tpm_tools//proto/attest",
|
||||
"@com_github_stretchr_testify//assert",
|
||||
"@com_github_stretchr_testify//require",
|
||||
],
|
||||
|
@ -14,7 +14,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
|
||||
"github.com/edgelesssys/constellation/v2/internal/oid"
|
||||
@ -55,7 +54,7 @@ type akSigner struct {
|
||||
}
|
||||
|
||||
// getAttestationCert returns the DER encoded certificate of the TPM's attestation key and it's CA.
|
||||
func (i *Issuer) getAttestationCert(tpm io.ReadWriteCloser) ([]byte, error) {
|
||||
func (i *Issuer) getAttestationCert(ctx context.Context, tpm io.ReadWriteCloser, _ []byte) ([]byte, error) {
|
||||
certDER, err := tpm2.NVReadEx(tpm, tpmAkCertIdx, tpm2.HandleOwner, "", 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading attestation key certificate from TPM: %w", err)
|
||||
@ -71,8 +70,6 @@ func (i *Issuer) getAttestationCert(tpm io.ReadWriteCloser) ([]byte, error) {
|
||||
// if no CA certificate can be loaded, an error is returned
|
||||
var caCert *x509.Certificate
|
||||
for _, caCertURL := range cert.IssuingCertificateURL {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*20)
|
||||
defer cancel()
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, caCertURL, nil)
|
||||
if err != nil {
|
||||
continue
|
||||
|
@ -8,6 +8,7 @@ package trustedlaunch
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
@ -19,9 +20,11 @@ import (
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/simulator"
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
|
||||
"github.com/edgelesssys/constellation/v2/internal/crypto"
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
tpmclient "github.com/google/go-tpm-tools/client"
|
||||
"github.com/google/go-tpm-tools/proto/attest"
|
||||
"github.com/google/go-tpm/tpm2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -183,13 +186,20 @@ func TestGetAttestationCert(t *testing.T) {
|
||||
issuer := NewIssuer(logger.NewTest(t))
|
||||
issuer.hClient = newTestClient(tc.crlServer)
|
||||
|
||||
certs, err := issuer.getAttestationCert(tpm)
|
||||
certs, err := issuer.getAttestationCert(context.Background(), tpm, nil)
|
||||
if tc.wantIssueErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
|
||||
attDoc := vtpm.AttestationDocument{
|
||||
InstanceInfo: certs,
|
||||
Attestation: &attest.Attestation{
|
||||
AkPub: akPub,
|
||||
},
|
||||
}
|
||||
|
||||
validator := NewValidator(measurements.M{}, nil)
|
||||
cert, err := x509.ParseCertificate(rootCert.Raw)
|
||||
require.NoError(err)
|
||||
@ -197,7 +207,7 @@ func TestGetAttestationCert(t *testing.T) {
|
||||
roots.AddCert(cert)
|
||||
validator.roots = roots
|
||||
|
||||
key, err := validator.verifyAttestationKey(akPub, certs)
|
||||
key, err := validator.verifyAttestationKey(context.Background(), attDoc, nil)
|
||||
if tc.wantValidateErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
|
@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
package trustedlaunch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
@ -49,14 +50,14 @@ func NewValidator(pcrs measurements.M, log vtpm.AttestationLogger) *Validator {
|
||||
|
||||
// verifyAttestationKey establishes trust in an attestation key.
|
||||
// It does so by verifying the certificate chain of the attestation key certificate.
|
||||
func (v *Validator) verifyAttestationKey(akPub, instanceInfo []byte) (crypto.PublicKey, error) {
|
||||
pubArea, err := tpm2.DecodePublic(akPub)
|
||||
func (v *Validator) verifyAttestationKey(_ context.Context, attDoc vtpm.AttestationDocument, _ []byte) (crypto.PublicKey, error) {
|
||||
pubArea, err := tpm2.DecodePublic(attDoc.Attestation.AkPub)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decoding attestation key public area: %w", err)
|
||||
}
|
||||
|
||||
var akSigner akSigner
|
||||
if err := json.Unmarshal(instanceInfo, &akSigner); err != nil {
|
||||
if err := json.Unmarshal(attDoc.InstanceInfo, &akSigner); err != nil {
|
||||
return nil, fmt.Errorf("unmarshaling attestation key signer info: %w", err)
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ go_test(
|
||||
srcs = ["choose_test.go"],
|
||||
embed = [":choose"],
|
||||
deps = [
|
||||
"//internal/attestation/idkeydigest",
|
||||
"//internal/oid",
|
||||
"@com_github_stretchr_testify//assert",
|
||||
"@com_github_stretchr_testify//require",
|
||||
|
@ -43,9 +43,7 @@ func Issuer(variant oid.Getter, log vtpm.AttestationLogger) (atls.Issuer, error)
|
||||
|
||||
// Validator returns the validator for the given variant.
|
||||
func Validator(
|
||||
variant oid.Getter, measurements measurements.M,
|
||||
idKeyDigest idkeydigest.IDKeyDigests, enfoceIDKeyDigest bool,
|
||||
log vtpm.AttestationLogger,
|
||||
variant oid.Getter, measurements measurements.M, idKeyCfg idkeydigest.Config, log vtpm.AttestationLogger,
|
||||
) (atls.Validator, error) {
|
||||
switch variant {
|
||||
case oid.AWSNitroTPM{}:
|
||||
@ -53,7 +51,7 @@ func Validator(
|
||||
case oid.AzureTrustedLaunch{}:
|
||||
return trustedlaunch.NewValidator(measurements, log), nil
|
||||
case oid.AzureSEVSNP{}:
|
||||
return snp.NewValidator(measurements, idKeyDigest, enfoceIDKeyDigest, log), nil
|
||||
return snp.NewValidator(measurements, idKeyCfg, log), nil
|
||||
case oid.GCPSEVES{}:
|
||||
return gcp.NewValidator(measurements, log), nil
|
||||
case oid.QEMUVTPM{}:
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
"encoding/asn1"
|
||||
"testing"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
|
||||
"github.com/edgelesssys/constellation/v2/internal/oid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -95,7 +96,7 @@ func TestValidator(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
validator, err := Validator(tc.variant, nil, nil, false, nil)
|
||||
validator, err := Validator(tc.variant, nil, idkeydigest.Config{}, nil)
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
|
@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
package gcp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
@ -37,12 +38,12 @@ func NewIssuer(log vtpm.AttestationLogger) *Issuer {
|
||||
}
|
||||
|
||||
// getGCEInstanceInfo fetches VM metadata used for attestation.
|
||||
func getGCEInstanceInfo(client gcpMetadataClient) func(io.ReadWriteCloser) ([]byte, error) {
|
||||
func getGCEInstanceInfo(client gcpMetadataClient) func(context.Context, io.ReadWriteCloser, []byte) ([]byte, error) {
|
||||
// Ideally we would want to use the endorsement public key certificate
|
||||
// However, this is not available on GCE instances
|
||||
// Workaround: Provide ShieldedVM instance info
|
||||
// The attestating party can request the VMs signing key using Google's API
|
||||
return func(io.ReadWriteCloser) ([]byte, error) {
|
||||
return func(context.Context, io.ReadWriteCloser, []byte) ([]byte, error) {
|
||||
projectID, err := client.projectID()
|
||||
if err != nil {
|
||||
return nil, errors.New("unable to fetch projectID")
|
||||
|
@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
package gcp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
@ -65,7 +66,7 @@ func TestGetGCEInstanceInfo(t *testing.T) {
|
||||
require := require.New(t)
|
||||
var tpm io.ReadWriteCloser
|
||||
|
||||
out, err := getGCEInstanceInfo(tc.client)(tpm)
|
||||
out, err := getGCEInstanceInfo(tc.client)(context.Background(), tpm, nil)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
} else {
|
||||
|
@ -13,7 +13,6 @@ import (
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
compute "cloud.google.com/go/compute/apiv1"
|
||||
"cloud.google.com/go/compute/apiv1/computepb"
|
||||
@ -31,18 +30,23 @@ const minimumGceVersion = 1
|
||||
type Validator struct {
|
||||
oid.GCPSEVES
|
||||
*vtpm.Validator
|
||||
|
||||
restClient func(context.Context, ...option.ClientOption) (gcpRestClient, error)
|
||||
}
|
||||
|
||||
// NewValidator initializes a new GCP validator with the provided PCR values.
|
||||
func NewValidator(pcrs measurements.M, log vtpm.AttestationLogger) *Validator {
|
||||
return &Validator{
|
||||
Validator: vtpm.NewValidator(
|
||||
pcrs,
|
||||
trustedKeyFromGCEAPI(newInstanceClient),
|
||||
validateCVM,
|
||||
log,
|
||||
),
|
||||
v := &Validator{
|
||||
restClient: newInstanceClient,
|
||||
}
|
||||
v.Validator = vtpm.NewValidator(
|
||||
pcrs,
|
||||
v.trustedKeyFromGCEAPI,
|
||||
validateCVM,
|
||||
log,
|
||||
)
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
type gcpRestClient interface {
|
||||
@ -64,41 +68,38 @@ func newInstanceClient(ctx context.Context, opts ...option.ClientOption) (gcpRes
|
||||
|
||||
// trustedKeyFromGCEAPI queries the GCE API for a shieldedVM's public signing key.
|
||||
// This key can be used to verify attestation statements issued by the VM.
|
||||
func trustedKeyFromGCEAPI(getClient func(ctx context.Context, opts ...option.ClientOption) (gcpRestClient, error)) func(akPub []byte, instanceInfoRaw []byte) (crypto.PublicKey, error) {
|
||||
return func(akPub, instanceInfoRaw []byte) (crypto.PublicKey, error) {
|
||||
var instanceInfo attest.GCEInstanceInfo
|
||||
if err := json.Unmarshal(instanceInfoRaw, &instanceInfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
client, err := getClient(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating GCE client: %w", err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
instance, err := client.GetShieldedInstanceIdentity(ctx, &computepb.GetShieldedInstanceIdentityInstanceRequest{
|
||||
Instance: instanceInfo.GetInstanceName(),
|
||||
Project: instanceInfo.GetProjectId(),
|
||||
Zone: instanceInfo.GetZone(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("retrieving VM identity: %w", err)
|
||||
}
|
||||
|
||||
if instance.SigningKey == nil || instance.SigningKey.EkPub == nil {
|
||||
return nil, fmt.Errorf("received no signing key from GCP API")
|
||||
}
|
||||
|
||||
// Parse the signing key return by GetShieldedInstanceIdentity
|
||||
block, _ := pem.Decode([]byte(*instance.SigningKey.EkPub))
|
||||
if block == nil || block.Type != "PUBLIC KEY" {
|
||||
return nil, fmt.Errorf("failed to decode PEM block containing public key")
|
||||
}
|
||||
|
||||
return x509.ParsePKIXPublicKey(block.Bytes)
|
||||
func (v *Validator) trustedKeyFromGCEAPI(ctx context.Context, attDoc vtpm.AttestationDocument, _ []byte) (crypto.PublicKey, error) {
|
||||
client, err := v.restClient(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating GCE client: %w", err)
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
var instanceInfo attest.GCEInstanceInfo
|
||||
if err := json.Unmarshal(attDoc.InstanceInfo, &instanceInfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
instance, err := client.GetShieldedInstanceIdentity(ctx, &computepb.GetShieldedInstanceIdentityInstanceRequest{
|
||||
Instance: instanceInfo.GetInstanceName(),
|
||||
Project: instanceInfo.GetProjectId(),
|
||||
Zone: instanceInfo.GetZone(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("retrieving VM identity: %w", err)
|
||||
}
|
||||
|
||||
if instance.SigningKey == nil || instance.SigningKey.EkPub == nil {
|
||||
return nil, fmt.Errorf("received no signing key from GCP API")
|
||||
}
|
||||
|
||||
// Parse the signing key return by GetShieldedInstanceIdentity
|
||||
block, _ := pem.Decode([]byte(*instance.SigningKey.EkPub))
|
||||
if block == nil || block.Type != "PUBLIC KEY" {
|
||||
return nil, fmt.Errorf("failed to decode PEM block containing public key")
|
||||
}
|
||||
|
||||
return x509.ParsePKIXPublicKey(block.Bytes)
|
||||
}
|
||||
|
||||
// validateCVM checks that the machine state represents a GCE AMD-SEV VM.
|
||||
|
@ -146,7 +146,12 @@ Y+t5OxL3kL15VzY1Ob0d5cMCAwEAAQ==
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
out, err := trustedKeyFromGCEAPI(tc.getClient)(nil, tc.instanceInfo)
|
||||
v := &Validator{
|
||||
restClient: tc.getClient,
|
||||
}
|
||||
attDoc := vtpm.AttestationDocument{InstanceInfo: tc.instanceInfo}
|
||||
|
||||
out, err := v.trustedKeyFromGCEAPI(context.Background(), attDoc, nil)
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
|
@ -3,7 +3,10 @@ load("//bazel/go:go_test.bzl", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "idkeydigest",
|
||||
srcs = ["idkeydigest.go"],
|
||||
srcs = [
|
||||
"enforceidkeydigest_string.go",
|
||||
"idkeydigest.go",
|
||||
],
|
||||
importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest",
|
||||
visibility = ["//:__subpackages__"],
|
||||
deps = ["//internal/cloud/cloudprovider"],
|
||||
|
@ -0,0 +1,26 @@
|
||||
// Code generated by "stringer -type=EnforceIDKeyDigest"; DO NOT EDIT.
|
||||
|
||||
package idkeydigest
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[Unknown-0]
|
||||
_ = x[StrictChecking-1]
|
||||
_ = x[MAAFallback-2]
|
||||
_ = x[WarnOnly-3]
|
||||
}
|
||||
|
||||
const _EnforceIDKeyDigest_name = "UnknownStrictCheckingMAAFallbackWarnOnly"
|
||||
|
||||
var _EnforceIDKeyDigest_index = [...]uint8{0, 7, 21, 32, 40}
|
||||
|
||||
func (i EnforceIDKeyDigest) String() string {
|
||||
if i >= EnforceIDKeyDigest(len(_EnforceIDKeyDigest_index)-1) {
|
||||
return "EnforceIDKeyDigest(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _EnforceIDKeyDigest_name[_EnforceIDKeyDigest_index[i]:_EnforceIDKeyDigest_index[i+1]]
|
||||
}
|
@ -4,6 +4,8 @@ Copyright (c) Edgeless Systems GmbH
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
// Package idkeydigest contains policies and type definitions
|
||||
// for checking the ID Key Digest value in SEV-SNP attestation.
|
||||
package idkeydigest
|
||||
|
||||
import (
|
||||
@ -11,10 +13,105 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
)
|
||||
|
||||
// Config contains the configuration for ID Key Digest validation.
|
||||
type Config struct {
|
||||
IDKeyDigests IDKeyDigests `json:"idKeyDigests"`
|
||||
EnforcementPolicy EnforceIDKeyDigest `json:"enforcementPolicy"`
|
||||
MAAURL string `json:"maaURL,omitempty"`
|
||||
}
|
||||
|
||||
//go:generate stringer -type=EnforceIDKeyDigest
|
||||
|
||||
// EnforceIDKeyDigest defines the behavior of the validator when the ID key digest is not found in the expected list.
|
||||
type EnforceIDKeyDigest uint32
|
||||
|
||||
// TODO: Decide on final value naming.
|
||||
const (
|
||||
// Unknown is reserved for invalid configurations.
|
||||
Unknown EnforceIDKeyDigest = iota
|
||||
// StrictChecking will return an error if the ID key digest is not found in the expected list.
|
||||
StrictChecking
|
||||
// MAAFallback attempts to verify the attestation using Microsoft Azure Attestation (MAA),
|
||||
// if the ID key digest is not found in the expected list.
|
||||
MAAFallback
|
||||
// WarnOnly logs a warning if the ID key digest is not found in the expected list.
|
||||
// No error is returned.
|
||||
WarnOnly
|
||||
)
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||
func (e *EnforceIDKeyDigest) UnmarshalJSON(b []byte) error {
|
||||
return e.unmarshal(func(val any) error {
|
||||
return json.Unmarshal(b, val)
|
||||
})
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
func (e EnforceIDKeyDigest) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(e.String())
|
||||
}
|
||||
|
||||
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
||||
func (e *EnforceIDKeyDigest) UnmarshalYAML(unmarshal func(any) error) error {
|
||||
return e.unmarshal(unmarshal)
|
||||
}
|
||||
|
||||
// MarshalYAML implements the yaml.Marshaler interface.
|
||||
func (e EnforceIDKeyDigest) MarshalYAML() (any, error) {
|
||||
return e.String(), nil
|
||||
}
|
||||
|
||||
func (e *EnforceIDKeyDigest) unmarshal(unmarshalFunc func(any) error) error {
|
||||
// Check for legacy format: EnforceIDKeyDigest might be a boolean.
|
||||
// If set to true, the value will be set to StrictChecking.
|
||||
// If set to false, the value will be set to WarnOnly.
|
||||
var legacyEnforce bool
|
||||
legacyErr := unmarshalFunc(&legacyEnforce)
|
||||
if legacyErr == nil {
|
||||
if legacyEnforce {
|
||||
*e = StrictChecking
|
||||
} else {
|
||||
*e = WarnOnly
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var enforce string
|
||||
if err := unmarshalFunc(&enforce); err != nil {
|
||||
return errors.Join(
|
||||
err,
|
||||
fmt.Errorf("trying legacy format: %w", legacyErr),
|
||||
)
|
||||
}
|
||||
|
||||
*e = EnforcePolicyFromString(enforce)
|
||||
if *e == Unknown {
|
||||
return fmt.Errorf("unknown EnforceIDKeyDigest value: %q", enforce)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnforcePolicyFromString returns EnforceIDKeyDigest from string.
|
||||
func EnforcePolicyFromString(s string) EnforceIDKeyDigest {
|
||||
s = strings.ToLower(s)
|
||||
switch s {
|
||||
case "strictchecking":
|
||||
return StrictChecking
|
||||
case "maafallback":
|
||||
return MAAFallback
|
||||
case "warnonly":
|
||||
return WarnOnly
|
||||
default:
|
||||
return Unknown
|
||||
}
|
||||
}
|
||||
|
||||
// IDKeyDigests is a list of trusted digest values for the ID key.
|
||||
type IDKeyDigests [][]byte
|
||||
|
||||
|
@ -117,3 +117,126 @@ func TestUnmarshal(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnforceIDKeyDigestMarshal(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
input EnforceIDKeyDigest
|
||||
wantJSON string
|
||||
wantYAML string
|
||||
}{
|
||||
"strict": {
|
||||
input: StrictChecking,
|
||||
wantJSON: `"StrictChecking"`,
|
||||
wantYAML: "StrictChecking",
|
||||
},
|
||||
"maaFallback": {
|
||||
input: MAAFallback,
|
||||
wantJSON: `"MAAFallback"`,
|
||||
wantYAML: "MAAFallback",
|
||||
},
|
||||
"warnOnly": {
|
||||
input: WarnOnly,
|
||||
wantJSON: `"WarnOnly"`,
|
||||
wantYAML: "WarnOnly",
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
{
|
||||
// YAML
|
||||
yaml, err := yaml.Marshal(tc.input)
|
||||
require.NoError(err)
|
||||
assert.YAMLEq(tc.wantYAML, string(yaml))
|
||||
}
|
||||
|
||||
{
|
||||
// JSON
|
||||
json, err := json.Marshal(tc.input)
|
||||
require.NoError(err)
|
||||
assert.JSONEq(tc.wantJSON, string(json))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnforceIDKeyDigestUnmarshal(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
inputJSON string
|
||||
inputYAML string
|
||||
want EnforceIDKeyDigest
|
||||
wantErr bool
|
||||
}{
|
||||
"strict": {
|
||||
inputJSON: `"StrictChecking"`,
|
||||
inputYAML: "StrictChecking",
|
||||
want: StrictChecking,
|
||||
},
|
||||
"maaFallback": {
|
||||
inputJSON: `"MAAFallback"`,
|
||||
inputYAML: "MAAFallback",
|
||||
want: MAAFallback,
|
||||
},
|
||||
"warnOnly": {
|
||||
inputJSON: `"WarnOnly"`,
|
||||
inputYAML: "WarnOnly",
|
||||
want: WarnOnly,
|
||||
},
|
||||
"legacyTrue": {
|
||||
inputJSON: `true`,
|
||||
inputYAML: "true",
|
||||
want: StrictChecking,
|
||||
},
|
||||
"legacyFalse": {
|
||||
inputJSON: `false`,
|
||||
inputYAML: "false",
|
||||
want: WarnOnly,
|
||||
},
|
||||
"invalid": {
|
||||
inputJSON: `"invalid"`,
|
||||
inputYAML: "invalid",
|
||||
wantErr: true,
|
||||
},
|
||||
"invalidType": {
|
||||
inputJSON: `{"object": "invalid"}`,
|
||||
inputYAML: "object: invalid",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
{
|
||||
// YAML
|
||||
var got EnforceIDKeyDigest
|
||||
err := yaml.Unmarshal([]byte(tc.inputYAML), &got)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(err)
|
||||
assert.Equal(tc.want, got)
|
||||
}
|
||||
|
||||
{
|
||||
// JSON
|
||||
var got EnforceIDKeyDigest
|
||||
err := json.Unmarshal([]byte(tc.inputJSON), &got)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(err)
|
||||
assert.Equal(tc.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
package qemu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
|
||||
@ -26,7 +27,7 @@ func NewIssuer(log vtpm.AttestationLogger) *Issuer {
|
||||
Issuer: vtpm.NewIssuer(
|
||||
vtpm.OpenVTPM,
|
||||
tpmclient.AttestationKeyRSA,
|
||||
func(tpm io.ReadWriteCloser) ([]byte, error) { return nil, nil },
|
||||
func(context.Context, io.ReadWriteCloser, []byte) ([]byte, error) { return nil, nil },
|
||||
log,
|
||||
),
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
package qemu
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
|
||||
@ -35,8 +36,8 @@ func NewValidator(pcrs measurements.M, log vtpm.AttestationLogger) *Validator {
|
||||
}
|
||||
|
||||
// unconditionalTrust returns the given public key as the trusted attestation key.
|
||||
func unconditionalTrust(akPub, _ []byte) (crypto.PublicKey, error) {
|
||||
pubArea, err := tpm2.DecodePublic(akPub)
|
||||
func unconditionalTrust(_ context.Context, attDoc vtpm.AttestationDocument, _ []byte) (crypto.PublicKey, error) {
|
||||
pubArea, err := tpm2.DecodePublic(attDoc.Attestation.AkPub)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ package vtpm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
@ -58,9 +59,9 @@ type (
|
||||
// GetTPMAttestationKey loads a TPM key to perform attestation.
|
||||
GetTPMAttestationKey func(tpm io.ReadWriter) (*tpmClient.Key, error)
|
||||
// GetTPMTrustedAttestationPublicKey verifies and returns the attestation public key.
|
||||
GetTPMTrustedAttestationPublicKey func(akPub []byte, instanceInfo []byte) (crypto.PublicKey, error)
|
||||
GetTPMTrustedAttestationPublicKey func(context.Context, AttestationDocument, []byte) (crypto.PublicKey, error)
|
||||
// GetInstanceInfo returns VM metdata.
|
||||
GetInstanceInfo func(tpm io.ReadWriteCloser) ([]byte, error)
|
||||
GetInstanceInfo func(ctx context.Context, tpm io.ReadWriteCloser, extraData []byte) ([]byte, error)
|
||||
// ValidateCVM validates confidential computing capabilities of the instance issuing the attestation.
|
||||
ValidateCVM func(attestation AttestationDocument, state *attest.MachineState) error
|
||||
)
|
||||
@ -135,7 +136,7 @@ func (i *Issuer) Issue(userData []byte, nonce []byte) (res []byte, err error) {
|
||||
}
|
||||
|
||||
// Fetch instance info of the VM
|
||||
instanceInfo, err := i.getInstanceInfo(tpm)
|
||||
instanceInfo, err := i.getInstanceInfo(context.TODO(), tpm, extraData) // TODO(daniel-weisse): update Issue/Validate to use context
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fetching instance info: %w", err)
|
||||
}
|
||||
@ -193,8 +194,10 @@ func (v *Validator) Validate(attDocRaw []byte, nonce []byte) (userData []byte, e
|
||||
return nil, fmt.Errorf("unmarshaling TPM attestation document: %w", err)
|
||||
}
|
||||
|
||||
extraData := makeExtraData(attDoc.UserData, nonce)
|
||||
|
||||
// Verify and retrieve the trusted attestation public key using the provided instance info
|
||||
aKP, err := v.getTrustedKey(attDoc.Attestation.AkPub, attDoc.InstanceInfo)
|
||||
aKP, err := v.getTrustedKey(context.TODO(), attDoc, extraData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("validating attestation public key: %w", err)
|
||||
}
|
||||
@ -203,7 +206,7 @@ func (v *Validator) Validate(attDocRaw []byte, nonce []byte) (userData []byte, e
|
||||
state, err := tpmServer.VerifyAttestation(
|
||||
attDoc.Attestation,
|
||||
tpmServer.VerifyOpts{
|
||||
Nonce: makeExtraData(attDoc.UserData, nonce),
|
||||
Nonce: extraData,
|
||||
TrustedAKs: []crypto.PublicKey{aKP},
|
||||
AllowSHA1: false,
|
||||
},
|
||||
|
@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
package vtpm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@ -51,7 +52,7 @@ func (s simTPMWithEventLog) EventLog() ([]byte, error) {
|
||||
return header, nil
|
||||
}
|
||||
|
||||
func fakeGetInstanceInfo(_ io.ReadWriteCloser) ([]byte, error) {
|
||||
func fakeGetInstanceInfo(_ context.Context, _ io.ReadWriteCloser, _ []byte) ([]byte, error) {
|
||||
return []byte("unit-test"), nil
|
||||
}
|
||||
|
||||
@ -59,8 +60,8 @@ func TestValidate(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
fakeValidateCVM := func(AttestationDocument, *attest.MachineState) error { return nil }
|
||||
fakeGetTrustedKey := func(aKPub, instanceInfo []byte) (crypto.PublicKey, error) {
|
||||
pubArea, err := tpm2.DecodePublic(aKPub)
|
||||
fakeGetTrustedKey := func(_ context.Context, attDoc AttestationDocument, _ []byte) (crypto.PublicKey, error) {
|
||||
pubArea, err := tpm2.DecodePublic(attDoc.Attestation.AkPub)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -175,7 +176,7 @@ func TestValidate(t *testing.T) {
|
||||
"untrusted attestation public key": {
|
||||
validator: NewValidator(
|
||||
testExpectedPCRs,
|
||||
func(akPub, instanceInfo []byte) (crypto.PublicKey, error) {
|
||||
func(context.Context, AttestationDocument, []byte) (crypto.PublicKey, error) {
|
||||
return nil, errors.New("untrusted")
|
||||
},
|
||||
fakeValidateCVM, warnLog),
|
||||
@ -301,7 +302,7 @@ func TestFailIssuer(t *testing.T) {
|
||||
issuer: NewIssuer(
|
||||
newSimTPMWithEventLog,
|
||||
tpmclient.AttestationKeyRSA,
|
||||
func(io.ReadWriteCloser) ([]byte, error) { return nil, errors.New("failure") },
|
||||
func(context.Context, io.ReadWriteCloser, []byte) ([]byte, error) { return nil, errors.New("failure") },
|
||||
nil,
|
||||
),
|
||||
userData: []byte("Constellation"),
|
||||
|
@ -20,7 +20,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
|
||||
@ -51,12 +50,8 @@ func New(ctx context.Context) (*Cloud, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("loading credentials: %w", err)
|
||||
}
|
||||
// The default http client may use a system-wide proxy and it is recommended to disable the proxy explicitly:
|
||||
// https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service?tabs=linux#proxies
|
||||
// See also: https://github.com/microsoft/azureimds/blob/master/imdssample.go#L10
|
||||
imdsAPI := imdsClient{
|
||||
client: &http.Client{Transport: &http.Transport{Proxy: nil}},
|
||||
}
|
||||
|
||||
imdsAPI := NewIMDSClient()
|
||||
subscriptionID, err := imdsAPI.subscriptionID(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("retrieving subscription ID: %w", err)
|
||||
@ -91,7 +86,7 @@ func New(ctx context.Context) (*Cloud, error) {
|
||||
}
|
||||
|
||||
return &Cloud{
|
||||
imds: &imdsAPI,
|
||||
imds: imdsAPI,
|
||||
netIfacAPI: networkInterfacesAPI,
|
||||
virtNetAPI: virtualNetworksAPI,
|
||||
secGroupAPI: securityGroupsAPI,
|
||||
|
@ -28,15 +28,42 @@ const (
|
||||
maxCacheAge = 12 * time.Hour
|
||||
)
|
||||
|
||||
type imdsClient struct {
|
||||
// IMDSClient is a client for the Azure Instance Metadata Service.
|
||||
type IMDSClient struct {
|
||||
client *http.Client
|
||||
|
||||
cache metadataResponse
|
||||
cacheTime time.Time
|
||||
}
|
||||
|
||||
// NewIMDSClient creates a new IMDSClient.
|
||||
func NewIMDSClient() *IMDSClient {
|
||||
// The default http client may use a system-wide proxy and it is recommended to disable the proxy explicitly:
|
||||
// https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service?tabs=linux#proxies
|
||||
// See also: https://github.com/microsoft/azureimds/blob/master/imdssample.go#L10
|
||||
return &IMDSClient{
|
||||
client: &http.Client{Transport: &http.Transport{Proxy: nil}},
|
||||
}
|
||||
}
|
||||
|
||||
// Tags returns the tags of the instance the function is called from.
|
||||
func (c *IMDSClient) Tags(ctx context.Context) (map[string]string, error) {
|
||||
if c.timeForUpdate() || len(c.cache.Compute.Tags) == 0 {
|
||||
if err := c.update(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
tags := make(map[string]string, len(c.cache.Compute.Tags))
|
||||
for _, tag := range c.cache.Compute.Tags {
|
||||
tags[tag.Name] = tag.Value
|
||||
}
|
||||
|
||||
return tags, nil
|
||||
}
|
||||
|
||||
// providerID returns the provider ID of the instance the function is called from.
|
||||
func (c *imdsClient) providerID(ctx context.Context) (string, error) {
|
||||
func (c *IMDSClient) providerID(ctx context.Context) (string, error) {
|
||||
if c.timeForUpdate() || c.cache.Compute.ResourceID == "" {
|
||||
if err := c.update(ctx); err != nil {
|
||||
return "", err
|
||||
@ -50,7 +77,7 @@ func (c *imdsClient) providerID(ctx context.Context) (string, error) {
|
||||
return c.cache.Compute.ResourceID, nil
|
||||
}
|
||||
|
||||
func (c *imdsClient) name(ctx context.Context) (string, error) {
|
||||
func (c *IMDSClient) name(ctx context.Context) (string, error) {
|
||||
if c.timeForUpdate() || c.cache.Compute.OSProfile.ComputerName == "" {
|
||||
if err := c.update(ctx); err != nil {
|
||||
return "", err
|
||||
@ -66,7 +93,7 @@ func (c *imdsClient) name(ctx context.Context) (string, error) {
|
||||
|
||||
// subscriptionID returns the subscription ID of the instance the function
|
||||
// is called from.
|
||||
func (c *imdsClient) subscriptionID(ctx context.Context) (string, error) {
|
||||
func (c *IMDSClient) subscriptionID(ctx context.Context) (string, error) {
|
||||
if c.timeForUpdate() || c.cache.Compute.SubscriptionID == "" {
|
||||
if err := c.update(ctx); err != nil {
|
||||
return "", err
|
||||
@ -82,7 +109,7 @@ func (c *imdsClient) subscriptionID(ctx context.Context) (string, error) {
|
||||
|
||||
// resourceGroup returns the resource group of the instance the function
|
||||
// is called from.
|
||||
func (c *imdsClient) resourceGroup(ctx context.Context) (string, error) {
|
||||
func (c *IMDSClient) resourceGroup(ctx context.Context) (string, error) {
|
||||
if c.timeForUpdate() || c.cache.Compute.ResourceGroup == "" {
|
||||
if err := c.update(ctx); err != nil {
|
||||
return "", err
|
||||
@ -98,7 +125,7 @@ func (c *imdsClient) resourceGroup(ctx context.Context) (string, error) {
|
||||
|
||||
// uid returns the UID of the cluster, based on the tags on the instance
|
||||
// the function is called from, which are inherited from the scale set.
|
||||
func (c *imdsClient) uid(ctx context.Context) (string, error) {
|
||||
func (c *IMDSClient) uid(ctx context.Context) (string, error) {
|
||||
if c.timeForUpdate() || len(c.cache.Compute.Tags) == 0 {
|
||||
if err := c.update(ctx); err != nil {
|
||||
return "", err
|
||||
@ -116,7 +143,7 @@ func (c *imdsClient) uid(ctx context.Context) (string, error) {
|
||||
|
||||
// initSecretHash returns the hash of the init secret of the cluster, based on the tags on the instance
|
||||
// the function is called from, which are inherited from the scale set.
|
||||
func (c *imdsClient) initSecretHash(ctx context.Context) (string, error) {
|
||||
func (c *IMDSClient) initSecretHash(ctx context.Context) (string, error) {
|
||||
if c.timeForUpdate() || len(c.cache.Compute.Tags) == 0 {
|
||||
if err := c.update(ctx); err != nil {
|
||||
return "", err
|
||||
@ -133,7 +160,7 @@ func (c *imdsClient) initSecretHash(ctx context.Context) (string, error) {
|
||||
}
|
||||
|
||||
// role returns the role of the instance the function is called from.
|
||||
func (c *imdsClient) role(ctx context.Context) (role.Role, error) {
|
||||
func (c *IMDSClient) role(ctx context.Context) (role.Role, error) {
|
||||
if c.timeForUpdate() || len(c.cache.Compute.Tags) == 0 {
|
||||
if err := c.update(ctx); err != nil {
|
||||
return role.Unknown, err
|
||||
@ -150,12 +177,12 @@ func (c *imdsClient) role(ctx context.Context) (role.Role, error) {
|
||||
}
|
||||
|
||||
// timeForUpdate checks whether an update is needed due to cache age.
|
||||
func (c *imdsClient) timeForUpdate() bool {
|
||||
func (c *IMDSClient) timeForUpdate() bool {
|
||||
return time.Since(c.cacheTime) > maxCacheAge
|
||||
}
|
||||
|
||||
// update updates instance metadata from the azure imds API.
|
||||
func (c *imdsClient) update(ctx context.Context) error {
|
||||
func (c *IMDSClient) update(ctx context.Context) error {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, imdsURL, http.NoBody)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -92,6 +92,11 @@ func TestIMDSClient(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
defaultWantTags := map[string]string{
|
||||
cloud.TagUID: "uid",
|
||||
cloud.TagRole: "worker",
|
||||
}
|
||||
|
||||
testCases := map[string]struct {
|
||||
server httpBufconnServer
|
||||
wantProviderIDErr bool
|
||||
@ -106,6 +111,8 @@ func TestIMDSClient(t *testing.T) {
|
||||
wantName string
|
||||
wantSubscriptionErr bool
|
||||
wantSubscriptionID string
|
||||
wantTagsErr bool
|
||||
wantTags map[string]string
|
||||
}{
|
||||
"metadata response parsed": {
|
||||
server: newHTTPBufconnServerWithMetadataResponse(response),
|
||||
@ -115,6 +122,7 @@ func TestIMDSClient(t *testing.T) {
|
||||
wantRole: role.Worker,
|
||||
wantName: "computer-name",
|
||||
wantSubscriptionID: "subscription-id",
|
||||
wantTags: defaultWantTags,
|
||||
},
|
||||
"metadata response without resource ID": {
|
||||
server: newHTTPBufconnServerWithMetadataResponse(responseWithoutID),
|
||||
@ -124,6 +132,7 @@ func TestIMDSClient(t *testing.T) {
|
||||
wantRole: role.Worker,
|
||||
wantName: "computer-name",
|
||||
wantSubscriptionID: "subscription-id",
|
||||
wantTags: defaultWantTags,
|
||||
},
|
||||
"metadata response without UID tag": {
|
||||
server: newHTTPBufconnServerWithMetadataResponse(responseWithoutUID),
|
||||
@ -133,6 +142,7 @@ func TestIMDSClient(t *testing.T) {
|
||||
wantRole: role.Worker,
|
||||
wantName: "computer-name",
|
||||
wantSubscriptionID: "subscription-id",
|
||||
wantTags: map[string]string{cloud.TagRole: "worker"},
|
||||
},
|
||||
"metadata response without role tag": {
|
||||
server: newHTTPBufconnServerWithMetadataResponse(responseWithoutRole),
|
||||
@ -142,6 +152,7 @@ func TestIMDSClient(t *testing.T) {
|
||||
wantRoleErr: true,
|
||||
wantName: "computer-name",
|
||||
wantSubscriptionID: "subscription-id",
|
||||
wantTags: map[string]string{cloud.TagUID: "uid"},
|
||||
},
|
||||
"metadata response without resource group": {
|
||||
server: newHTTPBufconnServerWithMetadataResponse(responseWithoutGroup),
|
||||
@ -151,6 +162,7 @@ func TestIMDSClient(t *testing.T) {
|
||||
wantRole: role.Worker,
|
||||
wantName: "computer-name",
|
||||
wantSubscriptionID: "subscription-id",
|
||||
wantTags: defaultWantTags,
|
||||
},
|
||||
"metadata response without name": {
|
||||
server: newHTTPBufconnServerWithMetadataResponse(responseWithoutName),
|
||||
@ -160,6 +172,7 @@ func TestIMDSClient(t *testing.T) {
|
||||
wantRole: role.Worker,
|
||||
wantNameErr: true,
|
||||
wantSubscriptionID: "subscription-id",
|
||||
wantTags: defaultWantTags,
|
||||
},
|
||||
"metadata response without subscription ID": {
|
||||
server: newHTTPBufconnServerWithMetadataResponse(responseWithoutSubscriptionID),
|
||||
@ -169,6 +182,7 @@ func TestIMDSClient(t *testing.T) {
|
||||
wantRole: role.Worker,
|
||||
wantName: "computer-name",
|
||||
wantSubscriptionErr: true,
|
||||
wantTags: defaultWantTags,
|
||||
},
|
||||
"invalid imds response detected": {
|
||||
server: newHTTPBufconnServer(func(writer http.ResponseWriter, request *http.Request) {
|
||||
@ -180,6 +194,7 @@ func TestIMDSClient(t *testing.T) {
|
||||
wantRoleErr: true,
|
||||
wantNameErr: true,
|
||||
wantSubscriptionErr: true,
|
||||
wantTagsErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
@ -197,7 +212,7 @@ func TestIMDSClient(t *testing.T) {
|
||||
DialTLS: tc.server.Dial,
|
||||
},
|
||||
}
|
||||
iClient := imdsClient{client: &hClient}
|
||||
iClient := IMDSClient{client: &hClient}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
@ -248,6 +263,14 @@ func TestIMDSClient(t *testing.T) {
|
||||
assert.NoError(err)
|
||||
assert.Equal(tc.wantSubscriptionID, subscriptionID)
|
||||
}
|
||||
|
||||
tags, err := iClient.Tags(ctx)
|
||||
if tc.wantTagsErr {
|
||||
assert.Error(err)
|
||||
} else {
|
||||
assert.NoError(err)
|
||||
assert.Equal(tc.wantTags, tags)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ func NewLogger(ctx context.Context) (*Logger, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("loading credentials: %w", err)
|
||||
}
|
||||
imdsAPI := &imdsClient{
|
||||
imdsAPI := &IMDSClient{
|
||||
client: &http.Client{Transport: &http.Transport{Proxy: nil}},
|
||||
}
|
||||
subscriptionID, err := imdsAPI.subscriptionID(ctx)
|
||||
|
@ -192,7 +192,7 @@ type AzureConfig struct {
|
||||
IDKeyDigest Digests `yaml:"idKeyDigest" validate:"required_if=EnforceIdKeyDigest true,omitempty"`
|
||||
// description: |
|
||||
// Enforce the specified idKeyDigest value during remote attestation.
|
||||
EnforceIDKeyDigest *bool `yaml:"enforceIdKeyDigest" validate:"required"`
|
||||
EnforceIDKeyDigest idkeydigest.EnforceIDKeyDigest `yaml:"enforceIdKeyDigest" validate:"required"`
|
||||
// description: |
|
||||
// Expected confidential VM measurements.
|
||||
Measurements Measurements `yaml:"measurements" validate:"required,no_placeholders"`
|
||||
@ -329,7 +329,7 @@ func Default() *Config {
|
||||
StateDiskType: "Premium_LRS",
|
||||
DeployCSIDriver: toPtr(true),
|
||||
IDKeyDigest: idkeydigest.DefaultsFor(cloudprovider.Azure),
|
||||
EnforceIDKeyDigest: toPtr(true),
|
||||
EnforceIDKeyDigest: idkeydigest.MAAFallback,
|
||||
ConfidentialVM: toPtr(true),
|
||||
SecureBoot: toPtr(false),
|
||||
Measurements: measurements.DefaultsFor(cloudprovider.Azure),
|
||||
@ -514,9 +514,12 @@ func (c *Config) GetMeasurements() measurements.M {
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnforcesIDKeyDigest checks whether ID Key Digest should be enforced for respective cloud provider.
|
||||
func (c *Config) EnforcesIDKeyDigest() bool {
|
||||
return c.Provider.Azure != nil && c.Provider.Azure.EnforceIDKeyDigest != nil && *c.Provider.Azure.EnforceIDKeyDigest
|
||||
// IDKeyDigestPolicy returns the IDKeyDigest checking policy for a cloud provider.
|
||||
func (c *Config) IDKeyDigestPolicy() idkeydigest.EnforceIDKeyDigest {
|
||||
if c.Provider.Azure != nil {
|
||||
return c.Provider.Azure.EnforceIDKeyDigest
|
||||
}
|
||||
return idkeydigest.Unknown
|
||||
}
|
||||
|
||||
// EnforcedPCRs returns the list of enforced PCRs for the configured cloud provider.
|
||||
|
@ -265,7 +265,7 @@ func init() {
|
||||
AzureConfigDoc.Fields[12].Description = "List of accepted values for the field 'idkeydigest' in the AMD SEV-SNP attestation report. Only usable with ConfidentialVMs. See 4.6 and 7.3 in: https://www.amd.com/system/files/TechDocs/56860.pdf"
|
||||
AzureConfigDoc.Fields[12].Comments[encoder.LineComment] = "List of accepted values for the field 'idkeydigest' in the AMD SEV-SNP attestation report. Only usable with ConfidentialVMs. See 4.6 and 7.3 in: https://www.amd.com/system/files/TechDocs/56860.pdf"
|
||||
AzureConfigDoc.Fields[13].Name = "enforceIdKeyDigest"
|
||||
AzureConfigDoc.Fields[13].Type = "bool"
|
||||
AzureConfigDoc.Fields[13].Type = "EnforceIDKeyDigest"
|
||||
AzureConfigDoc.Fields[13].Note = ""
|
||||
AzureConfigDoc.Fields[13].Description = "Enforce the specified idKeyDigest value during remote attestation."
|
||||
AzureConfigDoc.Fields[13].Comments[encoder.LineComment] = "Enforce the specified idKeyDigest value during remote attestation."
|
||||
|
@ -740,7 +740,7 @@ func TestConfigVersionCompatibility(t *testing.T) {
|
||||
ConfidentialVM: toPtr(true),
|
||||
InstanceType: "Standard_DC4as_v5",
|
||||
IDKeyDigest: idkeydigest.IDKeyDigests{{0x03, 0x56, 0x21, 0x58, 0x82, 0xa8, 0x25, 0x27, 0x9a, 0x85, 0xb3, 0x00, 0xb0, 0xb7, 0x42, 0x93, 0x1d, 0x11, 0x3b, 0xf7, 0xe3, 0x2d, 0xde, 0x2e, 0x50, 0xff, 0xde, 0x7e, 0xc7, 0x43, 0xca, 0x49, 0x1e, 0xcd, 0xd7, 0xf3, 0x36, 0xdc, 0x28, 0xa6, 0xe0, 0xb2, 0xbb, 0x57, 0xaf, 0x7a, 0x44, 0xa3}},
|
||||
EnforceIDKeyDigest: toPtr(false),
|
||||
EnforceIDKeyDigest: idkeydigest.WarnOnly,
|
||||
SecureBoot: toPtr(false),
|
||||
DeployCSIDriver: toPtr(true),
|
||||
Measurements: measurements.DefaultsFor(cloudprovider.Azure),
|
||||
@ -772,7 +772,7 @@ func TestConfigVersionCompatibility(t *testing.T) {
|
||||
{0x57, 0x48, 0x6a, 0x44, 0x7e, 0xc0, 0xf1, 0x95, 0x80, 0x02, 0xa2, 0x2a, 0x06, 0xb7, 0x67, 0x3b, 0x9f, 0xd2, 0x7d, 0x11, 0xe1, 0xc6, 0x52, 0x74, 0x98, 0x05, 0x60, 0x54, 0xc5, 0xfa, 0x92, 0xd2, 0x3c, 0x50, 0xf9, 0xde, 0x44, 0x07, 0x27, 0x60, 0xfe, 0x2b, 0x6f, 0xb8, 0x97, 0x40, 0xb6, 0x96},
|
||||
{0x03, 0x56, 0x21, 0x58, 0x82, 0xa8, 0x25, 0x27, 0x9a, 0x85, 0xb3, 0x00, 0xb0, 0xb7, 0x42, 0x93, 0x1d, 0x11, 0x3b, 0xf7, 0xe3, 0x2d, 0xde, 0x2e, 0x50, 0xff, 0xde, 0x7e, 0xc7, 0x43, 0xca, 0x49, 0x1e, 0xcd, 0xd7, 0xf3, 0x36, 0xdc, 0x28, 0xa6, 0xe0, 0xb2, 0xbb, 0x57, 0xaf, 0x7a, 0x44, 0xa3},
|
||||
},
|
||||
EnforceIDKeyDigest: toPtr(false),
|
||||
EnforceIDKeyDigest: idkeydigest.WarnOnly,
|
||||
SecureBoot: toPtr(false),
|
||||
DeployCSIDriver: toPtr(true),
|
||||
Measurements: measurements.DefaultsFor(cloudprovider.Azure),
|
||||
|
@ -116,6 +116,9 @@ const (
|
||||
IDKeyDigestFilename = "idkeydigests"
|
||||
// EnforceIDKeyDigestFilename is the name of the file configuring whether idkeydigest is enforced or not.
|
||||
EnforceIDKeyDigestFilename = "enforceIdKeyDigest"
|
||||
// IDKeyConfigFilename is the name of the file holding the configuration for validating the SEV-SNP ID key digest.
|
||||
IDKeyConfigFilename = "idKeyConfig"
|
||||
|
||||
// K8sVersionFieldName is the name of the of the key holding the wanted Kubernetes version.
|
||||
K8sVersionFieldName = "cluster-version"
|
||||
// ComponentsListKey is the name of the key holding the list of components in the components configMap.
|
||||
|
@ -19,6 +19,7 @@ go_library(
|
||||
"//internal/logger",
|
||||
"//internal/oid",
|
||||
"@com_github_fsnotify_fsnotify//:fsnotify",
|
||||
"@com_github_spf13_afero//:afero",
|
||||
"@org_uber_go_zap//:zap",
|
||||
],
|
||||
)
|
||||
|
@ -9,9 +9,9 @@ package watcher
|
||||
import (
|
||||
"encoding/asn1"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/edgelesssys/constellation/v2/internal/atls"
|
||||
@ -22,6 +22,7 @@ import (
|
||||
"github.com/edgelesssys/constellation/v2/internal/file"
|
||||
"github.com/edgelesssys/constellation/v2/internal/logger"
|
||||
"github.com/edgelesssys/constellation/v2/internal/oid"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
// Updatable implements an updatable atls.Validator.
|
||||
@ -74,32 +75,48 @@ func (u *Updatable) Update() error {
|
||||
}
|
||||
u.log.Debugf("New measurements: %+v", measurements)
|
||||
|
||||
var digest idkeydigest.IDKeyDigests
|
||||
var enforceIDKeyDigest bool
|
||||
// Read ID Key config
|
||||
var idKeyCfg idkeydigest.Config
|
||||
if u.variant.OID().Equal(oid.AzureSEVSNP{}.OID()) {
|
||||
u.log.Infof("Updating encforceIdKeyDigest value")
|
||||
enforceRaw, err := u.fileHandler.Read(filepath.Join(constants.ServiceBasePath, constants.EnforceIDKeyDigestFilename))
|
||||
if err != nil {
|
||||
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 SEV-SNP ID Key config")
|
||||
|
||||
u.log.Infof("Updating expected idkeydigest")
|
||||
idkeydigestRaw, err := u.fileHandler.Read(filepath.Join(constants.ServiceBasePath, constants.IDKeyDigestFilename))
|
||||
err := u.fileHandler.ReadJSON(filepath.Join(constants.ServiceBasePath, constants.IDKeyConfigFilename), &idKeyCfg)
|
||||
if err != nil {
|
||||
return err
|
||||
if !errors.Is(err, afero.ErrFileNotFound) {
|
||||
return fmt.Errorf("reading ID Key config: %w", err)
|
||||
}
|
||||
|
||||
u.log.Warnf("ID Key config file not found, falling back to old format (v2.6 or earlier)")
|
||||
|
||||
// v2.6 fallback
|
||||
// TODO: Remove after v2.7 release
|
||||
var digest idkeydigest.IDKeyDigests
|
||||
var enforceIDKeyDigest idkeydigest.EnforceIDKeyDigest
|
||||
enforceRaw, err := u.fileHandler.Read(filepath.Join(constants.ServiceBasePath, constants.EnforceIDKeyDigestFilename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
enforceIDKeyDigest = idkeydigest.EnforcePolicyFromString(string(enforceRaw))
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing content of EnforceIdKeyDigestFilename: %s: %w", enforceRaw, err)
|
||||
}
|
||||
|
||||
idkeydigestRaw, err := u.fileHandler.Read(filepath.Join(constants.ServiceBasePath, constants.IDKeyDigestFilename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = json.Unmarshal(idkeydigestRaw, &digest); err != nil {
|
||||
return fmt.Errorf("unmarshaling content of IDKeyDigestFilename: %s: %w", idkeydigestRaw, err)
|
||||
}
|
||||
|
||||
idKeyCfg.IDKeyDigests = digest
|
||||
idKeyCfg.EnforcementPolicy = enforceIDKeyDigest
|
||||
}
|
||||
if err = json.Unmarshal(idkeydigestRaw, &digest); err != nil {
|
||||
return fmt.Errorf("unmarshaling content of IDKeyDigestFilename: %s: %w", idkeydigestRaw, err)
|
||||
}
|
||||
u.log.Debugf("New idkeydigest: %v", digest)
|
||||
|
||||
u.log.Debugf("New ID Key config: %+v", idKeyCfg)
|
||||
}
|
||||
|
||||
validator, err := choose.Validator(u.variant, measurements, digest, enforceIDKeyDigest, u.log)
|
||||
validator, err := choose.Validator(u.variant, measurements, idKeyCfg, u.log)
|
||||
if err != nil {
|
||||
return fmt.Errorf("updating validator: %w", err)
|
||||
}
|
||||
|
@ -79,15 +79,13 @@ func TestNewUpdateableValidator(t *testing.T) {
|
||||
filepath.Join(constants.ServiceBasePath, constants.MeasurementsFilename),
|
||||
measurements.M{11: measurements.WithAllBytes(0x00, false)},
|
||||
))
|
||||
keyDigest, err := json.Marshal(idkeydigest.DefaultsFor(cloudprovider.Azure))
|
||||
require.NoError(err)
|
||||
require.NoError(handler.Write(
|
||||
filepath.Join(constants.ServiceBasePath, constants.IDKeyDigestFilename),
|
||||
keyDigest,
|
||||
))
|
||||
require.NoError(handler.Write(
|
||||
filepath.Join(constants.ServiceBasePath, constants.EnforceIDKeyDigestFilename),
|
||||
[]byte("false"),
|
||||
|
||||
require.NoError(handler.WriteJSON(
|
||||
filepath.Join(constants.ServiceBasePath, constants.IDKeyConfigFilename),
|
||||
idkeydigest.Config{
|
||||
IDKeyDigests: idkeydigest.DefaultsFor(cloudprovider.Azure),
|
||||
EnforcementPolicy: idkeydigest.WarnOnly,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
@ -126,13 +124,12 @@ func TestUpdate(t *testing.T) {
|
||||
filepath.Join(constants.ServiceBasePath, constants.MeasurementsFilename),
|
||||
measurements.M{11: measurements.WithAllBytes(0x00, false)},
|
||||
))
|
||||
require.NoError(handler.Write(
|
||||
filepath.Join(constants.ServiceBasePath, constants.IDKeyDigestFilename),
|
||||
[]byte{},
|
||||
))
|
||||
require.NoError(handler.Write(
|
||||
filepath.Join(constants.ServiceBasePath, constants.EnforceIDKeyDigestFilename),
|
||||
[]byte("false"),
|
||||
require.NoError(handler.WriteJSON(
|
||||
filepath.Join(constants.ServiceBasePath, constants.IDKeyConfigFilename),
|
||||
idkeydigest.Config{
|
||||
IDKeyDigests: idkeydigest.IDKeyDigests{[]byte{0x00}},
|
||||
EnforcementPolicy: idkeydigest.WarnOnly,
|
||||
},
|
||||
))
|
||||
|
||||
// call update once to initialize the server's validator
|
||||
@ -167,6 +164,18 @@ func TestUpdate(t *testing.T) {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
assert.Error(err)
|
||||
|
||||
// test old ID Key Digest format
|
||||
require.NoError(handler.Write(
|
||||
filepath.Join(constants.ServiceBasePath, constants.IDKeyDigestFilename),
|
||||
[]byte{},
|
||||
))
|
||||
require.NoError(handler.Write(
|
||||
filepath.Join(constants.ServiceBasePath, constants.EnforceIDKeyDigestFilename),
|
||||
[]byte("false"),
|
||||
))
|
||||
|
||||
assert.NoError(validator.Update())
|
||||
}
|
||||
|
||||
func TestOIDConcurrency(t *testing.T) {
|
||||
@ -178,9 +187,12 @@ func TestOIDConcurrency(t *testing.T) {
|
||||
filepath.Join(constants.ServiceBasePath, constants.MeasurementsFilename),
|
||||
measurements.M{11: measurements.WithAllBytes(0x00, false)},
|
||||
))
|
||||
require.NoError(handler.Write(
|
||||
filepath.Join(constants.ServiceBasePath, constants.IDKeyDigestFilename),
|
||||
[]byte{},
|
||||
require.NoError(handler.WriteJSON(
|
||||
filepath.Join(constants.ServiceBasePath, constants.IDKeyConfigFilename),
|
||||
idkeydigest.Config{
|
||||
IDKeyDigests: idkeydigest.IDKeyDigests{[]byte{0x00}},
|
||||
EnforcementPolicy: idkeydigest.WarnOnly,
|
||||
},
|
||||
))
|
||||
|
||||
// create server
|
||||
@ -223,13 +235,12 @@ func TestUpdateConcurrency(t *testing.T) {
|
||||
measurements.M{11: measurements.WithAllBytes(0x00, false)},
|
||||
file.OptNone,
|
||||
))
|
||||
require.NoError(handler.Write(
|
||||
filepath.Join(constants.ServiceBasePath, constants.IDKeyDigestFilename),
|
||||
[]byte{},
|
||||
))
|
||||
require.NoError(handler.Write(
|
||||
filepath.Join(constants.ServiceBasePath, constants.EnforceIDKeyDigestFilename),
|
||||
[]byte("false"),
|
||||
require.NoError(handler.WriteJSON(
|
||||
filepath.Join(constants.ServiceBasePath, constants.IDKeyConfigFilename),
|
||||
idkeydigest.Config{
|
||||
IDKeyDigests: idkeydigest.IDKeyDigests{[]byte{0x00}},
|
||||
EnforcementPolicy: idkeydigest.WarnOnly,
|
||||
},
|
||||
))
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
Loading…
x
Reference in New Issue
Block a user