实现方式:iptables + ipset

1、安装必要工具

# 更新系统
sudo apt update
sudo apt upgrade -y
 
# 安装必要工具
sudo apt install iptables ipset curl wget cron -y
 
# 安装持久化工具
sudo apt install iptables-persistent netfilter-persistent -y

2、服务目录

sudo mkdir -p /mnt/ipfilter
sudo mkdir -p /var/log/ipfilter
cd /mnt/ipfilter

3、脚本配置

IP列表更新

sudo nano /mnt/ipfilter/update_ipsets.sh
#!/bin/bash
set -e
 
LOG_FILE="/var/log/ipfilter/update.log"
CHINA_IPS="/mnt/ipfilter/china_ips.txt"
ALLOWED_COUNTRIES="CN HK MO TW"
mkdir -p /var/log/ipfilter
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
cleanup() {
    rm -f /tmp/china_*.txt /tmp/cn_*.txt /tmp/ip_*.txt 2>/dev/null || true
}
 
error_exit() {
    log "ERROR: $1"
    cleanup
    exit 1
}
 
check_command() {
    if ! command -v "$1" >/dev/null 2>&1; then
        error_exit "Required command '$1' not found"
    fi
}
 
main() {
 
    check_command "curl"
    check_command "ipset"
    check_command "sort"
    check_command "uniq"
 
    cleanup
 
    SOURCES=(
        "https://raw.githubusercontent.com/17mon/china_ip_list/master/china.txt"
        "https://raw.githubusercontent.com/metowolf/iplist/master/data/country/CN.txt"
        "https://raw.githubusercontent.com/gaoyifan/china-operator-ip/raw/ip-lists/china.txt"
        "https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/ipip_country/ipip_country_cn.netset"
    )
 
    TEMP_FILE="/tmp/all_ips.txt"
 
    for url in "${SOURCES[@]}"; do
        curl -s --max-time 30 "$url" 2>/dev/null | \
            grep -E '^[0-9]' | grep -v '^#' | sort -u >> "$TEMP_FILE" || true
    done
 
    if [ ! -s "$TEMP_FILE" ]; then
        log "无法下载IP列表,使用备份文件"
        if [ -f "$CHINA_IPS.bak" ]; then
            cp "$CHINA_IPS.bak" "$TEMP_FILE"
        else
            error_exit "没有可用的IP列表"
        fi
    fi
 
    sort -u -t . -k 1,1n -k 2,2n -k 3,3n -k 4,4n "$TEMP_FILE" > "$CHINA_IPS.tmp"
 
    grep -E '^([0-9]{1,3}\.){3}[0-9]{1,3}(/[0-9]{1,2})?$' "$CHINA_IPS.tmp" > "$CHINA_IPS"
    
    IP_COUNT=$(wc -l < "$CHINA_IPS")
    log "获取到 $IP_COUNT 个IP段"
 
    if [ -f "$CHINA_IPS" ]; then
        cp "$CHINA_IPS" "$CHINA_IPS.bak"
    fi
 
    update_ipsets
 
    cleanup
}
 
update_ipsets() {
    ipset destroy china-ips-temp 2>/dev/null
    ipset destroy china-ips-temp-new 2>/dev/null
    ipset destroy china-ips-new 2>/dev/null
 
    if ! ipset list china-ips >/dev/null 2>&1; then
        ipset create china-ips hash:net hashsize 16384 maxelem 1000000
    else
        OLD_COUNT=$(ipset list china-ips | grep -c '^[0-9]')
    fi
 
    if [ ! -f "$CHINA_IPS" ]; then
        return 1
    fi
 
    ipset create china-ips-new hash:net hashsize 16384 maxelem 1000000
 
    TOTAL=$(wc -l < "$CHINA_IPS")
    added=0
    skipped=0
    count=0
 
    while read -r ip; do
        if [ -n "$ip" ]; then
            if ipset add china-ips-new "$ip" 2>/dev/null; then
                added=$((added + 1))
            else
                skipped=$((skipped + 1))
            fi
        fi
        
        count=$((count + 1))
        if [ $((count % 500)) -eq 0 ]; then
            log "已处理 $count/$TOTAL 条IP,已添加 $added 条"
        fi
    done < "$CHINA_IPS"
 
    NEW_COUNT=$(ipset list china-ips-new | grep -c "^[0-9]")
    log "临时集合添加完成: 共处理 $count 条, 成功添加 $added 条, 跳过 $skipped 条"
    log "临时集合实际IP数量: $NEW_COUNT"
 
    if [ $added -gt 0 ]; then
        ipset swap china-ips-new china-ips
 
        ipset destroy china-ips-new
    else
        ipset destroy china-ips-new
    fi
 
    mkdir -p /mnt/ipset
    ipset save > /mnt/ipset/ipset.conf
 
    update_iptables
}
 
