Container runner: wildcard-bound host services reachable from containers via host.containers.internal #216
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/coding-lab#216
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Found while validating PR #214 (containerized runner, ADR-0052). ADR-0052 states pasta gives "full egress with no route back to the host's loopback services ... never a host port." That is true only for services bound to the host loopback (
127.0.0.1). A host service bound to a wildcard/LAN address (0.0.0.0) remains reachable from inside a container viahost.containers.internal, which podman's--network=pastamaps by default.This is not a break of the loopback isolation the ADR relies on, and not a merge blocker — filing so we investigate and decide on hardening later.
Why it matters here
lab's own HTTP server runs with
--addr :8080(a wildcard bind — confirmed on the dev host). So a containerized run can reach lab's operator HTTP port athost.containers.internal:8080, and likewise any other0.0.0.0-bound service on the host (a database, metrics endpoint, another daemon).Mitigating factors (why it's not urgent):
/agent/v1) is served on the unix socket only, never on:8080.:8080requires a session cookie; a container has no cookie and the run token (LAB_TOKEN) only authenticates the agent socket. So reaching:8080is low-value.But the surface is wider than the ADR claims, and it depends on what else the operator runs on
0.0.0.0.Evidence (reproduced with podman's faithful default pasta args)
Faithful arg set:
--config-net --dns-forward 169.254.1.1 -t none -u none -T none -U none --no-map-gw --map-guest-addr 169.254.1.2. From inside the netns, against host services:127.0.0.1:PORT(guest loopback)127.0.0.1<gateway>:PORT127.0.0.1--no-map-gw)host.containers.internal:PORT127.0.0.1host.containers.internal:PORT0.0.0.0Mechanism: podman always appends
--map-guest-addr 169.254.1.2forhost.containers.internal; per the pasta(1) man page it maps to the host's global address (not loopback).--no-map-gw(podman default) is what blocks the loopback path.Possible mitigations to evaluate
--map-guest-addr noneto the pane argv (internal/podmanx/podmanx.goRunArgv) to drop thehost.containers.internalmapping entirely — the container has no need for it (it reaches lab via the mounted unix socket).127.0.0.1on hosts where the container runner is enabled (and document that co-located services should too), relying on the reverse proxy for external access.Notes
main/v5.8.x vendoredlibnetwork/pastaand the pasta(1) man page; reproduced with nixpkgspasston the dev host (rootless podman is not yet installed there).Agent Brief
Category: bug
Summary: Drop pasta's
host.containers.internalmapping from containerized runs so wildcard-bound host services are no longer reachable from inside a containerCurrent behavior:
The containerized-run argv renderer (
RunArgvin the podmanx package) passes bare--network=pasta. Podman's pasta defaults then append--map-guest-addr 169.254.1.2, which mapshost.containers.internalto the host's global address. Result: any host service bound to a wildcard/LAN address (0.0.0.0) — including lab's own operator HTTP server started with--addr :8080— is reachable from inside every run container viahost.containers.internal:<port>. Only loopback-bound host services are isolated (via podman's default--no-map-gw). This contradicts ADR-0052's isolation intent ("no route back to the host's services … never a host port"); the ADR and the flag-rationale comment currently carry a caveat documenting the hole.Reproduction evidence is in the issue body: with podman's faithful default pasta args, a probe from inside the netns reaches a
0.0.0.0-bound host service viahost.containers.internalwhile all loopback paths correctly fail.Desired behavior:
Containerized runs get no
host.containers.internalmapping at all. Nothing inside the container needs it: the agent reaches lab exclusively through the mounted agent unix socket, and a TCP--agent-urlis already deliberately unreachable per ADR-0052. The intended mechanism is passing the pasta option--map-guest-addr nonethrough podman's network option syntax (--network=pasta:--map-guest-addr,none) — verify against the pinned podman's docs that user-supplied pasta options compose with (not replace) podman's other defaults (--no-map-gw,-t none, etc.), and thatnonesuppresses the mapping rather than binding a literal address.Version floor:
--map-guest-addris a newer passt option (podman started appending it in 5.3, matching passt releases from late 2024). On a host with an older passt, the explicit option would make pasta fail at container start — after the preflight has already passed. Handle this explicitly: extend the podmanx preflight (which already checks podman ≥ 4 and pasta presence) to verify the installed pasta accepts--map-guest-addr, refusing container spawns with an actionable failure naming the too-old passt otherwise. If a cheap probe proves impractical, a documented version floor enforced by the preflight's existing version checks is acceptable — but silent spawn breakage on old hosts is not.All three run shapes rendered by
RunArgv(run session, login pane, non-interactive CLI poke) share the same network flag and should all lose the mapping — none of them talks to host ports.Key interfaces:
RunArgv/RunSpec(podmanx package) — the single renderer of thepodman runargv; the network flag changes here, for all shapes--network=pastaflag-rationale doc comment and ADR-0052's pasta bullet — both currently document the residual reachability as a caveat; both must be updated to state the mapping is dropped and why (the ops guidance to bind co-located services to127.0.0.1remains sound as defense-in-depth)Acceptance criteria:
--map-guest-addr none(exact spelling per podman's network-option syntax) for every shape that uses the pasta network--map-guest-addr, with a failure message naming the fix (upgrade passt), following the existing preflight failure conventions — or, if probing is impractical, the preflight version gate is tightened and the floor documentedOut of scope:
--addrbind or any co-located service's bind address — deployment/ops concern; the ADR's advice to bind co-located services to loopback stays as written