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 <katexochen0@gmail.com>
This commit is contained in:
Paul Meyer
2025-02-11 15:21:01 +01:00
parent 810a6dafad
commit a994f142d0
6 changed files with 79 additions and 13 deletions

View File

@@ -60,6 +60,18 @@ enable_annotations = @DEFENABLEANNOTATIONS@
# Your distribution recommends: @QEMUVALIDHYPERVISORPATHS@ # Your distribution recommends: @QEMUVALIDHYPERVISORPATHS@
valid_hypervisor_paths = @QEMUSNPVALIDHYPERVISORPATHS@ 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. # Optional space-separated list of options to pass to the guest kernel.
# For example, use `kernel_params = "vsyscall=emulate"` if you are having # For example, use `kernel_params = "vsyscall=emulate"` if you are having
# trouble running pre-2.15 glibc. # trouble running pre-2.15 glibc.

View File

@@ -60,6 +60,18 @@ enable_annotations = @DEFENABLEANNOTATIONS@
# Your distribution recommends: @QEMUVALIDHYPERVISORPATHS@ # Your distribution recommends: @QEMUVALIDHYPERVISORPATHS@
valid_hypervisor_paths = @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. # Optional space-separated list of options to pass to the guest kernel.
# For example, use `kernel_params = "vsyscall=emulate"` if you are having # For example, use `kernel_params = "vsyscall=emulate"` if you are having
# trouble running pre-2.15 glibc. # trouble running pre-2.15 glibc.

View File

@@ -318,6 +318,14 @@ type Object struct {
// QgsPort defines Intel Quote Generation Service port exposed from the host // QgsPort defines Intel Quote Generation Service port exposed from the host
QgsPort uint32 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. // 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("cbitpos=%d", object.CBitPos))
objectParams = append(objectParams, fmt.Sprintf("reduced-phys-bits=%d", object.ReducedPhysBits)) objectParams = append(objectParams, fmt.Sprintf("reduced-phys-bits=%d", object.ReducedPhysBits))
objectParams = append(objectParams, "kernel-hashes=on") 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 config.Bios = object.File
case SecExecGuest: case SecExecGuest:
objectParams = append(objectParams, string(object.Type)) objectParams = append(objectParams, string(object.Type))

View File

@@ -107,6 +107,8 @@ type hypervisor struct {
SeccompSandbox string `toml:"seccompsandbox"` SeccompSandbox string `toml:"seccompsandbox"`
BlockDeviceAIO string `toml:"block_device_aio"` BlockDeviceAIO string `toml:"block_device_aio"`
RemoteHypervisorSocket string `toml:"remote_hypervisor_socket"` RemoteHypervisorSocket string `toml:"remote_hypervisor_socket"`
SnpIdBlock string `toml:"snp_id_block"`
SnpIdAuth string `toml:"snp_id_auth"`
HypervisorPathList []string `toml:"valid_hypervisor_paths"` HypervisorPathList []string `toml:"valid_hypervisor_paths"`
JailerPathList []string `toml:"valid_jailer_paths"` JailerPathList []string `toml:"valid_jailer_paths"`
VirtioFSDaemonList []string `toml:"valid_virtio_fs_daemon_paths"` VirtioFSDaemonList []string `toml:"valid_virtio_fs_daemon_paths"`
@@ -986,6 +988,8 @@ func newQemuHypervisorConfig(h hypervisor) (vc.HypervisorConfig, error) {
DisableSeLinux: h.DisableSeLinux, DisableSeLinux: h.DisableSeLinux,
DisableGuestSeLinux: h.DisableGuestSeLinux, DisableGuestSeLinux: h.DisableGuestSeLinux,
ExtraMonitorSocket: extraMonitorSocket, ExtraMonitorSocket: extraMonitorSocket,
SnpIdBlock: h.SnpIdBlock,
SnpIdAuth: h.SnpIdAuth,
}, nil }, nil
} }

View File

@@ -461,6 +461,14 @@ type HypervisorConfig struct {
// The user maps to the uid. // The user maps to the uid.
User string 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 are additional guest kernel parameters.
KernelParams []Param KernelParams []Param

View File

@@ -13,6 +13,7 @@ import (
"time" "time"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types" "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
"github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/intel-go/cpuid" "github.com/intel-go/cpuid"
@@ -33,6 +34,10 @@ type qemuAmd64 struct {
sgxEPCSize int64 sgxEPCSize int64
qgsPort uint32 qgsPort uint32
snpIdBlock string
snpIdAuth string
} }
const ( const (
@@ -125,9 +130,11 @@ func newQemuArch(config HypervisorConfig) (qemuArch, error) {
protection: noneProtection, protection: noneProtection,
legacySerial: config.LegacySerial, legacySerial: config.LegacySerial,
}, },
vmFactory: factory, vmFactory: factory,
snpGuest: config.SevSnpGuest, snpGuest: config.SevSnpGuest,
qgsPort: config.QgsPort, qgsPort: config.QgsPort,
snpIdBlock: config.SnpIdBlock,
snpIdAuth: config.SnpIdAuth,
} }
if config.ConfidentialGuest { if config.ConfidentialGuest {
@@ -233,7 +240,8 @@ func (q *qemuAmd64) enableProtection() error {
"machine": q.qemuMachine, "machine": q.qemuMachine,
"kernel-params-debug": q.kernelParamsDebug, "kernel-params-debug": q.kernelParamsDebug,
"kernel-params-non-debug": q.kernelParamsNonDebug, "kernel-params-non-debug": q.kernelParamsNonDebug,
"kernel-params": q.kernelParams}) "kernel-params": q.kernelParams,
})
switch q.protection { switch q.protection {
case tdxProtection: case tdxProtection:
@@ -303,15 +311,23 @@ func (q *qemuAmd64) appendProtectionDevice(devices []govmmQemu.Device, firmware,
ReducedPhysBits: 1, ReducedPhysBits: 1,
}), "", nil }), "", nil
case snpProtection: case snpProtection:
return append(devices, obj := govmmQemu.Object{
govmmQemu.Object{ Type: govmmQemu.SNPGuest,
Type: govmmQemu.SNPGuest, ID: "snp",
ID: "snp", Debug: false,
Debug: false, File: firmware,
File: firmware, CBitPos: cpuid.AMDMemEncrypt.CBitPosition,
CBitPos: cpuid.AMDMemEncrypt.CBitPosition, ReducedPhysBits: 1,
ReducedPhysBits: 1, }
}), "", nil 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: case noneProtection:
return devices, firmware, nil return devices, firmware, nil