Skip to content

Run the devcontainer under rootless Podman#396

Open
krokicki wants to merge 3 commits into
mainfrom
podman-devcontainer
Open

Run the devcontainer under rootless Podman#396
krokicki wants to merge 3 commits into
mainfrom
podman-devcontainer

Conversation

@krokicki

@krokicki krokicki commented Jun 23, 2026

Copy link
Copy Markdown
Member

Makes the devcontainer runnable under rootless Podman on Linux while staying fully compatible with Docker/Colima on Mac, and hardens the egress firewall so the AI agent (which runs with approvals disabled) cannot loosen its own network constraints.

Runtime is selected with the FG_CONTAINER_RUNTIME environment variable (default: docker). devcontainer.json stays runtime-neutral; all Podman-specific behavior lives in a small wrapper and shim.

Changes

Cross-platform runtime selection

  • pyproject.toml: the four container-* tasks now call .devcontainer/dc.sh.
  • .devcontainer/dc.sh (new): passes --docker-path to the devcontainers CLI based on FG_CONTAINER_RUNTIME. On the Podman path it also routes through the shim and disables the CLI's uid-renumber step.
  • .devcontainer/podman-shim/podman (new): upgrades the CLI's forced --userns=keep-id to keep-id:uid=1000,gid=1000 so the host user maps onto the container's vscode user (uid 1000). This makes bind mounts writable using only a small subuid range, avoiding the unsafe ~1M-id range the CLI's renumber step would otherwise require on a shared LDAP host.

Tamper-proof egress firewall

  • .devcontainer/init-firewall.sh: DNS egress is pinned to the resolvers in /etc/resolv.conf instead of allowing port 53 to any host; the blanket outbound-22 rule is removed (SSH to GitHub still works via the allowed-domains set, SSH to other hosts is blocked); the blanket host /24 allow is removed.
  • .devcontainer/post-create.sh: revokes the vscode user's passwordless sudo as the final setup step. Without root, the agent cannot touch iptables/ipset, so the egress allowlist can no longer be flushed from inside the container. NET_ADMIN/NET_RAW remain in the image but are inert without root. A root shell is still available from the host via podman exec -u root / docker exec -u root.

Verification

Rebuilt under rootless Podman and confirmed on the running container:

  • vscode can no longer sudo, and both iptables -F and sudo iptables -F fail.
  • Egress policy shows DNS pinned to the three configured resolvers, no port-22-anywhere, no /24.
  • Normal egress still works: api.github.com returns 200, example.com is blocked.
  • Bind-mounted credentials are owned by vscode and readable; Claude and Codex run.
  • Host-side podman exec -u root still works for maintenance.

Notes

  • No changes to devcontainer.json; Mac/Colima continues to use the default Docker path and never sees the shim.
  • This bounds blast radius and prevents the agent from removing its own egress controls. It does not stop exfiltration through already-allowed endpoints (e.g. GitHub, the Anthropic/OpenAI APIs) or DNS tunneling through the permitted resolver; the structural fix for that is enforcing egress outside the container (e.g. a VM/hypervisor boundary).

@StephanPreibisch @JaneliaSciComp/fileglancer

krokicki and others added 3 commits June 23, 2026 15:54
Make the devcontainer runtime-neutral so the same pixi tasks and
devcontainer.json work with Docker/Colima on Mac and rootless Podman on
Linux. Runtime is selected via FG_CONTAINER_RUNTIME (default: docker).

- dc.sh: wrapper that passes --docker-path to the devcontainers CLI and,
  on the Podman path, routes through podman-shim and disables the CLI's
  uid-renumber step (--update-remote-user-uid-default never).
- podman-shim/podman: upgrades the CLI's forced --userns=keep-id to
  keep-id:uid=1000,gid=1000 so the host user maps onto vscode (uid 1000),
  making bind mounts writable without an unsafe ~1M subuid range.
- pyproject.toml: container-* tasks now invoke dc.sh.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Harden the rootless-Podman devcontainer so the agent (running as the
unprivileged vscode user with approvals disabled) cannot loosen its own
network constraints.

- init-firewall.sh: pin DNS egress to the resolvers in /etc/resolv.conf
  instead of allowing port 53 to any host (closes arbitrary-resolver
  tunneling); drop the blanket outbound-22 rule (SSH to GitHub still works
  via the allowed-domains set, SSH to other hosts is blocked); drop the
  blanket host /24 allow (prevents lateral movement to neighbors).
- post-create.sh: revoke vscode's passwordless sudo as the final setup
  step. Without root, vscode cannot touch iptables/ipset, so the egress
  allowlist can no longer be flushed from inside the container.
  NET_ADMIN/NET_RAW remain in the image but are inert without root.
  Maintenance root shell remains available from the host via
  `podman exec -u root` / `docker exec -u root`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@krokicki krokicki changed the title Run the devcontainer under rootless Podman, with a tamper-proof egress firewall Run the devcontainer under rootless Podman Jun 23, 2026
@mkitti

mkitti commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Is rootless podman meant for usage similar to how we might use apptainer on HPC?

@krokicki

Copy link
Copy Markdown
Member Author

This is meant to replace our use of Docker entirely, especially on Linux. Running agents inside of Docker is not safe because it effectively gives them root access to the system. On Mac we have other good options like Colima (though Podman also works there, for those who prefer it).

@mkitti

mkitti commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Yes, I understand why we are doing this. Those same attributes make it seem amenable to running as an unprivileged user on a cluster as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants