Skip to content

fix: copy AWF CA cert to chroot-accessible path for ssl-bump#1555

Merged
lpcox merged 3 commits intomainfrom
fix/ssl-bump-chroot-ca-cert
Apr 1, 2026
Merged

fix: copy AWF CA cert to chroot-accessible path for ssl-bump#1555
lpcox merged 3 commits intomainfrom
fix/ssl-bump-chroot-ca-cert

Conversation

@lpcox
Copy link
Copy Markdown
Collaborator

@lpcox lpcox commented Apr 1, 2026

Problem

When --ssl-bump and --chroot are both active, NODE_EXTRA_CA_CERTS points to /usr/local/share/ca-certificates/awf-ca.crt — a Docker volume mount on the container's overlay filesystem. After chroot /host, this path is inaccessible because it's not under the /host mount tree.

Symptom: Claude Code exhausts 10 API retries with EHOSTUNREACH. Squid logs show transaction-end-before-headers with NONE_NONE decisions because the dynamically-generated ssl-bump certificate (signed by the AWF CA) is rejected by Node.js.

Root Cause

The CA cert is mounted into the container at /usr/local/share/ca-certificates/awf-ca.crt by docker-manager.ts. The entrypoint.sh sets NODE_EXTRA_CA_CERTS to this path. But after chroot /host, the filesystem root changes to the host mount, and the container-side path no longer exists.

Solution

Apply the same copy pattern used for one-shot-token.so and get-claude-key.sh: copy the CA cert to /host/tmp/awf-lib/ before the chroot activates, then update env vars to the chroot-relative path /tmp/awf-lib/awf-ca.crt.

Changes in containers/agent/entrypoint.sh:

  1. Non-chroot ssl-bump (lines 104-119): Also set SSL_CERT_FILE and REQUESTS_CA_BUNDLE so non-Node.js tools (curl, git, Python) trust the AWF CA

  2. Chroot ssl-bump (new block after get-claude-key.sh copy): Copy CA cert to /host/tmp/awf-lib/awf-ca.crt and update NODE_EXTRA_CA_CERTS, SSL_CERT_FILE, and REQUESTS_CA_BUNDLE to the chroot-relative path

  3. Cleanup (line 726): Extended cleanup condition to also trigger when AWF_CA_CHROOT is set (ensures /tmp/awf-lib is cleaned up even if one-shot-token was not copied)

Env vars set for ssl-bump:

Variable Purpose Tools
NODE_EXTRA_CA_CERTS Node.js CA bundle Claude Code, npm, Yarn, Corepack
SSL_CERT_FILE OpenSSL CA file curl, git, Ruby
REQUESTS_CA_BUNDLE Python requests CA Python requests, pip

Verification

The fix can be verified by running with --ssl-bump --chroot and confirming:

  • NODE_EXTRA_CA_CERTS resolves to an accessible path after chroot
  • curl https://allowed-domain succeeds without certificate errors
  • Claude Code connects without EHOSTUNREACH

Fixes #1546
Upstream: github/gh-aw#23765

When ssl-bump and chroot are both active, NODE_EXTRA_CA_CERTS points to
/usr/local/share/ca-certificates/awf-ca.crt which is a Docker volume
mount on the container's overlay filesystem. After chroot /host, this
path is inaccessible, causing TLS failures (transaction-end-before-headers
in Squid, EHOSTUNREACH in Claude Code after 10 retries).

Fix: copy the CA cert to /host/tmp/awf-lib/awf-ca.crt before chroot
activates (same pattern as one-shot-token.so and get-claude-key.sh),
then update NODE_EXTRA_CA_CERTS to the chroot-relative path.

Also set SSL_CERT_FILE and REQUESTS_CA_BUNDLE so non-Node.js tools
(curl, git, Python requests, Ruby) trust the AWF CA in both chroot
and non-chroot ssl-bump modes.

Cleanup is handled by the existing /tmp/awf-lib removal in the EXIT
trap.

Fixes #1546

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@lpcox lpcox requested a review from Mossaka as a code owner April 1, 2026 19:23
Copilot AI review requested due to automatic review settings April 1, 2026 19:23
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 1, 2026

✅ Coverage Check Passed

Overall Coverage

Metric Base PR Delta
Lines 82.67% 82.77% 📈 +0.10%
Statements 82.34% 82.43% 📈 +0.09%
Functions 81.22% 81.22% ➡️ +0.00%
Branches 75.94% 76.00% 📈 +0.06%
📁 Per-file Coverage Changes (1 files)
File Lines (Before → After) Statements (Before → After)
src/docker-manager.ts 85.8% → 86.2% (+0.41%) 85.3% → 85.7% (+0.40%)

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

Fixes SSL-bump trust failures when running in --chroot mode by ensuring the AWF CA certificate is available at a chroot-accessible path and by exporting CA-related environment variables for common toolchains.

Changes:

  • Export SSL_CERT_FILE and REQUESTS_CA_BUNDLE alongside NODE_EXTRA_CA_CERTS when SSL-bump is enabled (non-chroot path).
  • In chroot mode, copy the mounted AWF CA cert to /host/tmp/awf-lib/awf-ca.crt and repoint CA env vars to /tmp/awf-lib/awf-ca.crt (chroot-relative).
  • Extend /tmp/awf-lib cleanup conditions to include CA-cert copying.
Comments suppressed due to low confidence (2)

containers/agent/entrypoint.sh:732

  • Cleanup relies on CLEANUP_CMD being executed by an EXIT trap, but the chrooted command later does trap '${CLEANUP_CMD}' EXIT and then immediately exec capsh .... A successful exec replaces the shell process and prevents the EXIT trap from running, so /tmp/awf-lib (including the copied CA cert) and ${SCRIPT_FILE} may be left behind on the host filesystem. To make the cleanup reliable, avoid exec at that point or move the trap/cleanup into the command run by capsh (or into ${SCRIPT_FILE} itself).
  # Clean up /tmp/awf-lib if anything was copied (one-shot-token, CA cert, key helper)
  if [ -n "${ONE_SHOT_TOKEN_LIB}" ] || [ -n "${AWF_CA_CHROOT}" ]; then
    CLEANUP_CMD="${CLEANUP_CMD}; rm -rf /tmp/awf-lib 2>/dev/null || true"
  fi

containers/agent/entrypoint.sh:493

  • After copying the CA cert, the script immediately exports NODE_EXTRA_CA_CERTS/SSL_CERT_FILE/REQUESTS_CA_BUNDLE. Consider verifying the destination file exists/readable (e.g., [ -f /host/tmp/awf-lib/awf-ca.crt ]) before exporting, similar to the one-shot-token copy, to avoid advertising a broken CA path if the copy is incomplete.
      if cp /usr/local/share/ca-certificates/awf-ca.crt /host/tmp/awf-lib/awf-ca.crt 2>/dev/null; then
        AWF_CA_CHROOT="/tmp/awf-lib/awf-ca.crt"
        export NODE_EXTRA_CA_CERTS="$AWF_CA_CHROOT"
        # SSL_CERT_FILE is respected by curl, git, Python requests, Ruby, and most
        # OpenSSL-based tools. This ensures non-Node.js tools also trust the AWF CA.

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

Comment on lines +488 to +489
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
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

The chroot CA-copy block silently ignores failures to create /host/tmp/awf-lib (mkdir). Consider logging a warning when mkdir fails so ssl-bump trust issues in chroot are easier to diagnose.

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

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 1, 2026

Smoke Test Results

GitHub MCP#1553 [WIP] Create daily token usage analysis workflow; #1552 [WIP] Fix NODE_EXTRA_CA_CERTS path issue after chroot for SSL bump
Playwright — github.com title contains "GitHub"
File Write/tmp/gh-aw/agent/smoke-test-claude-23866621178.txt created
Bash Verify — File contents confirmed

Overall: PASS

💥 [THE END] — Illustrated by Smoke Claude for issue #1555

lpcox and others added 2 commits April 1, 2026 12:28
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
- Verify destination file exists after copy ([ -f ] check)
- Log warning when mkdir /host/tmp/awf-lib fails
- Include CHROOT_KEY_HELPER in cleanup condition to prevent
  /tmp/awf-lib leak when only key helper was copied

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions bot mentioned this pull request Apr 1, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 1, 2026

Smoke Test Results

Test Status
GitHub MCP: PR #1550 "fix: decompress gzip responses for Anthropic token extraction"
GitHub MCP: PR #1549 "feat: include api-proxy token logs in firewall audit artifact"
Playwright: github.com title contains "GitHub"
File write: smoke-test-claude-23866922685.txt
Bash verify: file content confirmed

Overall: PASS

💥 [THE END] — Illustrated by Smoke Claude for issue #1555

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 1, 2026

Smoke test results — run 23866922726

Test Result
GitHub MCP (last 2 merged PRs) #1550 "fix: decompress gzip responses for Anthropic token extraction", #1549 "feat: include api-proxy token logs in firewall audit artifact"
Playwright (github.com title)
File write/read
Bash tool

Overall: PASS

PR author: @lpcox · No assignees

📰 BREAKING: Report filed by Smoke Copilot for issue #1555

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 1, 2026

Smoke Test Results

  • fix: decompress gzip responses for Anthropic token extraction
  • feat: include api-proxy token logs in firewall audit artifact
  • GitHub MCP review: ✅
  • safeinputs-gh PR query: ❌
  • Playwright github title check: ❌
  • Tavily search: ❌
  • File write + cat: ✅
  • Build (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 1, 2026

Smoke Test: GitHub Actions Services Connectivity

Service Check Result
Redis (host.docker.internal:6379) PING PONG
PostgreSQL (host.docker.internal:5432) pg_isready ✅ accepting connections
PostgreSQL (smoketest db) SELECT 1 ✅ returned 1

All checks passed. (redis-cli was not available, Redis was verified via raw socket RESP protocol.)

🔌 Service connectivity validated by Smoke Services

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 1, 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.0 v20.20.1 ❌ NO
Go go1.22.12 go1.22.12 ✅ YES

Overall: ❌ FAILED — Python and Node.js versions differ between host and chroot environments.

The chroot uses Ubuntu 22.04 system packages (Python 3.12.3, Node v20.20.1) while the host has newer versions installed via tool managers. Go matches because both use the same toolchain installation.

Tested by Smoke Chroot for issue #1555

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 1, 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

  • gh repo clone required authentication (GH_TOKEN not set); used git clone with HTTPS for public repos instead.
  • Java Maven local repository defaulted to a root-owned directory; redirected to /tmp/gh-aw/agent/.m2/repository via -s settings override.

Generated by Build Test Suite for issue #1555 ·

@lpcox lpcox merged commit f7361c5 into main Apr 1, 2026
64 of 65 checks passed
@lpcox lpcox deleted the fix/ssl-bump-chroot-ca-cert branch April 1, 2026 19:54
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.

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

2 participants