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
32 changes: 32 additions & 0 deletions src/host-iptables-host-access.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,38 @@ describe('host-iptables (host access)', () => {
]);
});

it('should skip invalid ports in allowHostServicePorts', async () => {
setupDefaultIptablesMocks();

mockedExeca.mockImplementation(((cmd: string, args: string[]) => {
if (cmd === 'docker' && args.includes('bridge')) {
return Promise.resolve({ stdout: '172.17.0.1', stderr: '', exitCode: 0 });
}
return Promise.resolve({ stdout: '', stderr: '', exitCode: 0 });
}) as any);

const hostAccess: HostAccessConfig = { enabled: true, allowHostServicePorts: 'abc,99999,-1' };
await setupHostIptables('172.30.0.10', 3128, ['8.8.8.8', '8.8.4.4'], undefined, undefined, hostAccess);

// Verify invalid service ports are NOT added
expect(mockedExeca).not.toHaveBeenCalledWith('iptables', expect.arrayContaining([
'--dport', 'abc',
]));
expect(mockedExeca).not.toHaveBeenCalledWith('iptables', expect.arrayContaining([
'--dport', '99999',
]));
expect(mockedExeca).not.toHaveBeenCalledWith('iptables', expect.arrayContaining([
'--dport', '-1',
]));

// Default ports should still be present
expect(mockedExeca).toHaveBeenCalledWith('iptables', [
'-t', 'filter', '-A', 'FW_WRAPPER',
'-p', 'tcp', '-d', '172.30.0.1', '--dport', '80',
'-j', 'ACCEPT',
]);
});

it('should skip invalid ports in allowHostPorts', async () => {
setupDefaultIptablesMocks();

Expand Down
18 changes: 7 additions & 11 deletions src/host-iptables-rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,16 +234,18 @@ export async function setupHostIptables(squidIp: string, squidPort: number, dnsS
]);
}

const needsGatewayIps = !!cliProxyConfig || !!hostAccess?.enabled;
const dockerBridgeGateway = needsGatewayIps ? await getDockerBridgeGateway() : null;
const gatewayIps = [AWF_NETWORK_GATEWAY];
if (dockerBridgeGateway) {
gatewayIps.push(dockerBridgeGateway);
}

// 5b2. Allow CLI proxy container to reach host DIFC proxy (when enabled)
// The cli-proxy container needs to TCP-tunnel to the external DIFC proxy on the host.
// Only the cli-proxy IP is allowed to reach the host gateway on the DIFC port.
if (cliProxyConfig) {
const { ip: cliProxyIp, difcProxyPort } = cliProxyConfig;
const gatewayIp = await getDockerBridgeGateway();
const gatewayIps = [AWF_NETWORK_GATEWAY];
if (gatewayIp) {
gatewayIps.push(gatewayIp);
}
for (const gwIp of gatewayIps) {
logger.debug(`Allowing CLI proxy (${cliProxyIp}) → host gateway (${gwIp}):${difcProxyPort}`);
await execa('iptables', [
Expand All @@ -258,12 +260,6 @@ export async function setupHostIptables(squidIp: string, squidPort: number, dnsS
// 5c. Allow traffic to host gateway when host access is enabled
// This is needed for Playwright localhost testing, MCP servers, etc.
if (hostAccess?.enabled) {
const gatewayIp = await getDockerBridgeGateway();
const gatewayIps = [AWF_NETWORK_GATEWAY];
if (gatewayIp) {
gatewayIps.push(gatewayIp);
}

// Default: allow HTTP (80) and HTTPS (443)
const defaultPorts = ['80', '443'];

Expand Down
20 changes: 20 additions & 0 deletions src/host-iptables-setup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,26 @@ describe('host-iptables (setup)', () => {
'-j', 'ACCEPT',
]);
});

it('should resolve Docker bridge gateway once when cliProxyConfig and hostAccess are both enabled', async () => {
setupDefaultIptablesMocks();

mockedExeca.mockImplementation(((cmd: string, args: string[]) => {
if (cmd === 'docker' && args.includes('bridge')) {
return Promise.resolve({ stdout: '172.17.0.1', stderr: '', exitCode: 0 });
}
return Promise.resolve({ stdout: '', stderr: '', exitCode: 0 });
}) as any);

const cliProxyConfig = { ip: '172.30.0.50', difcProxyPort: 18443 };
const hostAccess = { enabled: true };
await setupHostIptables('172.30.0.10', 3128, ['8.8.8.8', '8.8.4.4'], undefined, undefined, hostAccess, cliProxyConfig);

const bridgeGatewayCalls = mockedExeca.mock.calls.filter(([cmd, args]) =>
cmd === 'docker' && Array.isArray(args) && args.includes('bridge')
);
expect(bridgeGatewayCalls).toHaveLength(1);
});
});

describe('setupHostIptables with IPv6 DNS servers', () => {
Expand Down
Loading