Problem
When --ssl-bump is enabled and --chroot is active, Claude Code (and any Node.js process running inside the chroot) fails to make TLS connections through the Squid proxy because NODE_EXTRA_CA_CERTS points to a path that does not exist after chroot /host.
Symptom observed in the Squid audit log: repeated transaction-end-before-headers failures attributed to client: ::1, with NONE_NONE decisions and 0-byte status. Claude Code exhausts 10 API retries and exits with EHOSTUNREACH. MCP servers and other container-level processes work correctly because they run before the chroot activates and the container's /usr/local/share/ca-certificates/awf-ca.crt is still accessible.
Context
Upstream issue: github/gh-aw#23765 (follow-up from #23614).
Reported on:
- gh-aw CLI v0.65.1
- AWF container 0.25.5
- Claude Code CLI 2.1.87
Root Cause
Files involved
src/docker-manager.ts lines 962–969 — mounts the per-session CA cert into the agent container at /usr/local/share/ca-certificates/awf-ca.crt and sets NODE_EXTRA_CA_CERTS to that path
containers/agent/entrypoint.sh lines 106–119 — calls update-ca-certificates and exports NODE_EXTRA_CA_CERTS="/usr/local/share/ca-certificates/awf-ca.crt"
containers/agent/entrypoint.sh lines 724–729 — executes chroot /host /bin/bash -c "...", which changes the filesystem root to the host's /host mount
Why it fails
The CA certificate is a Docker volume mount from the workdir into the container at /usr/local/share/ca-certificates/awf-ca.crt. This path exists on the container's overlay filesystem but is not bind-mounted under /host. After chroot /host, the new root is the host filesystem. Node.js (Claude Code) reads NODE_EXTRA_CA_CERTS and tries to open /usr/local/share/ca-certificates/awf-ca.crt — which is now resolved relative to the host's /, where the AWF CA does not exist.
Squid uses ssl-bump (TLS interception), so after the CONNECT tunnel is established, it presents a dynamically-generated certificate signed by the AWF CA. Because Claude Code cannot load the AWF CA, it rejects the certificate, closes the TLS connection before sending any HTTP headers, and Squid logs transaction-end-before-headers. The client: ::1 label is an artifact of how Squid's ssl-bump internals log the failed inner ssl-bump connection.
Proposed Solution
Apply the same pattern used for one-shot-token.so and get-claude-key.sh (lines 420–474 in entrypoint.sh): copy the CA cert to /host/tmp/awf-lib/ before the chroot activates, then update NODE_EXTRA_CA_CERTS to the chroot-relative path.
In containers/agent/entrypoint.sh, add a block after the get-claude-key.sh copy section (around line 474) and before the capsh check:
# Copy AWF CA certificate to chroot-accessible path for ssl-bump TLS trust
# NODE_EXTRA_CA_CERTS is set to a container path (/usr/local/share/ca-certificates/awf-ca.crt)
# which is NOT accessible after chroot /host. Copy it to /tmp/awf-lib/ (always writable)
# so that Node.js processes (Claude Code, MCP servers) inside the chroot trust the Squid CA.
if [ "\$\{AWF_SSL_BUMP_ENABLED}" = "true" ] && [ -f /usr/local/share/ca-certificates/awf-ca.crt ]; then
if mkdir -p /host/tmp/awf-lib 2>/dev/null; then
if cp /usr/local/share/ca-certificates/awf-ca.crt /host/tmp/awf-lib/awf-ca.crt 2>/dev/null; then
export NODE_EXTRA_CA_CERTS="/tmp/awf-lib/awf-ca.crt"
echo "[entrypoint] AWF CA certificate copied to chroot at /tmp/awf-lib/awf-ca.crt"
echo "[entrypoint] NODE_EXTRA_CA_CERTS updated to \$\{NODE_EXTRA_CA_CERTS}"
else
echo "[entrypoint][WARN] Could not copy AWF CA certificate to chroot — ssl-bump TLS may fail in chroot"
fi
fi
fi
Additionally, to cover non-Node.js tools inside the chroot that rely on the system CA bundle (curl, git, Python requests, etc.), also copy the cert to the host's ca-certificates directory and note that a full update-ca-certificates on the host side is not feasible. For those tools, consider setting SSL_CERT_FILE=/tmp/awf-lib/awf-ca.crt or appending it to the chroot CA bundle (e.g., /etc/ssl/certs/ca-certificates.crt) inside the chroot's /host/etc/ssl/certs/.
The existing CLEANUP_CMD in the chroot exit trap already removes /tmp/awf-lib (line 702), so no extra cleanup is needed.
Also consider adding an integration test that:
- Runs with
--ssl-bump + chroot mode
- Asserts
NODE_EXTRA_CA_CERTS resolves to an accessible path after chroot
- Validates that
curl (alloweddomain/redacted) succeeds without certificate errors
Generated by Firewall Issue Dispatcher · ◷
Problem
When
--ssl-bumpis enabled and--chrootis active, Claude Code (and any Node.js process running inside the chroot) fails to make TLS connections through the Squid proxy becauseNODE_EXTRA_CA_CERTSpoints to a path that does not exist afterchroot /host.Symptom observed in the Squid audit log: repeated
transaction-end-before-headersfailures attributed toclient: ::1, withNONE_NONEdecisions and 0-byte status. Claude Code exhausts 10 API retries and exits withEHOSTUNREACH. MCP servers and other container-level processes work correctly because they run before the chroot activates and the container's/usr/local/share/ca-certificates/awf-ca.crtis still accessible.Context
Upstream issue: github/gh-aw#23765 (follow-up from #23614).
Reported on:
Root Cause
Files involved
src/docker-manager.tslines 962–969 — mounts the per-session CA cert into the agent container at/usr/local/share/ca-certificates/awf-ca.crtand setsNODE_EXTRA_CA_CERTSto that pathcontainers/agent/entrypoint.shlines 106–119 — callsupdate-ca-certificatesand exportsNODE_EXTRA_CA_CERTS="/usr/local/share/ca-certificates/awf-ca.crt"containers/agent/entrypoint.shlines 724–729 — executeschroot /host /bin/bash -c "...", which changes the filesystem root to the host's/hostmountWhy it fails
The CA certificate is a Docker volume mount from the workdir into the container at
/usr/local/share/ca-certificates/awf-ca.crt. This path exists on the container's overlay filesystem but is not bind-mounted under/host. Afterchroot /host, the new root is the host filesystem. Node.js (Claude Code) readsNODE_EXTRA_CA_CERTSand tries to open/usr/local/share/ca-certificates/awf-ca.crt— which is now resolved relative to the host's/, where the AWF CA does not exist.Squid uses ssl-bump (TLS interception), so after the
CONNECTtunnel is established, it presents a dynamically-generated certificate signed by the AWF CA. Because Claude Code cannot load the AWF CA, it rejects the certificate, closes the TLS connection before sending any HTTP headers, and Squid logstransaction-end-before-headers. Theclient: ::1label is an artifact of how Squid's ssl-bump internals log the failed inner ssl-bump connection.Proposed Solution
Apply the same pattern used for
one-shot-token.soandget-claude-key.sh(lines 420–474 inentrypoint.sh): copy the CA cert to/host/tmp/awf-lib/before the chroot activates, then updateNODE_EXTRA_CA_CERTSto the chroot-relative path.In
containers/agent/entrypoint.sh, add a block after theget-claude-key.shcopy section (around line 474) and before thecapshcheck:Additionally, to cover non-Node.js tools inside the chroot that rely on the system CA bundle (curl, git, Python requests, etc.), also copy the cert to the host's ca-certificates directory and note that a full
update-ca-certificateson the host side is not feasible. For those tools, consider settingSSL_CERT_FILE=/tmp/awf-lib/awf-ca.crtor appending it to the chroot CA bundle (e.g.,/etc/ssl/certs/ca-certificates.crt) inside the chroot's/host/etc/ssl/certs/.The existing
CLEANUP_CMDin the chroot exit trap already removes/tmp/awf-lib(line 702), so no extra cleanup is needed.Also consider adding an integration test that:
--ssl-bump+ chroot modeNODE_EXTRA_CA_CERTSresolves to an accessible path after chrootcurl (alloweddomain/redacted)succeeds without certificate errors