runtime: Add support for SEV pre-attestation

AMD SEV pre-attestation is handled by the runtime before the guest is
launched. Guest VM is started paused and the runtime communicates with a
remote keybroker service (e.g., simple-kbs) to validate the attestation
measurement and to receive launch secret. Upon validation, the launch
secret is injected into guest memory and the VM is started.

Fixes: #4280
Signed-off-by: Jim Cadden <jcadden@ibm.com>
Signed-off-by: Tobin Feldman-Fitzthum <tobin@ibm.com>
Signed-off-by: Dov Murik <dovmurik@linux.ibm.com>
This commit is contained in:
Jim Cadden
2022-07-18 20:04:37 -04:00
parent a51164f314
commit a87698fe56
22 changed files with 776 additions and 552 deletions

View File

@@ -283,6 +283,22 @@ type Object struct {
// Prealloc enables memory preallocation
Prealloc bool
// SevPolicy is the policy for the SEV instance. For more info, see AMD document 55766
// This is only relevant for sev-guest objects
SevPolicy uint32
// SevCertFilePath is the path to the guest DiffieHellman key
// This is only relevant for sev-guest objects
SevCertFilePath string
// SevSessionFilePath is the path to the launch blog
// This is only relevant for sev-guest objects
SevSessionFilePath string
// SevKernelHashes specifies whether the hashes of the kernel, initrd, & cmdline are included in the measurement
// This is only relevant for sev-guest objects
SevKernelHashes bool
}
// Valid returns true if the Object structure is valid and complete.
@@ -353,7 +369,17 @@ func (object Object) QemuParams(config *Config) []string {
objectParams = append(objectParams, fmt.Sprintf("id=%s", object.ID))
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("policy=%d", object.SevPolicy))
if object.SevCertFilePath != "" {
objectParams = append(objectParams, fmt.Sprintf("dh-cert-file=%s", object.SevCertFilePath))
}
if object.SevSessionFilePath != "" {
objectParams = append(objectParams, fmt.Sprintf("session-file=%s", object.SevSessionFilePath))
}
if object.SevKernelHashes {
objectParams = append(objectParams, "kernel-hashes=on")
}
// Add OVMF firmware as pflash drive
driveParams = append(driveParams, "if=pflash,format=raw,readonly=on")
driveParams = append(driveParams, fmt.Sprintf("file=%s", object.File))
case SecExecGuest:

View File

