Skip to content

feat(cli): add container hardening measures#96

Merged
Mossaka merged 3 commits intomainfrom
copilot/reimplement-gh-aw-firewall-76
Dec 19, 2025
Merged

feat(cli): add container hardening measures#96
Mossaka merged 3 commits intomainfrom
copilot/reimplement-gh-aw-firewall-76

Conversation

Copy link
Contributor

Copilot AI commented Dec 4, 2025

Reimplements PR #76 container security hardening on latest main branch.

Security Hardening

Capability restrictions:

  • Drops NET_RAW, SYS_PTRACE, SYS_MODULE, SYS_RAWIO, MKNOD from agent container
  • Reduces attack surface for iptables bypass and container escape vectors

Syscall filtering:

  • Seccomp profile blocks dangerous syscalls: kexec_*, *_module, mount, reboot, pivot_root, keyring operations
  • Default allow with explicit deny list for conservative compatibility

Resource limits:

  • Memory: 4GB hard limit, swap disabled
  • PIDs: 1000 process limit
  • CPU: default shares (1024)

DNS audit logging:

  • Logs all DNS queries (UDP/TCP, IPv4/IPv6) with [FW_DNS_QUERY] prefix
  • Detection layer for DNS tunneling attempts
  • Adapted to work with per-server DNS restrictions in current main

Implementation Notes

Seccomp profile copied to work directory at runtime with fallback paths for source/dist deployments. DNS logging implemented as pre-ACCEPT LOG rules inside existing per-DNS-server loop structure.

All 349 tests pass. No security vulnerabilities detected by CodeQL.

Original prompt

read #76 and reimplement on the latest main


💡 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] Reimplement changes from PR #76 on latest main feat(cli): add container hardening measures Dec 4, 2025
Copilot AI requested a review from Mossaka December 4, 2025 22:27
@Mossaka Mossaka marked this pull request as ready for review December 18, 2025 22:06
@github-actions
Copy link
Contributor

github-actions bot commented Dec 18, 2025

Test Coverage Report

Metric Coverage Covered/Total
Lines 66.1% 708/1071
Statements 66.27% 729/1100
Functions 70.73% 87/123
Branches 60.89% 232/381
Coverage Thresholds

The project has the following coverage thresholds configured:

  • Lines: 38%
  • Statements: 38%
  • Functions: 35%
  • Branches: 30%

Coverage report generated by `npm run test:coverage`

Copy link

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 reimplements container security hardening measures from PR #76 on the current main branch, adding multiple layers of defense to reduce the attack surface of the firewall agent container.

Key changes:

  • Drops dangerous Linux capabilities (NET_RAW, SYS_PTRACE, SYS_MODULE, SYS_RAWIO, MKNOD) to prevent container escape and firewall bypass vectors
  • Implements seccomp syscall filtering to block dangerous operations like kernel module loading, mount operations, and keyring manipulation
  • Adds resource limits (4GB memory, 1000 PIDs, no swap) to prevent DoS attacks via resource exhaustion
  • Introduces DNS query audit logging for all DNS traffic to detect potential DNS tunneling attempts

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/types.ts Adds TypeScript type definitions for new Docker Compose security and resource limit properties (cap_drop, security_opt, mem_limit, memswap_limit, pids_limit, cpu_shares)
containers/agent/seccomp-profile.json Defines seccomp profile that blocks dangerous syscalls while maintaining compatibility with firewall operations
src/docker-manager.ts Implements container hardening in Docker Compose generation and adds seccomp profile file copying with fallback paths for source/dist deployments
src/docker-manager.test.ts Adds comprehensive test coverage for all hardening measures including capability drops, seccomp configuration, and resource limits
src/host-iptables.ts Adds iptables LOG rules for DNS queries (UDP/TCP, IPv4/IPv6) before ACCEPT rules to create audit trail
docs/logging_quickref.md Documents DNS query logging feature with examples for viewing, filtering, and analyzing DNS traffic logs
Comments suppressed due to low confidence (2)

src/host-iptables.ts:303

  • The DNS logging feature adds LOG rules for DNS queries but lacks test coverage. Since the host-iptables.test.ts file has comprehensive test coverage for DNS rules (verifying both UDP and TCP ACCEPT rules for DNS servers), the new LOG rules should also be tested to ensure they are created correctly before the ACCEPT rules.

