Skip to content

feat: add podman and ssh plugin support#89

Open
wongtsejian wants to merge 23 commits into
donbader:mainfrom
wongtsejian:feat/podman-support
Open

feat: add podman and ssh plugin support#89
wongtsejian wants to merge 23 commits into
donbader:mainfrom
wongtsejian:feat/podman-support

Conversation

@wongtsejian
Copy link
Copy Markdown

@wongtsejian wongtsejian commented Jun 3, 2026

Summary

Add podman as a first-class supported container runtime alongside Docker, with automatic detection and security hardening via Linux capability dropping.

  • New internal/runtime package auto-detects docker/podman (configurable via CONTAINER_RUNTIME env var)
  • Compose passthrough uses detected runtime instead of hardcoded docker
  • Fleet mode expands include directives into multiple -f flags for podman-compose compatibility
  • All containers now drop all Linux capabilities, adding back only what's needed
  • Agent containers get userns_mode: keep-id on podman (single mode) to limit container escape blast radius

Context

The tool's purpose is secure sandboxing of AI coding agents. Podman offers stronger isolation out of the box (no root daemon, native rootless support, daemonless architecture). Supporting it gives users a more secure default without changing the existing Docker workflow.

Security hardening

Container Capabilities
Gateway cap_drop: ALL + NET_ADMIN, NET_BIND_SERVICE
Agent (gateway mode) cap_drop: ALL + NET_ADMIN, DAC_OVERRIDE, CHOWN, SETUID, SETGID, FOWNER
Agent (single mode) cap_drop: ALL

Testing

  • Built and ran examples/local-coding with podman 5.8.2 + podman-compose 1.5.0
  • Built and ran examples/multi-agent (fleet mode) — all 4 containers healthy
  • Unit tests pass: go test ./internal/... all green
  • Backward compatible with Docker Compose (all directives are standard compose spec, podman-only userns_mode is behind template conditional)

Deployment notes

None — this is additive. Existing Docker users see no change unless they set CONTAINER_RUNTIME=podman or have podman on PATH without docker.

…ardening

Add container runtime detection that supports both Docker and Podman,
with automatic fallback to podman-compose standalone when the built-in
compose plugin is unavailable.

Changes:
- New internal/runtime package with Detect()/DetectOrDefault() functions
- Compose passthrough uses detected runtime instead of hardcoded docker
- Fleet mode expands include directives into multiple -f flags (podman-compose compat)
- cap_drop: ALL on all containers, adding back only required capabilities
- userns_mode: keep-id on agent containers when podman detected (single mode)
- Explicit default network declaration for podman-compose compatibility
- Integration tests use detected runtime binary

Security hardening:
- Gateway: cap_drop ALL, cap_add NET_ADMIN + NET_BIND_SERVICE
- Agent: cap_drop ALL, cap_add NET_ADMIN + DAC_OVERRIDE + CHOWN + SETUID + SETGID + FOWNER
- Single mode: cap_drop ALL (no capabilities needed)

Tested with podman 5.8.2 + podman-compose 1.5.0 on local-coding and
multi-agent examples. Backward compatible with Docker Compose.
@wongtsejian wongtsejian force-pushed the feat/podman-support branch from 15fc53b to 11e2d02 Compare June 3, 2026 07:31
@wongtsejian wongtsejian marked this pull request as ready for review June 3, 2026 07:53
wongtsejian and others added 20 commits June 3, 2026 17:00
…nv var

Runtime detection now uses only the config override parameter and PATH
auto-detection. The CONTAINER_RUNTIME environment variable is no longer
consulted — callers pass the config value explicitly via DetectWithOverride().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add SSH feature plugin for remote development access (host key
  persistence, authorized_keys, root hooks for sshd setup)
- Extend FeatureContributions with RootHooks, Capabilities, and Ports
- Add HTTP reverse proxy to gateway for plain HTTP credential injection
- Gateway entrypoint template: iptables rules for HTTP port redirect
- Fix rewriter domain normalization (strip scheme/port for matching)
- Gate /etc/hosts cleanup and CA cert wait on HasMITM (not HasGateway)
- Split feature ports (agent) from runtime ports (gateway) in compose

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove stale HTTPDomains code from gateway main and update tests
to use HTTPServices structs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The private key was committed to the repo and must be considered
compromised. Replace with runtime generation on first boot.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…example

Remove SSH plugin, scripts, and key files from local-coding so it stays
focused on external-services + custom-runtime. The new local-coding-ssh
example preserves the full SSH setup for users who need shell access to
the agent container.
Allows plain HTTP URLs for services reachable without a Docker network
(e.g., host.containers.internal from inside a container).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The SSH plugin no longer reads private key contents at generate time.
Instead, it contributes volume mounts that bind the key files into
/run/ssh/ and the entrypoint copies them to the correct locations
at container start.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Without this, Zed remote sessions fail with "codex-acp is not
registered" since the settings aren't in the home override.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The gateway service now loads environment variables from ../.env,
allowing credentials like API keys to be injected without hardcoding
them in the compose file or agent.yaml.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…jection

The gateway rewriter injects the real API key from .env into HTTP
requests, replacing the dummy token in codex config.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove duplicate .zed/settings.json file
- Fix JSON formatting in .config/zed/settings.json
- Update gateway endpoints from localhost to production
- Modified AuthHeaderRewriter and OAuthRewriter to match against
  host:port first, then bare hostname
- Refactored domain normalization to preserve port information instead
  of stripping it
- Updated gateway config generation to use refined domain splitting
  logic
- Added env_file reference to docker-compose service template
Agent-level container_runtime overrides fleet-level shared config.
Priority: agent.yaml > fleet.yaml shared > PATH auto-detect.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat(plugins): add SSH plugin and HTTP reverse proxy for gateway
@wongtsejian wongtsejian changed the title feat: add podman support with runtime auto-detection and capability hardening feat: add podman and ssh plugin support Jun 3, 2026
wongtsejian and others added 2 commits June 3, 2026 18:27
The external-services plugin now supports http:// URLs, so the test
needs to use a truly unsupported scheme (ftp://) instead.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Lift http.Transport to HTTPProxy struct for connection reuse
- Add path traversal protection on SSH volume mount paths
- Validate SSH port range (1-65535)
- Handle non-NotExist os.Stat errors for host key
- Use port-aware domain matching in HTTP proxy (normalize to host:port)
- Remove dead collectHTTPPortsFromDomains function
- Remove unused Podman field from ComposeBuilder

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

1 participant