Merge pull request #12436 from mythi/tdx-updates-2026-3

runtime(-rs): tdx: use TDX QGS via unix-domain-socket by default
This commit is contained in:
Fabiano Fidêncio
2026-06-03 08:50:26 +02:00
committed by GitHub
7 changed files with 103 additions and 20 deletions

View File

@@ -214,7 +214,7 @@ DEFVFIOMODE := guest-kernel
DEFBINDMOUNTS := []
DEFDANCONF := /run/kata-containers/dans
DEFFORCEGUESTPULL := false
QEMUTDXQUOTEGENERATIONSERVICESOCKETPORT := 4050
QEMUTDXQUOTEGENERATIONSERVICESOCKETPORT := 0
# Create Container Timeout in seconds
DEFCREATECONTAINERTIMEOUT ?= 30

View File

@@ -433,6 +433,7 @@ guest_hook_path = ""
# It's important to note that setting "tdx_quote_generation_service_socket_port" to 0 enables communication via Unix Domain Sockets (UDS).
# To activate UDS, the QGS service itself must be launched with the "-port=0" parameter and the UDS will always be located at /var/run/tdx-qgs/qgs.socket.
# -object '{"qom-type":"tdx-guest","id":"tdx","quote-generation-socket":{"type":"unix","path":"/var/run/tdx-qgs/qgs.socket"}}'
# If port is set to 0 but /var/run/tdx-qgs/qgs.socket does not exist on the host, Kata will automatically fall back to vsock port 4050 for backwards compatibility.
tdx_quote_generation_service_socket_port = @QEMUTDXQUOTEGENERATIONSERVICESOCKETPORT@
#

View File

@@ -18,6 +18,8 @@ kernel = "@KERNELPATH_COCO@"
image = "@IMAGECONFIDENTIALPATH@"
# initrd = "@INITRDPATH@"
machine_type = "@MACHINETYPE@"
# Set to 0 to use Unix Domain Socket (/var/run/tdx-qgs/qgs.socket) instead of vsock.
# If port is 0 but the socket does not exist, Kata falls back to vsock port 4050.
tdx_quote_generation_service_socket_port = @QEMUTDXQUOTEGENERATIONSERVICESOCKETPORT@
# rootfs filesystem type:

View File

