Block Docker gateway addresses in egress proxy by default#4395
Merged
tgrunnagle merged 3 commits intomainfrom Mar 27, 2026
Merged
Block Docker gateway addresses in egress proxy by default#4395tgrunnagle merged 3 commits intomainfrom
tgrunnagle merged 3 commits intomainfrom
Conversation
## Why Containerized MCP servers can reach host services via `host.docker.internal`, `gateway.docker.internal`, and the Docker bridge gateway IP (`172.17.0.1`). This enables lateral movement from a compromised or malicious MCP server to services running on the host, bypassing the container network boundary. The existing `insecure_allow_all` permission flag does not protect against this: users enabling it intend to open general internet access, not necessarily host access. These are distinct threat surfaces and warrant separate opt-ins. ## What changed The Squid egress proxy config now emits ACL deny rules for the three Docker gateway addresses **before** any allow rules. Squid evaluates access control in first-match-wins order, so placing the deny first ensures it cannot be bypassed by a subsequent `http_access allow all`. A new `--allow-docker-gateway` CLI flag (default `false`) provides an explicit opt-in for the small number of MCP servers that legitimately need host access. The flag threads through the full call chain: ``` --allow-docker-gateway (run_flags.go) → RunConfig.AllowDockerGateway (config.go) → runtime.Setup() (setup.go) → DeployWorkloadOptions.AllowDockerGateway (types.go) → createEgressSquidContainer() (client.go) → createTempEgressSquidConf() (squid.go) ``` Generated Squid config with default settings (blocking active): ```squid acl docker_gateway_hosts dstdomain host.docker.internal gateway.docker.internal acl docker_gateway_ip dst 172.17.0.1 http_access deny docker_gateway_hosts http_access deny docker_gateway_ip http_access allow all # (or ACL-based allow rules) http_access deny all ```
4d8edb3 to
ef0e29d
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4395 +/- ##
==========================================
- Coverage 69.49% 69.48% -0.02%
==========================================
Files 485 485
Lines 49841 49875 +34
==========================================
+ Hits 34638 34654 +16
- Misses 12528 12543 +15
- Partials 2675 2678 +3 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
jhrozek
approved these changes
Mar 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Block Docker gateway addresses in egress proxy by default
Summary
host.docker.internal,gateway.docker.internal, and the Docker bridge gateway IP, enabling lateral movement from a compromised server to the host. This blocks those addresses in the Squid egress proxy by default, even wheninsecure_allow_allis set — host access is a separate threat surface from general internet access and requires its own explicit opt-in.--allow-docker-gatewayCLI flag (defaultfalse) that threads throughRunFlags→RunConfig→DeployWorkloadOptions→createEgressSquidContainer→createTempEgressSquidConf→ Squid ACL config.dstdomainanddstdeny rules for the gateway addresses before any allow rules (Squid is first-match-wins); when--allow-docker-gatewayis passed, the deny block is omitted entirely.docker network inspect bridgerather than hardcoded, so the correct IP is used across Linux (172.17.0.1), Docker Desktop on macOS (192.168.65.1), and Colima/Rancher Desktop variants.optionspointer passed withisolateNetwork=trueno longer panics;allowDockerGatewaydefaults tofalsein that case.Type of change
Test plan
task test)Changes
pkg/container/docker/squid.gowriteDockerGatewayDenyRules(sb, gatewayIP)emits deny ACLs;createTempEgressSquidConfandcreateEgressSquidContaineracceptallowDockerGateway boolandgatewayIP stringpkg/container/docker/client.gogetDockerBridgeGatewayIPinspects the bridge network at runtime;(*Client).createEgressSquidContainerwrapper resolves and threads the IP; nil-safeallowDockerGatewayguardpkg/container/runtime/types.goAllowDockerGateway booladded toDeployWorkloadOptionspkg/runtime/setup.goallowDockerGateway boolparam; setscontainerOptions.AllowDockerGatewaypkg/runner/config.goAllowDockerGateway boolfield inRunConfigpkg/runner/config_builder.goWithAllowDockerGateway()builder optionpkg/runner/runner.gor.Config.AllowDockerGatewaytoruntime.Setup()cmd/thv/app/run_flags.go--allow-docker-gatewayflag registered and wired intoBuildRunnerConfigpkg/container/docker/squid_test.gopkg/container/docker/client_deploy_test.gofakeDeployOpscapturesallowDockerGateway; new test asserts value is forwarded end-to-endDoes this introduce a user-facing change?
New flag:
thv run --allow-docker-gateway. When--isolate-networkis active, passing this flag permits the MCP server container to connect to Docker gateway addresses that are otherwise blocked. The flag is off by default; no existing behaviour changes unless the flag is passed.Special notes for reviewers
gateway.docker.internalis Docker Desktop (macOS) specific; blocking it on Linux is harmless because the name does not resolve there.dstIP rule covers direct-IP access that bypasses DNS.getDockerBridgeGatewayIPfalls back to172.17.0.1ifdocker network inspect bridgefails (e.g. custom network setups), so the rule is always emitted.deployOpsinterface is unchanged — gateway IP resolution is an implementation detail of*Clientand invisible to fakes.--allow-docker-gatewaywithout--isolate-networkis silently ignored (egress container is never created without network isolation). A follow-up could add aslog.Warnfor this case.Generated with Claude Code