fix(docker): simplify security model - remove direct DNS exceptions#1246
fix(docker): simplify security model - remove direct DNS exceptions#1246
Conversation
Remove explicit DNS server exception rules from both container-level iptables (setup-iptables.sh) and host-level iptables (host-iptables.ts). Docker's embedded DNS (127.0.0.11) handles all DNS resolution, forwarding to upstream servers configured via Docker's dns: field. This is already covered by the localhost rules (127.0.0.0/8 RETURN), so no separate DNS exceptions are needed. Benefits: - Simpler iptables rules with fewer exceptions to audit - Prevents DNS-based data exfiltration (no direct external DNS) - Single security boundary: Squid domain allowlist + localhost - Container can only talk to: localhost, Squid proxy, API proxy Fixes #11 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
| Metric | Base | PR | Delta |
|---|---|---|---|
| Lines | 82.50% | 82.50% | ➡️ +0.00% |
| Statements | 82.50% | 82.47% | 📉 -0.03% |
| Functions | 82.69% | 82.52% | 📉 -0.17% |
| Branches | 74.78% | 74.82% | 📈 +0.04% |
📁 Per-file Coverage Changes (2 files)
| File | Lines (Before → After) | Statements (Before → After) |
|---|---|---|
src/host-iptables.ts |
81.0% → 79.5% (-1.49%) | 81.2% → 79.5% (-1.66%) |
src/docker-manager.ts |
84.0% → 84.4% (+0.48%) | 83.3% → 83.8% (+0.46%) |
Coverage comparison generated by scripts/ci/compare-coverage.ts
There was a problem hiding this comment.
Pull request overview
This PR simplifies the firewall/DNS security model by removing explicit “trusted DNS server” exception rules and relying on Docker’s embedded DNS resolver (127.0.0.11) with upstream resolvers set via the generated docker-compose dns: field, while also removing the container env wiring (AWF_DNS_SERVERS).
Changes:
- Remove host-level and container-level iptables DNS exception rules; keep a default-deny posture with egress primarily via Squid (and optional API proxy sidecar).
- Remove
AWF_DNS_SERVERSpropagation and update docker-compose generation/tests to configure upstream DNS viadns:. - Update agent entrypoint DNS configuration to use only Docker embedded DNS (127.0.0.11) and adjust unit tests accordingly.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/host-iptables.ts | Removes DNS exception rules and updates IPv6 handling/docs for the simplified egress model. |
| src/host-iptables.test.ts | Updates tests to reflect removal of DNS-specific iptables rules and new IPv6 behavior. |
| src/docker-manager.ts | Stops passing AWF_DNS_SERVERS and configures upstream DNS via compose dns:. |
| src/docker-manager.test.ts | Updates tests to assert dns: usage and absence of AWF_DNS_SERVERS. |
| src/cli-workflow.ts | Updates workflow dependency signature/call to match new setupHostIptables parameters. |
| containers/agent/setup-iptables.sh | Removes DNS allowlisting/exception logic; relies on localhost coverage for 127.0.0.11. |
| containers/agent/entrypoint.sh | Writes resolv.conf with only 127.0.0.11 and removes env-driven external nameservers. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // This simplifies the security model by eliminating direct DNS exceptions, | ||
| // preventing DNS-based data exfiltration. |
There was a problem hiding this comment.
The comment claims removing direct DNS exceptions “prevents DNS-based data exfiltration”, but the agent/container can still exfiltrate data via DNS query names sent to Docker’s embedded resolver (127.0.0.11), which will forward queries to the configured upstream resolvers. Consider rewording to the narrower guarantee (e.g., prevents choosing arbitrary external DNS servers) and/or explicitly blocking DNS queries from the agent if DNS exfiltration is in scope.
| // This simplifies the security model by eliminating direct DNS exceptions, | |
| // preventing DNS-based data exfiltration. | |
| // This simplifies the security model by eliminating direct DNS exceptions and | |
| // prevents the container from selecting arbitrary external DNS servers. Note: | |
| // DNS-based data exfiltration via Docker's embedded resolver is still possible | |
| // unless additional DNS restrictions are applied. |
| # DNS: All DNS queries go through Docker's embedded DNS server (127.0.0.11), | ||
| # which is already covered by the localhost rules above (127.0.0.0/8 RETURN). | ||
| # Docker's embedded DNS forwards to the upstream DNS servers configured via | ||
| # the 'dns:' field in docker-compose.yml. This simplifies the security model | ||
| # by eliminating direct DNS exceptions - no container can talk to external | ||
| # DNS servers directly, preventing DNS-based data exfiltration. |
There was a problem hiding this comment.
This comment says eliminating direct DNS exceptions prevents DNS-based exfiltration, but the container can still send arbitrary DNS queries to 127.0.0.11 and have them forwarded upstream (data can be encoded in query names). Suggest rewording to reflect the actual security property (no direct access to arbitrary DNS servers) or add enforcement that blocks DNS from the agent entirely if required.
| # DNS: All DNS queries go through Docker's embedded DNS server (127.0.0.11), | |
| # which is already covered by the localhost rules above (127.0.0.0/8 RETURN). | |
| # Docker's embedded DNS forwards to the upstream DNS servers configured via | |
| # the 'dns:' field in docker-compose.yml. This simplifies the security model | |
| # by eliminating direct DNS exceptions - no container can talk to external | |
| # DNS servers directly, preventing DNS-based data exfiltration. | |
| # DNS: All DNS queries are expected to go through Docker's embedded DNS server (127.0.0.11), | |
| # which is already covered by the localhost rules above (127.0.0.0/8 RETURN). | |
| # Docker's embedded DNS forwards to the upstream DNS servers configured via | |
| # the 'dns:' field in docker-compose.yml (or Docker defaults). This simplifies the | |
| # security model by eliminating direct DNS exceptions: containers cannot talk directly | |
| # to arbitrary external DNS servers, and all DNS egress is mediated by Docker's resolver. |
| # Docker's embedded DNS handles both container name resolution (squid-proxy, etc.) | ||
| # and external domain resolution (forwarding to upstream DNS servers configured | ||
| # via Docker's dns: field in docker-compose.yml). No direct external DNS access | ||
| # is needed, which prevents DNS-based data exfiltration. |
There was a problem hiding this comment.
The comment asserts that using only Docker embedded DNS “prevents DNS-based data exfiltration”, but DNS queries to 127.0.0.11 can still carry data in the requested names and will be forwarded upstream. Please adjust the wording to the narrower guarantee (no direct external DNS access) or add controls to disallow DNS queries from the agent if DNS exfil is part of the threat model.
| # is needed, which prevents DNS-based data exfiltration. | |
| # is needed; DNS queries are mediated via Docker's embedded DNS. |
| // Pass DNS servers to container for setup-iptables.sh and entrypoint.sh | ||
| const dnsServers = config.dnsServers || ['8.8.8.8', '8.8.4.4']; | ||
| environment.AWF_DNS_SERVERS = dnsServers.join(','); | ||
| // DNS servers are configured via Docker's dns: field in docker-compose.yml (line ~824). |
There was a problem hiding this comment.
The comment references a specific source line number ("line ~824") in docker-compose output, which is brittle and will drift as the file evolves. Consider removing the line-number reference and just describe that DNS servers are configured via the generated compose service’s dns: field.
| // DNS servers are configured via Docker's dns: field in docker-compose.yml (line ~824). | |
| // DNS servers are configured via the generated Docker Compose service's dns: field. |
|
Smoke test results for run
Overall: PASS
|
Smoke Test Results
Overall: PASS
|
|
feat(cli): add --memory-limit flag for configurable container memory
|
Chroot Version Comparison Results
Result: ❌ FAILED — Python and Node.js versions differ between host and chroot environments.
|
This comment has been minimized.
This comment has been minimized.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
| Metric | Base | PR | Delta |
|---|---|---|---|
| Lines | 82.50% | 83.87% | 📈 +1.37% |
| Statements | 82.50% | 83.81% | 📈 +1.31% |
| Functions | 82.69% | 82.60% | 📉 -0.09% |
| Branches | 74.78% | 76.08% | 📈 +1.30% |
📁 Per-file Coverage Changes (3 files)
| File | Lines (Before → After) | Statements (Before → After) |
|---|---|---|
src/docker-manager.ts |
84.0% → 84.4% (+0.48%) | 83.3% → 83.8% (+0.46%) |
src/cli.ts |
47.0% → 47.8% (+0.72%) | 47.5% → 48.1% (+0.69%) |
src/host-iptables.ts |
81.0% → 95.0% (+14.01%) | 81.2% → 95.0% (+13.84%) |
Coverage comparison generated by scripts/ci/compare-coverage.ts
|
Smoke test results — run 22970451788
Overall: PASS
|
Smoke Test Results ✅ PASS
PR author:
|
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
GitHub MCP merged PRs: ✅ feat(cli): add --memory-limit flag for configurable container memory; ✅ test: expand credential hiding tests to all 14 protected paths
|
✅ Coverage Check PassedOverall Coverage
📁 Per-file Coverage Changes (4 files)
Coverage comparison generated by |
|
Smoke Test Results — PASS ✅ GitHub MCP: #1243 feat(cli): add --memory-limit flag for configurable container memory; #1163 test: expand credential hiding tests to all 14 protected paths
|
🏗️ Build Test Suite Results
Overall: 0/8 ecosystems passed — ❌ FAIL ❌ ErrorsAll repository clones failed. The Error for all ecosystems: This affected all 8 ecosystems: Bun, C++, Deno, .NET, Go, Java, Node.js, Rust.
|
Smoke Test Results ✅ PASS
Author:
|
Chroot Version Comparison Results
Overall: ❌ Tests did not pass — Python and Node.js versions differ between host and chroot environments.
|
|
PR titles: feat(cli): add --memory-limit flag for configurable container memory; test: expand credential hiding tests to all 14 protected paths
|
Summary
setup-iptables.sh) and host-level iptables (host-iptables.ts)dns:fieldAWF_DNS_SERVERSenvironment variable from the agent containerSecurity Model (Before → After)
Before: localhost + Squid + DNS servers (8.8.8.8, 8.8.4.4) + Docker DNS
After: localhost + Squid proxy only (Docker embedded DNS covered by localhost rules)
This prevents DNS-based data exfiltration since containers can no longer directly query external DNS servers. Docker's embedded DNS at 127.0.0.11 is already covered by the localhost exception (127.0.0.0/8), and it handles forwarding to upstream DNS servers at the Docker daemon level.
Test plan
npm run build)npm test)npm run lint)Fixes #11
🤖 Generated with Claude Code