Files
Auto-Trader/docker-compose.yml
T
Haozhe Wu 4e3c91c77a security: address all 10 findings from external audit (issue #476)
Implements VT-001 through VT-010 from the 2026-07-10 external security
audit (Trivy/Semgrep/ZAP/Gitleaks/pip-audit against a real deployment):

- VT-001 (Critical): AST hardening in backtest/runner.py rejects
  network/subprocess/eval/os.environ/unsafe-open calls reachable from
  generated SignalEngine method bodies (previously only checked
  top-level statements). Defense-in-depth: ephemeral per-run HOME,
  RLIMIT_AS/NOFILE caps, best-effort UID-drop to a new vibe-sandbox
  user (currently inert under Docker's cap model - see session doc;
  accepted as documented residual gap, primary AST defense unaffected).
- VT-002: multi-stage Dockerfile split so build-essential never
  reaches the runtime image.
- VT-003: SSE auth moved from a long-lived API key in the URL query
  string to short-lived single-use tickets; access-log redaction for
  any remaining query secrets.
- VT-004: agent/.env split-brain fixed (bind mount + atomic 0600
  writes) between docker-compose and Web UI settings writes.
- VT-005: /correlation now requires auth + per-IP rate limit; generic
  exceptions no longer leak raw text to clients.
- VT-006: CSP-Report-Only + security headers middleware; Google Fonts
  replaced with self-hosted @fontsource packages.
- VT-007: docker-compose hardening (cap_drop ALL + minimal cap_add,
  no-new-privileges, read_only rootfs, resource limits).
- VT-008: /health split into /live (liveness) and /ready (real
  provider-config readiness check); /health kept as a compat alias.
- VT-009: base images pinned by digest, GitHub Actions pinned by
  commit SHA, dependabot.yml added, hash-verified requirements-lock.txt
  now actually wired into the Docker build via --require-hashes.
- VT-010: universe-panel pickle cache sidecar upgraded from an
  unkeyed SHA-256 (forgeable by anyone with cache-dir write access)
  to a keyed HMAC.

Implemented via a 5-parcel Workflow (file-disjoint, independent
adversarial verification per parcel, then a holistic final audit).
The final audit caught a real regression (CI env-var-gate violation)
and confirmed a dependency-pinning gap, both fixed and re-verified
here, including a latent transitive-dependency bug the fix surfaced
(smartmoneyconcepts<0.0.27 pulls an unbuildable zigzag package) - see
docs/sessions/2026-07-13_session_vt476_security_hardening.md (local,
gitignored) for full evidence trail.

Full suite: 5191 passed, 6 pre-existing/environmental failures
(unchanged from main). Docker build + hardened compose boot verified
end-to-end.
2026-07-13 01:40:53 +08:00

83 lines
3.1 KiB
YAML

services:
vibe-trading:
build: .
ports:
- "127.0.0.1:8899:8899"
env_file:
- agent/.env
environment:
- VIBE_TRADING_TRUST_DOCKER_LOOPBACK=1
# Ollama runs on the host; from inside the container "localhost" is the
# container itself, so default to reaching the host via host.docker.internal.
# Override by exporting OLLAMA_BASE_URL (or setting it in a top-level .env)
# when Ollama runs elsewhere or on a different port.
- OLLAMA_BASE_URL=${OLLAMA_BASE_URL:-http://host.docker.internal:11434}
extra_hosts:
# Maps host.docker.internal -> host gateway (needed on Linux; harmless on Docker Desktop).
# Requires Docker Engine >= 20.10 / Compose v2 (host-gateway support).
- "host.docker.internal:host-gateway"
volumes:
- vibe-runs:/app/agent/runs
- vibe-sessions:/app/agent/sessions
# User-level agent state: persistent memory, cross-session search index
# (sessions.db), user-created skills, shadow accounts, hypothesis
# registry, broker connector config, agent.json. Without this volume a
# rebuild/recreate wipes it all (#197).
- vibe-home:/home/vibe/.vibe-trading
- vibe-swarm-runs:/app/agent/.swarm/runs
- vibe-uploads:/app/agent/uploads
# Persist Web-UI-driven agent/.env edits (_write_env_values in
# src/api/helpers.py writes to /app/agent/.env) across container recreation.
# env_file above still seeds the initial boot env from the same file.
- ./agent/.env:/app/agent/.env
# --- Runtime hardening (VT-007) ---
cap_drop:
- ALL
# SETUID/SETGID are kept on purpose: runner.py drops privilege to the
# unprivileged vibe-sandbox user for LLM-generated-code subprocesses, which
# needs setuid/setgid. They are granted from container start (not gained via
# escalation), so no-new-privileges below does not neutralize them.
cap_add:
- SETUID
- SETGID
security_opt:
- "no-new-privileges:true"
read_only: true
# Writable scratch on the read-only rootfs. Named volumes above already
# cover agent/runs, agent/sessions, agent/uploads, agent/.swarm/runs and
# /home/vibe/.vibe-trading; these tmpfs mounts cover /tmp and the cache dirs
# matplotlib/fontconfig write to during PDF rendering.
tmpfs:
- /tmp
- /home/vibe/.cache
- /home/vibe/.config
# Bound a single runaway backtest. Generous self-hosted defaults; override
# in a compose override file for constrained hosts.
mem_limit: 4g
cpus: 2
pids_limit: 512
restart: unless-stopped
frontend:
image: node:20@sha256:8f693eaa7e0a8e71560c9a82b55fd54c2ae920a2ba5d2cde28bac7d1c01c9ba5 # node:20 digest resolved 2026-07-13
working_dir: /app
ports:
- "127.0.0.1:5899:5899"
volumes:
- ./frontend:/app
environment:
- VITE_API_URL=http://vibe-trading:8899
command: sh -c "npm install && npm run dev -- --host --port 5899"
profiles:
- frontend
depends_on:
vibe-trading:
condition: service_started
volumes:
vibe-runs:
vibe-sessions:
vibe-home:
vibe-swarm-runs:
vibe-uploads: