diff --git a/src/runtime-rs/Makefile b/src/runtime-rs/Makefile index 64cb4977b3..64a1be32e1 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-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/Makefile b/src/runtime/Makefile index 2cf4540550..f2300b0d39 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)\"] diff --git a/src/runtime/pkg/govmm/qemu/qemu.go b/src/runtime/pkg/govmm/qemu/qemu.go index 87b00f6429..c0b7d1022e 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) }