# Gateway data-plane image (PRD 0024 bundle shape; PRD 0070 gateway). # # The egress / git-gate / supervise *data plane* — one image, run by # the consolidated per-host gateway (PRD 0070). It is NOT the # orchestrator control plane: that is the separate, lean # `bot-bottle-orchestrator` image (Dockerfile.orchestrator, #384), which # ships only python + the stdlib-only `bot_bottle` package and none of # this image's mitmproxy / git / gitleaks payload. # # Collapses the prior per-daemon images (egress, git-gate, # supervise) into one. A small stdlib-Python init supervisor # (`bot_bottle.gateway.bootstrap`) spawns all daemons, forwards SIGTERM, and # propagates per-daemon stdout/stderr to the container log with a # `[name]` prefix. See PRD 0024 for the rationale. # # Layout: # # /usr/bin/gitleaks gitleaks binary # /app/egress_addon.py mitmproxy addon entry point # /app/egress-entrypoint.sh mitmdump launcher # /usr/local/lib/python*/bot_bottle/ installed package (all daemons + shared modules) # /app/egress_addon.py one-line shim: re-exports addons from package # (mitmdump -s requires a file path, not a module) # /etc/egress/routes.yaml bind-mounted at run time # /etc/git-gate/pre-receive docker-cp'd at start time # /git-gate-entrypoint.sh docker-cp'd at start time # /git-gate/creds/* docker-cp'd at start time # /git/* bare repos, populated at runtime # /home/mitmproxy/.mitmproxy/ mitmproxy CA dir # (No bot-bottle.db mount: the data plane reaches the supervise queue over the # control-plane RPC and never opens the DB — PRD 0070 / issue #469.) # # Exposed ports inside the container: # 9099 egress (mitmproxy, agent-facing HTTPS proxy) # 9418 git-gate (git-daemon) # 9420 git-gate smart HTTP (VM-backend agent-facing transport) # 9100 supervise (MCP HTTP) # Based on `python:3.12-slim` (Debian trixie) rather than the # `mitmproxy/mitmproxy` image (Debian bookworm), matching the trixie base the # orchestrator image needs for buildah (Dockerfile.orchestrator.fc). mitmproxy # is pip-installed to the same effect as the upstream image. FROM python:3.12-slim # Runtime system deps: # git supplies the `git daemon` subcommand (no separate package) # plus the core `git` binary the pre-receive hook invokes. # openssh-client supplies the upstream SSH transport the # pre-receive hook uses to forward accepted refs. # ca-certificates is needed for mitmdump upstream TLS. RUN apt-get update \ && apt-get install -y --no-install-recommends \ git openssh-client ca-certificates \ && rm -rf /var/lib/apt/lists/* # mitmdump (the egress data plane). The upstream mitmproxy image baked # this in; on the plain python base we pip-install the same pinned # version. Its CA dir is set explicitly via `--set confdir=` in # egress-entrypoint.sh, so it doesn't depend on a `mitmproxy` home user. RUN pip install --no-cache-dir mitmproxy==11.1.3 # gitleaks (the pre-receive hook's secret scanner). Installed from its # official release, pinned by version + SHA256 and verified — rather than # using a third-party image as a build stage (supply-chain surface, and it # would pin us to that image's cadence). python (already present) does the # download so we add no curl/wget. trixie apt also ships gitleaks, but an # older 8.16; the pinned download keeps the verified 8.30.1. # # Arch-aware: the asset + SHA are picked from the build's target # architecture so an arm64 host (Apple Silicon) gets the arm64 binary # rather than an x86_64 one that dies with "Exec format error" the first # time the pre-receive hook runs it. TARGETARCH is auto-populated by # BuildKit; the dpkg fallback keeps it correct under a legacy builder. ARG GITLEAKS_VERSION=8.30.1 ARG GITLEAKS_SHA256_AMD64=551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb ARG GITLEAKS_SHA256_ARM64=e4a487ee7ccd7d3a7f7ec08657610aa3606637dab924210b3aee62570fb4b080 ARG TARGETARCH RUN arch="${TARGETARCH:-$(dpkg --print-architecture)}" \ && case "$arch" in \ amd64) asset="linux_x64"; sha="${GITLEAKS_SHA256_AMD64}" ;; \ arm64) asset="linux_arm64"; sha="${GITLEAKS_SHA256_ARM64}" ;; \ *) echo "unsupported gitleaks target arch: $arch" >&2; exit 1 ;; \ esac \ && url="https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_${asset}.tar.gz" \ && python3 -c "import sys,urllib.request; urllib.request.urlretrieve(sys.argv[1], '/tmp/gitleaks.tar.gz')" "$url" \ && echo "${sha} /tmp/gitleaks.tar.gz" | sha256sum -c - \ && tar -xzf /tmp/gitleaks.tar.gz -C /usr/bin gitleaks \ && rm /tmp/gitleaks.tar.gz # Install bot_bottle as a proper package so entry-point scripts can use # `from bot_bottle.X import Y` absolute imports. A rename or a missing # module is caught at pip-install time — not at container runtime. COPY pyproject.toml /src/ COPY bot_bottle/ /src/bot_bottle/ RUN pip install --no-cache-dir /src/ # mitmdump -s requires a file path, not a module. Write a one-line shim that # re-exports `addons` from the installed package; mitmdump finds it there. # WORKDIR here also creates /app so the shim + COPYs below can write into it # (nothing created /app before this point). WORKDIR /app RUN printf 'from bot_bottle.gateway.egress.addon import addons\n' > /app/egress_addon.py COPY bot_bottle/gateway/egress/entrypoint.sh /app/egress-entrypoint.sh RUN chmod +x /app/egress-entrypoint.sh # Pre-create runtime directories the compose renderer + start # step expect to exist. `docker cp` does not create intermediate # dirs, and bind mounts won't either if the parent is missing. RUN mkdir -p \ /etc/egress \ /etc/git-gate \ /git-gate/creds \ /git \ /run/supervise \ /home/mitmproxy/.mitmproxy # Documentation only — the compose renderer publishes whichever # subset the bottle uses. EXPOSE 8888 9099 9418 9420 9100 # PID 1 is the supervisor. It owns signal handling and exit-code # propagation; no `exec` chain in the entrypoint itself. ENTRYPOINT ["python3", "-m", "bot_bottle.gateway.bootstrap"]