Browse Source

feat: remove server-side geoblock role for CN destinations

CN destination routing is handled at the Surge client level.
Removing the server-side ipset/iptables geoblock role eliminates
redundant complexity.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
kotoyuuko 3 weeks ago
parent
commit
d789dcd3f2

+ 0 - 1
README.md

@@ -95,7 +95,6 @@ cp -r credentials/ /path/to/backup/
 │   └── landing.yml
 ├── roles/
 │   ├── base/           # SSH hardening, UFW, fail2ban
-│   ├── geoblock/       # Block outbound to China IPs
 │   ├── shadowsocks/    # shadowsocks-rust (relay)
 │   └── trojan/         # trojan-go + certbot (landing)
 ├── templates/

+ 2 - 0
openspec/changes/archive/2026-04-22-remove-server-geoblock/.openspec.yaml

@@ -0,0 +1,2 @@
+schema: spec-driven
+created: 2026-04-22

+ 23 - 0
openspec/changes/archive/2026-04-22-remove-server-geoblock/design.md

@@ -0,0 +1,23 @@
+## Context
+
+The geoblock role currently runs on all servers (relay + landing) via `site.yml`. It installs ipset/iptables, downloads China CIDR lists from ipdeny.com, populates an ipset named `cn-block`, adds an iptables OUTPUT chain DROP rule, and sets up a daily cron refresh + systemd boot restore. CN destination routing is already handled at the Surge client level via policy/group rules, making the server-side block redundant.
+
+## Goals / Non-Goals
+
+**Goals:**
+- Remove all server-side CN destination blocking infrastructure
+- Clean up all geoblock-related files, references, and specs
+- Leave Surge client routing untouched
+
+**Non-Goals:**
+- No changes to Surge client config or routing rules
+- No changes to other roles (base, shadowsocks, trojan)
+
+## Decisions
+
+Remove the entire `roles/geoblock/` directory rather than disabling it, since there is no reason to keep the code around. Remove the role reference from `site.yml` "Base server setup" play. Remove the `geoblock-cn` spec from `openspec/specs/`. Update README.md references.
+
+## Risks / Trade-offs
+
+- [Servers may still have active ipset/iptables rules after this change] → The Ansible removal won't actively clean up runtime iptables rules. A manual `iptables -D OUTPUT -m set --match-set cn-block dst -j DROP && ipset destroy cn-block` may be needed on existing servers, or a reboot will clear them.
+- [No server-side safety net for accidental CN traffic] → CN routing is handled by Surge client policies. If a client bypasses the proxy and connects directly to the server, the server could theoretically reach CN destinations. This is acceptable per user decision.

+ 27 - 0
openspec/changes/archive/2026-04-22-remove-server-geoblock/proposal.md

@@ -0,0 +1,27 @@
+## Why
+
+The server-side geoblock role that blocks outbound traffic to China destinations via ipset/iptables is no longer needed. CN destination routing is already handled at the Surge client level via policy rules, making the server-side block redundant.
+
+## What Changes
+
+- Remove `geoblock` role from `site.yml`
+- Delete `roles/geoblock/` directory entirely (tasks, templates, handlers, defaults)
+- Remove the `geoblock-cn` spec from `openspec/specs/`
+
+Surge client-side routing rules (in `surge-client.conf.j2`) remain unchanged.
+
+## Capabilities
+
+### New Capabilities
+<!-- none -->
+
+### Modified Capabilities
+- `geoblock-cn`: **REMOVED** — server-side CN destination blocking via ipset/iptables is being removed entirely
+
+## Impact
+
+- `site.yml`: geoblock role removed from the "Base server setup" play
+- `roles/geoblock/`: entire directory deleted
+- `openspec/specs/geoblock-cn/`: spec removed
+- `README.md`: any references to geoblock should be updated
+- Surge client config and routing: **no change**

+ 29 - 0
openspec/changes/archive/2026-04-22-remove-server-geoblock/specs/geoblock-cn/spec.md