update_iptables() {
 
    if ! iptables -L GEO_BLOCK >/dev/null 2>&1; then
        iptables -N GEO_BLOCK
    else
        iptables -F GEO_BLOCK
    fi
 
    iptables -A GEO_BLOCK -i lo -j ACCEPT
    iptables -A GEO_BLOCK -s 127.0.0.0/8 -j ACCEPT
 
    iptables -A GEO_BLOCK -s 10.0.0.0/8 -j ACCEPT
    iptables -A GEO_BLOCK -s 172.10.0.0/12 -j ACCEPT
    iptables -A GEO_BLOCK -s 192.168.0.0/16 -j ACCEPT
 
    iptables -A GEO_BLOCK -m state --state ESTABLISHED,RELATED -j ACCEPT
 
    iptables -A GEO_BLOCK -p icmp -j ACCEPT
 
    iptables -A GEO_BLOCK -m set --match-set china-ips src -j ACCEPT
 
    iptables -A GEO_BLOCK -m limit --limit 10/min -j LOG --log-prefix "GEO_BLOCK_DENY: "
 
    iptables -A GEO_BLOCK -j DROP
 
    if ! iptables -C INPUT -j GEO_BLOCK >/dev/null 2>&1; then
        iptables -I INPUT 1 -j GEO_BLOCK
    else
        log "GEO_BLOCK链已在INPUT链中"
    fi
}
 
main "$@"

配置文件

sudo nano /mnt/ipfilter/ipfilter.conf
# 允许访问的端口
ALLOW_PORTS="22,80,443,3306"
BLOCK_ACTION="DROP"
 
LOG_DENIED=true
 
LOG_LIMIT="10/min"
 
ALLOW_PRIVATE=true
 
ALLOW_ICMP=true
 
UPDATE_FREQUENCY=7
# 白名单
WHITELIST=(
    "127.0.0.1/32"
    "10.0.0.0/8"
    "172.16.0.0/12"
    "192.168.0.0/16"
)
# 黑名单
BLACKLIST=(
    # 这里添加要特别屏蔽的IP
)
 
DATA_SOURCES=(
    "https://raw.githubusercontent.com/17mon/china_ip_list/master/china.txt"
    "https://raw.githubusercontent.com/metowolf/iplist/master/data/country/CN.txt"
)
 
IPSET_NAME="china-ips"
IPSET_HASHSIZE="16384"
IPSET_MAXELEM="1000000"
IPTABLES_CHAIN="GEO_BLOCK"
 
#测试模式(设置为true时,只记录不拒绝)
TEST_MODE=false

控制脚本

sudo nano /mnt/ipfilter/ipfilter.sh
#!/bin/bash
set -e
 
CONFIG_FILE="/mnt/ipfilter/ipfilter.conf"
LOG_FILE="/var/log/ipfilter/ipfilter.log"
 
if [ -f "$CONFIG_FILE" ]; then
    source "$CONFIG_FILE"
else
    # 默认配置
    ALLOW_PORTS="22,80,443"
    BLOCK_ACTION="DROP"
    LOG_DENED=true
    LOG_LIMIT="10/min"
fi
 
initialize() {
    echo "初始化IP过滤系统..."
    if ! ipset list china-ips >/dev/null 2>&1; then
        echo "创建ipset集合..."
        ipset create china-ips hash:net hashsize 16384 maxelem 1000000
    fi
    iptables -N GEO_BLOCK 2>/dev/null || true
 
    iptables-save > /mnt/ipfilter/iptables.backup.$(date +%Y%m%d-%H%M%S)
 
    iptables -F
    iptables -X GEO_BLOCK 2>/dev/null
 
    iptables -N GEO_BLOCK
 
    iptables -P INPUT ACCEPT
    iptables -P FORWARD DROP
    iptables -P OUTPUT ACCEPT
    iptables -A INPUT -i lo -j ACCEPT
    iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
    iptables -A INPUT -p icmp -j ACCEPT
    iptables -A INPUT -j GEO_BLOCK
    
    echo "初始化完成"
}
 
start() {
    echo "启动IP过滤..."
 
    if ! ipset list china-ips >/dev/null 2>&1; then
        echo "警告: china-ips集合不存在,尝试创建..."
        ipset create china-ips hash:net hashsize 16384 maxelem 1000000 || {
            echo "错误: 无法创建ipset集合"
            return 1
        }
    fi
 
    if [ ! -f "/mnt/ipfilter/china_ips.txt" ] || [ ! -s "/mnt/ipfilter/china_ips.txt" ]; then
        echo "IP列表不存在或为空,开始更新..."
        /mnt/ipfilter/update_ipsets.sh
    fi
    enable_rules
    
    echo "IP过滤已启动"
}
 
stop() {
    echo "停止IP过滤..."
 
    iptables -D INPUT -j GEO_BLOCK 2>/dev/null || true
 
    iptables -F GEO_BLOCK
 
    iptables -P INPUT ACCEPT
    iptables -P FORWARD ACCEPT
    
    echo "IP过滤已停止"
}
 
enable_rules() {
    echo "启用防火墙规则..."
    if ! iptables -L GEO_BLOCK >/dev/null 2>&1; then
        echo "创建GEO_BLOCK链..."
        iptables -N GEO_BLOCK
    fi
    iptables -F GEO_BLOCK
    echo "添加基础允许规则..."
    iptables -A GEO_BLOCK -s 127.0.0.0/8 -j ACCEPT
    iptables -A GEO_BLOCK -s 10.0.0.0/8 -j ACCEPT
    iptables -A GEO_BLOCK -s 192.168.0.0/16 -j ACCEPT
    iptables -A GEO_BLOCK -m state --state ESTABLISHED,RELATED -j ACCEPT
    iptables -A GEO_BLOCK -p icmp -j ACCEPT
    IFS=',' read -ra PORTS <<< "$ALLOW_PORTS"
    for port in "${PORTS[@]}"; do
        port=$(echo "$port" | tr -d ' ')
        if [[ "$port" =~ ^[0-9]+$ ]]; then
            # 允许中国IP访问指定端口
            iptables -A GEO_BLOCK -p tcp --dport "$port" \
                -m set --match-set china-ips src -j ACCEPT
            echo "  允许中国IP访问端口: $port"
        fi
    done
    if [ "$LOG_DENIED" = true ]; then
        iptables -A GEO_BLOCK -m limit --limit "$LOG_LIMIT" \
            -j LOG --log-prefix "GEO_BLOCK_DENY: "
    fi
    iptables -A GEO_BLOCK -j "$BLOCK_ACTION"
    
    echo "规则已启用"
}
 
status() {
    echo "=== IP过滤系统状态 ==="
    echo
    
    echo "ipset集合状态:"
    if ipset list china-ips >/dev/null 2>&1; then
        ipset list china-ips | grep -E "^(Number|Size|References|Members)" | head -5
        echo "IP数量: $(ipset list china-ips | grep '^[0-9]' | wc -l)"
    else
        echo "  未找到china-ips集合"
    fi
    
    echo
    echo "iptables规则:"
    iptables -L GEO_BLOCK -n --line-numbers 2>/dev/null || echo "  未找到GEO_BLOCK链"
    
    echo
    echo "最近日志:"
    tail -20 /var/log/ipfilter/update.log 2>/dev/null || echo "  无日志文件"
    
    echo
    echo "系统信息:"
    echo "   最后更新: $(stat -c %y /mnt/ipfilter/china_ips.txt 2>/dev/null || echo '未知')"
    echo "   IP列表数量: $(wc -l /mnt/ipfilter/china_ips.txt 2>/dev/null | cut -d' ' -f1 || echo '0')"
}
 
reload() {
    echo "重新加载配置..."
    stop
    sleep 1
    start
}
 
usage() {
    echo "用法: $0 {start|stop|restart|status|reload|init|update}"
    echo
    echo "命令:"
    echo "  init    初始化系统"
    echo "  start   启动IP过滤"
    echo "  stop    停止IP过滤"
    echo "  restart 重启IP过滤"
    echo "  status  查看状态"
    echo "  reload  重新加载"
    echo "  update  更新IP列表"
    echo
}
 
case "$1" in
    init)
        initialize
        ;;
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        stop
        sleep 2
        start
        ;;
    status)
        status
        ;;
    reload)
        reload
        ;;
    update)
        /mnt/ipfilter/update_ipsets.sh
        ;;
    *)
        usage
        exit 1
        ;;
esac
 
exit 0

系统服务

sudo nano /etc/systemd/system/ipfilter.service
[Unit]
Description=IP Country Filter Service
After=network-online.target
Wants=network-online.target
Requires=network-online.target
Before=ssh.service nginx.service apache2.service
 
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStartPre=/bin/bash -c "iptables -C INPUT -i lo -j ACCEPT 2>/dev/null || iptables -A INPUT -i lo -j ACCEPT"
ExecStartPre=/bin/bash -c "iptables -C INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT 2>/dev/null || iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT"
ExecStart=/mnt/ipfilter/ipfilter.sh start
ExecStop=/mnt/ipfilter/ipfilter.sh stop
ExecReload=/mnt/ipfilter/ipfilter.sh reload
ExecStopPost=/bin/bash -c "if [ $? -ne 0 ]; then iptables -P INPUT ACCEPT; fi"
TimeoutStartSec=300
TimeoutStopSec=30
Restart=no
StandardOutput=journal
StandardError=journal
 
[Install]
WantedBy=multi-user.target

定时更新IP列表

sudo nano /etc/systemd/system/ipfilter.service
[Unit]
Description=IP Country Filter Service
After=network-online.target
Wants=network-online.target
Requires=network-online.target
Before=ssh.service nginx.service apache2.service
 
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStartPre=/bin/bash -c "iptables -C INPUT -i lo -j ACCEPT 2>/dev/null || iptables -A INPUT -i lo -j ACCEPT"
ExecStartPre=/bin/bash -c "iptables -C INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT 2>/dev/null || iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT"
ExecStart=/mnt/ipfilter/ipfilter.sh start
ExecStop=/mnt/ipfilter/ipfilter.sh stop
ExecReload=/mnt/ipfilter/ipfilter.sh reload
ExecStopPost=/bin/bash -c "if [ $? -ne 0 ]; then iptables -P INPUT ACCEPT; fi"
TimeoutStartSec=300
TimeoutStopSec=30
Restart=no
StandardOutput=journal
StandardError=journal
 
[Install]
WantedBy=multi-user.target
sudo nano /etc/systemd/system/ipfilter-update.timer
[Unit]
Description=Update IP Filter Database Weekly
Requires=ipfilter-update.service
 
[Timer]
OnCalendar=weekly
Persistent=true
RandomizedDelaySec=3600
 
[Install]
WantedBy=timers.target

4、启用禁止国外ip访问服务

1. 设置权限
sudo chmod +x /mnt/ipfilter/*.sh
sudo chown -R root:root /mnt/ipfilter
sudo chown -R root:root /var/log/ipfilter
2. 加载配置
sudo systemctl daemon-reload
3. 启用系统服务
sudo systemctl enable ipfilter.service
sudo systemctl enable ipfilter-update.timer
sudo systemctl start ipfilter.service
sudo systemctl start ipfilter-update.timer
4. 验证配置
sudo systemctl status ipfilter.service
sudo systemctl status ipfilter-update.timer
sudo ipset list china-ips | head -20
sudo iptables -L GEO_BLOCK -n -v
sudo tail -f /var/log/ipfilter/update.log

5、常用命令

# 启动/停止/重启
sudo systemctl start ipfilter
sudo systemctl stop ipfilter
sudo systemctl restart ipfilter
 
# 查看状态
sudo systemctl status ipfilter
sudo journalctl -u ipfilter -f
 
# 手动更新IP列表
sudo /mnt/ipfilter/update_ipsets.sh
 
# 强制更新并重启
sudo systemctl stop ipfilter
sudo /mnt/ipfilter/ipfilter.sh update
sudo systemctl start ipfilter