Skip to content

Instantly share code, notes, and snippets.

@williamzujkowski
Last active August 18, 2026 00:30
Show Gist options
  • Select an option

  • Save williamzujkowski/6eaf1ebe4f96aad330fc23fc5b57c671 to your computer and use it in GitHub Desktop.

Select an option

Save williamzujkowski/6eaf1ebe4f96aad330fc23fc5b57c671 to your computer and use it in GitHub Desktop.
Network segmentation firewall rules for AI VLAN using iptables
#!/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