From 3f8e4964adfa38b7cf25836b92220871de38bee3 Mon Sep 17 00:00:00 2001 From: Winter Date: Fri, 24 Jul 2026 13:47:15 -0500 Subject: [PATCH] spindle/microvm: bind vsock listeners to the host context explicitly vsock.Listen(port, nil) binds the local context ID, which resolves to the loopback context (VMADDR_CID_LOCAL) on kernels with vsock_loopback loaded. a loopback listener only accepts connections from the host itself, so guest agents can never reach any of the four host-side listeners (agent hub, read cache proxy, upload cache proxy, and the spindle-microvm-run dev listener). fedora kernels auto-load vsock_loopback; nixos hosts do not, which is why this only shows up off-nixos. use ListenContextID(vsock.Host, ...) so the listeners always bind the host context (VMADDR_CID_HOST) regardless of which modules are loaded. Signed-off-by: Winter --- cmd/spindle-microvm-run/main_linux.go | 4 +++- spindle/engines/microvm/agent.go | 4 +++- spindle/engines/microvm/read_cache_proxy.go | 4 +++- spindle/engines/microvm/upload_cache_proxy.go | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/cmd/spindle-microvm-run/main_linux.go b/cmd/spindle-microvm-run/main_linux.go index 2d37fff7..1a7d9064 100644 --- a/cmd/spindle-microvm-run/main_linux.go +++ b/cmd/spindle-microvm-run/main_linux.go @@ -137,7 +137,9 @@ func runMicroVMRunDev(ctx context.Context, cmd *cli.Command) error { if mib := cmd.Uint("memory-mib"); mib > 0 { imageSpec.MemoryMiB = int(mib) } - ln, err := vsock.Listen(port, nil) + // bind the host context explicitly: with vsock_loopback loaded, + // Listen() would land on the loopback context and never see guests. + ln, err := vsock.ListenContextID(vsock.Host, port, nil) if err != nil { return fmt.Errorf("listen on vsock port %d: %w", port, err) } diff --git a/spindle/engines/microvm/agent.go b/spindle/engines/microvm/agent.go index 9204b5b2..2e40e03c 100644 --- a/spindle/engines/microvm/agent.go +++ b/spindle/engines/microvm/agent.go @@ -28,7 +28,9 @@ type agentHub struct { } func newAgentHub(port uint32, l *slog.Logger) (*agentHub, error) { - ln, err := vsock.Listen(port, nil) + // bind the host context explicitly: with vsock_loopback loaded, + // Listen() would land on the loopback context and never see guests. + ln, err := vsock.ListenContextID(vsock.Host, port, nil) if err != nil { return nil, fmt.Errorf("listen for agent on vsock port %d: %w", port, err) } diff --git a/spindle/engines/microvm/read_cache_proxy.go b/spindle/engines/microvm/read_cache_proxy.go index 65d838c5..ca4aee60 100644 --- a/spindle/engines/microvm/read_cache_proxy.go +++ b/spindle/engines/microvm/read_cache_proxy.go @@ -195,7 +195,9 @@ func listenRandomVsockPort(ctx context.Context) (*vsock.Listener, uint32, error) if err != nil { return nil, 0, err } - ln, err := vsock.Listen(port, nil) + // bind the host context explicitly: with vsock_loopback loaded, + // Listen() would land on the loopback context and never see guests. + ln, err := vsock.ListenContextID(vsock.Host, port, nil) if err == nil { return ln, port, nil } diff --git a/spindle/engines/microvm/upload_cache_proxy.go b/spindle/engines/microvm/upload_cache_proxy.go index 9b9385d2..07fada2d 100644 --- a/spindle/engines/microvm/upload_cache_proxy.go +++ b/spindle/engines/microvm/upload_cache_proxy.go @@ -142,7 +142,9 @@ func listenRandomVsockUploadPort(ctx context.Context) (*vsock.Listener, uint32, if err != nil { return nil, 0, err } - ln, err := vsock.Listen(port, nil) + // bind the host context explicitly: with vsock_loopback loaded, + // Listen() would land on the loopback context and never see guests. + ln, err := vsock.ListenContextID(vsock.Host, port, nil) if err == nil { return ln, port, nil } -- 2.51.2