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:
Daniel Weiße 2023-03-21 12:46:49 +01:00 committed by GitHub
parent 9a9688583d
commit 5a0234b3f2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
66 changed files with 1073 additions and 542 deletions

View file

@ -1718,6 +1718,15 @@ def go_dependencies():
sum = "h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=", sum = "h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=",
version = "v1.1.0", 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( go_repository(
name = "com_github_edsrzf_mmap_go", name = "com_github_edsrzf_mmap_go",
build_file_generation = "on", build_file_generation = "on",
@ -2724,8 +2733,9 @@ def go_dependencies():
build_file_generation = "on", build_file_generation = "on",
build_file_proto_mode = "disable_global", build_file_proto_mode = "disable_global",
importpath = "github.com/google/go-tpm", importpath = "github.com/google/go-tpm",
sum = "h1:P/ZFNBZYXRxc+z7i5uyd8VP7MaDteuLZInzrH2idRGo=", replace = "github.com/thomasten/go-tpm",
version = "v0.3.3", sum = "h1:4/WcA7Q05f5to+jVERKcZjU5NJ89DiRHP3UUZHLfAtw=",
version = "v0.0.0-20230222180349-bb3cc5560299",
) )
go_repository( go_repository(
name = "com_github_google_go_tpm_tools", name = "com_github_google_go_tpm_tools",

View file

@ -35,9 +35,9 @@ type InitRequest struct {
KubernetesVersion string `protobuf:"bytes,8,opt,name=kubernetes_version,json=kubernetesVersion,proto3" json:"kubernetes_version,omitempty"` KubernetesVersion string `protobuf:"bytes,8,opt,name=kubernetes_version,json=kubernetesVersion,proto3" json:"kubernetes_version,omitempty"`
// repeated SSHUserKey ssh_user_keys = 9; removed // repeated SSHUserKey ssh_user_keys = 9; removed
// bytes salt = 10; removed // bytes salt = 10; removed
HelmDeployments []byte `protobuf:"bytes,11,opt,name=helm_deployments,json=helmDeployments,proto3" json:"helm_deployments,omitempty"` HelmDeployments []byte `protobuf:"bytes,11,opt,name=helm_deployments,json=helmDeployments,proto3" json:"helm_deployments,omitempty"`
EnforcedPcrs []uint32 `protobuf:"varint,12,rep,packed,name=enforced_pcrs,json=enforcedPcrs,proto3" json:"enforced_pcrs,omitempty"` EnforcedPcrs []uint32 `protobuf:"varint,12,rep,packed,name=enforced_pcrs,json=enforcedPcrs,proto3" json:"enforced_pcrs,omitempty"`
EnforceIdkeydigest bool `protobuf:"varint,13,opt,name=enforce_idkeydigest,json=enforceIdkeydigest,proto3" json:"enforce_idkeydigest,omitempty"` // bool enforce_idkeydigest = 13; removed
ConformanceMode bool `protobuf:"varint,14,opt,name=conformance_mode,json=conformanceMode,proto3" json:"conformance_mode,omitempty"` 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"` 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"` 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 return nil
} }
func (x *InitRequest) GetEnforceIdkeydigest() bool {
if x != nil {
return x.EnforceIdkeydigest
}
return false
}
func (x *InitRequest) GetConformanceMode() bool { func (x *InitRequest) GetConformanceMode() bool {
if x != nil { if x != nil {
return x.ConformanceMode return x.ConformanceMode
@ -291,7 +284,7 @@ var File_init_proto protoreflect.FileDescriptor
var file_init_proto_rawDesc = []byte{ var file_init_proto_rawDesc = []byte{
0x0a, 0x0a, 0x69, 0x6e, 0x69, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x69, 0x6e, 0x0a, 0x0a, 0x69, 0x6e, 0x69, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x69, 0x6e,
0x69, 0x74, 0x22, 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, 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, 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, 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, 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, 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, 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, 0x64, 0x50, 0x63, 0x72, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x6f, 0x72,
0x65, 0x5f, 0x69, 0x64, 0x6b, 0x65, 0x79, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x0d, 0x20, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x08,
0x01, 0x28, 0x08, 0x52, 0x12, 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x49, 0x64, 0x6b, 0x65, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x4d, 0x6f, 0x64,
0x79, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x6f, 0x65, 0x12, 0x4e, 0x0a, 0x15, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x5f,
0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b,
0x08, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x4d, 0x6f, 0x32, 0x19, 0x2e, 0x69, 0x6e, 0x69, 0x74, 0x2e, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74,
0x64, 0x65, 0x12, 0x4e, 0x0a, 0x15, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x65, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x52, 0x14, 0x6b, 0x75, 0x62,
0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74,
0x0b, 0x32, 0x19, 0x2e, 0x69, 0x6e, 0x69, 0x74, 0x2e, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x69, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74,
0x74, 0x65, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x52, 0x14, 0x6b, 0x75, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x69, 0x6e, 0x69, 0x74, 0x53, 0x65, 0x63, 0x72,
0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x65, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61,
0x74, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x69, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x6d, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65,
0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x69, 0x6e, 0x69, 0x74, 0x53, 0x65, 0x63, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x68, 0x0a, 0x0c, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x73,
0x72, 0x65, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x6b, 0x75, 0x62, 0x65, 0x63, 0x6f, 0x6e,
0x61, 0x6d, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6b, 0x75, 0x62, 0x65, 0x63,
0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x68, 0x0a, 0x0c, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x6b, 0x75, 0x62, 0x65, 0x63, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x49, 0x64,
0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6b, 0x75, 0x62, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x03,
0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22,
0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x49, 0x78, 0x0a, 0x13, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x6d,
0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20,
0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68,
0x22, 0x78, 0x0a, 0x13, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, 0x0c,
0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12,
0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08,
0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x52, 0x07, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x32, 0x34, 0x0a, 0x03, 0x41, 0x50, 0x49,
0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x2d, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x11, 0x2e, 0x69, 0x6e, 0x69, 0x74, 0x2e,
0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x69, 0x6e,
0x08, 0x52, 0x07, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x32, 0x34, 0x0a, 0x03, 0x41, 0x50, 0x69, 0x74, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42,
0x49, 0x12, 0x2d, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x11, 0x2e, 0x69, 0x6e, 0x69, 0x74, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x64,
0x2e, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x69, 0x67, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x73, 0x79, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x65,
0x6e, 0x69, 0x74, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x32, 0x2f, 0x62, 0x6f, 0x6f, 0x74, 0x73,
0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x74, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x2f, 0x69, 0x6e, 0x69, 0x74, 0x70, 0x72, 0x6f, 0x74,
0x64, 0x67, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x73, 0x79, 0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
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 ( var (

View file

@ -21,7 +21,7 @@ message InitRequest {
// bytes salt = 10; removed // bytes salt = 10; removed
bytes helm_deployments = 11; bytes helm_deployments = 11;
repeated uint32 enforced_pcrs = 12; repeated uint32 enforced_pcrs = 12;
bool enforce_idkeydigest = 13; // bool enforce_idkeydigest = 13; removed
bool conformance_mode = 14; bool conformance_mode = 14;
repeated KubernetesComponent kubernetes_components = 15; repeated KubernetesComponent kubernetes_components = 15;
bytes init_secret = 16; bytes init_secret = 16;

View file

@ -81,9 +81,8 @@ func New(cloudProvider string, clusterUtil clusterUtil, configProvider configura
// InitCluster initializes a new Kubernetes cluster and applies pod network provider. // InitCluster initializes a new Kubernetes cluster and applies pod network provider.
func (k *KubeWrapper) InitCluster( func (k *KubeWrapper) InitCluster(
ctx context.Context, cloudServiceAccountURI, versionString, clusterName string, ctx context.Context, cloudServiceAccountURI, versionString, clusterName string, measurementSalt []byte, enforcedPCRs []uint32,
measurementSalt []byte, enforcedPCRs []uint32, helmReleasesRaw []byte, conformanceMode bool, helmReleasesRaw []byte, conformanceMode bool, kubernetesComponents components.Components, log *logger.Logger,
kubernetesComponents components.Components, log *logger.Logger,
) ([]byte, error) { ) ([]byte, error) {
log.With(zap.String("version", versionString)).Infof("Installing Kubernetes components") log.With(zap.String("version", versionString)).Infof("Installing Kubernetes components")
if err := k.clusterUtil.InstallComponents(ctx, kubernetesComponents); err != nil { if err := k.clusterUtil.InstallComponents(ctx, kubernetesComponents); err != nil {

View file

@ -27,6 +27,7 @@ import (
"github.com/edgelesssys/constellation/v2/cli/internal/image" "github.com/edgelesssys/constellation/v2/cli/internal/image"
"github.com/edgelesssys/constellation/v2/cli/internal/libvirt" "github.com/edgelesssys/constellation/v2/cli/internal/libvirt"
"github.com/edgelesssys/constellation/v2/cli/internal/terraform" "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/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/constants"
@ -206,7 +207,7 @@ func (c *Creator) createAzure(ctx context.Context, cl terraformClient, config *c
ImageID: image, ImageID: image,
ConfidentialVM: *config.Provider.Azure.ConfidentialVM, ConfidentialVM: *config.Provider.Azure.ConfidentialVM,
SecureBoot: *config.Provider.Azure.SecureBoot, SecureBoot: *config.Provider.Azure.SecureBoot,
CreateMAA: *config.Provider.Azure.EnforceIDKeyDigest, CreateMAA: config.Provider.Azure.EnforceIDKeyDigest == idkeydigest.MAAFallback,
Debug: config.IsDebugCluster(), Debug: config.IsDebugCluster(),
} }

View file

@ -26,14 +26,13 @@ import (
type Validator struct { type Validator struct {
attestationVariant oid.Getter attestationVariant oid.Getter
pcrs measurements.M pcrs measurements.M
idkeydigests idkeydigest.IDKeyDigests idKeyConfig idkeydigest.Config
enforceIDKeyDigest bool
validator atls.Validator validator atls.Validator
log debugLog log debugLog
} }
// NewValidator creates a new Validator. // 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} v := Validator{log: log}
variant, err := oid.FromString(conf.AttestationVariant) variant, err := oid.FromString(conf.AttestationVariant)
if err != nil { if err != nil {
@ -46,8 +45,11 @@ func NewValidator(conf *config.Config, log debugLog) (*Validator, error) {
} }
if v.attestationVariant.OID().Equal(oid.AzureSEVSNP{}.OID()) { if v.attestationVariant.OID().Equal(oid.AzureSEVSNP{}.OID()) {
v.enforceIDKeyDigest = conf.EnforcesIDKeyDigest() v.idKeyConfig = idkeydigest.Config{
v.idkeydigests = conf.IDKeyDigests() IDKeyDigests: conf.Provider.Azure.IDKeyDigest,
EnforcementPolicy: conf.IDKeyDigestPolicy(),
MAAURL: maaURL,
}
} }
return &v, nil return &v, nil
@ -138,7 +140,7 @@ func (v *Validator) updateValidator(cmd *cobra.Command) {
log := warnLogger{cmd: cmd, log: v.log} log := warnLogger{cmd: cmd, log: v.log}
// Use of a valid variant has been check in NewValidator so we may drop the error // 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. // warnLogger implements logging of warnings for validators.

View file

@ -110,7 +110,7 @@ func TestNewValidator(t *testing.T) {
Azure: &config.AzureConfig{ Azure: &config.AzureConfig{
Measurements: testPCRs, Measurements: testPCRs,
IDKeyDigest: idkeydigest.IDKeyDigests{[]byte("414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141")}, 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) { t.Run(name, func(t *testing.T) {
assert := assert.New(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 { if tc.wantErr {
assert.Error(err) assert.Error(err)
@ -168,7 +168,11 @@ func TestValidatorV(t *testing.T) {
"azure cvm": { "azure cvm": {
variant: oid.AzureSEVSNP{}, variant: oid.AzureSEVSNP{},
pcrs: newTestPCRs(), 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": { "azure trusted launch": {
variant: oid.AzureTrustedLaunch{}, variant: oid.AzureTrustedLaunch{},

View file

@ -43,6 +43,7 @@ go_library(
"//cli/internal/terraform", "//cli/internal/terraform",
"//disk-mapper/recoverproto", "//disk-mapper/recoverproto",
"//internal/atls", "//internal/atls",
"//internal/attestation/idkeydigest",
"//internal/attestation/measurements", "//internal/attestation/measurements",
"//internal/cloud/azureshared", "//internal/cloud/azureshared",
"//internal/cloud/cloudprovider", "//internal/cloud/cloudprovider",

View file

@ -13,6 +13,7 @@ import (
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd" "github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd"
"github.com/edgelesssys/constellation/v2/cli/internal/terraform" "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/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/constants" "github.com/edgelesssys/constellation/v2/internal/constants"
@ -97,8 +98,8 @@ func (c *createCmd) create(cmd *cobra.Command, creator cloudCreator, fileHandler
if conf.IsAzureNonCVM() { if conf.IsAzureNonCVM() {
cmd.PrintErrln("Disabling Confidential VMs is insecure. Use only for evaluation purposes.") cmd.PrintErrln("Disabling Confidential VMs is insecure. Use only for evaluation purposes.")
printedAWarning = true printedAWarning = true
if conf.EnforcesIDKeyDigest() { if conf.IDKeyDigestPolicy() == idkeydigest.StrictChecking || conf.IDKeyDigestPolicy() == idkeydigest.MAAFallback {
cmd.PrintErrln("Your config asks for enforcing the idkeydigest. This is only available on Confidential VMs. It will not be enforced.") cmd.PrintErrln("Your config asks for validating the idkeydigest. This is only available on Confidential VMs. It will not be enforced.")
} }
} }

View file

@ -137,7 +137,9 @@ func (i *initCmd) initialize(cmd *cobra.Command, newDialer func(validator *cloud
cmd.PrintErrf("License check failed: %v", err) cmd.PrintErrf("License check failed: %v", err)
} }
i.log.Debugf("Checked license") 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 { if err != nil {
return err return err
} }
@ -153,7 +155,7 @@ func (i *initCmd) initialize(cmd *cobra.Command, newDialer func(validator *cloud
} }
helmLoader := helm.NewLoader(provider, k8sVersion) helmLoader := helm.NewLoader(provider, k8sVersion)
i.log.Debugf("Created new Helm loader") 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") i.log.Debugf("Loaded Helm deployments")
if err != nil { if err != nil {
return fmt.Errorf("loading Helm charts: %w", err) 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(), KubernetesComponents: versions.VersionConfigs[k8sVersion].KubernetesComponents.ToInitProto(),
HelmDeployments: helmDeployments, HelmDeployments: helmDeployments,
EnforcedPcrs: conf.EnforcedPCRs(), EnforcedPcrs: conf.EnforcedPCRs(),
EnforceIdkeydigest: conf.EnforcesIDKeyDigest(),
ConformanceMode: flags.conformance, ConformanceMode: flags.conformance,
InitSecret: idFile.InitSecret, InitSecret: idFile.InitSecret,
ClusterName: clusterName, ClusterName: clusterName,
@ -426,17 +427,6 @@ func (i *initCmd) readOrGenerateMasterSecret(outWriter io.Writer, fileHandler fi
return secret, nil 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) { func (i *initCmd) getMarshaledServiceAccountURI(provider cloudprovider.Provider, config *config.Config, fileHandler file.Handler) (string, error) {
i.log.Debugf("Getting service account URI") i.log.Debugf("Getting service account URI")
switch provider { switch provider {

View file

@ -16,6 +16,7 @@ import (
"time" "time"
"github.com/edgelesssys/constellation/v2/cli/internal/cloudcmd" "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/disk-mapper/recoverproto"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config" "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 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 { if err != nil {
return err return err
} }
@ -208,20 +210,23 @@ type recoverFlags struct {
endpoint string endpoint string
secretPath string secretPath string
configPath string configPath string
maaURL string
force bool force bool
} }
func (r *recoverCmd) parseRecoverFlags(cmd *cobra.Command, fileHandler file.Handler) (recoverFlags, error) { 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") endpoint, err := cmd.Flags().GetString("endpoint")
r.log.Debugf("Endpoint flag is %s", endpoint) r.log.Debugf("Endpoint flag is %s", endpoint)
if err != nil { if err != nil {
return recoverFlags{}, fmt.Errorf("parsing endpoint argument: %w", err) return recoverFlags{}, fmt.Errorf("parsing endpoint argument: %w", err)
} }
if endpoint == "" { if endpoint == "" {
endpoint, err = readIPFromIDFile(fileHandler) endpoint = idFile.IP
if err != nil {
return recoverFlags{}, fmt.Errorf("getting recovery endpoint: %w", err)
}
} }
endpoint, err = addPortIfMissing(endpoint, constants.RecoveryPort) endpoint, err = addPortIfMissing(endpoint, constants.RecoveryPort)
if err != nil { if err != nil {
@ -248,6 +253,7 @@ func (r *recoverCmd) parseRecoverFlags(cmd *cobra.Command, fileHandler file.Hand
endpoint: endpoint, endpoint: endpoint,
secretPath: masterSecretPath, secretPath: masterSecretPath,
configPath: configPath, configPath: configPath,
maaURL: idFile.AttestationURL,
force: force, force: force,
}, nil }, nil
} }

View file

@ -11,7 +11,6 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"io/fs"
"net" "net"
"strconv" "strconv"
"strings" "strings"
@ -84,9 +83,8 @@ func (v *verifyCmd) verify(cmd *cobra.Command, fileHandler file.Handler, verifyC
return err return err
} }
provider := conf.GetProvider() v.log.Debugf("Creating aTLS Validator for %s", conf.AttestationVariant)
v.log.Debugf("Creating aTLS Validator for %s", provider) validators, err := cloudcmd.NewValidator(conf, flags.maaURL, v.log)
validators, err := cloudcmd.NewValidator(conf, v.log)
if err != nil { if err != nil {
return err 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) 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 // Get empty values from ID file
emptyEndpoint := endpoint == "" emptyEndpoint := endpoint == ""
emptyIDs := ownerID == "" && clusterID == "" emptyIDs := ownerID == "" && clusterID == ""
if emptyEndpoint || emptyIDs { if emptyEndpoint || emptyIDs {
v.log.Debugf("Trying to supplement empty flag values from %q", constants.ClusterIDsFileName) v.log.Debugf("Trying to supplement empty flag values from %q", constants.ClusterIDsFileName)
var idFile clusterid.File if emptyEndpoint {
if err := fileHandler.ReadJSON(constants.ClusterIDsFileName, &idFile); err == nil { cmd.Printf("Using endpoint from %q. Specify --node-endpoint to override this.\n", constants.ClusterIDsFileName)
if emptyEndpoint { endpoint = idFile.IP
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)
if emptyIDs { ownerID = idFile.OwnerID
cmd.Printf("Using ID from %q. Specify --cluster-id to override this.\n", constants.ClusterIDsFileName) clusterID = idFile.ClusterID
ownerID = idFile.OwnerID
clusterID = idFile.ClusterID
}
} else if !errors.Is(err, fs.ErrNotExist) {
return verifyFlags{}, fmt.Errorf("reading cluster ID file: %w", err)
} }
} }
@ -178,6 +176,7 @@ func (v *verifyCmd) parseVerifyFlags(cmd *cobra.Command, fileHandler file.Handle
configPath: configPath, configPath: configPath,
ownerID: ownerID, ownerID: ownerID,
clusterID: clusterID, clusterID: clusterID,
maaURL: idFile.AttestationURL,
force: force, force: force,
}, nil }, nil
} }
@ -187,6 +186,7 @@ type verifyFlags struct {
ownerID string ownerID string
clusterID string clusterID string
configPath string configPath string
maaURL string
force bool force bool
} }

View file

@ -319,6 +319,7 @@ go_library(
importpath = "github.com/edgelesssys/constellation/v2/cli/internal/helm", importpath = "github.com/edgelesssys/constellation/v2/cli/internal/helm",
visibility = ["//cli:__subpackages__"], visibility = ["//cli:__subpackages__"],
deps = [ deps = [
"//internal/attestation/idkeydigest",
"//internal/cloud/cloudprovider", "//internal/cloud/cloudprovider",
"//internal/compatibility", "//internal/compatibility",
"//internal/config", "//internal/config",
@ -352,6 +353,7 @@ go_test(
data = glob(["testdata/**"]), data = glob(["testdata/**"]),
embed = [":helm"], embed = [":helm"],
deps = [ deps = [
"//internal/attestation/idkeydigest",
"//internal/attestation/measurements", "//internal/attestation/measurements",
"//internal/cloud/cloudprovider", "//internal/cloud/cloudprovider",
"//internal/compatibility", "//internal/compatibility",

View file

@ -7,9 +7,7 @@ data:
{{/* mustToJson is required so the json-strings passed from go are of type string in the rendered yaml. */}} {{/* mustToJson is required so the json-strings passed from go are of type string in the rendered yaml. */}}
measurements: {{ .Values.measurements | mustToJson }} measurements: {{ .Values.measurements | mustToJson }}
{{- if eq .Values.csp "Azure" }} {{- if eq .Values.csp "Azure" }}
{{/* ConfigMap.data is of type map[string]string. quote will not quote a quoted string. */}} idKeyConfig: {{ .Values.idKeyConfig | mustToJson }}
enforceIdKeyDigest: {{ .Values.enforceIdKeyDigest | quote }}
idkeydigests: {{ .Values.idkeydigests | mustToJson }}
{{- end }} {{- end }}
binaryData: binaryData:
measurementSalt: {{ .Values.measurementSalt }} measurementSalt: {{ .Values.measurementSalt }}

View file

@ -11,13 +11,13 @@
"examples": ["{'1':{'expected':'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA','warnOnly':true},'15':{'expected':'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=','warnOnly':true}}"] "examples": ["{'1':{'expected':'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA','warnOnly':true},'15':{'expected':'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=','warnOnly':true}}"]
}, },
"enforceIdKeyDigest": { "enforceIdKeyDigest": {
"description": "Whether or not idkeydigest should be enforced during attestation on azure.", "description": "ID Key Digest enforcement policy.",
"type": "boolean" "enum": ["StrictChecking", "MAAFallback", "WarnOnly"]
}, },
"idkeydigests": { "idKeyConfig": {
"description": "List of expected idkeydigest values for Azure SNP attestation.", "description": "Configuration for validating the ID Key Digest of the SEV-SNP attestation.",
"type": "string", "type": "string",
"examples": ["[\"57486a447ec0f1958002a22a06b7673b9fd27d11e1c6527498056054c5fa92d23c50f9de44072760fe2b6fb89740b696\", \"0356215882a825279a85b300b0b742931d113bf7e32dde2e50ffde7ec743ca491ecdd7f336dc28a6e0b2bb57af7a44a3\"]"] "examples": ["{'EnforcementPolicy': 'MAAFallback', 'MAAURL': 'https://192.0.2.1:8080/maa', 'IDKeyDigests': ['57486a447ec0f1958002a22a06b7673b9fd27d11e1c6527498056054c5fa92d23c50f9de44072760fe2b6fb89740b696', '0356215882a825279a85b300b0b742931d113bf7e32dde2e50ffde7ec743ca491ecdd7f336dc28a6e0b2bb57af7a44a3'}"]
}, },
"image": { "image": {
"description": "Container image to use for the spawned pods.", "description": "Container image to use for the spawned pods.",
@ -46,7 +46,7 @@
"properties": { "csp": { "const": "azure" } }, "properties": { "csp": { "const": "azure" } },
"required": ["csp"] "required": ["csp"]
}, },
"then": { "required": ["enforceIdKeyDigest", "idkeydigests"] }, "then": { "required": ["idKeyConfig"] },
"title": "Values", "title": "Values",
"type": "object" "type": "object"
} }

View file

@ -1,4 +1,7 @@
csp: "gcp" csp: "gcp"
attestationVariant: "" attestationVariant: ""
measurements: ""
idKeyConfig: ""
measurementSalt: ""
joinServicePort: 9090 joinServicePort: 9090
joinServiceNodePort: 30090 joinServiceNodePort: 30090

View file

@ -17,6 +17,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/compatibility" "github.com/edgelesssys/constellation/v2/internal/compatibility"
"github.com/edgelesssys/constellation/v2/internal/config" "github.com/edgelesssys/constellation/v2/internal/config"
@ -107,7 +108,7 @@ func AvailableServiceVersions() (string, error) {
} }
// Load the embedded helm charts. // 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) ciliumRelease, err := i.loadRelease(ciliumInfo)
if err != nil { if err != nil {
return nil, fmt.Errorf("loading cilium: %w", err) 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 { if err != nil {
return nil, fmt.Errorf("loading constellation-services: %w", err) 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) 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 // 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. // 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. // 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) keyServiceValues, ok := in["key-service"].(map[string]any)
if !ok { if !ok {
return errors.New("missing 'key-service' key") return errors.New("missing 'key-service' key")
@ -494,13 +497,17 @@ func extendConstellationServicesValues(in map[string]any, config *config.Config,
if !ok { if !ok {
return errors.New("invalid join-service values") return errors.New("invalid join-service values")
} }
joinServiceVals["enforceIdKeyDigest"] = config.EnforcesIDKeyDigest()
marshalledDigests, err := json.Marshal(config.IDKeyDigests()) idKeyCfg := idkeydigest.Config{
if err != nil { IDKeyDigests: config.IDKeyDigests(),
return fmt.Errorf("marshalling id key digests: %w", err) 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{ in["azure"] = map[string]any{
"deployCSIDriver": config.DeployCSIDriver(), "deployCSIDriver": config.DeployCSIDriver(),

View file

@ -18,6 +18,7 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements" "github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config" "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{}}} config := &config.Config{Provider: config.ProviderConfig{GCP: &config.GCPConfig{}}}
chartLoader := ChartLoader{csp: config.GetProvider()} 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) require.NoError(err)
var helmReleases helm.Releases var helmReleases helm.Releases
@ -72,7 +73,11 @@ func TestConstellationServices(t *testing.T) {
AttestationVariant: oid.AzureSEVSNP{}.String(), AttestationVariant: oid.AzureSEVSNP{}.String(),
Provider: config.ProviderConfig{Azure: &config.AzureConfig{ Provider: config.ProviderConfig{Azure: &config.AzureConfig{
DeployCSIDriver: toPtr(true), 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, enforceIDKeyDigest: true,
@ -127,7 +132,7 @@ func TestConstellationServices(t *testing.T) {
require.NoError(err) require.NoError(err)
values, err := chartLoader.loadConstellationServicesValues() values, err := chartLoader.loadConstellationServicesValues()
require.NoError(err) 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) require.NoError(err)
options := chartutil.ReleaseOptions{ options := chartutil.ReleaseOptions{

View file

@ -5,7 +5,6 @@ metadata:
namespace: testNamespace namespace: testNamespace
data: data:
measurements: "{\"1\":{\"expected\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"warnOnly\":false}}" measurements: "{\"1\":{\"expected\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"warnOnly\":false}}"
enforceIdKeyDigest: "true" idKeyConfig: "{\"idKeyDigests\":[\"baaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaad\",\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"],\"enforcementPolicy\":\"StrictChecking\",\"maaURL\":\"https://192.0.2.1:8080/maa\"}"
idkeydigests: "[\"baaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaadbaaaaaad\", \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"]"
binaryData: binaryData:
measurementSalt: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA measurementSalt: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

3
go.mod
View file

@ -33,6 +33,7 @@ replace (
replace ( replace (
github.com/edgelesssys/constellation/v2/operators/constellation-node-operator/v2/api => ./operators/constellation-node-operator/api 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 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/coreos/go-systemd/v22 v22.5.0
github.com/docker/docker v20.10.22+incompatible 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/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/fsnotify/fsnotify v1.6.0
github.com/go-playground/locales v0.14.1 github.com/go-playground/locales v0.14.1
github.com/go-playground/universal-translator v0.18.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-chi/chi v4.1.2+incompatible // indirect
github.com/go-errors/errors v1.4.2 // indirect github.com/go-errors/errors v1.4.2 // indirect
github.com/go-gorp/gorp/v3 v3.0.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-logr/logr v1.2.3 // indirect
github.com/go-openapi/analysis v0.21.4 // indirect github.com/go-openapi/analysis v0.21.4 // indirect
github.com/go-openapi/errors v0.20.3 // indirect github.com/go-openapi/errors v0.20.3 // indirect

9
go.sum
View file

@ -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-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/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/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/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc=
github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= 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 h1:ULqJXIekoqMx29FI5ekXXFoH1dT2Vc8UhnRzBg+Emz4=
github.com/go-gorp/gorp/v3 v3.0.2/go.mod h1:BJ3q1ejpV8cVALtcXvXaXyTOlMmJhWDxTmncaR6rwBY= 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-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.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.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= 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-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 h1:IjxtGAvzR+zSyAqMc1FWfYKCg1cwPkBly9+Xog3YMZc=
github.com/google/go-sev-guest v0.4.1/go.mod h1:UEi9uwoPbLdKGl1QHaq1G8pfCbQ4QP0swWX4J0k6r+Q= 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.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 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus=
github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI= 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/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 h1:habfDzTmpbzBLIFGWa2ZpVhYvFBoK0C1onC3a4zuPRA=
github.com/theupdateframework/go-tuf v0.5.2/go.mod h1:SyMV5kg5n4uEclsyxXJZI2UxPFJNDc4Y+r7wv+MlvTA= 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.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= 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= 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-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-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-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-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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=

View file

@ -50,10 +50,14 @@ require (
require ( require (
cloud.google.com/go/compute v1.18.0 // indirect cloud.google.com/go/compute v1.18.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // 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 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/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/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/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-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.28 // 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-metrics v0.0.1 // indirect
github.com/docker/go-units v0.5.0 // 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/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/emicklei/go-restful/v3 v3.9.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect
github.com/evanphx/json-patch v5.6.0+incompatible // 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/gcfg v1.5.0 // indirect
github.com/go-git/go-billy/v5 v5.4.1 // 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-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-logr/logr v1.2.3 // indirect
github.com/go-openapi/analysis v0.21.4 // indirect github.com/go-openapi/analysis v0.21.4 // indirect
github.com/go-openapi/errors v0.20.3 // 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/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.11.2 // indirect github.com/go-playground/validator/v10 v10.11.2 // indirect
github.com/gobwas/glob v0.2.3 // 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/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.4.2 // indirect github.com/golang-jwt/jwt/v4 v4.4.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // 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-isatty v0.0.17 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // 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/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect

View file

@ -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.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/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
cloud.google.com/go/trace v0.1.0/go.mod h1:wxEwsoeRVPbeSkt7ZC9nWCgmoKQRAoySN7XHW2AmI7g= 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= 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/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= 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/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 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/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-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/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= 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.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 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw=
github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= 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 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac=
github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= 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= 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-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/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/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/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc=
github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= 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 h1:ULqJXIekoqMx29FI5ekXXFoH1dT2Vc8UhnRzBg+Emz4=
github.com/go-gorp/gorp/v3 v3.0.2/go.mod h1:BJ3q1ejpV8cVALtcXvXaXyTOlMmJhWDxTmncaR6rwBY= 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-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.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.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= 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.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.0.4/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/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/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.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/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 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= 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/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/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.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/miekg/pkcs11 v1.0.3/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/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.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.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 h1:OoxbjfXVZyod1fmWYhI7SEyaD8B00ynP3T+D5GiyHOY=
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo/v2 v2.6.0 h1:9t9b9vRUbFq3C4qKFCGkVuq/fIHji802N1nrtkh1mNc= 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 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 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/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 h1:iGnD/q9160NWqKZZ5vY4p0dMiYMRknzctfSkqA4nBDw=
github.com/tent/canonical-json-go v0.0.0-20130607151641-96e4ba3a7613/go.mod h1:g6AnIpDSYMcphz193otpSIzN+11Rs+AAIIC6rm1enug= 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 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-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-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-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-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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=

View file

@ -43,6 +43,7 @@ go_test(
"@com_github_aws_aws_sdk_go_v2_service_ec2//types", "@com_github_aws_aws_sdk_go_v2_service_ec2//types",
"@com_github_aws_smithy_go//middleware", "@com_github_aws_smithy_go//middleware",
"@com_github_google_go_tpm_tools//client", "@com_github_google_go_tpm_tools//client",
"@com_github_google_go_tpm_tools//proto/attest",
"@com_github_stretchr_testify//assert", "@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require", "@com_github_stretchr_testify//require",
], ],

View file

@ -52,8 +52,8 @@ func getAttestationKey(tpm io.ReadWriter) (*tpmclient.Key, error) {
// getInstanceInfo returns information about the current instance using the aws Metadata SDK. // getInstanceInfo returns information about the current instance using the aws Metadata SDK.
// The returned bytes will be written into the attestation document. // The returned bytes will be written into the attestation document.
func getInstanceInfo(client awsMetaData) func(tpm io.ReadWriteCloser) ([]byte, error) { func getInstanceInfo(client awsMetaData) func(context.Context, io.ReadWriteCloser, []byte) ([]byte, error) {
return func(io.ReadWriteCloser) ([]byte, error) { return func(context.Context, io.ReadWriteCloser, []byte) ([]byte, error) {
ec2InstanceIdentityOutput, err := client.GetInstanceIdentityDocument(context.Background(), &imds.GetInstanceIdentityDocumentInput{}) ec2InstanceIdentityOutput, err := client.GetInstanceIdentityDocument(context.Background(), &imds.GetInstanceIdentityDocumentInput{})
if err != nil { if err != nil {
return nil, errors.New("unable to fetch instance identity document") return nil, errors.New("unable to fetch instance identity document")

View file

@ -91,7 +91,7 @@ func TestGetInstanceInfo(t *testing.T) {
instanceInfoFunc := getInstanceInfo(&tc.client) instanceInfoFunc := getInstanceInfo(&tc.client)
assert.NotNil(instanceInfoFunc) assert.NotNil(instanceInfoFunc)
info, err := instanceInfoFunc(tpm) info, err := instanceInfoFunc(context.Background(), tpm, nil)
if tc.wantErr { if tc.wantErr {
assert.Error(err) assert.Error(err)
assert.Nil(info) assert.Nil(info)

View file

@ -44,9 +44,9 @@ func NewValidator(pcrs measurements.M, log vtpm.AttestationLogger) *Validator {
// getTrustedKeys return the public area of the provides attestation key. // 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. // 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 // 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 { if err != nil {
return nil, err return nil, err
} }

View file

@ -16,23 +16,24 @@ import (
"github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/aws-sdk-go-v2/service/ec2/types" "github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
"github.com/google/go-tpm-tools/proto/attest"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestGeTrustedKey(t *testing.T) { func TestGeTrustedKey(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
attDoc []byte akPub []byte
nonce []byte info []byte
wantErr bool wantErr bool
}{ }{
"nul byte docs": { "nul byte docs": {
attDoc: []byte{0x00, 0x00, 0x00, 0x00}, akPub: []byte{0x00, 0x00, 0x00, 0x00},
nonce: []byte{0x00, 0x00, 0x00, 0x00}, info: []byte{0x00, 0x00, 0x00, 0x00},
wantErr: true, wantErr: true,
}, },
"nil": { "nil": {
attDoc: nil, akPub: nil,
nonce: nil, info: nil,
wantErr: true, wantErr: true,
}, },
} }
@ -40,7 +41,16 @@ func TestGeTrustedKey(t *testing.T) {
for name, tc := range testCases { for name, tc := range testCases {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
assert := assert.New(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 { if tc.wantErr {
assert.Error(err) assert.Error(err)

View file

@ -7,6 +7,7 @@ go_library(
"errors.go", "errors.go",
"imds.go", "imds.go",
"issuer.go", "issuer.go",
"maa.go",
"snp.go", "snp.go",
"validator.go", "validator.go",
], ],
@ -16,8 +17,10 @@ go_library(
"//internal/attestation/idkeydigest", "//internal/attestation/idkeydigest",
"//internal/attestation/measurements", "//internal/attestation/measurements",
"//internal/attestation/vtpm", "//internal/attestation/vtpm",
"//internal/cloud/azure",
"//internal/crypto", "//internal/crypto",
"//internal/oid", "//internal/oid",
"@com_github_edgelesssys_go_azguestattestation//maa",
"@com_github_google_go_tpm//tpm2", "@com_github_google_go_tpm//tpm2",
"@com_github_google_go_tpm_tools//client", "@com_github_google_go_tpm_tools//client",
"@com_github_google_go_tpm_tools//proto/attest", "@com_github_google_go_tpm_tools//proto/attest",
@ -41,8 +44,11 @@ go_test(
"//internal/attestation/idkeydigest", "//internal/attestation/idkeydigest",
"//internal/attestation/simulator", "//internal/attestation/simulator",
"//internal/attestation/vtpm", "//internal/attestation/vtpm",
"//internal/logger",
"@com_github_edgelesssys_go_azguestattestation//maa",
"@com_github_google_go_tpm//tpm2", "@com_github_google_go_tpm//tpm2",
"@com_github_google_go_tpm_tools//client", "@com_github_google_go_tpm_tools//client",
"@com_github_google_go_tpm_tools//proto/attest",
"@com_github_stretchr_testify//assert", "@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require", "@com_github_stretchr_testify//require",
], ],

View file

@ -59,7 +59,7 @@ func (e *idKeyError) Unwrap() error {
} }
func (e *idKeyError) Error() string { 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 { type versionError struct {

View file

@ -8,42 +8,28 @@ package snp
import ( import (
"context" "context"
"encoding/json" "fmt"
"net/http"
"github.com/edgelesssys/constellation/v2/internal/cloud/azure"
) )
// Modified version of bootstrapper/cloudprovider/azure/imds.go const tagMAAURL = "constellation-maa-url"
const (
imdsVcekURL = "http://169.254.169.254/metadata/THIM/amd/certification"
)
type imdsClient struct { type imdsClient struct {
client *http.Client imdsClient *azure.IMDSClient
} }
// Retrieve retrieves instance metadata from the azure imds API. func newIMDSClient() *imdsClient {
func (c imdsClient) getVcek(ctx context.Context) (vcekResponse, error) { return &imdsClient{
req, err := http.NewRequestWithContext(ctx, http.MethodGet, imdsVcekURL, http.NoBody) imdsClient: azure.NewIMDSClient(),
}
}
func (c *imdsClient) getMAAURL(ctx context.Context) (string, error) {
tags, err := c.imdsClient.Tags(ctx)
if err != nil { if err != nil {
return vcekResponse{}, err return "", fmt.Errorf("getting tags: %w", 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 res, nil return tags[tagMAAURL], nil
}
type vcekResponse struct {
VcekCert string
CertificateChain string
} }

View file

@ -7,106 +7,76 @@ SPDX-License-Identifier: AGPL-3.0-only
package snp package snp
import ( import (
"bytes"
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"net/http"
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
"github.com/edgelesssys/constellation/v2/internal/oid" "github.com/edgelesssys/constellation/v2/internal/oid"
"github.com/edgelesssys/go-azguestattestation/maa"
tpmclient "github.com/google/go-tpm-tools/client" tpmclient "github.com/google/go-tpm-tools/client"
"github.com/google/go-tpm/tpm2"
) )
const ( const tpmAkIdx = 0x81000003
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
}
// Issuer for Azure TPM attestation. // Issuer for Azure TPM attestation.
type Issuer struct { type Issuer struct {
oid.AzureSEVSNP oid.AzureSEVSNP
*vtpm.Issuer *vtpm.Issuer
imds imdsAPI
maa maaTokenCreator
} }
// NewIssuer initializes a new Azure Issuer. // NewIssuer initializes a new Azure Issuer.
func NewIssuer(log vtpm.AttestationLogger) *Issuer { func NewIssuer(log vtpm.AttestationLogger) *Issuer {
imdsAPI := imdsClient{ i := &Issuer{
client: &http.Client{Transport: &http.Transport{Proxy: nil}}, imds: newIMDSClient(),
maa: newMAAClient(),
} }
return &Issuer{ i.Issuer = vtpm.NewIssuer(
Issuer: vtpm.NewIssuer( vtpm.OpenVTPM,
vtpm.OpenVTPM, getAttestationKey,
getAttestationKey, i.getInstanceInfo,
getInstanceInfo(&tpmReport{}, imdsAPI), log,
log, )
), return i
}
} }
// getInstanceInfo loads and returns the SEV-SNP attestation report [1] and the func (i *Issuer) getInstanceInfo(ctx context.Context, tpm io.ReadWriteCloser, userData []byte) ([]byte, error) {
// AMD VCEK certificate chain. params, err := i.maa.newParameters(ctx, userData, tpm)
// The attestation report is loaded from the TPM, the certificate chain is queried if err != nil {
// from the cloud metadata API. return nil, fmt.Errorf("getting system parameters: %w", err)
// [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
} }
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. // 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 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 { 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)
} }

View file

@ -8,13 +8,13 @@ package snp
import ( import (
"context" "context"
"encoding/hex"
"encoding/json" "encoding/json"
"errors" "errors"
"io" "io"
"testing" "testing"
"github.com/edgelesssys/constellation/v2/internal/attestation/simulator" "github.com/edgelesssys/constellation/v2/internal/attestation/simulator"
"github.com/edgelesssys/go-azguestattestation/maa"
tpmclient "github.com/google/go-tpm-tools/client" tpmclient "github.com/google/go-tpm-tools/client"
"github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -23,27 +23,34 @@ import (
func TestGetSNPAttestation(t *testing.T) { func TestGetSNPAttestation(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
report string maaURL string
vcek string maaToken string
certChain string
apiError error apiError error
tokenErr error
paramsErr error
wantErr bool wantErr bool
}{ }{
"success": { "success without maa": {
report: "48434c41010000001c070000020000000000000000000000000000000000000002000000020000001f0003000000000001000000000000000000000000000000020000000000000000000000000000000000000001000000020000000000065d010000000000000000000000000000000ccc0895ef2f2c3b8c8568f5a2bb65ff5bf9387a09359742ad41e686cacfd38b00000000000000000000000000000000000000000000000000000000000000005677f1de87289e7ad2c7e99c805d0468b1a9ccd83f0d245afa5242d405da4d5725852f8c6550564870e5f3206dfb1841000000000000000000000000000000000000000000000000000000000000000057e229e0ffe5fa92d0faddff6cae0e61c926fc9ef9afd20a8b8cfcf7129db9338cbe5bf3f6987733a2bf65d06dc38fc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f7240b24a1babe2ece844c4f792bcd9844bf6907d14aeea00156310b9538daffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff020000000000065d0000000000000000000000000000000000000000000000009e44aaef02cfca6fddbaca669c6cfd29e1ab8d97ebc939857128acbb13b8740df31436d34e86e5f8ae0cdfeb3a0e185db46decac176cc77d761c22a1b9dcf25b020000000000065d0133010001330100020000000000065d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bcb7dc15abff884802e774b39adba8e6ff7efcf05e115c91588e657065151056a320f70c788d0e3619391052922e422b000000000000000000000000000000000000000000000000e8dbf581140443bbc681c50eca8639a76ef6cab34e0780cbca977e2e2a03f8b864fd4e9774b0f8055511567e031e59bf00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c020000010000000200000001000000480200007b226b657973223a5b7b226b6964223a2248434c416b507562222c226b65795f6f7073223a5b22656e6372797074225d2c226b7479223a22525341222c2265223a2241514142222c226e223a22747946717641414166324746656c6b5737566352684a6e4132597659364c6a427a65554e3276614d5a6e5a74685f74466e574d6b4b35415874757379434e656c337569703356475a7a54617a3558327447566a4772732d4d56486361703951647771555856573367394f515f74456269786378372d78626c554a516b474551666e626253646e5049326c764c7a4f73315a5f30766a65444178765351726d616773366e592d634a4157482d706744564a79487470735553735f5142576b6c617a44736f3557486d6e4d743973394d75696c57586f7830525379586e55656151796859316a753752545363526e5658754e7936377a5f454a6e774d393264727746623841556430534a5f396f687645596c34615a52444543476f3056726a635348552d4a474a6575574335566844425235454f6f4356424267716539653833765f6c4a784933574c65326f7653495a49497a416d625351227d5d2c22766d2d636f6e66696775726174696f6e223a7b22636f6e736f6c652d656e61626c6564223a747275652c2263757272656e742d74696d65223a313636313435353339312c227365637572652d626f6f74223a66616c73652c2274706d2d656e61626c6564223a747275652c22766d556e697175654964223a2242364339384333422d344543372d344441362d424432462d374439384432304437423735227d7d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", wantErr: false,
vcek: "someVCEKCert",
certChain: "ASKARK",
wantErr: false,
}, },
"report too short": { "success with maa": {
report: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", maaURL: "maaurl",
wantErr: true, maaToken: "maatoken",
wantErr: false,
}, },
"vcek api fails": { "api fails": {
report: "48434c41010000001c070000020000000000000000000000000000000000000002000000020000001f0003000000000001000000000000000000000000000000020000000000000000000000000000000000000001000000020000000000065d010000000000000000000000000000000ccc0895ef2f2c3b8c8568f5a2bb65ff5bf9387a09359742ad41e686cacfd38b00000000000000000000000000000000000000000000000000000000000000005677f1de87289e7ad2c7e99c805d0468b1a9ccd83f0d245afa5242d405da4d5725852f8c6550564870e5f3206dfb1841000000000000000000000000000000000000000000000000000000000000000057e229e0ffe5fa92d0faddff6cae0e61c926fc9ef9afd20a8b8cfcf7129db9338cbe5bf3f6987733a2bf65d06dc38fc100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f7240b24a1babe2ece844c4f792bcd9844bf6907d14aeea00156310b9538daffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff020000000000065d0000000000000000000000000000000000000000000000009e44aaef02cfca6fddbaca669c6cfd29e1ab8d97ebc939857128acbb13b8740df31436d34e86e5f8ae0cdfeb3a0e185db46decac176cc77d761c22a1b9dcf25b020000000000065d0133010001330100020000000000065d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bcb7dc15abff884802e774b39adba8e6ff7efcf05e115c91588e657065151056a320f70c788d0e3619391052922e422b000000000000000000000000000000000000000000000000e8dbf581140443bbc681c50eca8639a76ef6cab34e0780cbca977e2e2a03f8b864fd4e9774b0f8055511567e031e59bf00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005c020000010000000200000001000000480200007b226b657973223a5b7b226b6964223a2248434c416b507562222c226b65795f6f7073223a5b22656e6372797074225d2c226b7479223a22525341222c2265223a2241514142222c226e223a22747946717641414166324746656c6b5737566352684a6e4132597659364c6a427a65554e3276614d5a6e5a74685f74466e574d6b4b35415874757379434e656c337569703356475a7a54617a3558327447566a4772732d4d56486361703951647771555856573367394f515f74456269786378372d78626c554a516b474551666e626253646e5049326c764c7a4f73315a5f30766a65444178765351726d616773366e592d634a4157482d706744564a79487470735553735f5142576b6c617a44736f3557486d6e4d743973394d75696c57586f7830525379586e55656151796859316a753752545363526e5658754e7936377a5f454a6e774d393264727746623841556430534a5f396f687645596c34615a52444543476f3056726a635348552d4a474a6575574335566844425235454f6f4356424267716539653833765f6c4a784933574c65326f7653495a49497a416d625351227d5d2c22766d2d636f6e66696775726174696f6e223a7b22636f6e736f6c652d656e61626c6564223a747275652c2263757272656e742d74696d65223a313636313435353339312c227365637572652d626f6f74223a66616c73652c2274706d2d656e61626c6564223a747275652c22766d556e697175654964223a2242364339384333422d344543372d344441362d424432462d374439384432304437423735227d7d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
apiError: errors.New(""), apiError: errors.New(""),
wantErr: true, wantErr: true,
}, },
"createToken fails": {
maaURL: "maaurl",
tokenErr: errors.New(""),
wantErr: true,
},
"newParameters fails": {
paramsErr: errors.New(""),
wantErr: true,
},
} }
for name, tc := range testCases { for name, tc := range testCases {
@ -51,40 +58,55 @@ func TestGetSNPAttestation(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
require := require.New(t) require := require.New(t)
tpm, err := simulator.OpenSimulatedTPM()
require.NoError(err)
defer tpm.Close()
imdsClient := stubImdsClient{ imdsClient := stubImdsClient{
vcek: tc.vcek, maaURL: tc.maaURL,
certChain: tc.certChain, apiError: tc.apiError,
apiError: tc.apiError,
}
reportDecoded, err := hex.DecodeString(tc.report)
assert.NoError(err)
snpAttestationReport := stubTpmReport{
tpmContent: reportDecoded,
err: nil,
} }
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 { if tc.wantErr {
assert.Error(err) assert.Error(err)
return 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 var instanceInfo azureInstanceInfo
err = json.Unmarshal(attestationJSON, &instanceInfo) err = json.Unmarshal(attestationJSON, &instanceInfo)
assert.NoError(err) require.NoError(err)
if tc.wantErr { assert.Equal(params.VcekCert, instanceInfo.Vcek)
assert.NotEqualValues(tc.report[0x20:], instanceInfo.AttestationReport) assert.Equal(params.VcekChain, instanceInfo.CertChain)
} else { assert.Equal(params.SNPReport, instanceInfo.AttestationReport)
assert.EqualValues(reportDecoded[0x20:0x4a0+0x20], instanceInfo.AttestationReport) assert.Equal(params.RuntimeData, instanceInfo.RuntimeData)
assert.Equal(tc.vcek, string(instanceInfo.Vcek)) assert.Equal(tc.maaToken, instanceInfo.MAAToken)
assert.Equal(tc.certChain, string(instanceInfo.CertChain))
}
}) })
} }
} }
@ -126,30 +148,30 @@ func TestGetHCLAttestationKey(t *testing.T) {
} }
type stubImdsClient struct { type stubImdsClient struct {
vcek string maaURL string
certChain string apiError error
apiError error
} }
func (c stubImdsClient) getVcek(_ context.Context) (vcekResponse, error) { func (c stubImdsClient) getMAAURL(_ context.Context) (string, error) {
if c.apiError != nil { return c.maaURL, c.apiError
return vcekResponse{}, c.apiError
}
return vcekResponse{
VcekCert: c.vcek,
CertificateChain: c.certChain,
}, nil
} }
type stubTpmReport struct { type stubMaaTokenCreator struct {
tpmContent []byte token string
err error tokenErr error
gotTokenData []byte
params maa.Parameters
paramsErr error
gotParamsData []byte
} }
func (s *stubTpmReport) get(_ io.ReadWriteCloser) ([]byte, error) { func (s *stubMaaTokenCreator) newParameters(_ context.Context, data []byte, _ io.ReadWriter) (maa.Parameters, error) {
if s.err != nil { s.gotParamsData = data
return nil, s.err return s.params, s.paramsErr
} }
return s.tpmContent, nil
func (s *stubMaaTokenCreator) createToken(_ context.Context, _ io.ReadWriter, _ string, data []byte, _ maa.Parameters) (string, error) {
s.gotTokenData = data
return s.token, s.tokenErr
} }

View 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
}

View file

@ -8,6 +8,7 @@ package snp
import ( import (
"bytes" "bytes"
"context"
"crypto" "crypto"
"crypto/sha256" "crypto/sha256"
"crypto/x509" "crypto/x509"
@ -41,18 +42,36 @@ const (
type Validator struct { type Validator struct {
oid.AzureSEVSNP oid.AzureSEVSNP
*vtpm.Validator *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. // 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 { func NewValidator(pcrs measurements.M, idKeyConf idkeydigest.Config, log vtpm.AttestationLogger) *Validator {
return &Validator{ if log == nil {
Validator: vtpm.NewValidator( log = nopAttestationLogger{}
pcrs,
getTrustedKey(&azureInstanceInfo{}, idKeyDigests, enforceIDKeyDigest, log),
validateCVM,
log,
),
} }
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(). // 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. // getTrustedKey establishes trust in the given public key.
// It does so by verifying the SNP attestation statement in instanceInfo. // It does so by verifying the SNP attestation statement in instanceInfo.
func getTrustedKey( func (v *Validator) getTrustedKey(ctx context.Context, attDoc vtpm.AttestationDocument, extraData []byte) (crypto.PublicKey, error) {
hclAk HCLAkValidator, idKeyDigest idkeydigest.IDKeyDigests, enforceIDKeyDigest bool, log vtpm.AttestationLogger, var instanceInfo azureInstanceInfo
) func(akPub, instanceInfoRaw []byte) (crypto.PublicKey, error) { if err := json.Unmarshal(attDoc.InstanceInfo, &instanceInfo); err != nil {
return func(akPub, instanceInfoRaw []byte) (crypto.PublicKey, error) { return nil, fmt.Errorf("unmarshalling instanceInfoRaw: %w", err)
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()
} }
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. // 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 return vcek, nil
} }
func validateSNPReport( func (v *Validator) validateSNPReport(
cert *x509.Certificate, expectedIDKeyDigests idkeydigest.IDKeyDigests, enforceIDKeyDigest bool, ctx context.Context, cert *x509.Certificate, report snpAttestationReport, maaToken string, extraData []byte,
report snpAttestationReport, log vtpm.AttestationLogger,
) error { ) error {
if report.Policy.Debug() { if report.Policy.Debug() {
return errDebugEnabled return errDebugEnabled
@ -191,7 +205,7 @@ func validateSNPReport(
} }
hasExpectedIDKeyDigest := false hasExpectedIDKeyDigest := false
for _, digest := range expectedIDKeyDigests { for _, digest := range v.idKeyDigests {
if bytes.Equal(digest, report.IDKeyDigest[:]) { if bytes.Equal(digest, report.IDKeyDigest[:]) {
hasExpectedIDKeyDigest = true hasExpectedIDKeyDigest = true
break break
@ -199,11 +213,14 @@ func validateSNPReport(
} }
if !hasExpectedIDKeyDigest { if !hasExpectedIDKeyDigest {
if enforceIDKeyDigest { switch v.enforceIDKeyDigest {
return &idKeyError{report.IDKeyDigest[:], expectedIDKeyDigests} case idkeydigest.MAAFallback:
} v.log.Infof("configured idkeydigests %x don't contain reported idkeydigest %x, falling back to MAA validation", v.idKeyDigests, report.IDKeyDigest[:])
if log != nil { return v.maa.validateToken(ctx, v.maaURL, maaToken, extraData)
log.Warnf("configured idkeydigests %x doesn't contain reported idkeydigest %x", expectedIDKeyDigests, report.IDKeyDigest[:]) 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 CertChain []byte
AttestationReport []byte AttestationReport []byte
RuntimeData []byte RuntimeData []byte
MAAToken string
} }
// validateAk validates that the attestation key from the TPM is trustworthy. The steps are: // 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 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 is written by Azure, and sits between the Hypervisor and CVM OS.
// The HCL runs in the protected context of the CVM. // 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 validateAk(runtimeDataRaw []byte, reportData []byte, rsaParameters *tpm2.RSAParams) error
} }
@ -413,3 +431,16 @@ type akPub struct {
type runtimeData struct { type runtimeData struct {
Keys []akPub 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

View file

@ -33,10 +33,12 @@ go_test(
deps = [ deps = [
"//internal/attestation/measurements", "//internal/attestation/measurements",
"//internal/attestation/simulator", "//internal/attestation/simulator",
"//internal/attestation/vtpm",
"//internal/crypto", "//internal/crypto",
"//internal/logger", "//internal/logger",
"@com_github_google_go_tpm//tpm2", "@com_github_google_go_tpm//tpm2",
"@com_github_google_go_tpm_tools//client", "@com_github_google_go_tpm_tools//client",
"@com_github_google_go_tpm_tools//proto/attest",
"@com_github_stretchr_testify//assert", "@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require", "@com_github_stretchr_testify//require",
], ],

View file

@ -14,7 +14,6 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"time"
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
"github.com/edgelesssys/constellation/v2/internal/oid" "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. // 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) certDER, err := tpm2.NVReadEx(tpm, tpmAkCertIdx, tpm2.HandleOwner, "", 0)
if err != nil { if err != nil {
return nil, fmt.Errorf("reading attestation key certificate from TPM: %w", err) 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 // if no CA certificate can be loaded, an error is returned
var caCert *x509.Certificate var caCert *x509.Certificate
for _, caCertURL := range cert.IssuingCertificateURL { 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) req, err := http.NewRequestWithContext(ctx, http.MethodGet, caCertURL, nil)
if err != nil { if err != nil {
continue continue

View file

@ -8,6 +8,7 @@ package trustedlaunch
import ( import (
"bytes" "bytes"
"context"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"crypto/x509" "crypto/x509"
@ -19,9 +20,11 @@ import (
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements" "github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
"github.com/edgelesssys/constellation/v2/internal/attestation/simulator" "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/crypto"
"github.com/edgelesssys/constellation/v2/internal/logger" "github.com/edgelesssys/constellation/v2/internal/logger"
tpmclient "github.com/google/go-tpm-tools/client" tpmclient "github.com/google/go-tpm-tools/client"
"github.com/google/go-tpm-tools/proto/attest"
"github.com/google/go-tpm/tpm2" "github.com/google/go-tpm/tpm2"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -183,13 +186,20 @@ func TestGetAttestationCert(t *testing.T) {
issuer := NewIssuer(logger.NewTest(t)) issuer := NewIssuer(logger.NewTest(t))
issuer.hClient = newTestClient(tc.crlServer) issuer.hClient = newTestClient(tc.crlServer)
certs, err := issuer.getAttestationCert(tpm) certs, err := issuer.getAttestationCert(context.Background(), tpm, nil)
if tc.wantIssueErr { if tc.wantIssueErr {
assert.Error(err) assert.Error(err)
return return
} }
require.NoError(err) require.NoError(err)
attDoc := vtpm.AttestationDocument{
InstanceInfo: certs,
Attestation: &attest.Attestation{
AkPub: akPub,
},
}
validator := NewValidator(measurements.M{}, nil) validator := NewValidator(measurements.M{}, nil)
cert, err := x509.ParseCertificate(rootCert.Raw) cert, err := x509.ParseCertificate(rootCert.Raw)
require.NoError(err) require.NoError(err)
@ -197,7 +207,7 @@ func TestGetAttestationCert(t *testing.T) {
roots.AddCert(cert) roots.AddCert(cert)
validator.roots = roots validator.roots = roots
key, err := validator.verifyAttestationKey(akPub, certs) key, err := validator.verifyAttestationKey(context.Background(), attDoc, nil)
if tc.wantValidateErr { if tc.wantValidateErr {
assert.Error(err) assert.Error(err)
return return

View file

@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
package trustedlaunch package trustedlaunch
import ( import (
"context"
"crypto" "crypto"
"crypto/rsa" "crypto/rsa"
"crypto/x509" "crypto/x509"
@ -49,14 +50,14 @@ func NewValidator(pcrs measurements.M, log vtpm.AttestationLogger) *Validator {
// verifyAttestationKey establishes trust in an attestation key. // verifyAttestationKey establishes trust in an attestation key.
// It does so by verifying the certificate chain of the attestation key certificate. // It does so by verifying the certificate chain of the attestation key certificate.
func (v *Validator) verifyAttestationKey(akPub, instanceInfo []byte) (crypto.PublicKey, error) { func (v *Validator) verifyAttestationKey(_ context.Context, attDoc vtpm.AttestationDocument, _ []byte) (crypto.PublicKey, error) {
pubArea, err := tpm2.DecodePublic(akPub) pubArea, err := tpm2.DecodePublic(attDoc.Attestation.AkPub)
if err != nil { if err != nil {
return nil, fmt.Errorf("decoding attestation key public area: %w", err) return nil, fmt.Errorf("decoding attestation key public area: %w", err)
} }
var akSigner akSigner 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) return nil, fmt.Errorf("unmarshaling attestation key signer info: %w", err)
} }

View file

@ -25,6 +25,7 @@ go_test(
srcs = ["choose_test.go"], srcs = ["choose_test.go"],
embed = [":choose"], embed = [":choose"],
deps = [ deps = [
"//internal/attestation/idkeydigest",
"//internal/oid", "//internal/oid",
"@com_github_stretchr_testify//assert", "@com_github_stretchr_testify//assert",
"@com_github_stretchr_testify//require", "@com_github_stretchr_testify//require",

View file

@ -43,9 +43,7 @@ func Issuer(variant oid.Getter, log vtpm.AttestationLogger) (atls.Issuer, error)
// Validator returns the validator for the given variant. // Validator returns the validator for the given variant.
func Validator( func Validator(
variant oid.Getter, measurements measurements.M, variant oid.Getter, measurements measurements.M, idKeyCfg idkeydigest.Config, log vtpm.AttestationLogger,
idKeyDigest idkeydigest.IDKeyDigests, enfoceIDKeyDigest bool,
log vtpm.AttestationLogger,
) (atls.Validator, error) { ) (atls.Validator, error) {
switch variant { switch variant {
case oid.AWSNitroTPM{}: case oid.AWSNitroTPM{}:
@ -53,7 +51,7 @@ func Validator(
case oid.AzureTrustedLaunch{}: case oid.AzureTrustedLaunch{}:
return trustedlaunch.NewValidator(measurements, log), nil return trustedlaunch.NewValidator(measurements, log), nil
case oid.AzureSEVSNP{}: case oid.AzureSEVSNP{}:
return snp.NewValidator(measurements, idKeyDigest, enfoceIDKeyDigest, log), nil return snp.NewValidator(measurements, idKeyCfg, log), nil
case oid.GCPSEVES{}: case oid.GCPSEVES{}:
return gcp.NewValidator(measurements, log), nil return gcp.NewValidator(measurements, log), nil
case oid.QEMUVTPM{}: case oid.QEMUVTPM{}:

View file

@ -10,6 +10,7 @@ import (
"encoding/asn1" "encoding/asn1"
"testing" "testing"
"github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest"
"github.com/edgelesssys/constellation/v2/internal/oid" "github.com/edgelesssys/constellation/v2/internal/oid"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -95,7 +96,7 @@ func TestValidator(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
require := require.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 { if tc.wantErr {
assert.Error(err) assert.Error(err)

View file

@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
package gcp package gcp
import ( import (
"context"
"encoding/json" "encoding/json"
"errors" "errors"
"io" "io"
@ -37,12 +38,12 @@ func NewIssuer(log vtpm.AttestationLogger) *Issuer {
} }
// getGCEInstanceInfo fetches VM metadata used for attestation. // 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 // Ideally we would want to use the endorsement public key certificate
// However, this is not available on GCE instances // However, this is not available on GCE instances
// Workaround: Provide ShieldedVM instance info // Workaround: Provide ShieldedVM instance info
// The attestating party can request the VMs signing key using Google's API // 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() projectID, err := client.projectID()
if err != nil { if err != nil {
return nil, errors.New("unable to fetch projectID") return nil, errors.New("unable to fetch projectID")

View file

@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
package gcp package gcp
import ( import (
"context"
"encoding/json" "encoding/json"
"errors" "errors"
"io" "io"
@ -65,7 +66,7 @@ func TestGetGCEInstanceInfo(t *testing.T) {
require := require.New(t) require := require.New(t)
var tpm io.ReadWriteCloser var tpm io.ReadWriteCloser
out, err := getGCEInstanceInfo(tc.client)(tpm) out, err := getGCEInstanceInfo(tc.client)(context.Background(), tpm, nil)
if tc.wantErr { if tc.wantErr {
assert.Error(err) assert.Error(err)
} else { } else {

View file

@ -13,7 +13,6 @@ import (
"encoding/json" "encoding/json"
"encoding/pem" "encoding/pem"
"fmt" "fmt"
"time"
compute "cloud.google.com/go/compute/apiv1" compute "cloud.google.com/go/compute/apiv1"
"cloud.google.com/go/compute/apiv1/computepb" "cloud.google.com/go/compute/apiv1/computepb"
@ -31,18 +30,23 @@ const minimumGceVersion = 1
type Validator struct { type Validator struct {
oid.GCPSEVES oid.GCPSEVES
*vtpm.Validator *vtpm.Validator
restClient func(context.Context, ...option.ClientOption) (gcpRestClient, error)
} }
// NewValidator initializes a new GCP validator with the provided PCR values. // NewValidator initializes a new GCP validator with the provided PCR values.
func NewValidator(pcrs measurements.M, log vtpm.AttestationLogger) *Validator { func NewValidator(pcrs measurements.M, log vtpm.AttestationLogger) *Validator {
return &Validator{ v := &Validator{
Validator: vtpm.NewValidator( restClient: newInstanceClient,
pcrs,
trustedKeyFromGCEAPI(newInstanceClient),
validateCVM,
log,
),
} }
v.Validator = vtpm.NewValidator(
pcrs,
v.trustedKeyFromGCEAPI,
validateCVM,
log,
)
return v
} }
type gcpRestClient interface { 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. // 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. // 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) { func (v *Validator) trustedKeyFromGCEAPI(ctx context.Context, attDoc vtpm.AttestationDocument, _ []byte) (crypto.PublicKey, error) {
return func(akPub, instanceInfoRaw []byte) (crypto.PublicKey, error) { client, err := v.restClient(ctx)
var instanceInfo attest.GCEInstanceInfo if err != nil {
if err := json.Unmarshal(instanceInfoRaw, &instanceInfo); err != nil { return nil, fmt.Errorf("creating GCE client: %w", err)
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)
} }
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. // validateCVM checks that the machine state represents a GCE AMD-SEV VM.

View file

@ -146,7 +146,12 @@ Y+t5OxL3kL15VzY1Ob0d5cMCAwEAAQ==
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
assert := assert.New(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 { if tc.wantErr {
assert.Error(err) assert.Error(err)

View file

@ -3,7 +3,10 @@ load("//bazel/go:go_test.bzl", "go_test")
go_library( go_library(
name = "idkeydigest", name = "idkeydigest",
srcs = ["idkeydigest.go"], srcs = [
"enforceidkeydigest_string.go",
"idkeydigest.go",
],
importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest", importpath = "github.com/edgelesssys/constellation/v2/internal/attestation/idkeydigest",
visibility = ["//:__subpackages__"], visibility = ["//:__subpackages__"],
deps = ["//internal/cloud/cloudprovider"], deps = ["//internal/cloud/cloudprovider"],

View file

@ -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]]
}

View file

@ -4,6 +4,8 @@ Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only 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 package idkeydigest
import ( import (
@ -11,10 +13,105 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"strings"
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider" "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. // IDKeyDigests is a list of trusted digest values for the ID key.
type IDKeyDigests [][]byte type IDKeyDigests [][]byte

View file

@ -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)
}
})
}
}

View file

@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
package qemu package qemu
import ( import (
"context"
"io" "io"
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm" "github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
@ -26,7 +27,7 @@ func NewIssuer(log vtpm.AttestationLogger) *Issuer {
Issuer: vtpm.NewIssuer( Issuer: vtpm.NewIssuer(
vtpm.OpenVTPM, vtpm.OpenVTPM,
tpmclient.AttestationKeyRSA, tpmclient.AttestationKeyRSA,
func(tpm io.ReadWriteCloser) ([]byte, error) { return nil, nil }, func(context.Context, io.ReadWriteCloser, []byte) ([]byte, error) { return nil, nil },
log, log,
), ),
} }

View file

@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
package qemu package qemu
import ( import (
"context"
"crypto" "crypto"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements" "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. // unconditionalTrust returns the given public key as the trusted attestation key.
func unconditionalTrust(akPub, _ []byte) (crypto.PublicKey, error) { func unconditionalTrust(_ context.Context, attDoc vtpm.AttestationDocument, _ []byte) (crypto.PublicKey, error) {
pubArea, err := tpm2.DecodePublic(akPub) pubArea, err := tpm2.DecodePublic(attDoc.Attestation.AkPub)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -8,6 +8,7 @@ package vtpm
import ( import (
"bytes" "bytes"
"context"
"crypto" "crypto"
"crypto/sha256" "crypto/sha256"
"encoding/json" "encoding/json"
@ -58,9 +59,9 @@ type (
// GetTPMAttestationKey loads a TPM key to perform attestation. // GetTPMAttestationKey loads a TPM key to perform attestation.
GetTPMAttestationKey func(tpm io.ReadWriter) (*tpmClient.Key, error) GetTPMAttestationKey func(tpm io.ReadWriter) (*tpmClient.Key, error)
// GetTPMTrustedAttestationPublicKey verifies and returns the attestation public key. // 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 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 validates confidential computing capabilities of the instance issuing the attestation.
ValidateCVM func(attestation AttestationDocument, state *attest.MachineState) error 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 // 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 { if err != nil {
return nil, fmt.Errorf("fetching instance info: %w", err) 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) 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 // 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 { if err != nil {
return nil, fmt.Errorf("validating attestation public key: %w", err) 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( state, err := tpmServer.VerifyAttestation(
attDoc.Attestation, attDoc.Attestation,
tpmServer.VerifyOpts{ tpmServer.VerifyOpts{
Nonce: makeExtraData(attDoc.UserData, nonce), Nonce: extraData,
TrustedAKs: []crypto.PublicKey{aKP}, TrustedAKs: []crypto.PublicKey{aKP},
AllowSHA1: false, AllowSHA1: false,
}, },

View file

@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
package vtpm package vtpm
import ( import (
"context"
"crypto" "crypto"
"encoding/json" "encoding/json"
"errors" "errors"
@ -51,7 +52,7 @@ func (s simTPMWithEventLog) EventLog() ([]byte, error) {
return header, nil return header, nil
} }
func fakeGetInstanceInfo(_ io.ReadWriteCloser) ([]byte, error) { func fakeGetInstanceInfo(_ context.Context, _ io.ReadWriteCloser, _ []byte) ([]byte, error) {
return []byte("unit-test"), nil return []byte("unit-test"), nil
} }
@ -59,8 +60,8 @@ func TestValidate(t *testing.T) {
require := require.New(t) require := require.New(t)
fakeValidateCVM := func(AttestationDocument, *attest.MachineState) error { return nil } fakeValidateCVM := func(AttestationDocument, *attest.MachineState) error { return nil }
fakeGetTrustedKey := func(aKPub, instanceInfo []byte) (crypto.PublicKey, error) { fakeGetTrustedKey := func(_ context.Context, attDoc AttestationDocument, _ []byte) (crypto.PublicKey, error) {
pubArea, err := tpm2.DecodePublic(aKPub) pubArea, err := tpm2.DecodePublic(attDoc.Attestation.AkPub)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -175,7 +176,7 @@ func TestValidate(t *testing.T) {
"untrusted attestation public key": { "untrusted attestation public key": {
validator: NewValidator( validator: NewValidator(
testExpectedPCRs, testExpectedPCRs,
func(akPub, instanceInfo []byte) (crypto.PublicKey, error) { func(context.Context, AttestationDocument, []byte) (crypto.PublicKey, error) {
return nil, errors.New("untrusted") return nil, errors.New("untrusted")
}, },
fakeValidateCVM, warnLog), fakeValidateCVM, warnLog),
@ -301,7 +302,7 @@ func TestFailIssuer(t *testing.T) {
issuer: NewIssuer( issuer: NewIssuer(
newSimTPMWithEventLog, newSimTPMWithEventLog,
tpmclient.AttestationKeyRSA, 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, nil,
), ),
userData: []byte("Constellation"), userData: []byte("Constellation"),

View file

@ -20,7 +20,6 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"net/http"
"path" "path"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity" "github.com/Azure/azure-sdk-for-go/sdk/azidentity"
@ -51,12 +50,8 @@ func New(ctx context.Context) (*Cloud, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("loading credentials: %w", err) 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 imdsAPI := NewIMDSClient()
// See also: https://github.com/microsoft/azureimds/blob/master/imdssample.go#L10
imdsAPI := imdsClient{
client: &http.Client{Transport: &http.Transport{Proxy: nil}},
}
subscriptionID, err := imdsAPI.subscriptionID(ctx) subscriptionID, err := imdsAPI.subscriptionID(ctx)
if err != nil { if err != nil {
return nil, fmt.Errorf("retrieving subscription ID: %w", err) return nil, fmt.Errorf("retrieving subscription ID: %w", err)
@ -91,7 +86,7 @@ func New(ctx context.Context) (*Cloud, error) {
} }
return &Cloud{ return &Cloud{
imds: &imdsAPI, imds: imdsAPI,
netIfacAPI: networkInterfacesAPI, netIfacAPI: networkInterfacesAPI,
virtNetAPI: virtualNetworksAPI, virtNetAPI: virtualNetworksAPI,
secGroupAPI: securityGroupsAPI, secGroupAPI: securityGroupsAPI,

View file

@ -28,15 +28,42 @@ const (
maxCacheAge = 12 * time.Hour maxCacheAge = 12 * time.Hour
) )
type imdsClient struct { // IMDSClient is a client for the Azure Instance Metadata Service.
type IMDSClient struct {
client *http.Client client *http.Client
cache metadataResponse cache metadataResponse
cacheTime time.Time 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. // 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 c.timeForUpdate() || c.cache.Compute.ResourceID == "" {
if err := c.update(ctx); err != nil { if err := c.update(ctx); err != nil {
return "", err return "", err
@ -50,7 +77,7 @@ func (c *imdsClient) providerID(ctx context.Context) (string, error) {
return c.cache.Compute.ResourceID, nil 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 c.timeForUpdate() || c.cache.Compute.OSProfile.ComputerName == "" {
if err := c.update(ctx); err != nil { if err := c.update(ctx); err != nil {
return "", err 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 // subscriptionID returns the subscription ID of the instance the function
// is called from. // 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 c.timeForUpdate() || c.cache.Compute.SubscriptionID == "" {
if err := c.update(ctx); err != nil { if err := c.update(ctx); err != nil {
return "", err 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 // resourceGroup returns the resource group of the instance the function
// is called from. // 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 c.timeForUpdate() || c.cache.Compute.ResourceGroup == "" {
if err := c.update(ctx); err != nil { if err := c.update(ctx); err != nil {
return "", err 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 // 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. // 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 c.timeForUpdate() || len(c.cache.Compute.Tags) == 0 {
if err := c.update(ctx); err != nil { if err := c.update(ctx); err != nil {
return "", err 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 // 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. // 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 c.timeForUpdate() || len(c.cache.Compute.Tags) == 0 {
if err := c.update(ctx); err != nil { if err := c.update(ctx); err != nil {
return "", err 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. // 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 c.timeForUpdate() || len(c.cache.Compute.Tags) == 0 {
if err := c.update(ctx); err != nil { if err := c.update(ctx); err != nil {
return role.Unknown, err 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. // 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 return time.Since(c.cacheTime) > maxCacheAge
} }
// update updates instance metadata from the azure imds API. // 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) req, err := http.NewRequestWithContext(ctx, http.MethodGet, imdsURL, http.NoBody)
if err != nil { if err != nil {
return err return err

View file

@ -92,6 +92,11 @@ func TestIMDSClient(t *testing.T) {
}, },
} }
defaultWantTags := map[string]string{
cloud.TagUID: "uid",
cloud.TagRole: "worker",
}
testCases := map[string]struct { testCases := map[string]struct {
server httpBufconnServer server httpBufconnServer
wantProviderIDErr bool wantProviderIDErr bool
@ -106,6 +111,8 @@ func TestIMDSClient(t *testing.T) {
wantName string wantName string
wantSubscriptionErr bool wantSubscriptionErr bool
wantSubscriptionID string wantSubscriptionID string
wantTagsErr bool
wantTags map[string]string
}{ }{
"metadata response parsed": { "metadata response parsed": {
server: newHTTPBufconnServerWithMetadataResponse(response), server: newHTTPBufconnServerWithMetadataResponse(response),
@ -115,6 +122,7 @@ func TestIMDSClient(t *testing.T) {
wantRole: role.Worker, wantRole: role.Worker,
wantName: "computer-name", wantName: "computer-name",
wantSubscriptionID: "subscription-id", wantSubscriptionID: "subscription-id",
wantTags: defaultWantTags,
}, },
"metadata response without resource ID": { "metadata response without resource ID": {
server: newHTTPBufconnServerWithMetadataResponse(responseWithoutID), server: newHTTPBufconnServerWithMetadataResponse(responseWithoutID),
@ -124,6 +132,7 @@ func TestIMDSClient(t *testing.T) {
wantRole: role.Worker, wantRole: role.Worker,
wantName: "computer-name", wantName: "computer-name",
wantSubscriptionID: "subscription-id", wantSubscriptionID: "subscription-id",
wantTags: defaultWantTags,
}, },
"metadata response without UID tag": { "metadata response without UID tag": {
server: newHTTPBufconnServerWithMetadataResponse(responseWithoutUID), server: newHTTPBufconnServerWithMetadataResponse(responseWithoutUID),
@ -133,6 +142,7 @@ func TestIMDSClient(t *testing.T) {
wantRole: role.Worker, wantRole: role.Worker,
wantName: "computer-name", wantName: "computer-name",
wantSubscriptionID: "subscription-id", wantSubscriptionID: "subscription-id",
wantTags: map[string]string{cloud.TagRole: "worker"},
}, },
"metadata response without role tag": { "metadata response without role tag": {
server: newHTTPBufconnServerWithMetadataResponse(responseWithoutRole), server: newHTTPBufconnServerWithMetadataResponse(responseWithoutRole),
@ -142,6 +152,7 @@ func TestIMDSClient(t *testing.T) {
wantRoleErr: true, wantRoleErr: true,
wantName: "computer-name", wantName: "computer-name",
wantSubscriptionID: "subscription-id", wantSubscriptionID: "subscription-id",
wantTags: map[string]string{cloud.TagUID: "uid"},
}, },
"metadata response without resource group": { "metadata response without resource group": {
server: newHTTPBufconnServerWithMetadataResponse(responseWithoutGroup), server: newHTTPBufconnServerWithMetadataResponse(responseWithoutGroup),
@ -151,6 +162,7 @@ func TestIMDSClient(t *testing.T) {
wantRole: role.Worker, wantRole: role.Worker,
wantName: "computer-name", wantName: "computer-name",
wantSubscriptionID: "subscription-id", wantSubscriptionID: "subscription-id",
wantTags: defaultWantTags,
}, },
"metadata response without name": { "metadata response without name": {
server: newHTTPBufconnServerWithMetadataResponse(responseWithoutName), server: newHTTPBufconnServerWithMetadataResponse(responseWithoutName),
@ -160,6 +172,7 @@ func TestIMDSClient(t *testing.T) {
wantRole: role.Worker, wantRole: role.Worker,
wantNameErr: true, wantNameErr: true,
wantSubscriptionID: "subscription-id", wantSubscriptionID: "subscription-id",
wantTags: defaultWantTags,
}, },
"metadata response without subscription ID": { "metadata response without subscription ID": {
server: newHTTPBufconnServerWithMetadataResponse(responseWithoutSubscriptionID), server: newHTTPBufconnServerWithMetadataResponse(responseWithoutSubscriptionID),
@ -169,6 +182,7 @@ func TestIMDSClient(t *testing.T) {
wantRole: role.Worker, wantRole: role.Worker,
wantName: "computer-name", wantName: "computer-name",
wantSubscriptionErr: true, wantSubscriptionErr: true,
wantTags: defaultWantTags,
}, },
"invalid imds response detected": { "invalid imds response detected": {
server: newHTTPBufconnServer(func(writer http.ResponseWriter, request *http.Request) { server: newHTTPBufconnServer(func(writer http.ResponseWriter, request *http.Request) {
@ -180,6 +194,7 @@ func TestIMDSClient(t *testing.T) {
wantRoleErr: true, wantRoleErr: true,
wantNameErr: true, wantNameErr: true,
wantSubscriptionErr: true, wantSubscriptionErr: true,
wantTagsErr: true,
}, },
} }
@ -197,7 +212,7 @@ func TestIMDSClient(t *testing.T) {
DialTLS: tc.server.Dial, DialTLS: tc.server.Dial,
}, },
} }
iClient := imdsClient{client: &hClient} iClient := IMDSClient{client: &hClient}
ctx := context.Background() ctx := context.Background()
@ -248,6 +263,14 @@ func TestIMDSClient(t *testing.T) {
assert.NoError(err) assert.NoError(err)
assert.Equal(tc.wantSubscriptionID, subscriptionID) 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)
}
}) })
} }
} }

View file

@ -31,7 +31,7 @@ func NewLogger(ctx context.Context) (*Logger, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("loading credentials: %w", err) return nil, fmt.Errorf("loading credentials: %w", err)
} }
imdsAPI := &imdsClient{ imdsAPI := &IMDSClient{
client: &http.Client{Transport: &http.Transport{Proxy: nil}}, client: &http.Client{Transport: &http.Transport{Proxy: nil}},
} }
subscriptionID, err := imdsAPI.subscriptionID(ctx) subscriptionID, err := imdsAPI.subscriptionID(ctx)

View file

@ -192,7 +192,7 @@ type AzureConfig struct {
IDKeyDigest Digests `yaml:"idKeyDigest" validate:"required_if=EnforceIdKeyDigest true,omitempty"` IDKeyDigest Digests `yaml:"idKeyDigest" validate:"required_if=EnforceIdKeyDigest true,omitempty"`
// description: | // description: |
// Enforce the specified idKeyDigest value during remote attestation. // Enforce the specified idKeyDigest value during remote attestation.
EnforceIDKeyDigest *bool `yaml:"enforceIdKeyDigest" validate:"required"` EnforceIDKeyDigest idkeydigest.EnforceIDKeyDigest `yaml:"enforceIdKeyDigest" validate:"required"`
// description: | // description: |
// Expected confidential VM measurements. // Expected confidential VM measurements.
Measurements Measurements `yaml:"measurements" validate:"required,no_placeholders"` Measurements Measurements `yaml:"measurements" validate:"required,no_placeholders"`
@ -329,7 +329,7 @@ func Default() *Config {
StateDiskType: "Premium_LRS", StateDiskType: "Premium_LRS",
DeployCSIDriver: toPtr(true), DeployCSIDriver: toPtr(true),
IDKeyDigest: idkeydigest.DefaultsFor(cloudprovider.Azure), IDKeyDigest: idkeydigest.DefaultsFor(cloudprovider.Azure),
EnforceIDKeyDigest: toPtr(true), EnforceIDKeyDigest: idkeydigest.MAAFallback,
ConfidentialVM: toPtr(true), ConfidentialVM: toPtr(true),
SecureBoot: toPtr(false), SecureBoot: toPtr(false),
Measurements: measurements.DefaultsFor(cloudprovider.Azure), Measurements: measurements.DefaultsFor(cloudprovider.Azure),
@ -514,9 +514,12 @@ func (c *Config) GetMeasurements() measurements.M {
return nil return nil
} }
// EnforcesIDKeyDigest checks whether ID Key Digest should be enforced for respective cloud provider. // IDKeyDigestPolicy returns the IDKeyDigest checking policy for a cloud provider.
func (c *Config) EnforcesIDKeyDigest() bool { func (c *Config) IDKeyDigestPolicy() idkeydigest.EnforceIDKeyDigest {
return c.Provider.Azure != nil && c.Provider.Azure.EnforceIDKeyDigest != nil && *c.Provider.Azure.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. // EnforcedPCRs returns the list of enforced PCRs for the configured cloud provider.

View file

@ -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].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[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].Name = "enforceIdKeyDigest"
AzureConfigDoc.Fields[13].Type = "bool" AzureConfigDoc.Fields[13].Type = "EnforceIDKeyDigest"
AzureConfigDoc.Fields[13].Note = "" AzureConfigDoc.Fields[13].Note = ""
AzureConfigDoc.Fields[13].Description = "Enforce the specified idKeyDigest value during remote attestation." 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." AzureConfigDoc.Fields[13].Comments[encoder.LineComment] = "Enforce the specified idKeyDigest value during remote attestation."

View file

@ -740,7 +740,7 @@ func TestConfigVersionCompatibility(t *testing.T) {
ConfidentialVM: toPtr(true), ConfidentialVM: toPtr(true),
InstanceType: "Standard_DC4as_v5", 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}}, 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), SecureBoot: toPtr(false),
DeployCSIDriver: toPtr(true), DeployCSIDriver: toPtr(true),
Measurements: measurements.DefaultsFor(cloudprovider.Azure), 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}, {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}, {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), SecureBoot: toPtr(false),
DeployCSIDriver: toPtr(true), DeployCSIDriver: toPtr(true),
Measurements: measurements.DefaultsFor(cloudprovider.Azure), Measurements: measurements.DefaultsFor(cloudprovider.Azure),

View file

@ -116,6 +116,9 @@ const (
IDKeyDigestFilename = "idkeydigests" IDKeyDigestFilename = "idkeydigests"
// EnforceIDKeyDigestFilename is the name of the file configuring whether idkeydigest is enforced or not. // EnforceIDKeyDigestFilename is the name of the file configuring whether idkeydigest is enforced or not.
EnforceIDKeyDigestFilename = "enforceIdKeyDigest" 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 is the name of the of the key holding the wanted Kubernetes version.
K8sVersionFieldName = "cluster-version" K8sVersionFieldName = "cluster-version"
// ComponentsListKey is the name of the key holding the list of components in the components configMap. // ComponentsListKey is the name of the key holding the list of components in the components configMap.

View file

@ -19,6 +19,7 @@ go_library(
"//internal/logger", "//internal/logger",
"//internal/oid", "//internal/oid",
"@com_github_fsnotify_fsnotify//:fsnotify", "@com_github_fsnotify_fsnotify//:fsnotify",
"@com_github_spf13_afero//:afero",
"@org_uber_go_zap//:zap", "@org_uber_go_zap//:zap",
], ],
) )

View file

@ -9,9 +9,9 @@ package watcher
import ( import (
"encoding/asn1" "encoding/asn1"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"path/filepath" "path/filepath"
"strconv"
"sync" "sync"
"github.com/edgelesssys/constellation/v2/internal/atls" "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/file"
"github.com/edgelesssys/constellation/v2/internal/logger" "github.com/edgelesssys/constellation/v2/internal/logger"
"github.com/edgelesssys/constellation/v2/internal/oid" "github.com/edgelesssys/constellation/v2/internal/oid"
"github.com/spf13/afero"
) )
// Updatable implements an updatable atls.Validator. // Updatable implements an updatable atls.Validator.
@ -74,32 +75,48 @@ func (u *Updatable) Update() error {
} }
u.log.Debugf("New measurements: %+v", measurements) u.log.Debugf("New measurements: %+v", measurements)
var digest idkeydigest.IDKeyDigests // Read ID Key config
var enforceIDKeyDigest bool var idKeyCfg idkeydigest.Config
if u.variant.OID().Equal(oid.AzureSEVSNP{}.OID()) { if u.variant.OID().Equal(oid.AzureSEVSNP{}.OID()) {
u.log.Infof("Updating encforceIdKeyDigest value") u.log.Infof("Updating SEV-SNP ID Key config")
enforceRaw, err := u.fileHandler.Read(filepath.Join(constants.ServiceBasePath, constants.EnforceIDKeyDigestFilename))
if err != nil {
return err
}
enforceIDKeyDigest, err = strconv.ParseBool(string(enforceRaw))
if err != nil {
return fmt.Errorf("parsing content of EnforceIdKeyDigestFilename: %s: %w", enforceRaw, err)
}
u.log.Debugf("New encforceIdKeyDigest value: %v", enforceIDKeyDigest)
u.log.Infof("Updating expected idkeydigest") err := u.fileHandler.ReadJSON(filepath.Join(constants.ServiceBasePath, constants.IDKeyConfigFilename), &idKeyCfg)
idkeydigestRaw, err := u.fileHandler.Read(filepath.Join(constants.ServiceBasePath, constants.IDKeyDigestFilename))
if err != nil { 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 ID Key config: %+v", idKeyCfg)
}
u.log.Debugf("New idkeydigest: %v", digest)
} }
validator, err := choose.Validator(u.variant, measurements, digest, enforceIDKeyDigest, u.log) validator, err := choose.Validator(u.variant, measurements, idKeyCfg, u.log)
if err != nil { if err != nil {
return fmt.Errorf("updating validator: %w", err) return fmt.Errorf("updating validator: %w", err)
} }

View file

@ -79,15 +79,13 @@ func TestNewUpdateableValidator(t *testing.T) {
filepath.Join(constants.ServiceBasePath, constants.MeasurementsFilename), filepath.Join(constants.ServiceBasePath, constants.MeasurementsFilename),
measurements.M{11: measurements.WithAllBytes(0x00, false)}, measurements.M{11: measurements.WithAllBytes(0x00, false)},
)) ))
keyDigest, err := json.Marshal(idkeydigest.DefaultsFor(cloudprovider.Azure))
require.NoError(err) require.NoError(handler.WriteJSON(
require.NoError(handler.Write( filepath.Join(constants.ServiceBasePath, constants.IDKeyConfigFilename),
filepath.Join(constants.ServiceBasePath, constants.IDKeyDigestFilename), idkeydigest.Config{
keyDigest, IDKeyDigests: idkeydigest.DefaultsFor(cloudprovider.Azure),
)) EnforcementPolicy: idkeydigest.WarnOnly,
require.NoError(handler.Write( },
filepath.Join(constants.ServiceBasePath, constants.EnforceIDKeyDigestFilename),
[]byte("false"),
)) ))
} }
@ -126,13 +124,12 @@ func TestUpdate(t *testing.T) {
filepath.Join(constants.ServiceBasePath, constants.MeasurementsFilename), filepath.Join(constants.ServiceBasePath, constants.MeasurementsFilename),
measurements.M{11: measurements.WithAllBytes(0x00, false)}, measurements.M{11: measurements.WithAllBytes(0x00, false)},
)) ))
require.NoError(handler.Write( require.NoError(handler.WriteJSON(
filepath.Join(constants.ServiceBasePath, constants.IDKeyDigestFilename), filepath.Join(constants.ServiceBasePath, constants.IDKeyConfigFilename),
[]byte{}, idkeydigest.Config{
)) IDKeyDigests: idkeydigest.IDKeyDigests{[]byte{0x00}},
require.NoError(handler.Write( EnforcementPolicy: idkeydigest.WarnOnly,
filepath.Join(constants.ServiceBasePath, constants.EnforceIDKeyDigestFilename), },
[]byte("false"),
)) ))
// call update once to initialize the server's validator // call update once to initialize the server's validator
@ -167,6 +164,18 @@ func TestUpdate(t *testing.T) {
defer resp.Body.Close() defer resp.Body.Close()
} }
assert.Error(err) 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) { func TestOIDConcurrency(t *testing.T) {
@ -178,9 +187,12 @@ func TestOIDConcurrency(t *testing.T) {
filepath.Join(constants.ServiceBasePath, constants.MeasurementsFilename), filepath.Join(constants.ServiceBasePath, constants.MeasurementsFilename),
measurements.M{11: measurements.WithAllBytes(0x00, false)}, measurements.M{11: measurements.WithAllBytes(0x00, false)},
)) ))
require.NoError(handler.Write( require.NoError(handler.WriteJSON(
filepath.Join(constants.ServiceBasePath, constants.IDKeyDigestFilename), filepath.Join(constants.ServiceBasePath, constants.IDKeyConfigFilename),
[]byte{}, idkeydigest.Config{
IDKeyDigests: idkeydigest.IDKeyDigests{[]byte{0x00}},
EnforcementPolicy: idkeydigest.WarnOnly,
},
)) ))
// create server // create server
@ -223,13 +235,12 @@ func TestUpdateConcurrency(t *testing.T) {
measurements.M{11: measurements.WithAllBytes(0x00, false)}, measurements.M{11: measurements.WithAllBytes(0x00, false)},
file.OptNone, file.OptNone,
)) ))
require.NoError(handler.Write( require.NoError(handler.WriteJSON(
filepath.Join(constants.ServiceBasePath, constants.IDKeyDigestFilename), filepath.Join(constants.ServiceBasePath, constants.IDKeyConfigFilename),
[]byte{}, idkeydigest.Config{
)) IDKeyDigests: idkeydigest.IDKeyDigests{[]byte{0x00}},
require.NoError(handler.Write( EnforcementPolicy: idkeydigest.WarnOnly,
filepath.Join(constants.ServiceBasePath, constants.EnforceIDKeyDigestFilename), },
[]byte("false"),
)) ))
var wg sync.WaitGroup var wg sync.WaitGroup