From 733c6791d30922bd4dfafb085b9d4b2222ac2b53 Mon Sep 17 00:00:00 2001 From: Mikko Ylinen Date: Mon, 16 Feb 2026 16:29:50 +0200 Subject: [PATCH 1/2] runtime(-rs): make TDX QGS port=0 change backwards compatible Changing Kata runtime configurations to use TDX QGS port=0 (unix domain socket transport) means cluster admins must also reconfigure qgsd to the same and have /var/run/tdx-qgs/qgs.sock available. Since the early days of TDX attestation in Kata, the configuration has used vsock with cid=2, port=4050. To avoid unncessary breakages when Kata default moves to unix domain socket, fall back to the old configuration if /var/run/tdx-qgs/qgs.sock is not available on the worker node. Signed-off-by: Mikko Ylinen --- src/runtime-rs/crates/hypervisor/src/utils.rs | 80 ++++++++++++++++--- src/runtime/pkg/govmm/qemu/qemu.go | 14 +++- src/runtime/pkg/govmm/qemu/qemu_test.go | 22 ++++- 3 files changed, 98 insertions(+), 18 deletions(-) diff --git a/src/runtime-rs/crates/hypervisor/src/utils.rs b/src/runtime-rs/crates/hypervisor/src/utils.rs index d74418704d..76e0827c17 100644 --- a/src/runtime-rs/crates/hypervisor/src/utils.rs +++ b/src/runtime-rs/crates/hypervisor/src/utils.rs @@ -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); } diff --git a/src/runtime/pkg/govmm/qemu/qemu.go b/src/runtime/pkg/govmm/qemu/qemu.go index 9dca1e959e..57295c4293 100644 --- a/src/runtime/pkg/govmm/qemu/qemu.go +++ b/src/runtime/pkg/govmm/qemu/qemu.go @@ -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)} diff --git a/src/runtime/pkg/govmm/qemu/qemu_test.go b/src/runtime/pkg/govmm/qemu/qemu_test.go index e4616a8231..1cce08bae7 100644 --- a/src/runtime/pkg/govmm/qemu/qemu_test.go +++ b/src/runtime/pkg/govmm/qemu/qemu_test.go @@ -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) } From 2b38d9f45eb77f5b223ca6f20736ee3249a8c0b6 Mon Sep 17 00:00:00 2001 From: Mikko Ylinen Date: Fri, 23 Jan 2026 15:38:41 +0200 Subject: [PATCH 2/2] runtime(-rs): tdx: use TDX QGS via unix-domain-socket by default TDX QGS takes raw TD report from QEMU/guest VM and signs it in an SGX enclave. Historically, QGS has supported two transports: vsock and unix-domain-socket. The former was necessary before the guest kernel supported the GetQuote "TDVMCALL" hypercall: DCAP library inside the guest used vsock to talk to QGS directly. However, with GetQuote, QEMU gets the TDREPORT and sends it to QGS. In process-to-process communication, unix-domain-socket is a better approach. This is also the only transport supported by libvirt by default. With that, align Kata default configuration to use unix-domain-socket as well. The change in impacts QEMU commandline: old: "quote-generation-socket":{"type":"vsock","cid":"2","port":"4050"} new: "quote-generation-socket":{"type":"unix","path":"/var/run/tdx-qgs/qgs.socket"} Host QGS configuration must be changed to listen unix-domain-sockets. Signed-off-by: Mikko Ylinen --- src/runtime-rs/Makefile | 2 +- src/runtime-rs/config/configuration-qemu-runtime-rs.toml.in | 1 + src/runtime-rs/config/configuration-qemu-tdx-runtime-rs.toml.in | 2 ++ src/runtime/Makefile | 2 +- 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/runtime-rs/Makefile b/src/runtime-rs/Makefile index b9497d846c..083cb7b9c8 100644 --- a/src/runtime-rs/Makefile +++ b/src/runtime-rs/Makefile @@ -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 diff --git a/src/runtime-rs/config/configuration-qemu-runtime-rs.toml.in b/src/runtime-rs/config/configuration-qemu-runtime-rs.toml.in index ac70f140c9..4ed8d3ba38 100644 --- a/src/runtime-rs/config/configuration-qemu-runtime-rs.toml.in +++ b/src/runtime-rs/config/configuration-qemu-runtime-rs.toml.in @@ -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@ # diff --git a/src/runtime-rs/config/configuration-qemu-tdx-runtime-rs.toml.in b/src/runtime-rs/config/configuration-qemu-tdx-runtime-rs.toml.in index b5ce583fb7..5a242c5145 100644 --- a/src/runtime-rs/config/configuration-qemu-tdx-runtime-rs.toml.in +++ b/src/runtime-rs/config/configuration-qemu-tdx-runtime-rs.toml.in @@ -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: diff --git a/src/runtime/Makefile b/src/runtime/Makefile index 88ef8077ff..17129cc7c4 100644 --- a/src/runtime/Makefile +++ b/src/runtime/Makefile @@ -186,7 +186,7 @@ QEMUTDXEXPERIMENTALVALIDHYPERVISORPATHS := [\"$(QEMUTDXEXPERIMENTALPATH)\"] QEMUCCAEXPERIMENTALPATH := $(QEMUBINDIR)/$(QEMUCCAEXPERIMENTALCMD) QEMUCCAEXPERIMENTALVALIDHYPERVISORPATHS := [\"$(QEMUCCAEXPERIMENTALPATH)\"] -QEMUTDXQUOTEGENERATIONSERVICESOCKETPORT := 4050 +QEMUTDXQUOTEGENERATIONSERVICESOCKETPORT := 0 QEMUSNPPATH := $(QEMUBINDIR)/$(QEMUSNPCMD) QEMUSNPVALIDHYPERVISORPATHS := [\"$(QEMUSNPPATH)\"]