From a994f142d0e74d71b633368ff79d9c946d650b7d Mon Sep 17 00:00:00 2001 From: Paul Meyer Date: Tue, 11 Feb 2025 15:21:01 +0100 Subject: [PATCH] runtime: make SNP IDBlock configurable For a use case, we want to set the SNP IDBlock, which allows configuring the AMD ASP to enforce parameters like expected launch digest at launch. The struct with the config that should be enforced (IDBlock) is signed. The public key is placed in the auth block and the signature is verified by the ASP before launch. The digest of the public key is also part of the attestation report (ID_KEY_DIGESTS). Signed-off-by: Paul Meyer --- .../configuration-qemu-nvidia-gpu-snp.toml.in | 12 ++++++ .../config/configuration-qemu-snp.toml.in | 12 ++++++ src/runtime/pkg/govmm/qemu/qemu.go | 14 +++++++ src/runtime/pkg/katautils/config.go | 4 ++ src/runtime/virtcontainers/hypervisor.go | 8 ++++ src/runtime/virtcontainers/qemu_amd64.go | 42 +++++++++++++------ 6 files changed, 79 insertions(+), 13 deletions(-) diff --git a/src/runtime/config/configuration-qemu-nvidia-gpu-snp.toml.in b/src/runtime/config/configuration-qemu-nvidia-gpu-snp.toml.in index 223f7f9186..8501ffbd0e 100644 --- a/src/runtime/config/configuration-qemu-nvidia-gpu-snp.toml.in +++ b/src/runtime/config/configuration-qemu-nvidia-gpu-snp.toml.in @@ -60,6 +60,18 @@ enable_annotations = @DEFENABLEANNOTATIONS@ # Your distribution recommends: @QEMUVALIDHYPERVISORPATHS@ valid_hypervisor_paths = @QEMUSNPVALIDHYPERVISORPATHS@ +# SNP 'ID Block' and 'ID Authentication Information Structure'. +# If one of snp_id_block or snp_id_auth is specified, the other must be specified, too. +# Notice that the default SNP policy of QEMU (0x30000) is used by Kata, and the IDBlock +# must be generated with exactly this policy. +# +# 96-byte, base64-encoded blob to provide the ‘ID Block’ structure for the +# SNP_LAUNCH_FINISH command defined in the SEV-SNP firmware ABI (QEMU default: all-zero) +#snp_id_block = "" +# 4096-byte, base64-encoded blob to provide the ‘ID Authentication Information Structure’ +# for the SNP_LAUNCH_FINISH command defined in the SEV-SNP firmware ABI (QEMU default: all-zero) +#snp_id_auth = "" + # Optional space-separated list of options to pass to the guest kernel. # For example, use `kernel_params = "vsyscall=emulate"` if you are having # trouble running pre-2.15 glibc. diff --git a/src/runtime/config/configuration-qemu-snp.toml.in b/src/runtime/config/configuration-qemu-snp.toml.in index 46c537971a..dcfb809440 100644 --- a/src/runtime/config/configuration-qemu-snp.toml.in +++ b/src/runtime/config/configuration-qemu-snp.toml.in @@ -60,6 +60,18 @@ enable_annotations = @DEFENABLEANNOTATIONS@ # Your distribution recommends: @QEMUVALIDHYPERVISORPATHS@ valid_hypervisor_paths = @QEMUVALIDHYPERVISORPATHS@ +# SNP 'ID Block' and 'ID Authentication Information Structure'. +# If one of snp_id_block or snp_id_auth is specified, the other must be specified, too. +# Notice that the default SNP policy of QEMU (0x30000) is used by Kata, and the IDBlock +# must be generated with exactly this policy. +# +# 96-byte, base64-encoded blob to provide the ‘ID Block’ structure for the +# SNP_LAUNCH_FINISH command defined in the SEV-SNP firmware ABI (QEMU default: all-zero) +#snp_id_block = "" +# 4096-byte, base64-encoded blob to provide the ‘ID Authentication Information Structure’ +# for the SNP_LAUNCH_FINISH command defined in the SEV-SNP firmware ABI (QEMU default: all-zero) +#snp_id_auth = "" + # Optional space-separated list of options to pass to the guest kernel. # For example, use `kernel_params = "vsyscall=emulate"` if you are having # trouble running pre-2.15 glibc. diff --git a/src/runtime/pkg/govmm/qemu/qemu.go b/src/runtime/pkg/govmm/qemu/qemu.go index 28b129921a..4ff4333f8c 100644 --- a/src/runtime/pkg/govmm/qemu/qemu.go +++ b/src/runtime/pkg/govmm/qemu/qemu.go @@ -318,6 +318,14 @@ type Object struct { // QgsPort defines Intel Quote Generation Service port exposed from the host QgsPort uint32 + + // SnpIdBlock is the 96-byte, base64-encoded blob to provide the ‘ID Block’ structure + // for the SNP_LAUNCH_FINISH command defined in the SEV-SNP firmware ABI (default: all-zero) + SnpIdBlock string + + // SnpIdAuth is the 4096-byte, base64-encoded blob to provide the ‘ID Authentication Information Structure’ + // for the SNP_LAUNCH_FINISH command defined in the SEV-SNP firmware ABI (default: all-zero) + SnpIdAuth string } // Valid returns true if the Object structure is valid and complete. @@ -389,6 +397,12 @@ func (object Object) QemuParams(config *Config) []string { objectParams = append(objectParams, fmt.Sprintf("cbitpos=%d", object.CBitPos)) objectParams = append(objectParams, fmt.Sprintf("reduced-phys-bits=%d", object.ReducedPhysBits)) objectParams = append(objectParams, "kernel-hashes=on") + if object.SnpIdBlock != "" { + objectParams = append(objectParams, fmt.Sprintf("id-block=%s", object.SnpIdBlock)) + } + if object.SnpIdAuth != "" { + objectParams = append(objectParams, fmt.Sprintf("id-auth=%s", object.SnpIdAuth)) + } config.Bios = object.File case SecExecGuest: objectParams = append(objectParams, string(object.Type)) diff --git a/src/runtime/pkg/katautils/config.go b/src/runtime/pkg/katautils/config.go index b2590c88e6..79665e2593 100644 --- a/src/runtime/pkg/katautils/config.go +++ b/src/runtime/pkg/katautils/config.go @@ -107,6 +107,8 @@ type hypervisor struct { SeccompSandbox string `toml:"seccompsandbox"` BlockDeviceAIO string `toml:"block_device_aio"` RemoteHypervisorSocket string `toml:"remote_hypervisor_socket"` + SnpIdBlock string `toml:"snp_id_block"` + SnpIdAuth string `toml:"snp_id_auth"` HypervisorPathList []string `toml:"valid_hypervisor_paths"` JailerPathList []string `toml:"valid_jailer_paths"` VirtioFSDaemonList []string `toml:"valid_virtio_fs_daemon_paths"` @@ -986,6 +988,8 @@ func newQemuHypervisorConfig(h hypervisor) (vc.HypervisorConfig, error) { DisableSeLinux: h.DisableSeLinux, DisableGuestSeLinux: h.DisableGuestSeLinux, ExtraMonitorSocket: extraMonitorSocket, + SnpIdBlock: h.SnpIdBlock, + SnpIdAuth: h.SnpIdAuth, }, nil } diff --git a/src/runtime/virtcontainers/hypervisor.go b/src/runtime/virtcontainers/hypervisor.go index 8297301645..b6b75d5499 100644 --- a/src/runtime/virtcontainers/hypervisor.go +++ b/src/runtime/virtcontainers/hypervisor.go @@ -461,6 +461,14 @@ type HypervisorConfig struct { // The user maps to the uid. User string + // SnpIdBlock is the 96-byte, base64-encoded blob to provide the ‘ID Block’ structure + // for the SNP_LAUNCH_FINISH command defined in the SEV-SNP firmware ABI (default: all-zero) + SnpIdBlock string + + // SnpIdAuth is the 4096-byte, base64-encoded blob to provide the ‘ID Authentication Information Structure’ + // for the SNP_LAUNCH_FINISH command defined in the SEV-SNP firmware ABI (default: all-zero) + SnpIdAuth string + // KernelParams are additional guest kernel parameters. KernelParams []Param diff --git a/src/runtime/virtcontainers/qemu_amd64.go b/src/runtime/virtcontainers/qemu_amd64.go index ade7356eb6..dd0a929df0 100644 --- a/src/runtime/virtcontainers/qemu_amd64.go +++ b/src/runtime/virtcontainers/qemu_amd64.go @@ -13,6 +13,7 @@ import ( "time" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types" + "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/intel-go/cpuid" @@ -33,6 +34,10 @@ type qemuAmd64 struct { sgxEPCSize int64 qgsPort uint32 + + snpIdBlock string + + snpIdAuth string } const ( @@ -125,9 +130,11 @@ func newQemuArch(config HypervisorConfig) (qemuArch, error) { protection: noneProtection, legacySerial: config.LegacySerial, }, - vmFactory: factory, - snpGuest: config.SevSnpGuest, - qgsPort: config.QgsPort, + vmFactory: factory, + snpGuest: config.SevSnpGuest, + qgsPort: config.QgsPort, + snpIdBlock: config.SnpIdBlock, + snpIdAuth: config.SnpIdAuth, } if config.ConfidentialGuest { @@ -233,7 +240,8 @@ func (q *qemuAmd64) enableProtection() error { "machine": q.qemuMachine, "kernel-params-debug": q.kernelParamsDebug, "kernel-params-non-debug": q.kernelParamsNonDebug, - "kernel-params": q.kernelParams}) + "kernel-params": q.kernelParams, + }) switch q.protection { case tdxProtection: @@ -303,15 +311,23 @@ func (q *qemuAmd64) appendProtectionDevice(devices []govmmQemu.Device, firmware, ReducedPhysBits: 1, }), "", nil case snpProtection: - return append(devices, - govmmQemu.Object{ - Type: govmmQemu.SNPGuest, - ID: "snp", - Debug: false, - File: firmware, - CBitPos: cpuid.AMDMemEncrypt.CBitPosition, - ReducedPhysBits: 1, - }), "", nil + obj := govmmQemu.Object{ + Type: govmmQemu.SNPGuest, + ID: "snp", + Debug: false, + File: firmware, + CBitPos: cpuid.AMDMemEncrypt.CBitPosition, + ReducedPhysBits: 1, + } + if q.snpIdBlock != "" && q.snpIdAuth != "" { + obj.SnpIdBlock = q.snpIdBlock + obj.SnpIdAuth = q.snpIdAuth + } else if q.snpIdBlock != "" { + return nil, "", errors.New("specifying SNP IDBlock without SNP IDAuth is not allowed") + } else if q.snpIdAuth != "" { + return nil, "", errors.New("specifying SNP IDAuth without SNP IDBlock is not allowed") + } + return append(devices, obj), "", nil case noneProtection: return devices, firmware, nil