@@ -0,0 +1,29 @@
+## REMOVED Requirements
+
+### Requirement: ipset and iptables are installed on all servers
+**Reason**: Server-side CN destination blocking is no longer needed; Surge client handles CN routing.
+**Migration**: No replacement. CN destination routing remains at the Surge client level.
+
+### Requirement: China IP CIDR list is downloaded
+**Reason**: Server-side CN destination blocking is no longer needed.
+**Migration**: None required.
+
+### Requirement: ipset is populated with China CIDR ranges
+**Reason**: Server-side CN destination blocking is no longer needed.
+**Migration**: Existing `cn-block` ipset will no longer be updated. It can be manually destroyed.
+
+### Requirement: iptables blocks outbound to China IPs
+**Reason**: Server-side CN destination blocking is no longer needed.
+**Migration**: The OUTPUT chain DROP rule referencing `cn-block` can be manually removed.
+
+### Requirement: CN IP list is refreshed daily via cron
+**Reason**: Server-side CN destination blocking is no longer needed.
+**Migration**: The cron job `geoblock-refresh` will be removed by Ansible.
+
+### Requirement: ipset is restored on boot
+**Reason**: Server-side CN destination blocking is no longer needed.
+**Migration**: The geoblock systemd service will be removed by Ansible.
+
+### Requirement: Geoblock role is applied to all servers
+**Reason**: Server-side CN destination blocking is no longer needed.
+**Migration**: The geoblock role is removed from `site.yml`.

+ 15 - 0
openspec/changes/archive/2026-04-22-remove-server-geoblock/tasks.md

@@ -0,0 +1,15 @@
+## 1. Remove geoblock role from site.yml
+
+- [ ] 1.1 Remove `- geoblock` from the roles list in the "Base server setup" play of `site.yml`
+
+## 2. Delete geoblock role directory
+
+- [x] 2.1 Delete `roles/geoblock/` directory (tasks, templates, handlers, defaults)
+
+## 3. Remove geoblock spec
+
+- [x] 3.1 Delete `openspec/specs/geoblock-cn/` directory
+
+## 4. Update README references
+
+- [x] 4.1 Remove or update any geoblock-related references in `README.md`

+ 0 - 57
openspec/specs/geoblock-cn/spec.md

@@ -1,57 +0,0 @@
-## ADDED Requirements
-
-### Requirement: ipset and iptables are installed on all servers
-The geoblock role SHALL ensure `ipset` and `iptables` packages are installed.
-
-#### Scenario: Packages installed
-- **WHEN** the geoblock role runs
-- **THEN** `ipset` and `iptables` are installed and available
-
-### Requirement: China IP CIDR list is downloaded
-The geoblock role SHALL download the aggregated China CIDR list from ipdeny.com to a local file on each server.
-
-#### Scenario: Initial download
-- **WHEN** the geoblock role runs for the first time
-- **THEN** the CN aggregated zone file is downloaded to a configurable path (default: `/etc/geoblock/cn.zone`)
-
-### Requirement: ipset is populated with China CIDR ranges
-The geoblock role SHALL create an ipset named `cn-block` of type `hash:net` and populate it with all CIDRs from the downloaded zone file.
-
-#### Scenario: ipset created and loaded
-- **WHEN** the geoblock update script runs
-- **THEN** an ipset named `cn-block` exists containing all China CIDR entries
-- **THEN** the set is created atomically (build temp set, swap, destroy old)
-
-### Requirement: iptables blocks outbound to China IPs
-The geoblock role SHALL add an iptables OUTPUT chain rule that drops packets matching the `cn-block` ipset.
-
-#### Scenario: Outbound to China IP is dropped
-- **WHEN** the server attempts to send a packet to an IP in the `cn-block` ipset
-- **THEN** the packet is dropped by iptables
-
-#### Scenario: Outbound to non-China IP is allowed
-- **WHEN** the server attempts to send a packet to an IP NOT in the `cn-block` ipset
-- **THEN** the packet is allowed through
-
-### Requirement: CN IP list is refreshed daily via cron
-The geoblock role SHALL configure a cron job that re-downloads the CN zone file and reloads the ipset daily.
-
-#### Scenario: Daily refresh
-- **WHEN** the cron job fires
-- **THEN** the latest CN zone file is downloaded
-- **THEN** the ipset is atomically reloaded with updated data
-
-### Requirement: ipset is restored on boot
-The geoblock role SHALL configure a systemd service that runs at boot to restore the ipset and iptables rule, ensuring the block survives reboots.
-
-#### Scenario: Server reboots
-- **WHEN** the server restarts
-- **THEN** the geoblock systemd service loads the CN zone into ipset
-- **THEN** the iptables OUTPUT rule referencing `cn-block` is applied
-
-### Requirement: Geoblock role is applied to all servers
-The geoblock role SHALL be applied to both relay and landing servers via `site.yml`.
-
-#### Scenario: Both servers have geoblock
-- **WHEN** `site.yml` is run
-- **THEN** the geoblock role runs on hosts in both `relay` and `landing` groups

+ 0 - 7
roles/geoblock/defaults/main.yml

@@ -1,7 +0,0 @@
----
-geoblock_zone_url: "https://www.ipdeny.com/ipblocks/data/aggregated/cn-aggregated.zone"
-geoblock_zone_path: /etc/geoblock/cn.zone
-geoblock_script_path: /usr/local/bin/geoblock-update.sh
-geoblock_ipset_name: cn-block
-geoblock_cron_hour: "3"
-geoblock_cron_minute: "17"

+ 0 - 4
roles/geoblock/handlers/main.yml

@@ -1,4 +0,0 @@
----
-- name: reload geoblock
-  ansible.builtin.command:
-    cmd: "{{ geoblock_script_path }}"

+ 0 - 51
roles/geoblock/tasks/main.yml

@@ -1,51 +0,0 @@
----
-- name: Install ipset and iptables
-  ansible.builtin.apt:
-    name:
-      - ipset
-      - iptables
-    state: present
-
-- name: Create geoblock config directory
-  ansible.builtin.file:
-    path: "{{ geoblock_zone_path | dirname }}"
-    state: directory
-    owner: root
-    group: root
-    mode: "0755"
-
-- name: Deploy geoblock update script
-  ansible.builtin.template:
-    src: geoblock-update.sh.j2
-    dest: "{{ geoblock_script_path }}"
-    owner: root
-    group: root
-    mode: "0755"
-  notify: reload geoblock
-
-- name: Deploy geoblock systemd service
-  ansible.builtin.template:
-    src: geoblock.service.j2
-    dest: /etc/systemd/system/geoblock.service
-    owner: root
-    group: root
-    mode: "0644"
-
-- name: Run initial geoblock load
-  ansible.builtin.command:
-    cmd: "{{ geoblock_script_path }}"
-    creates: "{{ geoblock_zone_path }}"
-
-- name: Enable geoblock service for boot
-  ansible.builtin.systemd:
-    name: geoblock
-    daemon_reload: yes
-    enabled: yes
-
-- name: Configure daily cron for geoblock refresh
-  ansible.builtin.cron:
-    name: "geoblock-refresh"
-    hour: "{{ geoblock_cron_hour }}"
-    minute: "{{ geoblock_cron_minute }}"
-    job: "{{ geoblock_script_path }}"
-    user: root

+ 0 - 27
roles/geoblock/templates/geoblock-update.sh.j2

@@ -1,27 +0,0 @@
-#!/bin/bash
-set -euo pipefail
-
-ZONE_URL="{{ geoblock_zone_url }}"
-ZONE_FILE="{{ geoblock_zone_path }}"
-IPSET_NAME="{{ geoblock_ipset_name }}"
-IPSET_TMP="${IPSET_NAME}-tmp"
-
-mkdir -p "$(dirname "$ZONE_FILE")"
-
-curl -fsSL -o "$ZONE_FILE" "$ZONE_URL"
-
-ipset create "$IPSET_TMP" hash:net -exist
-ipset flush "$IPSET_TMP"
-
-while IFS= read -r cidr; do
-    [[ -z "$cidr" || "$cidr" == \#* ]] && continue
-    ipset add "$IPSET_TMP" "$cidr" -exist
-done < "$ZONE_FILE"
-
-ipset create "$IPSET_NAME" hash:net -exist
-ipset swap "$IPSET_TMP" "$IPSET_NAME"
-ipset destroy "$IPSET_TMP"
-
-if ! iptables -C OUTPUT -m set --match-set "$IPSET_NAME" dst -j DROP 2>/dev/null; then
-    iptables -A OUTPUT -m set --match-set "$IPSET_NAME" dst -j DROP
-fi

+ 0 - 12
roles/geoblock/templates/geoblock.service.j2

@@ -1,12 +0,0 @@
-[Unit]
-Description=Load China IP geoblock rules
-After=network-online.target
-Wants=network-online.target
-
-[Service]
-Type=oneshot
-ExecStart={{ geoblock_script_path }}
-RemainAfterExit=yes
-
-[Install]
-WantedBy=multi-user.target

+ 0 - 1
site.yml

@@ -12,7 +12,6 @@
   hosts: all
   roles:
     - base
-    - geoblock
 
 - name: Deploy Shadowsocks on relay server
   hosts: relay