diff --git a/containers/agent/api-proxy-health-check.sh b/containers/agent/api-proxy-health-check.sh index 30cc2223..e83b723f 100755 --- a/containers/agent/api-proxy-health-check.sh +++ b/containers/agent/api-proxy-health-check.sh @@ -64,17 +64,15 @@ if [ -n "$OPENAI_BASE_URL" ]; then echo "[health-check] Checking OpenAI API proxy configuration..." # Verify credentials are NOT in agent environment - # Note: CODEX_API_KEY check is temporarily disabled - Codex receives credentials directly - if [ -n "$OPENAI_API_KEY" ] || [ -n "$OPENAI_KEY" ]; then - echo "[health-check][ERROR] OpenAI API key found in agent environment!" + if [ -n "$OPENAI_API_KEY" ] || [ -n "$CODEX_API_KEY" ] || [ -n "$OPENAI_KEY" ]; then + echo "[health-check][ERROR] OpenAI/Codex API key found in agent environment!" echo "[health-check][ERROR] Credential isolation failed - keys should only be in api-proxy container" echo "[health-check][ERROR] OPENAI_API_KEY=${OPENAI_API_KEY:+}" - # echo "[health-check][ERROR] CODEX_API_KEY=${CODEX_API_KEY:+}" # Temporarily disabled - Codex uses direct credentials + echo "[health-check][ERROR] CODEX_API_KEY=${CODEX_API_KEY:+}" echo "[health-check][ERROR] OPENAI_KEY=${OPENAI_KEY:+}" exit 1 fi - echo "[health-check] ✓ OpenAI credentials NOT in agent environment (correct)" - # Note: CODEX_API_KEY is intentionally passed through for Codex agent compatibility + echo "[health-check] ✓ OpenAI/Codex credentials NOT in agent environment (correct)" # Perform health check using BASE_URL echo "[health-check] Testing connectivity to OpenAI API proxy at $OPENAI_BASE_URL..." diff --git a/src/docker-manager.test.ts b/src/docker-manager.test.ts index ae42c01d..fad43378 100644 --- a/src/docker-manager.test.ts +++ b/src/docker-manager.test.ts @@ -1545,13 +1545,12 @@ describe('docker-manager', () => { expect(dependsOn['api-proxy'].condition).toBe('service_healthy'); }); - it('should not set OPENAI_BASE_URL in agent when OpenAI key is provided (temporarily disabled)', () => { + it('should set OPENAI_BASE_URL in agent when OpenAI key is provided', () => { const configWithProxy = { ...mockConfig, enableApiProxy: true, openaiApiKey: 'sk-test-key' }; const result = generateDockerCompose(configWithProxy, mockNetworkConfigWithProxy); const agent = result.services.agent; const env = agent.environment as Record; - // OPENAI_BASE_URL temporarily disabled for Codex - will be re-enabled in future - expect(env.OPENAI_BASE_URL).toBeUndefined(); + expect(env.OPENAI_BASE_URL).toBe('http://172.30.0.30:10000/v1'); }); it('should configure HTTP_PROXY and HTTPS_PROXY in api-proxy to route through Squid', () => { @@ -1573,13 +1572,12 @@ describe('docker-manager', () => { expect(env.CLAUDE_CODE_API_KEY_HELPER).toBe('/usr/local/bin/get-claude-key.sh'); }); - it('should only set ANTHROPIC_BASE_URL when both keys are provided (OPENAI_BASE_URL temporarily disabled)', () => { + it('should set both ANTHROPIC_BASE_URL and OPENAI_BASE_URL when both keys are provided', () => { const configWithProxy = { ...mockConfig, enableApiProxy: true, openaiApiKey: 'sk-test-openai-key', anthropicApiKey: 'sk-ant-test-key' }; const result = generateDockerCompose(configWithProxy, mockNetworkConfigWithProxy); const agent = result.services.agent; const env = agent.environment as Record; - // OPENAI_BASE_URL temporarily disabled for Codex - will be re-enabled in future - expect(env.OPENAI_BASE_URL).toBeUndefined(); + expect(env.OPENAI_BASE_URL).toBe('http://172.30.0.30:10000/v1'); expect(env.ANTHROPIC_BASE_URL).toBe('http://172.30.0.30:10001'); expect(env.ANTHROPIC_AUTH_TOKEN).toBe('placeholder-token-for-credential-isolation'); expect(env.CLAUDE_CODE_API_KEY_HELPER).toBe('/usr/local/bin/get-claude-key.sh'); @@ -1596,14 +1594,13 @@ describe('docker-manager', () => { expect(env.CLAUDE_CODE_API_KEY_HELPER).toBe('/usr/local/bin/get-claude-key.sh'); }); - it('should not set ANTHROPIC_BASE_URL or OPENAI_BASE_URL in agent when only OpenAI key is provided (OPENAI_BASE_URL temporarily disabled)', () => { + it('should set OPENAI_BASE_URL and not set ANTHROPIC_BASE_URL when only OpenAI key is provided', () => { const configWithProxy = { ...mockConfig, enableApiProxy: true, openaiApiKey: 'sk-test-key' }; const result = generateDockerCompose(configWithProxy, mockNetworkConfigWithProxy); const agent = result.services.agent; const env = agent.environment as Record; expect(env.ANTHROPIC_BASE_URL).toBeUndefined(); - // OPENAI_BASE_URL temporarily disabled for Codex - will be re-enabled in future - expect(env.OPENAI_BASE_URL).toBeUndefined(); + expect(env.OPENAI_BASE_URL).toBe('http://172.30.0.30:10000/v1'); }); it('should set AWF_API_PROXY_IP in agent environment', () => { @@ -1674,8 +1671,8 @@ describe('docker-manager', () => { const env = agent.environment as Record; // Agent should NOT have the raw API key — only the sidecar gets it expect(env.OPENAI_API_KEY).toBeUndefined(); - // OPENAI_BASE_URL temporarily disabled for Codex - will be re-enabled in future - expect(env.OPENAI_BASE_URL).toBeUndefined(); + // Agent should have OPENAI_BASE_URL to proxy through sidecar + expect(env.OPENAI_BASE_URL).toBe('http://172.30.0.30:10000/v1'); } finally { if (origKey !== undefined) { process.env.OPENAI_API_KEY = origKey; @@ -1685,9 +1682,9 @@ describe('docker-manager', () => { } }); - it('should pass CODEX_API_KEY to agent even when api-proxy is enabled with envAll', () => { + it('should not leak CODEX_API_KEY to agent when api-proxy is enabled with envAll', () => { // Simulate the key being in process.env AND envAll enabled - // CODEX_API_KEY is intentionally passed through (unlike other keys) for Codex agent compatibility + // CODEX_API_KEY is now excluded when api-proxy is enabled for credential isolation const origKey = process.env.CODEX_API_KEY; process.env.CODEX_API_KEY = 'sk-codex-secret'; try { @@ -1695,10 +1692,10 @@ describe('docker-manager', () => { const result = generateDockerCompose(configWithProxy, mockNetworkConfigWithProxy); const agent = result.services.agent; const env = agent.environment as Record; - // CODEX_API_KEY is intentionally passed to agent for Codex compatibility - expect(env.CODEX_API_KEY).toBe('sk-codex-secret'); - // OPENAI_BASE_URL temporarily disabled for Codex - will be re-enabled in future - expect(env.OPENAI_BASE_URL).toBeUndefined(); + // CODEX_API_KEY should NOT be passed to agent when api-proxy is enabled + expect(env.CODEX_API_KEY).toBeUndefined(); + // OPENAI_BASE_URL should be set when api-proxy is enabled with openaiApiKey + expect(env.OPENAI_BASE_URL).toBe('http://172.30.0.30:10000/v1'); } finally { if (origKey !== undefined) { process.env.CODEX_API_KEY = origKey; @@ -1719,8 +1716,8 @@ describe('docker-manager', () => { const env = agent.environment as Record; // Even with envAll, agent should NOT have OPENAI_API_KEY when api-proxy is enabled expect(env.OPENAI_API_KEY).toBeUndefined(); - // OPENAI_BASE_URL temporarily disabled for Codex - will be re-enabled in future - expect(env.OPENAI_BASE_URL).toBeUndefined(); + // Agent should have OPENAI_BASE_URL to proxy through sidecar + expect(env.OPENAI_BASE_URL).toBe('http://172.30.0.30:10000/v1'); } finally { if (origKey !== undefined) { process.env.OPENAI_API_KEY = origKey; diff --git a/src/docker-manager.ts b/src/docker-manager.ts index 5610729d..4c9d6308 100644 --- a/src/docker-manager.ts +++ b/src/docker-manager.ts @@ -328,10 +328,10 @@ export function generateDockerCompose( // When api-proxy is enabled, exclude API keys from agent environment // (they are held securely in the api-proxy sidecar instead) - // Note: CODEX_API_KEY is intentionally NOT excluded - Codex needs direct credential access if (config.enableApiProxy) { EXCLUDED_ENV_VARS.add('OPENAI_API_KEY'); EXCLUDED_ENV_VARS.add('OPENAI_KEY'); + EXCLUDED_ENV_VARS.add('CODEX_API_KEY'); EXCLUDED_ENV_VARS.add('ANTHROPIC_API_KEY'); EXCLUDED_ENV_VARS.add('CLAUDE_API_KEY'); } @@ -418,9 +418,8 @@ export function generateDockerCompose( if (process.env.GITHUB_PERSONAL_ACCESS_TOKEN) environment.GITHUB_PERSONAL_ACCESS_TOKEN = process.env.GITHUB_PERSONAL_ACCESS_TOKEN; // API keys for LLM providers — skip when api-proxy is enabled // (the sidecar holds the keys; the agent uses *_BASE_URL instead) - // Exception: CODEX_API_KEY is always passed through for Codex agent compatibility if (process.env.OPENAI_API_KEY && !config.enableApiProxy) environment.OPENAI_API_KEY = process.env.OPENAI_API_KEY; - if (process.env.CODEX_API_KEY) environment.CODEX_API_KEY = process.env.CODEX_API_KEY; + if (process.env.CODEX_API_KEY && !config.enableApiProxy) environment.CODEX_API_KEY = process.env.CODEX_API_KEY; if (process.env.ANTHROPIC_API_KEY && !config.enableApiProxy) environment.ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY; if (process.env.USER) environment.USER = process.env.USER; if (process.env.TERM) environment.TERM = process.env.TERM; @@ -1009,11 +1008,10 @@ export function generateDockerCompose( // Use IP address instead of hostname for BASE_URLs since Docker DNS may not resolve // container names in chroot mode environment.AWF_API_PROXY_IP = networkConfig.proxyIp; - // OPENAI_BASE_URL temporarily disabled for Codex - will be re-enabled in future - // if (config.openaiApiKey) { - // environment.OPENAI_BASE_URL = `http://${networkConfig.proxyIp}:10000/v1`; - // logger.debug(`OpenAI API will be proxied through sidecar at http://${networkConfig.proxyIp}:10000/v1`); - // } + if (config.openaiApiKey) { + environment.OPENAI_BASE_URL = `http://${networkConfig.proxyIp}:10000/v1`; + logger.debug(`OpenAI API will be proxied through sidecar at http://${networkConfig.proxyIp}:10000/v1`); + } if (config.anthropicApiKey) { environment.ANTHROPIC_BASE_URL = `http://${networkConfig.proxyIp}:10001`; logger.debug(`Anthropic API will be proxied through sidecar at http://${networkConfig.proxyIp}:10001`);