Problem
The Containerization framework provides no API to open a TCP or UDP connection
from the host into the network namespace of a running LinuxPod. The host
can reach the pod only if a port was published at VM creation time via
--publish (Apple Container CLI) or equivalent configuration.
For a Kubernetes pod-shared VM, publishing individual ports at creation time is
not viable because:
- The set of listening ports is not known until after sidecars start.
- Kubernetes
kubectl port-forward expects on-demand dial to an arbitrary
port inside the pod's network namespace — it is not a static mapping.
- Pod-shared mode shares one VM network namespace across all containers in
the pod; pre-publishing every container's ports defeats the purpose of
dynamic port-forward.
Use case
kubectl port-forward <pod> 8080:80 expects the CRI runtime to open a TCP
connection to 127.0.0.1:80 inside the pod's network namespace and
relay bytes bidirectionally. On the pod-shared path, the CRI runtime
dispatches a JSON-RPC pod.portDial call to podvmd. The daemon must
open a socket in the pod's netns and return a bidirectional byte stream.
Reproducer
In tools/podvmd/Sources/podvmd/PodRegistry.swift (line ~470):
// MARK: - pod.portDial
//
// Apple Containerization framework (as of 0.12.x) does not expose a
// network-namespace handle or in-guest TCP dial primitive.
func portDial(_ params: PodPortDialParams) async throws -> PodPortDialResult {
_ = try podEntry(params.handle)
guard params.network == "tcp" else {
throw PodVMError(
.methodNotFound,
"pod.portDial: unsupported network \"\(params.network)\"; only \"tcp\" is allowed"
)
}
guard params.port > 0 && params.port <= 65535 else {
throw PodVMError(
.invalidParams,
"pod.portDial: port must be between 1 and 65535, got \(params.port)"
)
}
throw PodVMError(
.methodNotFound,
"pod.portDial not supported by Apple Containerization framework on this host"
)
}
The Go CRI layer receives JSON-RPC error code -32601 and maps it to gRPC
codes.Unimplemented:
PortForward: pod-shared path: pod.portDial returned method_not_found
(-32601: pod.portDial not supported by Apple Containerization
framework on this host)
A kubectl port-forward against a pod-shared pod produces:
error: unable to upgrade connection: port forward not supported for this container
Proposed API shape
/// Open a TCP or UDP connection inside the pod's network namespace.
/// Returns a bidirectional stream (or pair of FileHandles) that the
/// caller can relay to an external client.
func dial(
containerID: String?, // nil = pod-level netns
network: String, // "tcp" or "udp"
address: String // "127.0.0.1:8080" or ":8080"
) async throws -> (any DuplexStream)
Alternatively, expose a vsock or virtio-net handle that lets the host
inject a connection into the guest's TCP stack.
Workarounds we've tried
--publish hostPort:containerPort at VM creation — requires knowing
all ports in advance. Does not work for dynamic port-forward. Occupies
a host port per published mapping.
exec a socat relay inside the pod — spawns socat TCP-LISTEN:$hostSide,fork TCP:127.0.0.1:$port as a sidecar exec.
Functionally works but adds a process per forwarded port, is fragile
(socat must be installed in the rootfs), and does not survive process
restarts.
- vsock from host to guest — Apple Containerization does not expose
vsock channel creation from the host side for an already-running pod.
Problem
The Containerization framework provides no API to open a TCP or UDP connection
from the host into the network namespace of a running
LinuxPod. The hostcan reach the pod only if a port was published at VM creation time via
--publish(Apple Container CLI) or equivalent configuration.For a Kubernetes pod-shared VM, publishing individual ports at creation time is
not viable because:
kubectl port-forwardexpects on-demand dial to an arbitraryport inside the pod's network namespace — it is not a static mapping.
the pod; pre-publishing every container's ports defeats the purpose of
dynamic port-forward.
Use case
kubectl port-forward <pod> 8080:80expects the CRI runtime to open a TCPconnection to
127.0.0.1:80inside the pod's network namespace andrelay bytes bidirectionally. On the pod-shared path, the CRI runtime
dispatches a JSON-RPC
pod.portDialcall topodvmd. The daemon mustopen a socket in the pod's netns and return a bidirectional byte stream.
Reproducer
In
tools/podvmd/Sources/podvmd/PodRegistry.swift(line ~470):The Go CRI layer receives JSON-RPC error code
-32601and maps it to gRPCcodes.Unimplemented:A
kubectl port-forwardagainst a pod-shared pod produces:Proposed API shape
Alternatively, expose a vsock or virtio-net handle that lets the host
inject a connection into the guest's TCP stack.
Workarounds we've tried
--publish hostPort:containerPortat VM creation — requires knowingall ports in advance. Does not work for dynamic port-forward. Occupies
a host port per published mapping.
execasocatrelay inside the pod — spawnssocat TCP-LISTEN:$hostSide,fork TCP:127.0.0.1:$portas a sidecar exec.Functionally works but adds a process per forwarded port, is fragile
(socat must be installed in the rootfs), and does not survive process
restarts.
vsock channel creation from the host side for an already-running pod.