Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions containers/agent/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,11 @@ if [ "${AWF_SSL_BUMP_ENABLED}" = "true" ]; then
echo "[entrypoint] SSL Bump mode detected - updating CA certificates..."
if [ -f /usr/local/share/ca-certificates/awf-ca.crt ]; then
update-ca-certificates 2>/dev/null
# Set NODE_EXTRA_CA_CERTS so Node.js tools (Yarn 4, Corepack, npm) trust the AWF CA.
# Node.js uses its own CA bundle, not the system CA store updated by update-ca-certificates.
export NODE_EXTRA_CA_CERTS="/usr/local/share/ca-certificates/awf-ca.crt"
echo "[entrypoint] CA certificates updated for SSL Bump"
echo "[entrypoint] NODE_EXTRA_CA_CERTS set to $NODE_EXTRA_CA_CERTS"
echo "[entrypoint] ⚠️ WARNING: HTTPS traffic will be intercepted for URL inspection"
else
echo "[entrypoint][WARN] SSL Bump enabled but CA certificate not found"
Expand Down
39 changes: 39 additions & 0 deletions src/docker-manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -490,10 +490,49 @@ describe('docker-manager', () => {

expect(env.HTTP_PROXY).toBe('http://172.30.0.10:3128');
expect(env.HTTPS_PROXY).toBe('http://172.30.0.10:3128');
expect(env.https_proxy).toBe('http://172.30.0.10:3128');
expect(env.SQUID_PROXY_HOST).toBe('squid-proxy');
expect(env.SQUID_PROXY_PORT).toBe('3128');
});

it('should set lowercase https_proxy for Yarn 4 and Corepack compatibility', () => {
const result = generateDockerCompose(mockConfig, mockNetworkConfig);
const agent = result.services.agent;
const env = agent.environment as Record<string, string>;

// Yarn 4 (undici), Corepack, and some Node.js HTTP clients only check lowercase
expect(env.https_proxy).toBe(env.HTTPS_PROXY);
// http_proxy is intentionally NOT set - see comment in docker-manager.ts
expect(env.http_proxy).toBeUndefined();
});

it('should set NODE_EXTRA_CA_CERTS when SSL Bump is enabled', () => {
const sslBumpConfig = { ...mockConfig, sslBump: true };
const ssl = {
caFiles: {
certPath: '/tmp/awf-test/ssl/ca-cert.pem',
keyPath: '/tmp/awf-test/ssl/ca-key.pem',
derPath: '/tmp/awf-test/ssl/ca-cert.der',
},
sslDbPath: '/tmp/awf-test/ssl_db',
};
const result = generateDockerCompose(sslBumpConfig, mockNetworkConfig, ssl);
const agent = result.services.agent;
const env = agent.environment as Record<string, string>;

expect(env.NODE_EXTRA_CA_CERTS).toBe('/usr/local/share/ca-certificates/awf-ca.crt');
expect(env.AWF_SSL_BUMP_ENABLED).toBe('true');
});

it('should not set NODE_EXTRA_CA_CERTS when SSL Bump is disabled', () => {
const result = generateDockerCompose(mockConfig, mockNetworkConfig);
const agent = result.services.agent;
const env = agent.environment as Record<string, string>;

expect(env.NODE_EXTRA_CA_CERTS).toBeUndefined();
expect(env.AWF_SSL_BUMP_ENABLED).toBeUndefined();
});

it('should set NO_COLOR=1 to disable ANSI color output from CLI tools', () => {
const result = generateDockerCompose(mockConfig, mockNetworkConfig);
const agent = result.services.agent;
Expand Down
15 changes: 15 additions & 0 deletions src/docker-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,13 @@ export function generateDockerCompose(
const environment: Record<string, string> = {
HTTP_PROXY: `http://${networkConfig.squidIp}:${SQUID_PORT}`,
HTTPS_PROXY: `http://${networkConfig.squidIp}:${SQUID_PORT}`,
// Lowercase https_proxy for tools that only check lowercase (e.g., Yarn 4/undici, Corepack).
// NOTE: We intentionally do NOT set lowercase http_proxy. Some curl builds (Ubuntu 22.04)
// ignore uppercase HTTP_PROXY for HTTP URLs (httpoxy mitigation), which means HTTP traffic
// falls through to iptables DNAT interception — the correct behavior for connection-level
// blocking. Setting http_proxy would route HTTP through the forward proxy where Squid's
// 403 error page returns exit code 0, breaking security expectations.
https_proxy: `http://${networkConfig.squidIp}:${SQUID_PORT}`,
SQUID_PROXY_HOST: 'squid-proxy',
SQUID_PROXY_PORT: SQUID_PORT.toString(),
HOME: homeDir,
Expand Down Expand Up @@ -699,6 +706,10 @@ export function generateDockerCompose(
agentVolumes.push(`${sslConfig.caFiles.certPath}:/usr/local/share/ca-certificates/awf-ca.crt:ro`);
// Set environment variable to indicate SSL Bump is enabled
environment.AWF_SSL_BUMP_ENABLED = 'true';
// Tell Node.js to trust the AWF session CA certificate.
// Without this, Node.js tools (Yarn 4, Corepack, npm) fail with EPROTO
// because Node.js uses its own CA bundle, not the system CA store.
environment.NODE_EXTRA_CA_CERTS = '/usr/local/share/ca-certificates/awf-ca.crt';
}

// SECURITY: Selective mounting to prevent credential exfiltration
Expand Down Expand Up @@ -1015,6 +1026,10 @@ export function generateDockerCompose(
// Route through Squid to respect domain whitelisting
HTTP_PROXY: `http://${networkConfig.squidIp}:${SQUID_PORT}`,
HTTPS_PROXY: `http://${networkConfig.squidIp}:${SQUID_PORT}`,
https_proxy: `http://${networkConfig.squidIp}:${SQUID_PORT}`,
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

The api-proxy sidecar healthcheck uses curl http://localhost:..., but adding lowercase http_proxy/https_proxy here will cause curl to proxy localhost unless NO_PROXY/no_proxy is set (or the healthcheck explicitly disables proxying). This can make the api-proxy container permanently unhealthy and block agent startup via depends_on: service_healthy. Consider adding NO_PROXY=no_proxy=localhost,127.0.0.1,::1 to the api-proxy environment (or change the healthcheck to curl --noproxy '*' ...).

Suggested change
https_proxy: `http://${networkConfig.squidIp}:${SQUID_PORT}`,
https_proxy: `http://${networkConfig.squidIp}:${SQUID_PORT}`,
// Ensure localhost traffic (e.g., healthcheck) bypasses the proxy
NO_PROXY: 'localhost,127.0.0.1,::1',
no_proxy: 'localhost,127.0.0.1,::1',

Copilot uses AI. Check for mistakes.
// Prevent curl health check from routing localhost through Squid
NO_PROXY: `localhost,127.0.0.1,::1`,
no_proxy: `localhost,127.0.0.1,::1`,
// Rate limiting configuration
...(config.rateLimitConfig && {
AWF_RATE_LIMIT_ENABLED: String(config.rateLimitConfig.enabled),
Expand Down
Loading