Skip to content

Commit 293d857

Browse files
Mossakaclaude
andcommitted
fix: use env var injection for squid config to support DinD environments
Replace the squid.conf file bind mount with a base64-encoded environment variable (AWF_SQUID_CONFIG_B64) to support Docker-in-Docker environments like ARC self-hosted runners. In DinD, the Docker daemon runs in a separate container and cannot access files on the host filesystem. File bind mounts fail with "not a directory" errors because the daemon creates a directory at the non-existent path. Instead of bind-mounting squid.conf, the config is: 1. Base64-encoded and passed as AWF_SQUID_CONFIG_B64 env var 2. Decoded by an entrypoint override before squid starts This works universally because env vars are part of the container spec sent via the Docker API, bypassing filesystem sharing entirely. The startup flow (docker compose up -d) is unchanged, so all existing integration tests and behavior remain compatible. Fixes github/gh-aw#18385 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c15f7ec commit 293d857

File tree

2 files changed

+46
-4
lines changed

2 files changed

+46
-4
lines changed

src/docker-manager.test.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,12 +477,29 @@ describe('docker-manager', () => {
477477
const squid = result.services['squid-proxy'];
478478

479479
expect(squid.container_name).toBe('awf-squid');
480-
expect(squid.volumes).toContain('/tmp/awf-test/squid.conf:/etc/squid/squid.conf:ro');
480+
// squid.conf is NOT bind-mounted; it's injected via AWF_SQUID_CONFIG_B64 env var
481+
expect(squid.volumes).not.toContainEqual(expect.stringContaining('squid.conf'));
481482
expect(squid.volumes).toContain('/tmp/awf-test/squid-logs:/var/log/squid:rw');
482483
expect(squid.healthcheck).toBeDefined();
483484
expect(squid.ports).toContain('3128:3128');
484485
});
485486

487+
it('should inject squid config via base64 env var when content is provided', () => {
488+
const squidConfig = 'http_port 3128\nacl allowed_domains dstdomain .github.com\n';
489+
const result = generateDockerCompose(mockConfig, mockNetworkConfig, undefined, squidConfig);
490+
const squid = result.services['squid-proxy'] as any;
491+
492+
// Should have AWF_SQUID_CONFIG_B64 env var with base64-encoded config
493+
expect(squid.environment.AWF_SQUID_CONFIG_B64).toBe(
494+
Buffer.from(squidConfig).toString('base64')
495+
);
496+
497+
// Should override entrypoint to decode config before starting squid
498+
expect(squid.entrypoint).toBeDefined();
499+
expect(squid.entrypoint[2]).toContain('base64 -d > /etc/squid/squid.conf');
500+
expect(squid.entrypoint[2]).toContain('entrypoint.sh');
501+
});
502+
486503
it('should configure agent container with proxy settings', () => {
487504
const result = generateDockerCompose(mockConfig, mockNetworkConfig);
488505
const agent = result.services.agent;

src/docker-manager.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,8 @@ export interface SslConfig {
230230
export function generateDockerCompose(
231231
config: WrapperConfig,
232232
networkConfig: { subnet: string; squidIp: string; agentIp: string; proxyIp?: string },
233-
sslConfig?: SslConfig
233+
sslConfig?: SslConfig,
234+
squidConfigContent?: string
234235
): DockerComposeConfig {
235236
const projectRoot = path.join(__dirname, '..');
236237

@@ -249,8 +250,12 @@ export function generateDockerCompose(
249250
: path.join(config.workDir, 'api-proxy-logs');
250251

251252
// Build Squid volumes list
253+
// Note: squid.conf is NOT bind-mounted. Instead, it's passed as a base64-encoded
254+
// environment variable (AWF_SQUID_CONFIG_B64) and decoded by the entrypoint override.
255+
// This supports Docker-in-Docker (DinD) environments where the Docker daemon runs
256+
// in a separate container and cannot access files on the host filesystem.
257+
// See: https://github.com/github/gh-aw/issues/18385
252258
const squidVolumes = [
253-
`${config.workDir}/squid.conf:/etc/squid/squid.conf:ro`,
254259
`${squidLogsPath}:/var/log/squid:rw`,
255260
];
256261

@@ -292,6 +297,26 @@ export function generateDockerCompose(
292297
],
293298
};
294299

300+
// Inject squid.conf via environment variable instead of bind mount.
301+
// In Docker-in-Docker (DinD) environments, the Docker daemon runs in a separate
302+
// container and cannot access files on the host filesystem. Bind-mounting
303+
// squid.conf fails because the daemon creates a directory at the missing path.
304+
// Passing the config as a base64-encoded env var works universally because
305+
// env vars are part of the container spec sent via the Docker API.
306+
if (squidConfigContent) {
307+
const configB64 = Buffer.from(squidConfigContent).toString('base64');
308+
squidService.environment = {
309+
...squidService.environment,
310+
AWF_SQUID_CONFIG_B64: configB64,
311+
};
312+
// Override entrypoint to decode the config before starting squid.
313+
// The original entrypoint (/usr/local/bin/entrypoint.sh) is called after decoding.
314+
squidService.entrypoint = [
315+
'/bin/bash', '-c',
316+
'echo "$AWF_SQUID_CONFIG_B64" | base64 -d > /etc/squid/squid.conf && exec /usr/local/bin/entrypoint.sh',
317+
];
318+
}
319+
295320
// Only enable host.docker.internal when explicitly requested via --enable-host-access
296321
// This allows containers to reach services on the host machine (e.g., MCP gateways)
297322
// Security note: When combined with allowing host.docker.internal domain,
@@ -1260,7 +1285,7 @@ export async function writeConfigs(config: WrapperConfig): Promise<void> {
12601285
// Write Docker Compose config
12611286
// Uses mode 0o600 (owner-only read/write) because this file contains sensitive
12621287
// environment variables (tokens, API keys) in plaintext
1263-
const dockerCompose = generateDockerCompose(config, networkConfig, sslConfig);
1288+
const dockerCompose = generateDockerCompose(config, networkConfig, sslConfig, squidConfig);
12641289
const dockerComposePath = path.join(config.workDir, 'docker-compose.yml');
12651290
fs.writeFileSync(dockerComposePath, yaml.dump(dockerCompose), { mode: 0o600 });
12661291
logger.debug(`Docker Compose config written to: ${dockerComposePath}`);

0 commit comments

Comments
 (0)