Skip to content

[awf] agent-container: NODE_EXTRA_CA_CERTS invalid after chroot breaks ssl-bump TLS trust for Claude Code #1546

@lpcox

Description

@lpcox

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:

  1. Runs with --ssl-bump + chroot mode
  2. Asserts NODE_EXTRA_CA_CERTS resolves to an accessible path after chroot
  3. Validates that curl (alloweddomain/redacted) succeeds without certificate errors

Generated by Firewall Issue Dispatcher ·

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions