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
2 changes: 0 additions & 2 deletions .github/workflows/test-chroot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,6 @@ jobs:
echo "Captured GOROOT: ${GOROOT_VALUE}"

# Rust/Cargo: CARGO_HOME is needed so entrypoint can add $CARGO_HOME/bin to PATH
# The rust-toolchain action sets CARGO_HOME but sudo may not preserve it
if [ -n "$CARGO_HOME" ]; then
echo "CARGO_HOME=${CARGO_HOME}" >> $GITHUB_ENV
echo "Captured CARGO_HOME: ${CARGO_HOME}"
Expand All @@ -198,7 +197,6 @@ jobs:
fi

# Java: JAVA_HOME is needed so entrypoint can add $JAVA_HOME/bin to PATH
# The setup-java action sets JAVA_HOME but sudo may not preserve it
if [ -n "$JAVA_HOME" ]; then
echo "JAVA_HOME=${JAVA_HOME}" >> $GITHUB_ENV
echo "Captured JAVA_HOME: ${JAVA_HOME}"
Expand Down
5 changes: 5 additions & 0 deletions docs/awf-config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,11 @@
"description": "Security and isolation configuration.",
"additionalProperties": false,
"properties": {
"securityMode": {
"type": "string",
"enum": ["strict", "compat"],
"description": "Security enforcement mode. 'strict' (default) enforces network-isolation, API proxy credential injection, and rejects host-access/DinD. 'compat' preserves legacy iptables-based mode (requires sudo)."
},
"sslBump": {
"type": "boolean",
"description": "Enable SSL bumping (TLS interception) in the Squid proxy. Requires a custom CA certificate."
Expand Down
5 changes: 5 additions & 0 deletions src/awf-config-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,11 @@
"description": "Security and isolation configuration.",
"additionalProperties": false,
"properties": {
"securityMode": {
"type": "string",
"enum": ["strict", "compat"],
"description": "Security enforcement mode. 'strict' (default) enforces network-isolation, API proxy credential injection, and rejects host-access/DinD. 'compat' preserves legacy iptables-based mode (requires sudo)."
},
"sslBump": {
"type": "boolean",
"description": "Enable SSL bumping (TLS interception) in the Squid proxy. Requires a custom CA certificate."
Expand Down
27 changes: 21 additions & 6 deletions src/cli-options.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Command } from 'commander';
import { Command, Option } from 'commander';
import * as path from 'path';
import * as os from 'os';
import { version } from '../package.json';
Expand Down Expand Up @@ -242,10 +242,14 @@ program
)
.option(
'--network-isolation',
'Experimental: enforce egress via Docker network topology (internal network +\n' +
'Enforce egress via Docker network topology (internal network +\n' +
' dual-homed proxy) instead of iptables. Requires no sudo/NET_ADMIN.\n' +
' Not yet supported with --dns-over-https or --enable-host-access.',
false
' Not yet supported with --dns-over-https or --enable-host-access.\n' +
' Enabled by default in --security-mode strict.'
)
.option(
'--no-network-isolation',
'Disable network-isolation mode (requires --security-mode compat in strict mode).'
)
.option(
'--topology-attach <name>',
Expand Down Expand Up @@ -274,6 +278,14 @@ program
' WARNING: allows firewall bypass via docker run',
false
)
.addOption(
new Option(
'--security-mode <mode>',
'Security enforcement mode (default: strict).\n' +
' strict: network-isolation + api-proxy, no sudo/iptables.\n' +
' compat: legacy iptables mode, requires sudo.',
).choices(['strict', 'compat']).default('strict')
)
.option(
'--enable-dlp',
'Enable DLP (Data Loss Prevention) scanning to block credential\n' +
Expand All @@ -285,8 +297,11 @@ program
.option(
'--enable-api-proxy',
'Enable API proxy sidecar for secure credential injection.\n' +
' Supports OpenAI (Codex) and Anthropic (Claude) APIs.',
false
' Supports OpenAI (Codex) and Anthropic (Claude) APIs.'
)
.option(
'--no-enable-api-proxy',
'Disable the API proxy sidecar (requires --security-mode compat in strict mode).'
)
.option(
'--copilot-api-target <host>',
Expand Down
5 changes: 3 additions & 2 deletions src/commands/build-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,16 +108,17 @@ export function buildConfig(inputs: BuildConfigInputs): WrapperConfig {
(options.sessionStateDir as string | undefined) || process.env.AWF_SESSION_STATE_DIR,
runnerToolCachePath: options.runnerToolCachePath as string | undefined,
enableHostAccess: options.enableHostAccess as boolean,
networkIsolation: options.networkIsolation as boolean,
networkIsolation: options.networkIsolation as boolean | undefined,
topologyAttach: options.topologyAttach as string[] | undefined,
localhostDetected,
allowHostPorts: options.allowHostPorts as string | undefined,
allowHostServicePorts: options.allowHostServicePorts as string | undefined,
sslBump: options.sslBump as boolean,
enableDind: options.enableDind as boolean,
enableDlp: options.enableDlp as boolean,
securityMode: options.securityMode as 'strict' | 'compat' | undefined,
allowedUrls,
enableApiProxy: options.enableApiProxy as boolean,
enableApiProxy: options.enableApiProxy as boolean | undefined,
modelFallback:
options.modelFallback as { enabled?: boolean; strategy?: 'middle_power' } | undefined,
requestedModel: options.requestedModel as string | undefined,
Expand Down
14 changes: 2 additions & 12 deletions src/commands/validators/config-assembly-flags.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,6 @@ describe('config-assembly', () => {
});

describe('network-isolation validation', () => {
it('should warn that network-isolation is experimental', () => {
mockBuildConfigOnce({ networkIsolation: true });

callAssembleWith();

expect(logger.warn).toHaveBeenCalledWith(
expect.stringContaining('--network-isolation is experimental'),
);
});

it('should exit if --network-isolation is combined with --dns-over-https', () => {
mockBuildConfigOnce({ networkIsolation: true, dnsOverHttps: true });

Expand All @@ -47,7 +37,7 @@ describe('config-assembly', () => {
}).toThrow('process.exit(1)');

expect(logger.error).toHaveBeenCalledWith(
expect.stringContaining('--network-isolation is not yet supported with --dns-over-https'),
expect.stringContaining('--network-isolation is not supported with --dns-over-https'),
);
});

Expand All @@ -59,7 +49,7 @@ describe('config-assembly', () => {
}).toThrow('process.exit(1)');

expect(logger.error).toHaveBeenCalledWith(
expect.stringContaining('--network-isolation is not yet supported with --enable-host-access'),
expect.stringContaining('--network-isolation is not supported with --enable-host-access'),
);
});

Expand Down
2 changes: 2 additions & 0 deletions src/commands/validators/config-assembly.test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ jest.mock('../build-config', () => ({
logLevel: args.logLevel,
allowedDomains: args.allowedDomains,
blockedDomains: args.blockedDomains,
securityMode: 'compat',
enableApiProxy: false,
enableTokenSteering: false,
envAll: false,
Expand Down Expand Up @@ -149,6 +150,7 @@ export const createBuildConfigResult = (
logLevel: 'info',
allowedDomains: ['example.com'],
blockedDomains: [],
securityMode: 'compat',
enableApiProxy: false,
enableTokenSteering: false,
envAll: false,
Expand Down
2 changes: 2 additions & 0 deletions src/commands/validators/config-assembly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { LogAndLimitsResult } from './log-and-limits';
import { NetworkOptionsResult } from './network-options';
import { AgentOptionsResult } from './agent-options';
import { validateInfrastructureOptions, applyRateLimitConfig, validateFeatureFlagCompatibility } from './infrastructure-validator';
import { applySecurityMode } from './security-mode';
import { validateHostAccessConfig } from './network-access-validator';
import { validateApiProxyOptions, validateCopilotModelOption } from './api-proxy-validator';

Expand Down Expand Up @@ -64,6 +65,7 @@ export function assembleAndValidateConfig(
});

validateInfrastructureOptions(config);
applySecurityMode(config);
applyAgentTimeout(options.agentTimeout as string | undefined, config, logger);
applyRateLimitConfig(config, options);
validateFeatureFlagCompatibility(config);
Expand Down
7 changes: 3 additions & 4 deletions src/commands/validators/infrastructure-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,21 +100,20 @@ export function validateFeatureFlagCompatibility(config: WrapperConfig): void {
logger.debug(`Loading environment variables from file: ${config.envFile}`);
}

// Network-isolation (topology) mode: reject combinations that are not yet
// Network-isolation (topology) mode: reject combinations that are not
// supported because they depend on host-iptables or a sidecar that needs
// direct external connectivity bypassing the dual-homed proxy.
if (config.networkIsolation) {
if (config.dnsOverHttps) {
logger.error('❌ --network-isolation is not yet supported with --dns-over-https.');
logger.error('❌ --network-isolation is not supported with --dns-over-https.');
logger.error(' The DoH proxy needs direct external connectivity, which the internal network does not provide.');
process.exit(1);
}
if (config.enableHostAccess) {
logger.error('❌ --network-isolation is not yet supported with --enable-host-access.');
logger.error('❌ --network-isolation is not supported with --enable-host-access.');
logger.error(' Host access relies on host-level iptables, which network-isolation mode does not configure.');
process.exit(1);
}
logger.warn('⚠️ --network-isolation is experimental: egress is enforced via Docker network topology instead of iptables.');
} else if (config.topologyAttach && config.topologyAttach.length > 0) {
logger.error('❌ --topology-attach requires --network-isolation.');
logger.error(' Trusted containers can only be attached to the internal topology network in network-isolation mode.');
Expand Down
Loading
Loading