#!/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 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"