Skip to content

Commit c32fe02

Browse files
ClaudelpcoxMossakaclaude
authored
fix(ci): add BASE_URL environment variables for CODEX api-proxy routing (#802)
* Initial plan * fix(ci): add BASE_URL environment variables for CODEX api-proxy routing CODEX was not being directed to use the api-proxy because the OPENAI_BASE_URL and ANTHROPIC_BASE_URL environment variables were not explicitly set in the smoke-codex workflow. While AWF automatically sets these variables when generating the docker-compose configuration (if API keys are present), explicitly setting them in the workflow env ensures they are available to the CODEX agent for routing API calls through the api-proxy sidecar. This fix adds: - OPENAI_BASE_URL=http://api-proxy:10000 - ANTHROPIC_BASE_URL=http://api-proxy:10001 to the 'Run Codex' step environment variables. Fixes job failure in run 63483600453. * fix(ci): remove API keys from agent env when api-proxy is enabled (#803) * Initial plan * fix(ci): remove API keys from agent env when api-proxy is enabled When api-proxy is enabled (indicated by BASE_URL environment variables), API keys should NOT be exposed to the agent container for security. The api-proxy sidecar holds the credentials and injects auth headers. Previously, the workflow was passing both: - CODEX_API_KEY and OPENAI_API_KEY (should NOT be in agent env) - OPENAI_BASE_URL and ANTHROPIC_BASE_URL (should be in agent env) This defeated the security isolation provided by api-proxy. Changes: - Removed CODEX_API_KEY and OPENAI_API_KEY from agent environment block - Kept OPENAI_BASE_URL and ANTHROPIC_BASE_URL for routing to api-proxy - The awf CLI still receives keys via `sudo -E` and `--env-all` - awf passes keys only to api-proxy container, not agent container Security model: - awf reads keys from host environment (process.env) - awf passes keys only to api-proxy sidecar (src/docker-manager.ts:908-909) - Agent only receives BASE_URL variables (src/docker-manager.ts:948-955) - api-proxy injects auth headers and routes through Squid 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --------- Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com> Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * fix(firewall): add api-proxy to allowed domains when enabled (#804) * Initial plan * fix(firewall): add api-proxy to allowed domains when enabled Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --------- Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com> Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * fix(squid): handle bare hostnames without leading dot for api-proxy (#805) * Initial plan * fix(squid): handle bare hostnames without leading dot for api-proxy Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --------- Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com> Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * fix(agent): enable direct api-proxy access and remove api key from env (#807) * Initial plan * fix(agent): enable direct api-proxy access and remove api key from env - Add iptables bypass for api-proxy in setup-iptables.sh - Pass AWF_ENABLE_API_PROXY env var from docker-manager.ts - Remove ANTHROPIC_API_KEY from agent env in workflows - Update postprocess script to strip API key from compiled workflows Fixes agent->api-proxy connectivity and security vulnerability where API key was exposed to agent container instead of isolated to api-proxy. --------- Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com> * fix: pass ANTHROPIC_API_KEY to validation in all Claude workflows (#808) * Initial plan * fix: pass ANTHROPIC_API_KEY to validation step in smoke-claude workflow Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * fix: pass ANTHROPIC_API_KEY to validation in all Claude workflows Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --------- Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com> Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * fix: only enable api-proxy when API keys are provided (#810) * Initial plan * fix: only enable api-proxy when API keys are provided Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * feat(workflow): enable api-proxy for smoke-claude workflow - Add --enable-api-proxy flag to awf command - Add --anthropic-api-key flag to pass API key to api-proxy sidecar - Add ANTHROPIC_API_KEY to env block for agent step - API key is shared with api-proxy, kept out of agent container - Agent uses ANTHROPIC_BASE_URL to direct requests to api-proxy Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * refactor(workflow): remove redundant --enable-api-proxy flag The --enable-api-proxy flag defaults to true (src/cli.ts:724), so it doesn't need to be explicitly specified. The api-proxy sidecar will automatically deploy when API keys are present. See docs/api-proxy-sidecar.md:51 which states "The API proxy is enabled by default and automatically deploys when API keys are present" Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --------- Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com> Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * fix: remove unknown --anthropic-api-key flag from smoke-claude workflow (#811) * Initial plan * fix: remove unknown --anthropic-api-key flag from smoke-claude workflow Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --------- Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com> Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * fix: use api-proxy IP address instead of hostname for BASE_URL (#813) * Initial plan * fix: use api-proxy IP address instead of hostname for BASE_URL Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --------- Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com> Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * fix: use api-proxy IP address instead of hostname for BASE_URL (#813) (#815) * Initial plan * fix: use api-proxy IP address instead of hostname for BASE_URL (#813) Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --------- Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com> Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * fix(api-proxy): use IP address for API base URLs to avoid DNS issues In chroot mode, Docker container hostname resolution can fail because the DNS resolver may not properly reach Docker's embedded DNS. Use the api-proxy IP address directly (e.g., http://172.30.0.30:10001) instead of the hostname (http://api-proxy:10001) to eliminate DNS resolution as a failure point. Also add test coverage for the host-iptables api-proxy ACCEPT rule. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: exclude API keys from agent when api-proxy is enabled (#814) * Initial plan * fix: exclude API keys from agent when api-proxy is enabled --------- Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com> * fix(agent): use AWF_API_PROXY_IP env var for api-proxy iptables rules (#817) * Initial plan * fix(agent): use AWF_API_PROXY_IP env var for api-proxy iptables Move api-proxy iptables rules to use pre-set AWF_API_PROXY_IP environment variable instead of dynamic hostname resolution, and place FILTER rules in the correct position (after NAT setup, before final DROP rule). Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --------- Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com> Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * Add debug logging for BASE_URL environment variables in agent container (#816) * Initial plan * feat: add debug logging for BASE_URL environment variables Add logging to display ANTHROPIC_BASE_URL and OPENAI_BASE_URL values that are set for the agent container. This helps debug configuration issues when running Claude Code CLI in the workflow. The logging is added after the Docker Compose config is generated, showing whether BASE_URL variables are set or using defaults. Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --------- Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com> Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * fix: simplify api-proxy iptables bypass condition (#819) * Initial plan * fix: simplify api-proxy iptables bypass condition The NAT bypass for api-proxy traffic was checking both AWF_ENABLE_API_PROXY and AWF_API_PROXY_IP, but this was too strict. When the HTTP_PROXY environment variable is set, HTTP clients will send requests through the proxy unless NO_PROXY is configured or iptables rules prevent it. The issue was that the NAT bypass required both flags, causing traffic to 172.30.0.30 (api-proxy) to be sent through Squid when it should go directly. Squid then blocked these requests because the IP address wasn't in the domain whitelist. The fix simplifies the condition to only check AWF_API_PROXY_IP, matching the pattern used for the OUTPUT FILTER ACCEPT rules (lines 285-289). This ensures that when AWF_API_PROXY_IP is set, traffic to that IP bypasses Squid at the NAT level, preventing HTTP clients from routing through the proxy regardless of HTTP_PROXY settings. Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --------- Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com> Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> * Initial plan (#818) Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com> * fix: add api-proxy IP to squid allowlist (#820) * Initial plan * fix: add api-proxy IP to squid allowlist Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --------- Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com> Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --------- Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com> Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> Co-authored-by: Landon Cox <landon.cox@microsoft.com> Co-authored-by: Jiaxiao (mossaka) Zhou <duibao55328@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 537e383 commit c32fe02

13 files changed

Lines changed: 208 additions & 36 deletions

.github/workflows/secret-digger-claude.lock.yml

Lines changed: 0 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.github/workflows/security-guard.lock.yml

Lines changed: 0 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.github/workflows/smoke-claude.lock.yml

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.github/workflows/smoke-codex.lock.yml

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

containers/agent/setup-iptables.sh

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,19 @@ fi
127127
echo "[iptables] Allow traffic to Squid proxy (${SQUID_IP}:${SQUID_PORT})..."
128128
iptables -t nat -A OUTPUT -d "$SQUID_IP" -j RETURN
129129

130-
# Allow direct traffic to API proxy sidecar (bypasses Squid)
131-
# The api-proxy container holds API keys and proxies to LLM APIs through Squid
130+
# Bypass Squid for api-proxy when API proxy IP is configured.
131+
# The agent needs to connect directly to api-proxy (not through Squid).
132+
# The api-proxy then routes outbound traffic through Squid to enforce domain whitelisting.
133+
# Architecture: agent -> api-proxy (direct) -> Squid -> internet
134+
# Use AWF_API_PROXY_IP environment variable set by docker-manager (172.30.0.30)
132135
if [ -n "$AWF_API_PROXY_IP" ]; then
133-
echo "[iptables] Allow direct traffic to API proxy sidecar (${AWF_API_PROXY_IP})..."
134-
iptables -t nat -A OUTPUT -d "$AWF_API_PROXY_IP" -j RETURN
136+
if is_valid_ipv4 "$AWF_API_PROXY_IP"; then
137+
echo "[iptables] Allow direct traffic to api-proxy (${AWF_API_PROXY_IP}) - bypassing Squid..."
138+
# NAT: skip DNAT to Squid for all traffic to api-proxy
139+
iptables -t nat -A OUTPUT -d "$AWF_API_PROXY_IP" -j RETURN
140+
else
141+
echo "[iptables] WARNING: AWF_API_PROXY_IP has invalid format '${AWF_API_PROXY_IP}', skipping api-proxy bypass"
142+
fi
135143
fi
136144

137145
# Bypass Squid for host.docker.internal when host access is enabled.
@@ -270,9 +278,12 @@ iptables -A OUTPUT -p tcp -d 127.0.0.11 --dport 53 -j ACCEPT
270278
# Allow traffic to Squid proxy (after NAT redirection)
271279
iptables -A OUTPUT -p tcp -d "$SQUID_IP" -j ACCEPT
272280

273-
# Allow traffic to API proxy sidecar (ports 10000/10001)
281+
# Allow traffic to API proxy sidecar (ports 10000 for OpenAI, 10001 for Anthropic)
282+
# Must be added before the final DROP rule
274283
if [ -n "$AWF_API_PROXY_IP" ]; then
275-
iptables -A OUTPUT -p tcp -d "$AWF_API_PROXY_IP" -j ACCEPT
284+
echo "[iptables] Allow traffic to api-proxy (${AWF_API_PROXY_IP}) ports 10000, 10001..."
285+
iptables -A OUTPUT -p tcp -d "$AWF_API_PROXY_IP" --dport 10000 -j ACCEPT
286+
iptables -A OUTPUT -p tcp -d "$AWF_API_PROXY_IP" --dport 10001 -j ACCEPT
276287
fi
277288

278289
# Drop all other TCP traffic (default deny policy)

containers/api-proxy/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Node.js-based API proxy that keeps LLM API credentials isolated from the agent c
66

77
```
88
Agent Container (172.30.0.20)
9-
↓ HTTP request to api-proxy:10000
9+
↓ HTTP request to 172.30.0.30:10000
1010
API Proxy Sidecar (172.30.0.30)
1111
↓ Injects Authorization header
1212
↓ Routes via HTTP_PROXY (172.30.0.10:3128)

docs/api-proxy-sidecar.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ When enabled, the API proxy sidecar:
2626
│ │ │ Agent Container │ │
2727
│ │ │ 172.30.0.20 │ │
2828
│ │ │ OPENAI_BASE_URL= │ │
29-
│ │ │ http://api-proxy:10000 │────┘
29+
│ │ │ http://172.30.0.30:10000 │────┘
3030
│ │ │ ANTHROPIC_BASE_URL= │
31-
│ │ │ http://api-proxy:10001
31+
│ │ │ http://172.30.0.30:10001 │
3232
│ │ └──────────────────────────────┘
3333
│ │
3434
└─────────┼─────────────────────────────────────┘
@@ -38,7 +38,7 @@ When enabled, the API proxy sidecar:
3838
```
3939

4040
**Traffic Flow:**
41-
1. Agent makes request to `api-proxy:10000` or `api-proxy:10001`
41+
1. Agent makes request to `172.30.0.30:10000` or `172.30.0.30:10001`
4242
2. API proxy injects authentication headers
4343
3. API proxy routes through Squid via HTTP_PROXY/HTTPS_PROXY
4444
4. Squid enforces domain whitelist (only allowed domains pass)
@@ -82,7 +82,7 @@ awf --enable-api-proxy \
8282
-- npx @openai/codex -p "write a hello world function"
8383
```
8484

85-
The agent container will automatically use `http://api-proxy:10000` as the base URL.
85+
The agent container will automatically use `http://172.30.0.30:10000` as the base URL.
8686

8787
### Claude Code Example
8888

@@ -94,7 +94,7 @@ awf --enable-api-proxy \
9494
-- claude-code "write a hello world function"
9595
```
9696

97-
The agent container will automatically use `http://api-proxy:10001` as the base URL.
97+
The agent container will automatically use `http://172.30.0.30:10001` as the base URL.
9898

9999
### Both Providers
100100

@@ -113,8 +113,8 @@ When API keys are provided, the sidecar sets these environment variables in the
113113

114114
| Variable | Value | When Set | Description |
115115
|----------|-------|----------|-------------|
116-
| `OPENAI_BASE_URL` | `http://api-proxy:10000` | When `OPENAI_API_KEY` is provided | OpenAI API proxy endpoint |
117-
| `ANTHROPIC_BASE_URL` | `http://api-proxy:10001` | When `ANTHROPIC_API_KEY` is provided | Anthropic API proxy endpoint |
116+
| `OPENAI_BASE_URL` | `http://172.30.0.30:10000` | When `OPENAI_API_KEY` is provided | OpenAI API proxy endpoint |
117+
| `ANTHROPIC_BASE_URL` | `http://172.30.0.30:10001` | When `ANTHROPIC_API_KEY` is provided | Anthropic API proxy endpoint |
118118

119119
These are standard environment variables recognized by:
120120
- OpenAI Python SDK
@@ -164,7 +164,7 @@ When API keys are present (or `--enable-api-proxy` is explicitly set):
164164

165165
```
166166
Agent Code
167-
↓ (makes HTTP request to api-proxy:10000)
167+
↓ (makes HTTP request to 172.30.0.30:10000)
168168
Node.js API Proxy
169169
↓ (injects Authorization: Bearer $OPENAI_API_KEY)
170170
↓ (routes via HTTP_PROXY to Squid)

scripts/ci/postprocess-smoke-workflows.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ const shallowDepthRegex = /^(\s+)depth: 1\n/gm;
9494
// instead of pre-built GHCR images that may be stale.
9595
const imageTagRegex = /--image-tag\s+[0-9.]+\s+--skip-pull/g;
9696

97+
// Remove ANTHROPIC_API_KEY from agent environment (security: API key should only be in api-proxy sidecar)
98+
// The API key is passed to the api-proxy container by awf CLI when --enable-api-proxy is set.
99+
// Match the env key + value line with any indentation
100+
const anthropicApiKeyRegex = /^(\s*)ANTHROPIC_API_KEY:\s+\$\{\{\s*secrets\.ANTHROPIC_API_KEY\s*\}\}\n/gm;
101+
97102
for (const workflowPath of workflowPaths) {
98103
let content = fs.readFileSync(workflowPath, 'utf-8');
99104
let modified = false;
@@ -139,6 +144,14 @@ for (const workflowPath of workflowPaths) {
139144
console.log(` Replaced ${imageTagMatches.length} --image-tag/--skip-pull with --build-local`);
140145
}
141146

147+
// Remove ANTHROPIC_API_KEY from agent environment (security issue: key should only be in api-proxy)
148+
const apiKeyMatches = content.match(anthropicApiKeyRegex);
149+
if (apiKeyMatches) {
150+
content = content.replace(anthropicApiKeyRegex, '');
151+
modified = true;
152+
console.log(` Removed ${apiKeyMatches.length} ANTHROPIC_API_KEY env var(s) from agent`);
153+
}
154+
142155
if (modified) {
143156
fs.writeFileSync(workflowPath, content);
144157
console.log(`Updated ${workflowPath}`);

0 commit comments

Comments
 (0)