@@ -257,6 +257,22 @@ type StatusInfo struct {
Status string `json:"status"`
}
// SEVInfo represents the SEV guest information
type SEVInfo struct {
State string `json:"state"`
Enabled bool `json:"enabled"`
APIMajor uint32 `json:"api-major"`
APIMinor uint32 `json:"api-minor"`
BuildId uint32 `json:"build-id"`
Policy uint32 `json:"policy"`
Handle uint32 `json:"handle"`
}
// SEVLaunchMeasurement represents the SEV prelaunch measurement
type SEVLaunchMeasurement struct {
Measurement string `json:"data"`
}
func (q *QMP) readLoop(fromVMCh chan<- []byte) {
scanner := bufio.NewScanner(q.conn)
if q.cfg.MaxCapacity > 0 {
@@ -1661,3 +1677,53 @@ func (q *QMP) ExecuteDumpGuestMemory(ctx context.Context, protocol string, pagin
return q.executeCommand(ctx, "dump-guest-memory", args, nil)
}
// ExecuteQuerySEV queries SEV hardware details
func (q *QMP) ExecuteQuerySEV(ctx context.Context) (SEVInfo, error) {
response, err := q.executeCommandWithResponse(ctx, "query-sev", nil, nil, nil)
if err != nil {
return SEVInfo{}, err
}
data, err := json.Marshal(response)
if err != nil {
return SEVInfo{}, fmt.Errorf("unable to extract SEV information: %v", err)
}
var info SEVInfo
if err = json.Unmarshal(data, &info); err != nil {
return SEVInfo{}, fmt.Errorf("unable to convert SEV information: %v", err)
}
return info, nil
}
// ExecuteQuerySEVLaunchMeasure queries SEV launch measurement
func (q *QMP) ExecuteQuerySEVLaunchMeasure(ctx context.Context) (SEVLaunchMeasurement, error) {
response, err := q.executeCommandWithResponse(ctx, "query-sev-launch-measure", nil, nil, nil)
if err != nil {
return SEVLaunchMeasurement{}, err
}
data, err := json.Marshal(response)
if err != nil {
return SEVLaunchMeasurement{}, fmt.Errorf("unable to extract launch measurement: %v", err)
}
var measurement SEVLaunchMeasurement
if err = json.Unmarshal(data, &measurement); err != nil {
return SEVLaunchMeasurement{}, fmt.Errorf("unable to convert launch measurement: %v", err)
}
return measurement, nil
}
// ExecuteSEVInjectLaunchSecret injects launch secret bundle into SEV guest
func (q *QMP) ExecuteSEVInjectLaunchSecret(ctx context.Context, packetHeader string, secret string) error {
args := map[string]interface{}{
"packet-header": packetHeader,
"secret": secret,
}
return q.executeCommand(ctx, "sev-inject-launch-secret", args, nil)
}

View File

@@ -90,6 +90,13 @@ const defaultRootlessHypervisor = false
const defaultDisableSeccomp = false
const defaultVfioMode = "guest-kernel"
const defaultLegacySerial = false
const defaultGuestPreAttestation = false
const defaultGuestPreAttestationProxy string = ""
const defaultGuestPreAttestationKeyset string = ""
const defaultGuestPreAttestationSecretGuid string = ""
const defaultGuestPreAttestationSecretType string = ""
const defaultSEVCertChainPath string = ""
const defaultSEVGuestPolicy uint32 = 0
var defaultSGXEPCSize = int64(0)

View File

@@ -99,6 +99,11 @@ type hypervisor struct {
GuestHookPath string `toml:"guest_hook_path"`
GuestMemoryDumpPath string `toml:"guest_memory_dump_path"`
SeccompSandbox string `toml:"seccompsandbox"`
GuestPreAttestationProxy string `toml:"guest_pre_attestation_proxy"`
GuestPreAttestationKeyset string `toml:"guest_pre_attestation_keyset"`
GuestPreAttestationSecretGuid string `toml:"guest_pre_attestation_secret_guid"`
GuestPreAttestationSecretType string `toml:"guest_pre_attestation_secret_type"`
SEVCertChainPath string `toml:"sev_cert_chain"`
HypervisorPathList []string `toml:"valid_hypervisor_paths"`
JailerPathList []string `toml:"valid_jailer_paths"`
CtlPathList []string `toml:"valid_ctlpaths"`
@@ -128,6 +133,8 @@ type hypervisor struct {
DefaultBridges uint32 `toml:"default_bridges"`
Msize9p uint32 `toml:"msize_9p"`
PCIeRootPort uint32 `toml:"pcie_root_port"`
GuestPreAttestationGRPCTimeout uint32 `toml:"guest_pre_attestation_grpc_timeout"`
SEVGuestPolicy uint32 `toml:"sev_guest_policy"`
NumVCPUs int32 `toml:"default_vcpus"`
BlockDeviceCacheSet bool `toml:"block_device_cache_set"`
BlockDeviceCacheDirect bool `toml:"block_device_cache_direct"`
@@ -152,6 +159,7 @@ type hypervisor struct {
DisableSeccomp bool `toml:"disable_seccomp"`
DisableSeLinux bool `toml:"disable_selinux"`
LegacySerial bool `toml:"use_legacy_serial"`
GuestPreAttestation bool `toml:"guest_pre_attestation"`
}
type runtime struct {
@@ -745,68 +753,75 @@ func newQemuHypervisorConfig(h hypervisor) (vc.HypervisorConfig, error) {
txRateLimiterMaxRate := h.getTxRateLimiterCfg()
return vc.HypervisorConfig{
HypervisorPath: hypervisor,
HypervisorPathList: h.HypervisorPathList,
KernelPath: kernel,
InitrdPath: initrd,
ImagePath: image,
FirmwarePath: firmware,
FirmwareVolumePath: firmwareVolume,
PFlash: pflashes,
MachineAccelerators: machineAccelerators,
CPUFeatures: cpuFeatures,
KernelParams: vc.DeserializeParams(strings.Fields(kernelParams)),
HypervisorMachineType: machineType,
NumVCPUs: h.defaultVCPUs(),
DefaultMaxVCPUs: h.defaultMaxVCPUs(),
MemorySize: h.defaultMemSz(),
MemSlots: h.defaultMemSlots(),
MemOffset: h.defaultMemOffset(),
DefaultMaxMemorySize: h.defaultMaxMemSz(),
VirtioMem: h.VirtioMem,
EntropySource: h.GetEntropySource(),
EntropySourceList: h.EntropySourceList,
DefaultBridges: h.defaultBridges(),
DisableBlockDeviceUse: h.DisableBlockDeviceUse,
SharedFS: sharedFS,
VirtioFSDaemon: h.VirtioFSDaemon,
VirtioFSDaemonList: h.VirtioFSDaemonList,
VirtioFSCacheSize: h.VirtioFSCacheSize,
VirtioFSCache: h.defaultVirtioFSCache(),
VirtioFSExtraArgs: h.VirtioFSExtraArgs,
MemPrealloc: h.MemPrealloc,
HugePages: h.HugePages,
IOMMU: h.IOMMU,
IOMMUPlatform: h.getIOMMUPlatform(),
FileBackedMemRootDir: h.FileBackedMemRootDir,
FileBackedMemRootList: h.FileBackedMemRootList,
Debug: h.Debug,
DisableNestingChecks: h.DisableNestingChecks,
BlockDeviceDriver: blockDriver,
BlockDeviceCacheSet: h.BlockDeviceCacheSet,
BlockDeviceCacheDirect: h.BlockDeviceCacheDirect,
BlockDeviceCacheNoflush: h.BlockDeviceCacheNoflush,
EnableIOThreads: h.EnableIOThreads,
Msize9p: h.msize9p(),
DisableImageNvdimm: h.DisableImageNvdimm,
HotplugVFIOOnRootBus: h.HotplugVFIOOnRootBus,
PCIeRootPort: h.PCIeRootPort,
DisableVhostNet: h.DisableVhostNet,
EnableVhostUserStore: h.EnableVhostUserStore,
VhostUserStorePath: h.vhostUserStorePath(),
VhostUserStorePathList: h.VhostUserStorePathList,
SeccompSandbox: h.SeccompSandbox,
GuestHookPath: h.guestHookPath(),
RxRateLimiterMaxRate: rxRateLimiterMaxRate,
TxRateLimiterMaxRate: txRateLimiterMaxRate,
EnableAnnotations: h.EnableAnnotations,
GuestMemoryDumpPath: h.GuestMemoryDumpPath,
GuestMemoryDumpPaging: h.GuestMemoryDumpPaging,
ConfidentialGuest: h.ConfidentialGuest,
GuestSwap: h.GuestSwap,
Rootless: h.Rootless,
LegacySerial: h.LegacySerial,
DisableSeLinux: h.DisableSeLinux,
HypervisorPath: hypervisor,
HypervisorPathList: h.HypervisorPathList,
KernelPath: kernel,
InitrdPath: initrd,
ImagePath: image,
FirmwarePath: firmware,
FirmwareVolumePath: firmwareVolume,
PFlash: pflashes,
MachineAccelerators: machineAccelerators,
CPUFeatures: cpuFeatures,
KernelParams: vc.DeserializeParams(strings.Fields(kernelParams)),
HypervisorMachineType: machineType,
NumVCPUs: h.defaultVCPUs(),
DefaultMaxVCPUs: h.defaultMaxVCPUs(),
MemorySize: h.defaultMemSz(),
MemSlots: h.defaultMemSlots(),
MemOffset: h.defaultMemOffset(),
DefaultMaxMemorySize: h.defaultMaxMemSz(),
VirtioMem: h.VirtioMem,
EntropySource: h.GetEntropySource(),
EntropySourceList: h.EntropySourceList,
DefaultBridges: h.defaultBridges(),
DisableBlockDeviceUse: h.DisableBlockDeviceUse,
SharedFS: sharedFS,
VirtioFSDaemon: h.VirtioFSDaemon,
VirtioFSDaemonList: h.VirtioFSDaemonList,
VirtioFSCacheSize: h.VirtioFSCacheSize,
VirtioFSCache: h.defaultVirtioFSCache(),
VirtioFSExtraArgs: h.VirtioFSExtraArgs,
MemPrealloc: h.MemPrealloc,
HugePages: h.HugePages,
IOMMU: h.IOMMU,
IOMMUPlatform: h.getIOMMUPlatform(),
FileBackedMemRootDir: h.FileBackedMemRootDir,
FileBackedMemRootList: h.FileBackedMemRootList,
Debug: h.Debug,
DisableNestingChecks: h.DisableNestingChecks,
BlockDeviceDriver: blockDriver,
BlockDeviceCacheSet: h.BlockDeviceCacheSet,
BlockDeviceCacheDirect: h.BlockDeviceCacheDirect,
BlockDeviceCacheNoflush: h.BlockDeviceCacheNoflush,
EnableIOThreads: h.EnableIOThreads,
Msize9p: h.msize9p(),
DisableImageNvdimm: h.DisableImageNvdimm,
HotplugVFIOOnRootBus: h.HotplugVFIOOnRootBus,
PCIeRootPort: h.PCIeRootPort,
DisableVhostNet: h.DisableVhostNet,
EnableVhostUserStore: h.EnableVhostUserStore,
VhostUserStorePath: h.vhostUserStorePath(),
VhostUserStorePathList: h.VhostUserStorePathList,
SeccompSandbox: h.SeccompSandbox,
GuestHookPath: h.guestHookPath(),
RxRateLimiterMaxRate: rxRateLimiterMaxRate,
TxRateLimiterMaxRate: txRateLimiterMaxRate,
EnableAnnotations: h.EnableAnnotations,
GuestMemoryDumpPath: h.GuestMemoryDumpPath,
GuestMemoryDumpPaging: h.GuestMemoryDumpPaging,
ConfidentialGuest: h.ConfidentialGuest,
GuestSwap: h.GuestSwap,
Rootless: h.Rootless,
LegacySerial: h.LegacySerial,
DisableSeLinux: h.DisableSeLinux,
GuestPreAttestation: h.GuestPreAttestation,
GuestPreAttestationProxy: h.GuestPreAttestationProxy,
GuestPreAttestationKeyset: h.GuestPreAttestationKeyset,
GuestPreAttestationSecretGuid: h.GuestPreAttestationSecretGuid,
GuestPreAttestationSecretType: h.GuestPreAttestationSecretType,
SEVGuestPolicy: h.SEVGuestPolicy,
SEVCertChainPath: h.SEVCertChainPath,
}, nil
}
@@ -1129,50 +1144,57 @@ func updateRuntimeConfig(configPath string, tomlConf tomlConfig, config *oci.Run
func GetDefaultHypervisorConfig() vc.HypervisorConfig {
return vc.HypervisorConfig{
HypervisorPath: defaultHypervisorPath,
JailerPath: defaultJailerPath,
KernelPath: defaultKernelPath,
ImagePath: defaultImagePath,
InitrdPath: defaultInitrdPath,
FirmwarePath: defaultFirmwarePath,
FirmwareVolumePath: defaultFirmwareVolumePath,
MachineAccelerators: defaultMachineAccelerators,
CPUFeatures: defaultCPUFeatures,
HypervisorMachineType: defaultMachineType,
NumVCPUs: defaultVCPUCount,
DefaultMaxVCPUs: defaultMaxVCPUCount,
MemorySize: defaultMemSize,
MemOffset: defaultMemOffset,
VirtioMem: defaultVirtioMem,
DisableBlockDeviceUse: defaultDisableBlockDeviceUse,
DefaultBridges: defaultBridgesCount,
MemPrealloc: defaultEnableMemPrealloc,
HugePages: defaultEnableHugePages,
IOMMU: defaultEnableIOMMU,
IOMMUPlatform: defaultEnableIOMMUPlatform,
FileBackedMemRootDir: defaultFileBackedMemRootDir,
Debug: defaultEnableDebug,
DisableNestingChecks: defaultDisableNestingChecks,
BlockDeviceDriver: defaultBlockDeviceDriver,
BlockDeviceCacheSet: defaultBlockDeviceCacheSet,
BlockDeviceCacheDirect: defaultBlockDeviceCacheDirect,
BlockDeviceCacheNoflush: defaultBlockDeviceCacheNoflush,
EnableIOThreads: defaultEnableIOThreads,
Msize9p: defaultMsize9p,
HotplugVFIOOnRootBus: defaultHotplugVFIOOnRootBus,
PCIeRootPort: defaultPCIeRootPort,
GuestHookPath: defaultGuestHookPath,
VhostUserStorePath: defaultVhostUserStorePath,
VirtioFSCache: defaultVirtioFSCacheMode,
DisableImageNvdimm: defaultDisableImageNvdimm,
RxRateLimiterMaxRate: defaultRxRateLimiterMaxRate,
TxRateLimiterMaxRate: defaultTxRateLimiterMaxRate,
SGXEPCSize: defaultSGXEPCSize,
ConfidentialGuest: defaultConfidentialGuest,
GuestSwap: defaultGuestSwap,
Rootless: defaultRootlessHypervisor,
DisableSeccomp: defaultDisableSeccomp,
LegacySerial: defaultLegacySerial,
HypervisorPath: defaultHypervisorPath,
JailerPath: defaultJailerPath,
KernelPath: defaultKernelPath,
ImagePath: defaultImagePath,
InitrdPath: defaultInitrdPath,
FirmwarePath: defaultFirmwarePath,
FirmwareVolumePath: defaultFirmwareVolumePath,
MachineAccelerators: defaultMachineAccelerators,
CPUFeatures: defaultCPUFeatures,
HypervisorMachineType: defaultMachineType,
NumVCPUs: defaultVCPUCount,
DefaultMaxVCPUs: defaultMaxVCPUCount,
MemorySize: defaultMemSize,
MemOffset: defaultMemOffset,
VirtioMem: defaultVirtioMem,
DisableBlockDeviceUse: defaultDisableBlockDeviceUse,
DefaultBridges: defaultBridgesCount,
MemPrealloc: defaultEnableMemPrealloc,
HugePages: defaultEnableHugePages,
IOMMU: defaultEnableIOMMU,
IOMMUPlatform: defaultEnableIOMMUPlatform,
FileBackedMemRootDir: defaultFileBackedMemRootDir,
Debug: defaultEnableDebug,
DisableNestingChecks: defaultDisableNestingChecks,
BlockDeviceDriver: defaultBlockDeviceDriver,
BlockDeviceCacheSet: defaultBlockDeviceCacheSet,
BlockDeviceCacheDirect: defaultBlockDeviceCacheDirect,
BlockDeviceCacheNoflush: defaultBlockDeviceCacheNoflush,
EnableIOThreads: defaultEnableIOThreads,
Msize9p: defaultMsize9p,
HotplugVFIOOnRootBus: defaultHotplugVFIOOnRootBus,
PCIeRootPort: defaultPCIeRootPort,
GuestHookPath: defaultGuestHookPath,
VhostUserStorePath: defaultVhostUserStorePath,
VirtioFSCache: defaultVirtioFSCacheMode,
DisableImageNvdimm: defaultDisableImageNvdimm,
RxRateLimiterMaxRate: defaultRxRateLimiterMaxRate,
TxRateLimiterMaxRate: defaultTxRateLimiterMaxRate,
SGXEPCSize: defaultSGXEPCSize,
ConfidentialGuest: defaultConfidentialGuest,
GuestSwap: defaultGuestSwap,
Rootless: defaultRootlessHypervisor,
DisableSeccomp: defaultDisableSeccomp,
LegacySerial: defaultLegacySerial,
GuestPreAttestation: defaultGuestPreAttestation,
GuestPreAttestationProxy: defaultGuestPreAttestationProxy,
GuestPreAttestationKeyset: defaultGuestPreAttestationKeyset,
GuestPreAttestationSecretGuid: defaultGuestPreAttestationSecretGuid,
GuestPreAttestationSecretType: defaultGuestPreAttestationSecretType,
SEVGuestPolicy: defaultSEVGuestPolicy,
SEVCertChainPath: defaultSEVCertChainPath,
}
}

View File

@@ -1205,8 +1205,8 @@ func TestNewMount(t *testing.T) {
assert := assert.New(t)
testCases := []struct {
out vc.Mount
in specs.Mount
out vc.Mount
}{
{
in: specs.Mount{

View File

@@ -16,14 +16,17 @@ import (
)
type GuestPreAttestationConfig struct {
Proxy string
Policy uint32
Keyset string
LaunchId string
KernelPath string
InitrdPath string
FwPath string
KernelParameters string
Proxy string
Keyset string
LaunchId string
KernelPath string
InitrdPath string
FwPath string
KernelParameters string
CertChainPath string
KeyBrokerSecretType string
KeyBrokerSecretGuid string
Policy uint32
}
type guidLE [16]byte