runtime: Enable connection to Quote Generation Service (QGS)

For the TD attestation to work the connection to QGS on the host is needed.
By default QGS runs on vsock port 4050, but can be modified by the host owner.
Format of the qemu object follows the SocketAddress structure, so it needs to be provided in the JSON format, as in the example below:
-object '{"qom-type":"tdx-guest","id":"tdx","quote-generation-socket":{"type":"vsock","cid":"2","port":"4050"}}'

Fixes: #9497
Signed-off-by: Jakub Ledworowski <jakub.ledworowski@intel.com>
This commit is contained in:
Jakub Ledworowski 2024-05-16 12:37:23 +02:00
parent 0331859740
commit fc680139e5
8 changed files with 80 additions and 6 deletions

View File

@ -179,6 +179,7 @@ QEMUVALIDHYPERVISORPATHS := [\"$(QEMUPATH)\"]
#QEMUTDXPATH := $(QEMUBINDIR)/$(QEMUTDXCMD) #QEMUTDXPATH := $(QEMUBINDIR)/$(QEMUTDXCMD)
QEMUTDXPATH := PLACEHOLDER_FOR_DISTRO_QEMU_WITH_TDX_SUPPORT QEMUTDXPATH := PLACEHOLDER_FOR_DISTRO_QEMU_WITH_TDX_SUPPORT
QEMUTDXVALIDHYPERVISORPATHS := [\"$(QEMUTDXPATH)\"] QEMUTDXVALIDHYPERVISORPATHS := [\"$(QEMUTDXPATH)\"]
QEMUTDXQUOTEGENERATIONSERVICESOCKETPORT := 4050
QEMUSNPPATH := $(QEMUBINDIR)/$(QEMUSNPCMD) QEMUSNPPATH := $(QEMUBINDIR)/$(QEMUSNPCMD)
QEMUSNPVALIDHYPERVISORPATHS := [\"$(QEMUSNPPATH)\"] QEMUSNPVALIDHYPERVISORPATHS := [\"$(QEMUSNPPATH)\"]
@ -705,6 +706,7 @@ USER_VARS += QEMUTDXCMD
USER_VARS += QEMUSNPCMD USER_VARS += QEMUSNPCMD
USER_VARS += QEMUPATH USER_VARS += QEMUPATH
USER_VARS += QEMUTDXPATH USER_VARS += QEMUTDXPATH
USER_VARS += QEMUTDXQUOTEGENERATIONSERVICESOCKETPORT
USER_VARS += QEMUSNPPATH USER_VARS += QEMUSNPPATH
USER_VARS += QEMUVALIDHYPERVISORPATHS USER_VARS += QEMUVALIDHYPERVISORPATHS
USER_VARS += QEMUTDXVALIDHYPERVISORPATHS USER_VARS += QEMUTDXVALIDHYPERVISORPATHS

View File

@ -17,6 +17,7 @@ kernel = "@KERNELCONFIDENTIALPATH@"
image = "@IMAGECONFIDENTIALPATH@" image = "@IMAGECONFIDENTIALPATH@"
# initrd = "@INITRDPATH@" # initrd = "@INITRDPATH@"
machine_type = "@MACHINETYPE@" machine_type = "@MACHINETYPE@"
tdx_quote_generation_service_socket_port = @QEMUTDXQUOTEGENERATIONSERVICESOCKETPORT@
# rootfs filesystem type: # rootfs filesystem type:
# - ext4 (default) # - ext4 (default)

View File

@ -15,6 +15,7 @@ package qemu
import ( import (
"context" "context"
"encoding/json"
"fmt" "fmt"
"io" "io"
"log" "log"
@ -44,6 +45,12 @@ const (
MachineTypeMicrovm string = "microvm" MachineTypeMicrovm string = "microvm"
) )
const (
// Well known vsock CID for host system.
// https://man7.org/linux/man-pages/man7/vsock.7.html
VsockHostCid uint64 = 2
)
// Device is the qemu device interface. // Device is the qemu device interface.
type Device interface { type Device interface {
Valid() bool Valid() bool
@ -306,6 +313,9 @@ type Object struct {
// Prealloc enables memory preallocation // Prealloc enables memory preallocation
Prealloc bool Prealloc bool
// QgsPort defines Intel Quote Generation Service port exposed from the host
QgsPort uint32
} }
// Valid returns true if the Object structure is valid and complete. // Valid returns true if the Object structure is valid and complete.
@ -316,7 +326,7 @@ func (object Object) Valid() bool {
case MemoryBackendEPC: case MemoryBackendEPC:
return object.ID != "" && object.Size != 0 return object.ID != "" && object.Size != 0
case TDXGuest: case TDXGuest:
return object.ID != "" && object.File != "" && object.DeviceID != "" return object.ID != "" && object.File != "" && object.DeviceID != "" && object.QgsPort != 0
case SEVGuest: case SEVGuest:
fallthrough fallthrough
case SNPGuest: case SNPGuest:
@ -362,11 +372,7 @@ func (object Object) QemuParams(config *Config) []string {
} }
case TDXGuest: case TDXGuest:
objectParams = append(objectParams, string(object.Type)) objectParams = append(objectParams, prepareObjectWithTdxQgs(object))
objectParams = append(objectParams, fmt.Sprintf("id=%s", object.ID))
if object.Debug {
objectParams = append(objectParams, "debug=on")
}
config.Bios = object.File config.Bios = object.File
case SEVGuest: case SEVGuest:
fallthrough fallthrough
@ -408,6 +414,52 @@ func (object Object) QemuParams(config *Config) []string {
return qemuParams return qemuParams
} }
type SocketAddress struct {
Type string `json:"type"`
Cid string `json:"cid"`
Port string `json:"port"`
}
type TdxQomObject struct {
QomType string `json:"qom-type"`
Id string `json:"id"`
QuoteGenerationSocket SocketAddress `json:"quote-generation-socket"`
Debug *bool `json:"debug,omitempty"`
}
func (this *SocketAddress) String() string {
b, err := json.Marshal(*this)
if err != nil {
log.Fatalf("Unable to marshal SocketAddress object: %s", err.Error())
return ""
}
return string(b)
}
func (this *TdxQomObject) String() string {
b, err := json.Marshal(*this)
if err != nil {
log.Fatalf("Unable to marshal TDX QOM object: %s", err.Error())
return ""
}
return string(b)
}
func prepareObjectWithTdxQgs(object Object) string {
qgsSocket := SocketAddress{"vsock", fmt.Sprint(VsockHostCid), fmt.Sprint(object.QgsPort)}
tdxObject := TdxQomObject{string(object.Type), object.ID, qgsSocket, nil}
if object.Debug {
*tdxObject.Debug = true
}
return tdxObject.String()
}
// Virtio9PMultidev filesystem behaviour to deal // Virtio9PMultidev filesystem behaviour to deal
// with multiple devices being shared with a 9p export. // with multiple devices being shared with a 9p export.
type Virtio9PMultidev string type Virtio9PMultidev string

View File

@ -58,6 +58,7 @@ var systemdUnitName = "kata-containers.target"
const defaultKernelParams = "" const defaultKernelParams = ""
const defaultMachineType = "q35" const defaultMachineType = "q35"
const defaultQgsPort = 4050
const defaultVCPUCount uint32 = 1 const defaultVCPUCount uint32 = 1
const defaultMaxVCPUCount uint32 = 0 const defaultMaxVCPUCount uint32 = 0

View File

@ -89,6 +89,7 @@ type hypervisor struct {
CPUFeatures string `toml:"cpu_features"` CPUFeatures string `toml:"cpu_features"`
KernelParams string `toml:"kernel_params"` KernelParams string `toml:"kernel_params"`
MachineType string `toml:"machine_type"` MachineType string `toml:"machine_type"`
QgsPort uint32 `toml:"tdx_quote_generation_service_socket_port"`
BlockDeviceDriver string `toml:"block_device_driver"` BlockDeviceDriver string `toml:"block_device_driver"`
EntropySource string `toml:"entropy_source"` EntropySource string `toml:"entropy_source"`
SharedFS string `toml:"shared_fs"` SharedFS string `toml:"shared_fs"`
@ -373,6 +374,14 @@ func (h hypervisor) machineType() string {
return h.MachineType return h.MachineType
} }
func (h hypervisor) qgsPort() uint32 {
if h.QgsPort == 0 {
return defaultQgsPort
}
return h.QgsPort
}
func (h hypervisor) GetEntropySource() string { func (h hypervisor) GetEntropySource() string {
if h.EntropySource == "" { if h.EntropySource == "" {
return defaultEntropySource return defaultEntropySource
@ -890,6 +899,7 @@ func newQemuHypervisorConfig(h hypervisor) (vc.HypervisorConfig, error) {
CPUFeatures: cpuFeatures, CPUFeatures: cpuFeatures,
KernelParams: vc.DeserializeParams(vc.KernelParamFields(kernelParams)), KernelParams: vc.DeserializeParams(vc.KernelParamFields(kernelParams)),
HypervisorMachineType: machineType, HypervisorMachineType: machineType,
QgsPort: h.qgsPort(),
NumVCPUsF: h.defaultVCPUs(), NumVCPUsF: h.defaultVCPUs(),
DefaultMaxVCPUs: h.defaultMaxVCPUs(), DefaultMaxVCPUs: h.defaultMaxVCPUs(),
MemorySize: h.defaultMemSz(), MemorySize: h.defaultMemSz(),

View File

@ -182,6 +182,7 @@ func createAllRuntimeConfigFiles(dir, hypervisor string) (testConfig testRuntime
VirtioFSCache: defaultVirtioFSCacheMode, VirtioFSCache: defaultVirtioFSCacheMode,
PFlash: []string{}, PFlash: []string{},
SGXEPCSize: epcSize, SGXEPCSize: epcSize,
QgsPort: defaultQgsPort,
} }
if goruntime.GOARCH == "arm64" && len(hypervisorConfig.PFlash) == 0 && hypervisorConfig.FirmwarePath == "" { if goruntime.GOARCH == "arm64" && len(hypervisorConfig.PFlash) == 0 && hypervisorConfig.FirmwarePath == "" {

View File

@ -671,6 +671,9 @@ type HypervisorConfig struct {
// ExtraMonitorSocket allows to add an extra HMP or QMP socket when the VMM is Qemu // ExtraMonitorSocket allows to add an extra HMP or QMP socket when the VMM is Qemu
ExtraMonitorSocket govmmQemu.MonitorProtocol ExtraMonitorSocket govmmQemu.MonitorProtocol
// QgsPort defines Intel Quote Generation Service port exposed from the host
QgsPort uint32
} }
// vcpu mapping from vcpu number to thread number // vcpu mapping from vcpu number to thread number

View File

@ -31,6 +31,8 @@ type qemuAmd64 struct {
devLoadersCount uint32 devLoadersCount uint32
sgxEPCSize int64 sgxEPCSize int64
qgsPort uint32
} }
const ( const (
@ -125,6 +127,7 @@ func newQemuArch(config HypervisorConfig) (qemuArch, error) {
}, },
vmFactory: factory, vmFactory: factory,
snpGuest: config.SevSnpGuest, snpGuest: config.SevSnpGuest,
qgsPort: config.QgsPort,
} }
if config.ConfidentialGuest { if config.ConfidentialGuest {
@ -282,6 +285,7 @@ func (q *qemuAmd64) appendProtectionDevice(devices []govmmQemu.Device, firmware,
govmmQemu.Object{ govmmQemu.Object{
Driver: govmmQemu.Loader, Driver: govmmQemu.Loader,
Type: govmmQemu.TDXGuest, Type: govmmQemu.TDXGuest,
QgsPort: q.qgsPort,
ID: "tdx", ID: "tdx",
DeviceID: fmt.Sprintf("fd%d", id), DeviceID: fmt.Sprintf("fd%d", id),
Debug: false, Debug: false,