From d75d5f3e48d481207ee13ea1161471cc8f2be782 Mon Sep 17 00:00:00 2001 From: didericis Date: Mon, 25 May 2026 17:25:35 -0400 Subject: [PATCH] fix(egress-proxy-apply): chmod tmp file 0644 so mitmproxy can read post-cp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit apply_routes_change wrote the proposed routes via `tempfile.mkstemp` (default mode 0600) then `docker cp`'d into the egress-proxy container. docker cp preserves mode + host uid, so the file landed inside the container as 0600 owned by the host user's uid — which is not the mitmproxy user (uid 1000) the addon runs as. The SIGHUP-triggered reload then failed with PermissionError on the re-read, the old routes table stayed in memory, and the operator-approved route never took effect. Symptoms reported: - Operator approves egress-proxy-block proposal that adds google.com to routes. - Agent retries `curl https://google.com` and still gets 403 "egress-proxy: host 'google.com' is not in the bottle's egress_proxy.routes allowlist." - `docker exec cat /etc/egress-proxy/routes.yaml` returns "Permission denied" (mitmproxy user can't read it, so the reload couldn't either). Fix: chmod 0644 on the host tmp file before docker cp. Mirrors the same pattern in DockerEgressProxy.start which already chmods the original routes.yaml + the CAs before cp. The proposed routes content carries no secrets (tokens live in the egress-proxy container's environ, not the routes file), so 0644 in /tmp for the brief window between write and cp is safe. Co-Authored-By: Claude Opus 4.7 --- claude_bottle/backend/docker/egress_proxy_apply.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/claude_bottle/backend/docker/egress_proxy_apply.py b/claude_bottle/backend/docker/egress_proxy_apply.py index 6fe8b56..0d0e68b 100644 --- a/claude_bottle/backend/docker/egress_proxy_apply.py +++ b/claude_bottle/backend/docker/egress_proxy_apply.py @@ -80,6 +80,17 @@ def apply_routes_change(slug: str, new_content: str) -> tuple[str, str]: try: with os.fdopen(fd, "w") as f: f.write(new_content) + # mkstemp creates the file with mode 0600. `docker cp` + # preserves mode + host uid into the container, so without + # chmod the file lands as 0600 owned by the host user's uid, + # which inside the container is not mitmproxy (uid 1000) — + # the addon's reload then fails with PermissionError on the + # SIGHUP-triggered re-read and the old routes table stays in + # memory. Bump to 0644 so mitmproxy can read it post-cp; + # the host stage_dir doesn't apply to this tmp file but the + # content isn't secret (no tokens — those live in the + # container's environ), so 0644 in /tmp is fine. + os.chmod(tmp_path, 0o644) cp = subprocess.run( ["docker", "cp", tmp_path, f"{container}:{EGRESS_PROXY_ROUTES_IN_CONTAINER}"],