Skip to content

fix: use Docker default allowlist seccomp profile#1068

Open
Copilot wants to merge 2 commits intomainfrom
copilot/update-security-review-results
Open

fix: use Docker default allowlist seccomp profile#1068
Copilot wants to merge 2 commits intomainfrom
copilot/update-security-review-results

Conversation

Copy link
Contributor

Copilot AI commented Feb 26, 2026

Security review finding H-1: the custom seccomp profile used SCMP_ACT_ALLOW (blocklist) as default action, only blocking 3 syscall groups (~28 syscalls) while leaving ~280+ dangerous syscalls accessible.

Replaced with Docker's built-in default seccomp profile which uses SCMP_ACT_ERRNO (allowlist), blocking ~44 dangerous syscall families. This is the approach recommended in the security review as the alternative option.

Changes

  • Removed containers/agent/seccomp-profile.json — custom blocklist profile no longer needed
  • Removed seccomp= from security_opt in docker-manager.ts — Docker automatically applies its default allowlist profile when no custom profile is specified
  • Removed seccomp copy logic from writeConfigs() and pkg.assets
  • Updated tests to assert no custom seccomp profile is configured

Why Docker's default is sufficient

The container's existing capability model already enforces the needed restrictions:

Concern Mitigation
ptrace / process_vm_readv/writev cap_drop: ['SYS_PTRACE'] — blocked at capability level even though Docker's default allows on kernel ≥ 4.8
mount, unshare, setns, clone3 Docker's default conditionally allows with CAP_SYS_ADMIN; capsh --drop irrevocably removes it before user code
chroot Conditionally allowed with CAP_SYS_CHROOT; also dropped via capsh
SUID/SGID escalation no-new-privileges:true unchanged
Original prompt

This section details on the original issue you should resolve

<issue_title>[Security Review] Daily Security Review and Threat Model — 2026-02-26</issue_title>
<issue_description>## 📊 Executive Summary

This review covers a deep, evidence-based security analysis of the gh-aw-firewall codebase. The architecture is well-designed and layered; no critical bypass that works by default was found. However, several medium-to-high-severity gaps were identified that could weaken the security guarantee under specific conditions or configurations.

Metric Value
Security-critical files analysed 8 (host-iptables.ts, docker-manager.ts, squid-config.ts, domain-patterns.ts, setup-iptables.sh, entrypoint.sh, seccomp-profile.json, one-shot-token.c)
npm audit findings 1 moderate (ajv, dev-only)
Attack surfaces identified 9
STRIDE threats identified 8

🔍 Findings from Previous Security Testing

The security-review workflow was queried but log download was unavailable for this run. The analysis below is based entirely on direct codebase inspection.


🛡️ Architecture Security Analysis

Network Security Assessment

Evidence — iptables rule ordering (containers/agent/setup-iptables.sh):

The NAT OUTPUT chain processes rules in order:

  1. localhost → RETURN (allowed)
  2. Agent self-IP → RETURN (allowed)
  3. DNS to trusted servers → RETURN (allowed)
  4. Squid IP → RETURN (allowed)
  5. Dangerous ports → RETURN (falls through to DROP in filter chain)
  6. HTTP/HTTPS → DNAT to Squid

The filter chain ends with:

iptables -A OUTPUT -p tcp -j DROP  # line ~295
```

**Rule ordering is correct.** Dangerous ports are explicitly bypassed from DNAT so they hit the final DROP.

**⚠️ Finding NW-1 (Medium): IPv6 TCP not blocked in container when ip6tables is unavailable**

```
# From setup-iptables.sh lines 29–36:
IP6TABLES_AVAILABLE=false
if has_ip6tables; then
  IP6TABLES_AVAILABLE=true
else
  echo "[iptables] WARNING: ip6tables is not available, IPv6 rules will be skipped"
fi

The container setup-iptables.sh only applies NAT rules for IPv6 DNS, but never adds a default DROP for IPv6 TCP traffic. If ip6tables is unavailable (common on some hosts), IPv6 TCP connections are neither redirected to Squid nor dropped. A process inside the container could make direct IPv6 TCP connections to the internet, bypassing domain whitelisting entirely.

The host-level chain (FW_WRAPPER_V6) also has a conditional gate:

// src/host-iptables.ts line ~313:
if (ipv6DnsServers.length > 0) {
  // ... set up IPv6 chain with DROP default
}

If no IPv6 DNS servers are configured (the default), the FW_WRAPPER_V6 chain is never created, and IPv6 egress from containers on the bridge is unfiltered at the host level.

⚠️ Finding NW-2 (Medium): Docker network created without --ipv6=false

// src/host-iptables.ts lines 79–88:
await execa('docker', [
  'network', 'create', NETWORK_NAME,
  '--subnet', NETWORK_SUBNET,
  '--opt', 'com.docker.network.bridge.name=fw-bridge',
]);

No --ipv6=false flag is passed. If the Docker daemon has IPv6 enabled globally ("ipv6": true in /etc/docker/daemon.json), containers on awf-net will receive an IPv6 address and can initiate IPv6 connections. Combined with NW-1, this is a potential bypass path.

✅ DNS exfiltration prevention is solid. Both the host chain and container iptables only allow DNS to explicitly whitelisted servers. UDP is blocked globally after DNS ACCEPT rules.


Container Security Assessment

⚠️ Finding CS-1 (High): Seccomp profile is "blocklist-on-allow-all" — much weaker than Docker's default

# Evidence from seccomp-profile.json:
$ cat containers/agent/seccomp-profile.json | python3 -c "import json,sys; d=json.load(sys.stdin); print('default action:', d.get('defaultAction')); print('blocked syscall groups:', len(d.get('syscalls',[])))"
default action: SCMP_ACT_ALLOW
blocked syscall groups: 3

The three blocked groups are:

  • ptrace, process_vm_readv, process_vm_writev (process inspection)
  • kexec_load, reboot, init_module, delete_module, add_key, keyctl, ... (kernel/system)
  • umount, umount2 (unmount)

Docker's default seccomp profile blocks ~44 syscall families. This custom profile only blocks 3 groups, leaving dangerous syscalls like clone (namespace creation), unshare, setns, mknod, mount (allowed!), and many others accessible. An attacker who escapes the capability drop could use these syscalls for privilege escalation or container escape.

Specific concern: mount is explicitly allowed (per the comment: "mount is allowed for procfs but unmount is not needed"). Once CAP_SYS_ADMIN is dropped before user code runs, mount should fail anyway — but the seccomp profile itself is not enforcing this boundary.

✅ Capability drop is correctly implemented (containers/agent/entrypoint.sh line ~6...


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Co-authored-by: Mossaka <5447827+Mossaka@users.noreply.github.com>
Copilot AI changed the title [WIP] Update daily security review and threat model findings fix: use Docker default allowlist seccomp profile Feb 26, 2026
Copilot AI requested a review from Mossaka February 26, 2026 20:19
@Mossaka Mossaka marked this pull request as ready for review February 27, 2026 00:30
Copilot AI review requested due to automatic review settings February 27, 2026 00:30
@github-actions
Copy link
Contributor

github-actions bot commented Feb 27, 2026

✅ Coverage Check Passed

Overall Coverage

Metric Base PR Delta
Lines 82.03% 82.41% 📈 +0.38%
Statements 82.01% 82.38% 📈 +0.37%
Functions 82.50% 82.50% ➡️ +0.00%
Branches 74.20% 74.46% 📈 +0.26%
📁 Per-file Coverage Changes (1 files)
File Lines (Before → After) Statements (Before → After)
src/docker-manager.ts 83.1% → 84.6% (+1.51%) 82.4% → 83.9% (+1.45%)

Coverage comparison generated by scripts/ci/compare-coverage.ts

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses a critical security finding (H-1) by replacing the custom seccomp profile with Docker's built-in default allowlist-based profile. The custom profile used SCMP_ACT_ALLOW (blocklist) as the default action, blocking only ~28 syscalls across 3 groups, leaving ~280+ dangerous syscalls accessible. Docker's default profile uses SCMP_ACT_ERRNO (allowlist) and blocks ~44 dangerous syscall families, providing significantly stronger protection.

Changes:

  • Removed custom containers/agent/seccomp-profile.json file that used a weak blocklist approach
  • Removed seccomp configuration from docker-manager.ts to use Docker's default profile automatically
  • Updated tests to verify no custom seccomp profile is configured
  • Removed seccomp file from pkg.assets in package.json

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
src/docker-manager.ts Removed custom seccomp configuration and added comprehensive comments explaining Docker's default profile and the layered security model (capabilities + seccomp)
src/docker-manager.test.ts Updated test to verify no custom seccomp profile is configured and added explanatory comments about Docker's default profile
package.json Removed containers/agent/seccomp-profile.json from pkg assets array
containers/agent/seccomp-profile.json Deleted the weak blocklist-based custom seccomp profile

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions
Copy link
Contributor

Smoke Test Results — Run 22499024993

✅ GitHub MCP — Last 2 merged PRs:

✅ Playwright — github.com title contains "GitHub"
✅ File write — /tmp/gh-aw/agent/smoke-test-copilot-22499024993.txt created
✅ Bash verify — file contents confirmed

Overall: PASS

📰 BREAKING: Report filed by Smoke Copilot for issue #1068

@github-actions
Copy link
Contributor

🟢 Build Test: Node.js — PASS

Project Install Tests Status
clsx All passed PASS
execa All passed PASS
p-limit All passed PASS

Overall: PASS

Generated by Build Test Node.js for issue #1068

@github-actions
Copy link
Contributor

C++ Build Test Results

Project CMake Build Status
fmt PASS
json PASS

Overall: PASS

Generated by Build Test C++ for issue #1068

@github-actions
Copy link
Contributor

Smoke Test Results

Overall: PASS

💥 [THE END] — Illustrated by Smoke Claude for issue #1068

@github-actions
Copy link
Contributor

🦀 Rust Build Test Results

Project Build Tests Status
fd 1/1 PASS
zoxide 1/1 PASS

Overall: ✅ PASS

Generated by Build Test Rust for issue #1068

@github-actions
Copy link
Contributor

Go Build Test Results

Project Download Tests Status
color pass PASS
env pass PASS
uuid pass PASS

Overall: PASS

Generated by Build Test Go for issue #1068

@github-actions
Copy link
Contributor

Deno Build Test Results

Project Tests Status
oak 1/1 ✅ PASS
std 1/1 ✅ PASS

Overall: ✅ PASS

Generated by Build Test Deno for issue #1068

@github-actions
Copy link
Contributor

PR titles: fix: add explicit execute directive to smoke-codex to prevent noop; fix(deps): resolve high-severity rollup vulnerability in docs-site
Tests: GitHub MCP ✅ | safeinputs-gh ✅ | Playwright ✅ | Tavily ❌ | file write ✅ | bash cat ✅ | discussion ✅ | build ✅
Overall: FAIL

🔮 The oracle has spoken through Smoke Codex for issue #1068

@github-actions
Copy link
Contributor

🧪 Bun Build Test Results

Project Install Tests Status
elysia 1/1 PASS
hono 1/1 PASS

Overall: ✅ PASS

Bun version: 1.3.10

Generated by Build Test Bun for issue #1068

@github-actions
Copy link
Contributor

.NET Build Test Results

Project Restore Build Run Status
hello-world PASS
json-parse PASS

Overall: PASS

Run output

hello-world:

Hello, World!
```

**json-parse:**
```
{
  "Name": "AWF Test",
  "Version": 1,
  "Success": true
}
Name: AWF Test, Success: True

Generated by Build Test .NET for issue #1068

@github-actions
Copy link
Contributor

Java Build Test Results

Project Compile Tests Status
gson 1/1 PASS
caffeine 1/1 PASS

Overall: PASS

Generated by Build Test Java for issue #1068

@github-actions github-actions bot mentioned this pull request Feb 27, 2026
@github-actions
Copy link
Contributor

Chroot Version Comparison Results

Runtime Host Version Chroot Version Match?
Python Python 3.12.12 Python 3.12.3 ❌ NO
Node.js v24.13.1 v20.20.0 ❌ NO
Go go1.22.12 go1.22.12 ✅ YES

Overall: ❌ Not all versions matched — Python and Node.js versions differ between host and chroot environments.

Tested by Smoke Chroot for issue #1068

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Security Review] Daily Security Review and Threat Model — 2026-02-26

3 participants