本文介绍如何在 Ubuntu 24 系统上配置 iptables 和 ipset,实现按国家限制特定端口的访问,主要用于保护服务器关键端口(如 SSH、RCON)免受国外 IP 扫描和攻击。
Ubuntu 24 配置限制端口按国家访问
在搭建完 Minecraft 服务器后,我发现 RCON 端口频繁遭受国外 IP 的扫描攻击,SSH 端口也可能面临类似问题。本文介绍如何针对特定端口设置国家访问限制。
获取 IP 地址列表
由于大多数攻击来自国外,我们将使用中国 IP 地址作为白名单。
下载 IP 地址列表
我们使用 Loyalsoldier/geoip 项目的数据,该项目对 MaxMind 官方 GeoIP 数据进行了优化:
- 中国大陆 IPv4 数据融合了 IPIP.net 和 @gaoyifan/china-operator-ip
- 中国大陆 IPv6 数据融合了 MaxMind GeoLite2 和 @gaoyifan/china-operator-ip
需要下载的文件:
1 2 3
| mkdir -p /etc/firewall_persist/geoip wget -O /etc/firewall_persist/geoip/cn.txt https://github.com/Loyalsoldier/geoip/raw/release/text/cn.txt wget -O /etc/firewall_persist/geoip/private.txt https://github.com/Loyalsoldier/geoip/raw/release/text/private.txt
|
分离 IPv4 和 IPv6 地址
创建脚本 /usr/local/bin/split_ipv4_ipv6
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #!/bin/bash
log_file="/var/log/split_ipv4_ipv6.log"
if [ -z "$1" ]; then echo "$(date): Error: No input file provided." >> "$log_file" exit 1 fi
input_file="$1" base_name=$(basename "$input_file" .txt) ipv4_file="${base_name}_ipv4.txt" ipv6_file="${base_name}_ipv6.txt"
> "$ipv4_file" > "$ipv6_file"
while read -r line; do if [[ $line =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+(/[0-9]+)?$ ]]; then echo "$line" >> "$ipv4_file" elif [[ $line =~ ^[0-9a-fA-F:]+(/[0-9]+)?$ ]]; then echo "$line" >> "$ipv6_file" else echo "$(date): Invalid line: $line" >> "$log_file" fi done < "$input_file"
echo "$(date): IPv4 addresses saved to $ipv4_file" >> "$log_file" echo "$(date): IPv6 addresses saved to $ipv6_file" >> "$log_file"
|
设置执行权限并运行:
1 2 3 4
| chmod +x /usr/local/bin/split_ipv4_ipv6 cd /etc/firewall_persist/geoip split_ipv4_ipv6 /etc/firewall_persist/geoip/cn.txt split_ipv4_ipv6 /etc/firewall_persist/geoip/private.txt
|
配置 ipset
安装 ipset:
创建 IP 地址集合:
1 2 3 4 5 6 7
| ipset create cn_ip_v4 hash:net family inet ipset create private_ip_v4 hash:net family inet
ipset create cn_ip_v6 hash:net family inet6 ipset create private_ip_v6 hash:net family inet6
|
将 IP 地址添加到集合:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| while read -r cidr; do ipset add cn_ip_v4 "$cidr" done < /etc/firewall_persist/geoip/cn_ipv4.txt
while read -r cidr; do ipset add private_ip_v4 "$cidr" done < /etc/firewall_persist/geoip/private_ipv4.txt
while read -r cidr; do ipset add cn_ip_v6 "$cidr" done < /etc/firewall_persist/geoip/cn_ipv6.txt
while read -r cidr; do ipset add private_ip_v6 "$cidr" done < /etc/firewall_persist/geoip/private_ipv6.txt
|
创建防火墙规则
以 SSH 端口(22)为例:
1 2 3 4 5 6
| iptables -A INPUT -p tcp --dport 22 -m set --match-set private_ip_v4 src -j ACCEPT ip6tables -A INPUT -p tcp --dport 22 -m set --match-set private_ip_v6 src -j ACCEPT iptables -A INPUT -p tcp --dport 22 -m set --match-set cn_ip_v4 src -j ACCEPT ip6tables -A INPUT -p tcp --dport 22 -m set --match-set cn_ip_v6 src -j ACCEPT iptables -A INPUT -p tcp --dport 22 -j DROP ip6tables -A INPUT -p tcp --dport 22 -j DROP
|
规则持久化
创建持久化脚本 /usr/local/sbin/firewall_persist
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
| #!/bin/bash
IPTABLES_SAVE_FILE="/etc/firewall_persist/iptables_rules.v4" IP6TABLES_SAVE_FILE="/etc/firewall_persist/iptables_rules.v6" IPSET_SAVE_FILE="/etc/firewall_persist/ipset.conf"
if [ "$EUID" -ne 0 ]; then echo "请以root权限运行此脚本" exit 1 fi
mkdir -p /etc/firewall_persist
save_rules() { echo "正在保存防火墙规则..." iptables-save > "$IPTABLES_SAVE_FILE" if [ $? -eq 0 ]; then echo "IPv4规则已保存到 $IPTABLES_SAVE_FILE" else echo "保存IPv4规则失败" fi ip6tables-save > "$IP6TABLES_SAVE_FILE" if [ $? -eq 0 ]; then echo "IPv6规则已保存到 $IP6TABLES_SAVE_FILE" else echo "保存IPv6规则失败" fi ipset save > "$IPSET_SAVE_FILE" if [ $? -eq 0 ]; then echo "ipset规则已保存到 $IPSET_SAVE_FILE" else echo "保存ipset规则失败" fi }
load_rules() { echo "正在加载防火墙规则..." if [ -f "$IPSET_SAVE_FILE" ]; then ipset restore < "$IPSET_SAVE_FILE" echo "已加载ipset规则" else echo "ipset规则文件不存在" fi
if [ -f "$IPTABLES_SAVE_FILE" ]; then iptables-restore < "$IPTABLES_SAVE_FILE" echo "已加载IPv4规则" else echo "IPv4规则文件不存在" fi if [ -f "$IP6TABLES_SAVE_FILE" ]; then ip6tables-restore < "$IP6TABLES_SAVE_FILE" echo "已加载IPv6规则" else echo "IPv6规则文件不存在" fi }
case "$1" in "save") save_rules ;; "load") load_rules ;; *) echo "用法: $0 {save|load}" echo "save - 保存当前iptables和ipset规则" echo "load - 加载保存的iptables和ipset规则" exit 1 ;; esac
exit 0
|
设置权限并保存当前规则:
1 2
| chmod +x /usr/local/sbin/firewall_persist firewall_persist save
|
设置开机自启
创建服务文件:
1
| nano /etc/systemd/system/firewall-restore.service
|
写入以下内容:
1 2 3 4 5 6 7 8 9 10 11
| [Unit] Description=Restore iptables and ipset rules After=network.target
[Service] Type=oneshot ExecStart=/usr/local/sbin/firewall_persist load RemainAfterExit=yes
[Install] WantedBy=multi-user.target
|
启用服务:
1
| systemctl enable firewall-restore.service
|