Skip to content
Closed
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
42 changes: 42 additions & 0 deletions containers/agent/seccomp-profile.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"defaultAction": "SCMP_ACT_ALLOW",
"architectures": [
"SCMP_ARCH_X86_64",
"SCMP_ARCH_X86",
"SCMP_ARCH_AARCH64"
],
"syscalls": [
{
"names": [
"kexec_load",
"kexec_file_load",
"reboot",
"init_module",
"finit_module",
"delete_module",
"acct",
"swapon",
"swapoff",
"mount",
"umount",
"umount2",
"pivot_root",
"syslog",
"add_key",
"request_key",
"keyctl",
"uselib",
"personality",
"ustat",
"sysfs",
"vhangup",
"get_kernel_syms",
"query_module",
"create_module",
"nfsservctl"
],
"action": "SCMP_ACT_ERRNO",
"errnoRet": 1
}
]
}
20 changes: 20 additions & 0 deletions docs/logging_quickref.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,26 @@ docker exec awf-agent dmesg | grep FW_BLOCKED
sudo journalctl -k | grep FW_BLOCKED
```

### DNS Query Logging (Audit Trail)
```bash
# View all DNS queries made by containers
sudo dmesg | grep FW_DNS_QUERY

# Using journalctl (systemd)
sudo journalctl -k | grep FW_DNS_QUERY

# Real-time DNS query monitoring
sudo dmesg -w | grep FW_DNS_QUERY

# Count DNS queries by destination
sudo dmesg | grep FW_DNS_QUERY | grep -oP 'DST=\K[^ ]+' | sort | uniq -c | sort -rn

# Show DNS queries to specific resolver (e.g., 8.8.8.8)
sudo dmesg | grep FW_DNS_QUERY | grep 'DST=8.8.8.8'
```

**Note:** DNS queries are logged for audit trail purposes. This helps detect potential DNS tunneling attempts or unusual DNS activity. The log prefix `[FW_DNS_QUERY]` is used to identify DNS traffic.

## Log Format

### Squid Log Entry
Expand Down
32 changes: 32 additions & 0 deletions src/docker-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,21 @@ export function generateDockerCompose(
},
},
cap_add: ['NET_ADMIN'], // Required for iptables
// Drop capabilities to reduce attack surface (security hardening)
cap_drop: [
'NET_RAW', // Prevents raw socket creation (iptables bypass attempts)
'SYS_PTRACE', // Prevents process inspection/debugging (container escape vector)
'SYS_MODULE', // Prevents kernel module loading
'SYS_RAWIO', // Prevents raw I/O access
'MKNOD', // Prevents device node creation
],
// Apply seccomp profile to restrict dangerous syscalls
security_opt: [`seccomp=${config.workDir}/seccomp-profile.json`],
// Resource limits to prevent DoS attacks (conservative defaults)
mem_limit: '4g', // 4GB memory limit
memswap_limit: '4g', // No swap (same as mem_limit)
pids_limit: 1000, // Max 1000 processes
cpu_shares: 1024, // Default CPU share
stdin_open: true,
tty: config.tty || false, // Use --tty flag, default to false for clean logs
// Escape $ with $$ for Docker Compose variable interpolation
Expand Down Expand Up @@ -342,6 +357,23 @@ export async function writeConfigs(config: WrapperConfig): Promise<void> {
};
logger.debug(`Using network config: ${networkConfig.subnet} (squid: ${networkConfig.squidIp}, agent: ${networkConfig.agentIp})`);

// Copy seccomp profile to work directory for container security
const seccompSourcePath = path.join(__dirname, '..', 'containers', 'agent', 'seccomp-profile.json');
const seccompDestPath = path.join(config.workDir, 'seccomp-profile.json');
if (fs.existsSync(seccompSourcePath)) {
fs.copyFileSync(seccompSourcePath, seccompDestPath);
logger.debug(`Seccomp profile written to: ${seccompDestPath}`);
} else {
// If running from dist, try relative to dist
const altSeccompPath = path.join(__dirname, '..', '..', 'containers', 'agent', 'seccomp-profile.json');
if (fs.existsSync(altSeccompPath)) {
fs.copyFileSync(altSeccompPath, seccompDestPath);
logger.debug(`Seccomp profile written to: ${seccompDestPath}`);
} else {
logger.warn(`Seccomp profile not found at ${seccompSourcePath} or ${altSeccompPath}`);
}
}

// Write Squid config
const squidConfig = generateSquidConfig({
domains: config.allowedDomains,
Expand Down
15 changes: 14 additions & 1 deletion src/host-iptables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,26 @@ export async function setupHostIptables(squidIp: string, squidPort: number): Pro
'-j', 'ACCEPT',
]);

// 4. Allow DNS (UDP and TCP port 53)
// 4. Allow DNS (UDP and TCP port 53) with logging for audit trail
// Log DNS queries first (LOG doesn't terminate processing)
await execa('iptables', [
'-t', 'filter', '-A', chainName,
'-p', 'udp', '--dport', '53',
'-j', 'LOG', '--log-prefix', '[FW_DNS_QUERY] ', '--log-level', '4',
]);

await execa('iptables', [
'-t', 'filter', '-A', chainName,
'-p', 'udp', '--dport', '53',
'-j', 'ACCEPT',
]);

await execa('iptables', [
'-t', 'filter', '-A', chainName,
'-p', 'tcp', '--dport', '53',
'-j', 'LOG', '--log-prefix', '[FW_DNS_QUERY] ', '--log-level', '4',
]);

await execa('iptables', [
'-t', 'filter', '-A', chainName,
'-p', 'tcp', '--dport', '53',
Expand Down
63 changes: 61 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -437,14 +437,73 @@ export interface DockerService {

/**
* Linux capabilities to add to the container
*
*
* Grants additional privileges beyond the default container capabilities.
* The agent container requires NET_ADMIN for iptables manipulation.
*
*
* @example ['NET_ADMIN']
*/
cap_add?: string[];

/**
* Linux capabilities to drop from the container
*
* Removes specific capabilities to reduce attack surface. The firewall drops
* capabilities that could be used for container escape or firewall bypass.
*
* @example ['NET_RAW', 'SYS_PTRACE', 'SYS_MODULE']
*/
cap_drop?: string[];

/**
* Security options for the container
*
* Used for seccomp profiles, AppArmor profiles, and other security configurations.
*
* @example ['seccomp=/path/to/profile.json']
*/
security_opt?: string[];

/**
* Memory limit for the container
*
* Maximum amount of memory the container can use. Prevents DoS attacks
* via memory exhaustion.
*
* @example '4g'
* @example '512m'
*/
mem_limit?: string;

/**
* Total memory limit including swap
*
* Set equal to mem_limit to disable swap usage.
*
* @example '4g'
*/
memswap_limit?: string;

/**
* Maximum number of PIDs (processes) in the container
*
* Limits fork bombs and process exhaustion attacks.
*
* @example 1000
*/
pids_limit?: number;

/**
* CPU shares (relative weight)
*
* Controls CPU allocation relative to other containers.
* Default is 1024.
*
* @example 1024
* @example 512
*/
cpu_shares?: number;

/**
* Keep STDIN open even if not attached
*
Expand Down