@@ -334,20 +334,51 @@ pub struct SocketAddress {
impl SocketAddress {
pub fn new(port: u32) -> Self {
Self::new_with_socket_path(port, QGS_SOCKET_PATH)
}
fn new_with_socket_path(port: u32, socket_path: &str) -> Self {
if port == 0 {
Self {
typ: "unix".to_string(),
cid: "".to_string(),
port: "".to_string(),
path: QGS_SOCKET_PATH.to_string(),
match std::fs::metadata(socket_path) {
Ok(_) => {
return Self {
typ: "unix".to_string(),
cid: "".to_string(),
port: "".to_string(),
path: socket_path.to_string(),
};
}
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
// Socket not present; fall back to vsock for backwards compatibility.
warn!(
sl!(),
"QGS socket {} not found, falling back to vsock port 4050", socket_path
);
}
Err(e) => {
// Unexpected error (e.g. permission denied) — log it so misconfiguration
// is not silently masked, then fall back to vsock.
warn!(
sl!(),
"QGS socket {} inaccessible ({}), falling back to vsock port 4050",
socket_path,
e
);
}
}
} else {
Self {
return Self {
typ: "vsock".to_string(),
cid: format!("{}", 2),
port: port.to_string(),
port: "4050".to_string(),
path: "".to_string(),
}
};
}
Self {
typ: "vsock".to_string(),
cid: format!("{}", 2),
port: port.to_string(),
path: "".to_string(),
}
}
}
@@ -462,9 +493,20 @@ mod tests {
#[test]
fn test_unix_address_new() {
let socket = SocketAddress::new(0);
let dir = TempDir::new().unwrap();
let sock = dir.path().join("qgs.socket");
std::fs::File::create(&sock).unwrap();
// Socket present: must return unix type
let socket = SocketAddress::new_with_socket_path(0, sock.to_str().unwrap());
assert_eq!(socket.typ, "unix");
assert_eq!(socket.path, "/var/run/tdx-qgs/qgs.socket");
assert_eq!(socket.path, sock.to_str().unwrap());
// Socket absent: must fall back to vsock port 4050
let socket = SocketAddress::new_with_socket_path(0, "/nonexistent/qgs.socket");
assert_eq!(socket.typ, "vsock");
assert_eq!(socket.cid, "2");
assert_eq!(socket.port, "4050");
}
#[test]
@@ -476,9 +518,21 @@ mod tests {
#[test]
fn test_socket_address_serialize_deserialize() {
let socket = SocketAddress::new(0);
let dir = TempDir::new().unwrap();
let sock = dir.path().join("qgs.socket");
std::fs::File::create(&sock).unwrap();
let sock_str = sock.to_str().unwrap();
// Socket present: unix type
let socket = SocketAddress::new_with_socket_path(0, sock_str);
let serialized = serde_json::to_string(&socket).unwrap();
let expected_json = r#"{"type":"unix","path":"/var/run/tdx-qgs/qgs.socket"}"#;
let expected_json = format!(r#"{{"type":"unix","path":"{sock_str}"}}"#);
assert_eq!(expected_json, serialized);
// Socket absent: vsock fallback
let socket = SocketAddress::new_with_socket_path(0, "/nonexistent/qgs.socket");
let serialized = serde_json::to_string(&socket).unwrap();
let expected_json = r#"{"type":"vsock","cid":"2","port":"4050"}"#;
assert_eq!(expected_json, serialized);
}

View File

@@ -186,7 +186,7 @@ QEMUTDXEXPERIMENTALVALIDHYPERVISORPATHS := [\"$(QEMUTDXEXPERIMENTALPATH)\"]
QEMUCCAEXPERIMENTALPATH := $(QEMUBINDIR)/$(QEMUCCAEXPERIMENTALCMD)
QEMUCCAEXPERIMENTALVALIDHYPERVISORPATHS := [\"$(QEMUCCAEXPERIMENTALPATH)\"]
QEMUTDXQUOTEGENERATIONSERVICESOCKETPORT := 4050
QEMUTDXQUOTEGENERATIONSERVICESOCKETPORT := 0
QEMUSNPPATH := $(QEMUBINDIR)/$(QEMUSNPCMD)
QEMUSNPVALIDHYPERVISORPATHS := [\"$(QEMUSNPPATH)\"]

View File

@@ -46,10 +46,12 @@ type Machine struct {
const (
// MachineTypeMicrovm is the QEMU microvm machine type for amd64
MachineTypeMicrovm string = "microvm"
// (fixed) Unix Domain Socket Path served by Intel TDX Quote Generation Service
qgsSocketPath string = "/var/run/tdx-qgs/qgs.socket"
)
// qgsSocketPath is the Unix Domain Socket path served by the Intel TDX Quote
// Generation Service. Declared as a var so tests can override it.
var qgsSocketPath = "/var/run/tdx-qgs/qgs.socket"
// hasPCIeRoot reports whether the configured QEMU machine type exposes a
// `pcie.0` root complex (q35 on x86, virt on arm64). Machines such as
// pseries (ppc64le -> pci.0), s390-ccw-virtio (s390x -> CCW transport)
@@ -538,7 +540,13 @@ func (t *TdxQomObject) String() string {
func getQgsSocketAddress(portNum uint32) SocketAddress {
if portNum == 0 {
return SocketAddress{Type: "unix", Path: qgsSocketPath}
// Check if the Unix socket exists
if _, err := os.Stat(qgsSocketPath); err == nil {
return SocketAddress{Type: "unix", Path: qgsSocketPath}
}
// Fall back to port 4050 with vsock for backwards compatibility
log.Printf("Warning: QGS socket %s not found, falling back to vsock port 4050", qgsSocketPath)
return SocketAddress{Type: "vsock", Cid: fmt.Sprint(VsockHostCid), Port: "4050"}
}
return SocketAddress{Type: "vsock", Cid: fmt.Sprint(VsockHostCid), Port: fmt.Sprint(portNum)}

View File

@@ -139,10 +139,23 @@ func TestAppendDeviceNVDIMM(t *testing.T) {
var (
tdxObjectVsock = `-object {"qom-type":"tdx-guest","id":"tdx","quote-generation-socket":{"type":"vsock","cid":"2","port":"4050"}}`
tdxObjectUnix = `-object {"qom-type":"tdx-guest","id":"tdx","quote-generation-socket":{"type":"unix","path":"/var/run/tdx-qgs/qgs.socket"}}`
)
func TestTdxQuoteSocket(t *testing.T) {
// Create a temp socket file so the unix path is always exercised.
dir := t.TempDir()
origPath := qgsSocketPath
qgsSocketPath = dir + "/qgs.socket"
defer func() { qgsSocketPath = origPath }()
f, err := os.Create(qgsSocketPath)
if err != nil {
t.Fatalf("failed to create temp socket file: %v", err)
}
f.Close()
tdxObjectUnix := fmt.Sprintf(`-object {"qom-type":"tdx-guest","id":"tdx","quote-generation-socket":{"type":"unix","path":"%s"}}`, qgsSocketPath)
object := Object{
Type: TDXGuest,
ID: "tdx",
@@ -151,10 +164,15 @@ func TestTdxQuoteSocket(t *testing.T) {
QgsPort: 0,
}
// port=0 with socket present: use Unix socket
testAppend(object, tdxObjectUnix, t)
object.QgsPort = 4050
// port=0 without socket present: fall back to vsock port 4050
qgsSocketPath = dir + "/missing.socket"
testAppend(object, tdxObjectVsock, t)
// Explicit vsock port
object.QgsPort = 4050
testAppend(object, tdxObjectVsock, t)
}