Repository navigation
feat(podman): relay callbacks through the rootless network namespace #2540
Description
Problem Statement
Rootless Podman sandboxes using slirp4netns cannot connect directly to a
gateway callback listener in the host network namespace. Podman launches
slirp4netns with --disable-host-loopback; QEMU guest validation showed that
listeners bound to loopback, the host default-route address, and 0.0.0.0 were
all unreachable from an OpenShell-style user-defined Podman network.
PR #2492 therefore restricts direct local rootless callbacks to an explicitly
reported pasta helper. Explicit slirp4netns, missing helper metadata, and
unknown helpers fail closed unless the operator configures a genuinely remote
grpc_endpoint.
OpenShell should support local callbacks with rootless slirp4netns without
requiring operators to deploy remote gateway ingress.
Proposed Design
Add a driver-owned, byte-transparent callback relay spanning Podman's rootless
network namespace and the gateway host namespace:
sandbox supervisor
-> host.openshell.internal:<callback-port>
-> TCP relay inside `podman unshare --rootless-netns`
-> mode-0600 Unix socket under $XDG_RUNTIME_DIR/openshell
-> host-side relay
-> gateway callback-only listener on IPv4 loopback
The initial proof in PR #2473 used two socat processes:
- A host-namespace Unix listener forwarding to the gateway loopback listener.
- A TCP listener launched with
podman unshare --rootless-netns, forwarding
to that Unix socket.
Production support should implement and supervise the relay in OpenShell code
rather than require socat:
- Add a Podman-driver relay manager, or a narrowly scoped relay subcommand
launched by the driver underpodman unshare --rootless-netns. - Use one relay per driver/network and callback port, not one per sandbox.
- Start and verify the relay before persisted sandboxes resume or new sandboxes
are created. - Retry transient forwarding failures, surface terminal setup failures as
driver preconditions, and clean up child processes and socket files on
shutdown. - Report/request only an IPv4 loopback callback listener from the gateway. Do
not broaden the primary listener. - Preserve supervisor-to-gateway mTLS end to end; the relay must not terminate
TLS or inspect application payloads. - Create the Unix socket in a user-owned runtime directory with mode
0600,
reject unsafe/stale socket paths, and avoid logging credentials or payloads. - Keep callback listeners restricted to sandbox-callable gRPC methods.
- Probe
podman unshare --rootless-netnssupport and fail closed when the relay
topology cannot be established.
Explicitly reported slirp4netns should select this relay. Missing helper
metadata may use it only when the relay capability and reachability can be
verified; absence of rootlessNetworkCmd must not be treated as proof of a
legacy direct-listener topology.
Alternatives Considered
- Bind the gateway to the host default-route address: works with pasta, but
failed with slirp4netns because host loopback access is disabled. - Bind the gateway to
0.0.0.0: the diagnostic wildcard listener was also
unreachable and would unnecessarily broaden the gateway attack surface. - Set
host_gateway_ip: changing the alias target cannot bypass
slirp4netns host-loopback isolation. - Require pasta permanently: safe as the current interim behavior, but
excludes distributions and installations that use slirp4netns. - Require a remote
grpc_endpoint: already supported and appropriate for
deployments with secure routable ingress, but adds unnecessary
infrastructure for a single-host local gateway.
Agent Investigation
- Related contract: refactor(compute): define a uniform gateway callback endpoint contract #2215.
- Interim implementation: PR feat(compute): negotiate gateway callback listeners #2492.
- Disposable QEMU tooling: PRs test(e2e): add reusable QEMU infrastructure for E2E tests #2471 and test(e2e): add VM-backed E2E suite runner #2473.
- Fedora 44 / Podman 5.8.1 / pasta reached the host default-route listener.
- CentOS Stream 10 / Podman 6.0.2 / pasta reached the host default-route
listener. - Fedora 44 / Podman 5.8.1 / forced slirp4netns reported
slirp4netns,
launched it with--disable-host-loopback, and could reach neither the
address-specific nor wildcard host listener. - Ubuntu 24.04 / Podman 4.9.3 omitted
rootlessNetworkCmd; the live helper was
slirp4netns with--disable-host-loopback, and neither host listener was
reachable. - The PR test(e2e): add VM-backed E2E suite runner #2473 guest relay made the callback path reachable, demonstrating the
namespace-spanning relay topology, but intentionally masked the direct
listener behavior measured above. - Duplicate searches for "rootless Podman relay", "slirp4netns callback", and
"rootless network namespace" found no existing issue with this scope.
Acceptance Criteria
- Rootless Podman with explicitly reported slirp4netns establishes local
supervisor callbacks through the relay. - Relay startup is readiness-gated and failures prevent sandbox startup
with an actionable error. - Supervisor-to-gateway mTLS and callback-only RPC authorization remain
end to end. - Relay sockets and processes are securely created, supervised, restarted,
and cleaned up. - Rootless pasta retains the direct default-route listener path.
- Rootful Podman retains the exact bridge-gateway listener path.
- Explicitly remote callback endpoints remain unchanged and request no
local listener. - Missing helper metadata fails closed unless relay capability and
reachability are positively verified. - QEMU E2E coverage includes Fedora and Ubuntu slirp4netns guests plus a
pasta regression case. - Architecture, Podman networking, gateway configuration, and
troubleshooting documentation describe the supported topologies.
Checklist
- I've reviewed existing issues and the architecture docs
- This is a design proposal, not a "please build this" request
Activity
elezar commentedon Jul 29, 2026
Implementation Plan
Issue type:
featComplexity: High
Confidence: Medium — the relay topology is validated on Ubuntu 24.04 with
Podman 4.9.3, but listener-purpose deduplication and driver lifecycle semantics
need maintainer confirmation.
Summary
Add a driver-owned, byte-transparent callback relay for rootless Podman
slirp4netns, stacked on PR #2492. The initial implementation will prepare theshared relay idempotently from
GetGatewayListenerRequirements, return adedicated callback listener requirement, and fail closed if the relay cannot be
made ready. It will not split
CreateSandboxinto Create/Start because relayreadiness is driver-wide infrastructure rather than per-sandbox state.
The validated transport is:
The wildcard bind exists only inside Podman's rootless network namespace. The
gateway listener remains loopback or otherwise narrowly scoped and
callback-only.
Validated Findings
slirp4netns --disable-host-loopbackwhilerootlessNetworkCmdisunreported.
host-gatewaymaps the sandbox alias to10.0.2.15in thistopology. A direct callback to the host fails, and a relay in the rootless
network namespace does not make that blocked slirp host path usable.
(
10.89.0.1in the test) and relaying from the rootless network namespacesucceeds.
10.89.0.1fails withEADDRNOTAVAILbecause Podman does not materializethat bridge address until a container joins the network.
0.0.0.0:<callback-port>inside the rootless network namespacebefore sandbox creation succeeds; after the first container materializes the
bridge, traffic to
10.89.0.1:<callback-port>reaches that listener.IP_FREEBINDmay permit an exact pre-bind to the future gateway address, butit is Linux-specific, requires relay support and additional validation, and
is optional hardening rather than an initial implementation dependency.
socket connected the namespace relay to a host-loopback tagged responder.
The direct path failed and the relayed path returned the expected tag.
Scope
proto/compute_driver.proto: clarify that listener-requirement discovery mayidempotently materialize driver-owned routing prerequisites. Add no new
sandbox lifecycle RPC in the initial implementation.
crates/openshell-driver-podman/src/relay.rs(new): own the secure runtimedirectory, Unix listener, host forwarding tasks, child readiness handshake,
supervision, bounded connection handling, and cleanup.
crates/openshell-driver-podman/src/main.rsand CLI/config modules: add aprivate relay-child mode invoked with structured arguments under
podman unshare --rootless-netns; do not invoke a shell or requiresocat.crates/openshell-driver-podman/src/driver.rsandgrpc.rs: select the relayfor explicit
slirp4netns; allow missing helper metadata only after positiverelay capability/readiness checks; map the sandbox callback alias to the
inspected Podman network gateway for the relay topology; keep pasta,
rootful, Podman Machine, and remote endpoint paths unchanged; make
requirement discovery async and idempotent as needed.
crates/openshell-server/src/lib.rsand listener tests from PR feat(compute): negotiate gateway callback listeners #2492: ensurethe relay target is a callback-only listener and cannot be silently
de-duplicated onto a full-primary listener with broader routing authority.
nix/test-guest/QEMU coverage: add or extend Fedora and Ubuntu rootlessslirp4netns callback tests, plus pasta and rootful regression coverage. Reuse
PRs test(e2e): add reusable QEMU infrastructure for E2E tests #2471/test(e2e): add VM-backed E2E suite runner #2473 after resolving their stack dependency.
crates/openshell-driver-podman/NETWORKING.md,architecture/gateway.md,docs/reference/sandbox-compute-drivers.mdx, and.agents/skills/debug-openshell-cluster/SKILL.md: document topology,readiness, failure behavior, and troubleshooting.
Implementation Steps
materialization only as needed so the relay targets a listener whose purpose
remains
ComputeDriverCallback. A primary loopback or wildcard listenermust not satisfy the requirement if that would expose the full multiplexed
API through the relay. Prefer a dedicated loopback address/port when it can
coexist safely; otherwise introduce the smallest typed callback endpoint
needed by the requirement contract.
launched as
podman unshare --rootless-netns <driver> <relay-child> ....It binds
0.0.0.0:<callback-port>inside that namespace before any sandboxmaterializes the Podman bridge address, reports readiness over an explicit
pipe/socket handshake, and forwards opaque bidirectional streams to the
private Unix socket with half-close and cancellation propagation. Do not
bind the gateway process itself to a wildcard address.
XDG_RUNTIME_DIR, reject symlinks/unexpected owners/unsafe permissions,create a mode-0600 Unix socket, forward accepted streams to the
callback-only gateway listener, bound connections and buffers, and remove
only artifacts whose identity this process established.
GetGatewayListenerRequirementsidempotently ensure one relay perdriver/rootless-network-namespace/port, wait for the namespace listener,
Unix socket, and parent relay tasks to become locally ready, and then return
the dedicated callback listener requirement. The gateway materializes that
host listener before sandbox resume or creation. Concurrent or repeated
calls must reuse the same healthy relay and roll back partial startup.
OpenShell user-defined Podman network, map
host.containers.internal(orthe callback alias selected by feat(compute): negotiate gateway callback listeners #2492) to its gateway address, and do not use
Podman's
host-gatewayvalue for this relay topology.driver lifetime. Treat unexpected death as a terminal driver health failure
(or reject subsequent validate/create calls) rather than falling back to a
host listener. Ensure gateway bind rejection/startup failure drops the
managed driver and reaps relay children and sockets.
default-route listener, rootful Podman on the exact bridge gateway, Podman
Machine on gvproxy loopback, and explicitly remote
grpc_endpointvalueswithout local requirements. Unknown named helpers remain unsupported.
idempotency, concurrency, readiness timeout, child exit, filesystem attacks,
raw-byte fidelity, half-closes, backpressure, cleanup, port collisions,
listener-purpose isolation, and unchanged topology paths.
helper metadata a regression case: prove the direct callback fails and the
relayed mTLS callback through the inspected network gateway succeeds. Also
run Fedora with explicit slirp4netns, restart/resume behavior, and
pasta/rootful regressions.
relay's trust boundary, prerequisites, observable failures, and
SELinux/AppArmor troubleshooting without adding a new operator-facing
configuration field unless implementation proves one necessary.
Test Plan
prepare; secure runtime-directory and socket creation;
stale/symlink/ownership rejection; child handshake/timeout/exit; cleanup;
relay byte fidelity, half-close, cancellation, bounded concurrency, and port
collisions.
prove sandbox resume/create does not proceed through an unready relay; prove
primary-only methods remain rejected through the relay target.
network-gateway relay establishes supervisor mTLS callbacks; Fedora explicit
slirp4netns and restart/resume succeed; pasta and rootful callbacks remain
direct.
mise run pre-commit,mise run test, relevant Podman/QEMUE2E lanes, and
mise run cibefore PR creation.Risks & Open Questions
GetGatewayListenerRequirementsas an idempotent prepare/ensure operation.If maintainers require strict read-only semantics, add a driver-wide
activation RPC after listener binding; do not split
CreateSandbox.coexist with a primary
127.0.0.1:<port>or0.0.0.0:<port>listener?Existing address-coverage de-duplication must not widen the relay's API
surface.
same-user Podman rootless network namespace, but other containers sharing
that namespace may reach the callback port. Retain mTLS, expose only
callback-authorized methods, detect port collisions, and never relay the
gateway's primary API surface.
gateway observes driver loss. Confirm whether gateway-wide unhealthiness or
driver-scoped unavailability is the desired blast radius.
podman unshare --rootless-netnsand the complete relay readiness handshakesucceed. Missing metadata is never evidence of a direct topology.
multiple Podman networks that share a rootless network namespace and port.
Relay identity and ownership must therefore be keyed by the actual namespace
and port, while socket names must also avoid cross-driver collisions and path
traversal.
/proc/<pid>/exe; SELinux can returnENOENTacross labels. Use an explicitreadiness handshake and test Fedora enforcing SELinux and Ubuntu AppArmor
behavior for
podman unshare, Unix sockets, and child execution.test-guest tooling. Decide whether test(e2e): add reusable QEMU infrastructure for E2E tests #2471 lands first, the stacked PR carries
the minimal harness commits temporarily, or E2E lands as a follow-up commit
after rebasing.
IP_FREEBINDcould narrow the namespace listenerto the future bridge gateway address, but it requires a Linux-specific relay
implementation and independent kernel/namespace validation. It is deferred
hardening; wildcard binding inside the isolated rootless namespace is the
validated initial mechanism.
Documentation Impact
topology, startup ordering, authorization boundary, and failure behavior.
runtime prerequisites.
debug-openshell-clusterdiagnostics for relay readiness, childfailure, runtime-directory permissions, port collisions, and
SELinux/AppArmor checks.
necessary, update
docs/reference/gateway-config.mdxin the same branch.Revision 2 — incorporates the Ubuntu 24.04/Podman 4.9.3 proof of concept:
network-gateway aliasing, namespace-only wildcard pre-bind, concrete readiness
ordering, and
IP_FREEBINDas optional future hardening.Revision 1 — initial plan; selects idempotent requirement-time relay
preparation and records callback-listener isolation as the main review
decision.
github-actions commentedon Aug 13, 2026
This issue has had no activity for 14 days and is now marked stale. It may be closed in 7 days if there is no further activity. Comment or remove the state:stale label to keep it open.
elezar commentedon Aug 21, 2026
A second callback-relay use case surfaced in the Fedora RPM release canary: the gateway itself runs inside a nested container and uses rootful Podman. Listener discovery returns the future Podman bridge gateway (currently 10.89.0.1), but that address is not present in the gateway network namespace until the first sandbox joins the network. The gateway therefore fails at startup when it attempts the exact secondary bind, as seen in release-canary run 32448768823, job 96673135310: https://github.com/NVIDIA/OpenShell/actions/runs/32448768823/job/96673135310
We are handling this in two stages:
Longer term, the driver-owned relay proposed here may be the cleaner common abstraction if it is generalized beyond rootless slirp4netns. It could keep the gateway callback listener on loopback and place any required wildcard bind inside the driver-controlled Podman network namespace, covering both slirp4netns reachability and the nested-rootful bridge-materialization race. This is not necessarily the same implementation path: the current issue assumes a rootless network namespace and its acceptance criteria explicitly retain the exact rootful bridge listener. The nested-rootful topology should therefore be evaluated as an additional relay mode, not silently folded into the existing rootless assumptions.
github-actions commentedon Sep 6, 2026
This issue has had no activity for 14 days and is now marked stale. It may be closed in 7 days if there is no further activity. Comment or remove the state:stale label to keep it open.