Last active
August 18, 2026 00:30
-
-
Save williamzujkowski/6eaf1ebe4f96aad330fc23fc5b57c671 to your computer and use it in GitHub Desktop.
Network segmentation firewall rules for AI VLAN using iptables
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Egress policy for an AI-workload VLAN. | |
| # Source: https://williamzujkowski.github.io/posts/2025-04-10-securing-personal-ai-experiments/ | |
| # | |
| # THREE THINGS THE OBVIOUS VERSION OF THIS SCRIPT GETS WRONG. | |
| # | |
| # 1. DO NOT PUT HOSTNAMES IN iptables RULES. | |
| # iptables -A FORWARD -s $AI_VLAN -d api.openai.com -j ACCEPT # WRONG | |
| # iptables resolves the name ONCE, at rule-insertion time, and installs | |
| # whatever A records it got as literal addresses. Those services sit behind | |
| # CDNs with rotating pools, so within hours the rule permits addresses they | |
| # no longer use — and permits them to whoever holds that address now. Use an | |
| # ipset refreshed on a timer, or filter on SNI at an egress proxy. | |
| # | |
| # 2. AN ALLOW-LIST NEEDS A DEFAULT DENY. Appending ACCEPT rules and then a | |
| # couple of DROPs leaves everything else — the entire internet — governed by | |
| # the chain policy. If you never set it, this is a deny-list wearing an | |
| # allow-list's clothing. | |
| # | |
| # 3. STATEFUL TRAFFIC NEEDS conntrack. Without an ESTABLISHED,RELATED rule, | |
| # a default-DROP policy kills all return traffic and nothing works at all. | |
| # | |
| # Note on platform: if your gateway is a UniFi appliance, rules appended here | |
| # by hand do not survive a firmware update. Configure it in the controller. | |
| set -euo pipefail | |
| AI_VLAN="10.0.50.0/24" | |
| INTERNAL_REPO="10.0.10.5" | |
| WAN_IF="eth0" | |
| # Idempotency: rebuild our own chain rather than appending duplicates on rerun. | |
| iptables -N AI-EGRESS 2>/dev/null || iptables -F AI-EGRESS | |
| iptables -C FORWARD -s "$AI_VLAN" -j AI-EGRESS 2>/dev/null || \ | |
| iptables -I FORWARD -s "$AI_VLAN" -j AI-EGRESS | |
| # Return traffic for connections we already allowed. | |
| iptables -A AI-EGRESS -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT | |
| # Explicit denies first, so a later ACCEPT cannot widen them by accident. | |
| iptables -A AI-EGRESS -d 10.0.1.0/24 -j DROP # home network | |
| iptables -A AI-EGRESS -d 10.0.0.0/24 -j DROP # management network | |
| # Internal model repository. | |
| iptables -A AI-EGRESS -d "$INTERNAL_REPO" -j ACCEPT | |
| # External API allow-list, by ipset rather than by hostname. Populate and | |
| # refresh the set from a resolver on a timer; see note 1 above. | |
| # ipset create ai-allowed hash:ip timeout 600 | |
| # for h in api.openai.com huggingface.co; do | |
| # for ip in $(dig +short "$h" A); do ipset add ai-allowed "$ip" -exist; done | |
| # done | |
| if ipset list ai-allowed >/dev/null 2>&1; then | |
| iptables -A AI-EGRESS -o "$WAN_IF" -m set --match-set ai-allowed dst \ | |
| -p tcp --dport 443 -j ACCEPT | |
| fi | |
| # Rate-limited logging. An unlimited LOG rule on a host pulling model weights | |
| # will fill the disk with your own traffic. | |
| iptables -A AI-EGRESS -m limit --limit 5/min --limit-burst 10 \ | |
| -j LOG --log-prefix "AI-VLAN: " | |
| # Default deny. This is the line that makes the rules above an allow-list. | |
| iptables -A AI-EGRESS -j DROP |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment