T-HUB/infra/occupymedia/edge/edge-firewall.sh

67 lines
3 KiB
Bash
Executable file

#!/bin/bash
# edge-firewall.sh — restrict the edge ports to Cloudflare's ranges only.
# Mode file ./firewall.mode: "CF" (default, CF ranges only) or "OPEN" (anyone —
# grey mode: visitors connect directly, nginx rejects unknown SNI).
# Idempotent; safe to re-run (rebuilds the DOCKER-USER rules from scratch).
set -euo pipefail
cd "$(dirname "$0")"
MODE="CF"
[[ -f firewall.mode ]] && MODE=$(cat firewall.mode)
CF4_URL="https://www.cloudflare.com/ips-v4"
CF6_URL="https://www.cloudflare.com/ips-v6"
# Reset our rules, keep anything else in DOCKER-USER.
iptables -F DOCKER-USER
ip6tables -F DOCKER-USER 2>/dev/null || true
# DOCKER-USER sees forwarded traffic in BOTH directions. Exempt everything
# originating from a docker bridge (container egress) BEFORE any port rules —
# otherwise the CF-range catch-all below also drops containers' outbound 443
# (Matrix/R2/platform calls). Inbound filtering by source is unaffected.
for br in $(ls /sys/class/net/ | grep -E '^(br-|docker0)'); do
iptables -I DOCKER-USER 1 -i "$br" -j RETURN
done
# The ipfs3 ingress relay arrives over tailscale0 and terminates on the edge
# nginx; exempt it from the CF-ranges-only filter (trusted tailnet source).
iptables -C DOCKER-USER -i tailscale0 -j RETURN 2>/dev/null || \
iptables -I DOCKER-USER 1 -i tailscale0 -j RETURN
# Transit hop for occ-files (CT 320): CF-bound traffic from it rides this CT so
# the exit-node failover below covers oCIS blob writes too. Forward + NAT only;
# occ-files adds the CF-range routes toward this CT's LAN IP.
if [ "$(hostname -I 2>/dev/null | awk '{print $1}')" = "192.168.13.221" ]; then
iptables -C FORWARD -s 192.168.13.210 -d 192.168.13.0/24 -j ACCEPT 2>/dev/null || \
iptables -I FORWARD 1 -s 192.168.13.210 -d 192.168.13.0/24 -j ACCEPT
iptables -C FORWARD -s 192.168.13.210 -j ACCEPT 2>/dev/null || \
iptables -I FORWARD 1 -s 192.168.13.210 -j ACCEPT
iptables -t nat -C POSTROUTING -s 192.168.13.210 -d 192.168.13.0/24 -j RETURN 2>/dev/null || \
iptables -t nat -A POSTROUTING -s 192.168.13.210 -d 192.168.13.0/24 -j RETURN
iptables -t nat -C POSTROUTING -s 192.168.13.210 -j MASQUERADE 2>/dev/null || \
iptables -t nat -A POSTROUTING -s 192.168.13.210 -j MASQUERADE
fi
if [[ $MODE == OPEN ]]; then
echo "edge firewall: OPEN (any source allowed on 80/443; nginx SNI gate applies)"
exit 0
fi
mapfile -t CF4 < <(curl -s -m 15 "$CF4_URL" | grep -v '^$')
mapfile -t CF6 < <(curl -s -m 15 "$CF6_URL" | grep -v '^$')
((${#CF4[@]} > 5)) || { echo "cf ips-v4 fetch failed" >&2; exit 1; }
for net in "${CF4[@]}"; do
iptables -A DOCKER-USER -p tcp -m multiport --dports 80,443 -s "$net" -j RETURN
done
iptables -A DOCKER-USER -p tcp -m multiport --dports 80,443 -j DROP
if ((${#CF6[@]} > 0)); then
for net in "${CF6[@]}"; do
ip6tables -A DOCKER-USER -p tcp -m multiport --dports 80,443 -s "$net" -j RETURN 2>/dev/null || true
done
ip6tables -A DOCKER-USER -p tcp -m multiport --dports 80,443 -j DROP 2>/dev/null || true
fi
echo "edge firewall: ${#CF4[@]} v4 + ${#CF6[@]} v6 CF ranges allowed on 80/443"