Add test assertions to verify that LOG rules are created with the correct parameters including the log prefix '[FW_DNS_QUERY]' and log level '4' for both UDP and TCP DNS traffic.

    // Log DNS queries first (LOG doesn't terminate processing)
    await execa('iptables', [
      '-t', 'filter', '-A', CHAIN_NAME,
      '-p', 'udp', '-d', dnsServer, '--dport', '53',
      '-j', 'LOG', '--log-prefix', '[FW_DNS_QUERY] ', '--log-level', '4',
    ]);

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

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

    await execa('iptables', [
      '-t', 'filter', '-A', CHAIN_NAME,
      '-p', 'tcp', '-d', dnsServer, '--dport', '53',
      '-j', 'ACCEPT',
    ]);
  }

src/host-iptables.ts:375

  • The IPv6 DNS logging feature adds LOG rules for IPv6 DNS queries but lacks test coverage. Since the host-iptables.test.ts file has test coverage for IPv6 DNS rules (verifying ip6tables usage for IPv6 DNS servers), the new LOG rules should also be tested to ensure they are created correctly with ip6tables before the ACCEPT rules.

Add test assertions to verify that ip6tables LOG rules are created with the correct parameters including the log prefix '[FW_DNS_QUERY]' and log level '4' for both UDP and TCP DNS traffic to IPv6 servers.

        // Log DNS queries first (LOG doesn't terminate processing)
        await execa('ip6tables', [
          '-t', 'filter', '-A', CHAIN_NAME_V6,
          '-p', 'udp', '-d', dnsServer, '--dport', '53',
          '-j', 'LOG', '--log-prefix', '[FW_DNS_QUERY] ', '--log-level', '4',
        ]);

        await execa('ip6tables', [
          '-t', 'filter', '-A', CHAIN_NAME_V6,
          '-p', 'udp', '-d', dnsServer, '--dport', '53',
          '-j', 'ACCEPT',
        ]);

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

        await execa('ip6tables', [
          '-t', 'filter', '-A', CHAIN_NAME_V6,
          '-p', 'tcp', '-d', dnsServer, '--dport', '53',
          '-j', 'ACCEPT',
        ]);

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

fs.copyFileSync(altSeccompPath, seccompDestPath);
logger.debug(`Seccomp profile written to: ${seccompDestPath}`);
} else {
logger.warn(`Seccomp profile not found at ${seccompSourcePath} or ${altSeccompPath}`);
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

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

The seccomp profile is referenced in the Docker Compose configuration but if the file is not found, only a warning is logged and the container will fail to start. The security_opt field at line 273 will reference a non-existent file path, causing Docker Compose to fail with an error like "open /path/to/seccomp-profile.json: no such file or directory".

Consider either throwing an error if the seccomp profile cannot be found, or conditionally adding the security_opt field only when the file exists. Since this is a security hardening feature, it's better to fail explicitly rather than silently degrading security.

Suggested change
logger.warn(`Seccomp profile not found at ${seccompSourcePath} or ${altSeccompPath}`);
const message = `Seccomp profile not found at ${seccompSourcePath} or ${altSeccompPath}`;
logger.error(message);
throw new Error(message);

Copilot uses AI. Check for mistakes.
'MKNOD', // Prevents device node creation
],
// Apply seccomp profile to restrict dangerous syscalls
security_opt: [`seccomp=${config.workDir}/seccomp-profile.json`],
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

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

The seccomp profile path is hardcoded in the Docker Compose configuration, but the seccomp file copy logic allows for the file to not exist (only logging a warning). This creates a disconnect where the Docker Compose configuration will always reference the seccomp profile at line 273, even if the file wasn't successfully copied.

This needs to be coordinated with the file copy logic - either make the seccomp profile mandatory and fail if it's missing, or conditionally add the security_opt configuration only when the file exists.

Copilot uses AI. Check for mistakes.
- throw error instead of warning when seccomp profile is not found,
  preventing docker from failing at runtime with cryptic errors
- add test coverage for dns query logging iptables rules (ipv4 and ipv6)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@Mossaka Mossaka merged commit a9aa317 into main Dec 19, 2025
10 checks passed
@Mossaka Mossaka deleted the copilot/reimplement-gh-aw-firewall-76 branch December 19, 2025 08:29
@Mossaka Mossaka added the smoke label Dec 19, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Dec 19, 2025

📰 DEVELOPING STORY: Smoke Copilot reports failed. Our correspondents are investigating the incident...

@github-actions
Copy link
Contributor

github-actions bot commented Dec 19, 2025

💫 TO BE CONTINUED... Smoke Claude failed! Our hero faces unexpected challenges...

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants