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}`,
Comment on lines 344 to +352

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

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

Now that both uppercase and lowercase proxy variables are set, --env / additionalEnv overrides can easily desync them (e.g., overriding only HTTP_PROXY leaves http_proxy pointing at the default). Similar to the existing NO_PROXY/no_proxy normalization below, consider normalizing HTTP_PROXYhttp_proxy and HTTPS_PROXYhttps_proxy after applying additionalEnv so a single override behaves consistently across clients.

Copilot uses AI. Check for mistakes.
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}`,

Copilot AI Mar 11, 2026

Copy link

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.
Comment on lines 1026 to +1029

Copilot AI Mar 11, 2026

Copy link

Choose a reason for hiding this comment

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

When SSL Bump is enabled, Squid presents certificates signed by the session CA for all clients using the proxy port. The api-proxy sidecar routes upstream requests through Squid (HttpsProxyAgent(HTTPS_PROXY)), but the compose spec does not mount the CA cert into the api-proxy container or set NODE_EXTRA_CA_CERTS there, so upstream TLS requests are likely to fail in --ssl-bump --enable-api-proxy mode. Consider mounting the CA cert into the api-proxy container and setting NODE_EXTRA_CA_CERTS (or otherwise exempting api-proxy traffic from SSL bump).

Suggested change
// Route through Squid to respect domain whitelisting
HTTP_PROXY: `http://${networkConfig.squidIp}:${SQUID_PORT}`,
HTTPS_PROXY: `http://${networkConfig.squidIp}:${SQUID_PORT}`,
http_proxy: `http://${networkConfig.squidIp}:${SQUID_PORT}`,
https_proxy: `http://${networkConfig.squidIp}:${SQUID_PORT}`,
// Route through Squid to respect domain whitelisting when SSL bump is disabled.
// When SSL bump is enabled, avoid proxying api-proxy HTTPS traffic through Squid
// to prevent TLS failures due to untrusted session CA certificates.
...(!config.enableSslBump && {
HTTP_PROXY: `http://${networkConfig.squidIp}:${SQUID_PORT}`,
HTTPS_PROXY: `http://${networkConfig.squidIp}:${SQUID_PORT}`,
http_proxy: `http://${networkConfig.squidIp}:${SQUID_PORT}`,
https_proxy: `http://${networkConfig.squidIp}:${SQUID_PORT}`,
}),

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