## Context Both servers (relay and landing) run as proxy endpoints. Clients are already configured to route China traffic directly via Surge rules. Any China-destined traffic that reaches the servers is unwanted. We need server-side enforcement to drop outbound connections to CN IP ranges. ## Goals / Non-Goals **Goals:** - Block outbound TCP/UDP to China IP ranges on both servers using ipset + iptables - Automate CN IP list download and periodic refresh - Idempotent Ansible role applicable to all hosts **Non-Goals:** - Blocking inbound from China (servers need to accept connections from Chinese clients) - DNS-level blocking - Application-layer filtering ## Decisions ### 1. ipset + iptables over raw iptables rules Use `ipset` to store the China CIDR list as a hash:net set, then a single iptables rule referencing the set. This is far more efficient than thousands of individual iptables rules. **Why over nftables**: iptables + ipset is universally available on Ubuntu/Debian and simpler to manage via Ansible. nftables sets would also work but add migration complexity. ### 2. IP list source: ipdeny.com aggregated zones Use `https://www.ipdeny.com/ipblocks/data/aggregated/cn-aggregated.zone` for the China CIDR list. It's a widely-used, frequently updated, plain-text list of CIDR blocks. **Why over MaxMind GeoLite2**: No account/license required. Plain text format is trivial to load into ipset. Aggregated format minimizes the number of entries. ### 3. Refresh via cron A daily cron job downloads the latest CN zone file and reloads the ipset. The update script is idempotent — it creates a temporary set, swaps it atomically, then destroys the old one. ### 4. iptables rule placement The iptables rule is inserted in the OUTPUT chain to block traffic originating from the server itself (proxy daemon forwarding). A corresponding FORWARD chain rule is not needed since these servers don't act as network routers. ## Risks / Trade-offs - **[IP list accuracy]** → ipdeny.com may lag behind real-time IP allocations. Mitigation: daily refresh keeps it reasonably current; exact accuracy is not critical for this use case. - **[Legitimate CN access blocked]** → If a server needs to reach a Chinese API (unlikely for proxy servers), it would be blocked. Mitigation: add specific IPs to an allowlist variable if needed. - **[ipset persistence across reboot]** → ipset sets are in-memory and lost on reboot. Mitigation: the update script runs at boot via a systemd service, and the cron job refreshes daily.