Having spent considerable time analyzing agent runtime behaviors at the syscall layer, I've consistently observed that network egress is the ultimate privilege boundary. While we meticulously sandbox with namespaces and seccomp, a poorly configured network egress policy can render those controls moot. The kernel's netfilter stack is our final gatekeeper.
To move from reactive log analysis to proactive rule generation, I've written a tool that parses various logs (agent stdout/stderr, kernel audit logs, netfilter logs) for connection attempts and synthesizes suggested `iptables` rules. The core philosophy is to whitelist based on observed, legitimate FQDNs and IP ranges, then default-deny everything else. The script correlates DNS resolutions to mitigate the risk of blocking based on ephemeral IPs.
Here is the core parsing logic, which focuses on extracting destination data and categorizing by process context:
```python
#!/usr/bin/env python3
"""
syscall_log_parser.py - Parse agent logs for egress suggestions.
Inputs: dmesg, audit.log, agent.json.log
Outputs: suggested iptables rulesets (IPv4/IPv6).
"""
import re
import sys
import json
from collections import defaultdict
def parse_audit_log(line):
"""Extract dst and dst6 from audit records (type=SYSCALL msg=audit(...))."""
patterns = {
'dst': r'dst=(d+.d+.d+.d+)',
'dst6': r'dst6=([0-9a-fA-F:]+)',
'exe': r'exe="([^"]+)"',
'comm': r'comm="([^"]+)"'
}
result = {}
for key, pat in patterns.items():
match = re.search(pat, line)
if match:
result[key] = match.group(1)
return result if result else None
# ... additional parsers for agent-specific JSON logs and netfilter logs ...
def generate_iptables_suggestions(parsed_connections):
"""Convert aggregated connections into whitelist rules."""
rules = []
rules.append("# Generated by syscall_log_parser.py")
rules.append("*filter")
rules.append(":INPUT DROP [0:0]")
rules.append(":FORWARD DROP [0:0]")
rules.append(":OUTPUT DROP [0:0]")
rules.append("")
# Group by executable and destination
for (exe, dst), ports in parsed_connections.items():
for port in ports:
rules.append(f"-A OUTPUT -m owner --uid-owner agent-uid -d {dst} -p tcp --dport {port} -j ACCEPT")
rules.append(f"-A OUTPUT -m owner --uid-owner agent-uid -d {dst} -p udp --dport {port} -j ACCEPT")
rules.append("COMMIT")
return "n".join(rules)
```
The script emphasizes several key principles for robust egress filtering:
* **Process Context Binding:** Rules are tied to the agent's user/group ID (`--uid-owner`, `--gid-owner`). This prevents rules from inadvertently permitting traffic from other, potentially compromised processes.
* **Dual-Stack Handling:** It separately aggregates IPv4 and IPv6 addresses, generating rules for both `iptables` and `ip6tables`. Neglecting IPv6 is a common oversight that creates a bypass vector.
* **Port Specificity:** It suggests rules for specific destination ports observed in the logs, moving beyond blanket IP permits. This limits the potential for an allowed IP to be used for unexpected services.
* **Default Deny:** The generated ruleset starts from a `DROP` policy for `OUTPUT`, forcing explicit consideration for every permitted flow.
This approach is particularly useful for baselining a new agent deployment. Run the agent in a monitored, non-enforcing environment for a typical workload, collect logs, then generate and review the proposed ruleset before applying it to production. It turns observational data into declarative policy. The next step would be to integrate this with a framework like `ferm` or to output `nftables` syntax for modern systems.
Sara
Syscalls don't lie.