Skip to content

Harden Docker infra, add credential passthrough and SSH agent forwarding#29226

Closed
pnlpleasr wants to merge 9 commits intoopenclaw:mainfrom
pnlpleasr:docker-infra-hardening
Closed

Harden Docker infra, add credential passthrough and SSH agent forwarding#29226
pnlpleasr wants to merge 9 commits intoopenclaw:mainfrom
pnlpleasr:docker-infra-hardening

Conversation

@pnlpleasr
Copy link
Copy Markdown

Summary

  • SSH agent forwarding: forward the host SSH agent socket into sandbox containers so agents can git push via SSH without exposing private keys. Exact-match allowlist in the security validator with 4 regression tests.
  • Credential passthrough: explicitly pass TELEGRAM_BOT_TOKEN, GOOGLE_PLACES_API_KEY, NOTION_API_KEY, OPENAI_WHISPER_API_KEY, and CLAUDE_* session keys through docker-compose.yml to gateway/CLI containers.
  • Security hardening: robust .env parser (quote stripping, export prefix, key validation, unmatched quote warnings), tryRealpathAbsolute fail-closed, chmod 600 on temp files before writing secrets, trap cleanup, env var sanitizer coverage for SESSION_KEY/COOKIE patterns.
  • Inline documentation: architecture comments across Dockerfile, Dockerfile.sandbox, docker-compose.yml, docker-setup.sh, and validate-sandbox-security.ts.

Changes

File What
Dockerfile Docker CLI install, chmod 1777 runs as USER root, architecture header
Dockerfile.sandbox SSH agent forwarding design, --groups root for socket access
docker-compose.yml UID/GID parameterization, credential passthrough with ${VAR:-} defaults, vault mount (long-form for spaces), loopback port binding
docker-setup.sh Robust .env loader, UID/GID detection + root warning, vault dir allows spaces, chmod 600 + trap in upsert_env, unmatched quote warning
validate-sandbox-security.ts ALLOWED_HOST_PATHS allowlist, tryRealpathAbsolute fail-closed for non-absolute paths
validate-sandbox-security.test.ts 4 allowlist regression tests (exact match, sibling blocked, parent blocked, traversal blocked)
sanitize-env-vars.ts SESSION_KEY, SESSION_TOKEN, COOKIE added to blocklist
.dockerignore .env excluded from build context

Test plan

  • npx vitest run src/agents/sandbox/validate-sandbox-security.test.ts — 19 tests pass
  • npx vitest run src/agents/sandbox/sanitize-env-vars.test.ts — 4 tests pass
  • Gateway image rebuilt and container running
  • End-to-end: send Telegram message asking agent to clone a repo via SSH

🤖 Generated with Claude Code

pnlpleasr and others added 9 commits February 22, 2026 00:44
Design for a pre-execution verification gateway that supports HTTP
webhooks and built-in Telegram approval for tool calls. Layers on top
of existing tool policy and exec allowlist systems.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
9-task TDD plan covering config types, zod validation, scope matcher,
webhook client, Telegram verifier, orchestrator, pipeline wiring,
documentation, and integration tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…oc and HTML briefs

Incorporates P0/P1 fixes from tech lead review:
- Telegram: direct API polling instead of long-polling bot lifecycle
- Telegram: sender validation via allowedUserIds
- Webhook: HTTPS enforcement, param redaction, response size limits
- Orchestrator: most-restrictive-wins failMode resolution

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SVG component architecture diagram showing existing pipeline, verifier
subsystem, and external systems. Webhook verification sequence diagram
showing the 9-step allow path and alt deny path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Enable sandbox containers to git push/pull via SSH without raw private
keys entering the container. Docker Desktop's SSH agent socket is bind-
mounted and proxied, so only cryptographic signatures cross the boundary.

- Add openssh-client to sandbox image
- Add sandbox user to group root for agent socket access (root:root 0660)
- Allowlist /run/host-services/ssh-auth.sock in security validator
- Add 4 test cases for allowlist (sibling, parent, traversal all blocked)

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

Docker infrastructure:
- Install Docker CLI in gateway image for sandbox management
- Parameterize UID/GID for macOS compatibility (UID 501)
- Bind ports to loopback only (127.0.0.1)
- Mount vault, docker.sock, and duplicate host-path volumes
- Fix chmod 1777 to run as root (was running as node)
- Warn when docker-setup.sh runs as root (UID 0)
- Redact token from setup script output

Credential passthrough:
- Pass TELEGRAM_BOT_TOKEN, GOOGLE_PLACES_API_KEY, NOTION_API_KEY,
  OPENAI_WHISPER_API_KEY through docker-compose.yml to gateway/CLI
- Load ~/.openclaw/.env secrets in docker-setup.sh with robust parser
  (handles quotes, export prefix, key validation, malformed line warnings)
- Persist secrets to project .env via upsert_env; chmod 600 the file
- Default OPENCLAW_VAULT_DIR to workspace/vault with validation

Security hardening:
- tryRealpathAbsolute now fails closed (returns null + logs) instead of
  silently falling back to unresolved path on realpath errors
- validateBindMounts throws on unresolvable paths (closes symlink bypass)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add architecture comments, design rationale, and operational notes to:
- Dockerfile: image purpose, design decisions, build/run references
- Dockerfile.sandbox: security constraints, SSH agent forwarding design
- docker-compose.yml: service roles, mount rationale, security notes
- docker-setup.sh: script overview, section headers, env var docs
- validate-sandbox-security.ts: defense layer summary, allowlist docs

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

P0 fixes:
- Add .env to .dockerignore to prevent secrets baking into image layers
- Fix chmod 600 race in upsert_env: set permissions on temp file before
  writing secrets, add trap cleanup on failure
- Add :- defaults to CLAUDE_* env vars so compose doesn't fail when unset
- Make tryRealpathAbsolute fail closed for non-absolute paths (return null)

P1 fixes:
- Allow spaces in OPENCLAW_VAULT_DIR for iCloud paths (Mobile Documents)
- Add SESSION_KEY, SESSION_TOKEN, COOKIE to env var sanitizer blocklist
- Add trap to clean up temp file containing secrets on script failure
- Warn on unmatched quotes in .env parser to prevent silent credential corruption

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation docker Docker and sandbox tooling agents Agent runtime and tooling size: M labels Feb 27, 2026
@pnlpleasr pnlpleasr closed this Feb 27, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8a67a3faf3

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread docker-compose.yml
# GID 0 (root group) grants access to /var/run/docker.sock inside the
# container, which is needed to spawn sibling sandbox containers.
group_add:
- "0"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use docker.sock group GID instead of hardcoded root group

This hardcodes supplementary group 0, but on standard Linux Docker installs /var/run/docker.sock is typically owned by the docker group (non-zero GID). With user: "${OPENCLAW_UID}:${OPENCLAW_GID}", the gateway process remains non-root and will get permission denied when trying to create sandbox containers unless the socket happens to be group root; this breaks sandbox execution on those hosts.

Useful? React with 👍 / 👎.

Comment thread docker-compose.yml
# Obsidian vault (or any knowledge base directory). Uses long-form
# syntax because iCloud paths contain spaces.
- type: bind
source: "${OPENCLAW_VAULT_DIR}"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Provide fallback for new OPENCLAW_VAULT_DIR bind source

The new bind mount source uses ${OPENCLAW_VAULT_DIR} with no default, so upgrades from older setups (whose existing project .env lacks this key) expand to an empty source and make docker compose up fail mount validation. Since docker-setup.sh is documented as one-time setup, this introduces a startup regression unless users rerun setup or manually patch .env.

Useful? React with 👍 / 👎.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Feb 27, 2026

Greptile Summary

This PR implements SSH agent forwarding and credential passthrough for the Docker infrastructure while adding comprehensive security hardening. The implementation is well-documented with inline architecture comments throughout.

Key improvements:

  • SSH agent forwarding via exact-match allowlist (/run/host-services/ssh-auth.sock) with 4 regression tests covering siblings, parents, and traversal attacks
  • Fail-closed tryRealpathAbsolute that returns null on resolution failures to prevent symlink escapes
  • Robust .env parser with quote stripping, export prefix handling, key validation, and unmatched quote warnings
  • chmod 600 on temp files before writing secrets with trap-based cleanup
  • Loopback-only port binding (127.0.0.1) prevents LAN exposure
  • .env files excluded from Docker build context

Security architecture:
The PR grants elevated privileges (Docker socket access, group root membership) that are necessary for the feature set but increase attack surface. These tradeoffs are explicitly documented in code comments. The security validation layer (validate-sandbox-security.ts) provides defense-in-depth against misconfiguration, with comprehensive test coverage (19 tests including symlink escape prevention).

Test coverage:
All security-critical paths have test coverage including allowlist exact-match validation, symlink resolution, and env var sanitization patterns.

Confidence Score: 4/5

  • This PR is safe to merge with careful operational monitoring
  • Score reflects high code quality and comprehensive test coverage, offset by the inherent security tradeoffs of Docker socket access. The implementation is correct with no logical errors found. Security validation is well-tested (19 tests pass). The main risks are architectural (Docker socket mount, group root membership) rather than implementation bugs, and these are explicitly documented and mitigated via validation layers.
  • Monitor validate-sandbox-security.ts closely in production - this is the critical defense layer that prevents Docker socket abuse. Ensure it's always enforced and never bypassed.

Last reviewed commit: 8a67a3f

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

Labels

agents Agent runtime and tooling docker Docker and sandbox tooling docs Improvements or additions to documentation size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant