在CentOS服务器运维中,如果你需要限制来自特定国家的IP地址访问你的服务,最直接有效的方法就是利用firewalld的富规则(Rich Rules)功能,配合GeoIP数据库来实现基于地理位置的访问控制。具体操作流程是:先安装GeoIP工具和firewalld的GeoIP模块支持,下载国家IP段数据库,然后编写富规则将目标国家的IP段加入拒绝列表。这套方案不需要额外安装第三方防火墙软件,纯原生firewalld就能搞定,而且规则持久化、重启不丢失。

很多运维人员面对大量来自境外的恶意扫描、暴力破解时,第一反应是封禁单个IP,但这种方式效率极低。攻击者往往使用大量不同IP轮换攻击,你封一个他换一个。而按国家维度批量封禁,可以一次性屏蔽整个国家或地区的IP段,从源头大幅降低攻击面。这篇文章会从环境准备、数据库安装、规则编写、批量操作到验证测试,一步步讲清楚怎么落地。

一、环境准备与前置条件

首先确认你的CentOS系统版本和firewalld状态。CentOS 7和CentOS 8/Stream都自带firewalld,但CentOS 8以上版本对GeoIP的支持方式有所不同。建议使用CentOS 7或Rocky Linux 8/9,兼容性最好。

检查firewalld是否正在运行:

systemctl status firewalld

如果没有运行,启动并设置开机自启:

systemctl start firewalld
systemctl enable firewalld

确认firewalld版本是否支持rich rules的source地址匹配:

firewall-cmd --version

一般firewalld 0.6.x以上版本都支持。另外需要确认系统已安装iptables相关工具,因为firewalld底层依赖iptables/nftables来实现规则下发。

二、安装GeoIP工具和数据库

CentOS自带的firewalld本身不包含GeoIP数据库,需要额外安装GeoIP相关工具。执行以下命令:

yum install -y geoip geoip-devel GeoIP-data

安装完成后,你会在/usr/share/GeoIP/目录下看到几个关键的.dat文件,其中GeoIP.dat是按国家分类的IP数据库,GeoIPCity.dat是按城市分类的。我们做国家级封禁主要用GeoIP.dat。

验证数据库是否可用:

geoiplookup 8.8.8.8

如果能正确返回国家信息,说明数据库安装成功。如果提示找不到数据库文件,检查/usr/share/GeoIP/目录下文件是否完整,或者从MaxMind官网下载最新的免费版GeoLite2 Country数据库替换。

需要注意的是,firewalld的富规则本身不能直接读取GeoIP数据库,我们需要借助一个辅助脚本来将国家对应的IP段提取出来,然后批量添加到firewalld规则中。这是整个方案的核心环节。

三、编写IP段提取脚本

由于firewalld的富规则只接受CIDR格式的IP地址或IP段,我们需要写一个脚本把指定国家的所有IP段从GeoIP数据库中提取出来。以下是一个实用的Shell脚本:

#!/bin/bash
# 用法: ./get_country_ips.sh CN
# 参数为国家代码,如CN、US、RU等

COUNTRY_CODE=$1
GEOIP_DB="/usr/share/GeoIP/GeoIP.dat"

if [ -z "$COUNTRY_CODE" ]; then
    echo "Usage: $0 "
    exit 1
fi

if [ ! -f "$GEOIP_DB" ]; then
    echo "GeoIP database not found at $GEOIP_DB"
    exit 1
fi

# 使用geoiplookup批量查询并提取IP段
# 注意:geoiplookup不支持批量,这里用awk处理已知IP列表
# 更高效的方式是使用mmdblookup工具(需要安装libmaxminddb)

# 方法一:使用mmdblookup(推荐)
if command -v mmdblookup &> /dev/null; then
    mmdblookup --file "$GEOIP_DB" --ip "$COUNTRY_CODE" 2>/dev/null | \
    awk -F'"' '{print $4}' | sort -u
else
    echo "mmdblookup not found, installing libmaxminddb..."
    yum install -y libmaxminddb
    mmdblookup --file "$GEOIP_DB" --ip "$COUNTRY_CODE" 2>/dev/null | \
    awk -F'"' '{print $4}' | sort -u
fi

上面这个脚本是简化版,实际生产环境中建议使用更成熟的工具。推荐安装MaxMind的mmdb工具:

yum install -y epel-release
yum install -y libmaxminddb mmdb-bin

然后使用mmdblookup命令精确提取某个国家的所有IP段:

mmdblookup --file /usr/share/GeoIP/GeoIP.dat --ip 1.2.3.4 2>/dev/null

但mmdblookup是按单个IP查询的,要批量获取某国所有IP段,更实用的方法是下载一个现成的国家IP段列表,或者使用geoip2csv等工具转换数据库。这里给一个更直接的方案:下载按国家分类的IP段文本文件。

四、获取国家IP段列表文件

最简单高效的方式是从公开的IP段数据源下载按国家分类的CIDR列表。比如ipdeny.com或者nirsoft提供的免费IP段列表。以中国为例,下载中国IP段列表:

wget -O /tmp/cn_ips.txt "https://raw.githubusercontent.com/fate0/proxylist/master/proxy.list"

或者使用更权威的来源,比如从MaxMind的GeoLite2免费数据库中导出。不过对于大多数运维场景,直接使用社区维护的IP段列表文件就足够了。将文件保存到/etc/firewalld/geoip/目录下方便管理:

mkdir -p /etc/firewalld/geoip
mv /tmp/cn_ips.txt /etc/firewalld/geoip/

你可以为每个需要封禁的国家都准备一个对应的IP段文件,比如us_ips.txt、ru_ips.txt等。

五、编写批量添加富规则的脚本

有了IP段列表后,就可以批量添加firewalld富规则了。以下脚本会读取IP段文件,逐条添加拒绝规则:

#!/bin/bash
# 用法: ./block_country.sh cn
# 将指定国家的IP段全部加入firewalld拒绝规则

COUNTRY=$1
IP_FILE="/etc/firewalld/geoip/${COUNTRY}_ips.txt"

if [ ! -f "$IP_FILE" ]; then
    echo "IP list file not found: $IP_FILE"
    exit 1
fi

# 切换到永久规则区域
firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source address="0.0.0.0/0" drop'

# 逐行读取IP段并添加
while IFS= read -r ip_cidr; do
    # 跳过空行和注释
    [[ -z "$ip_cidr" || "$ip_cidr" =~ ^# ]] && continue
    
    echo "Blocking: $ip_cidr"
    firewall-cmd --permanent --zone=public --add-rich-rule="rule family='ipv4' source address='$ip_cidr' drop"
done < "$IP_FILE"

# 重新加载规则
firewall-cmd --reload

echo "Done! Blocked $(wc -l < $IP_FILE) IP ranges from $COUNTRY"

这个脚本会将指定国家的所有IP段以富规则形式添加到public区域,动作设为drop(直接丢弃,不返回任何响应)。如果你希望返回拒绝响应而不是静默丢弃,可以把drop改成reject:

firewall-cmd --permanent --zone=public --add-rich-rule="rule family='ipv4' source address='$ip_cidr' reject"
六、针对特定国家添加规则的实操示例

假设你要封禁来自俄罗斯(RU)的所有IP访问你的SSH服务(22端口),但允许其他服务正常访问,可以这样写更精确的规则:

# 只针对SSH端口封禁俄罗斯IP
firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source address="1.2.3.0/24" port port="22" protocol="tcp" drop'

如果你要封禁整个国家对所有服务的访问,直接用:

# 封禁某个IP段的所有访问
firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source address="45.33.32.0/22" drop'

如果IP段数量非常多(比如中国有上万个IP段),逐条添加可能会导致firewalld性能下降。这时候建议改用ipset配合firewalld来实现:

# 创建ipset集合
ipset create blocked_country hash:net

# 批量导入IP段
while read ip; do
    ipset add blocked_country $ip
done < /etc/firewalld/geoip/cn_ips.txt

# 在firewalld中引用ipset
firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source ipset="blocked_country" drop'

firewall-cmd --reload

ipset方式的优势是规则数量不受firewalld富规则条数限制,性能也更好,适合大规模IP段封禁场景。这是生产环境中更推荐的做法。

七、规则验证与排错

规则添加完成后,必须验证是否生效。查看当前所有富规则:

firewall-cmd --zone=public --list-rich-rules

查看ipset中的IP数量:

ipset list blocked_country

测试某个被封禁的IP是否真的被拦截,可以从另一台机器尝试连接:

telnet <你的服务器IP> 22

如果连接超时或直接被拒绝,说明规则生效。如果仍然能连通,检查以下几点:规则是否添加到了正确的zone、是否执行了reload、IP段格式是否正确、是否有其他规则优先级更高放行了该IP。

查看firewalld的完整运行状态和规则优先级:

firewall-cmd --zone=public --list-all

特别注意firewalld规则的优先级顺序:富规则 > 端口规则 > 服务规则。如果你同时有允许某个端口的规则和拒绝某IP的富规则,需要确认拒绝规则是否在允许规则之后被正确匹配。

八、规则维护与更新策略

IP段数据不是一成不变的,国家的IP分配会定期调整。建议建立定期更新机制:

# 每月更新一次IP段列表
0 3 1 * * /usr/local/bin/update_geoip.sh >> /var/log/geoip_update.log 2>&1

更新脚本内容大致如下:

#!/bin/bash
# 下载最新IP段并重新应用规则
cd /etc/firewalld/geoip
wget -q -O cn_ips.txt "https://最新的中国IP段列表URL"

# 清空旧规则重新添加
ipset flush blocked_country
while read ip; do
    ipset add blocked_country $ip
done < cn_ips.txt

firewall-cmd --reload

另外要注意,封禁整个国家的IP可能会误封一些合法用户,比如海外华人、跨国企业员工等。建议在实施前先做小范围测试,或者采用更细粒度的策略:只封禁特定端口(如SSH、RDP)的境外访问,而不是一刀切封禁所有服务。

九、常见问题与注意事项

第一,firewalld的富规则有数量限制,单zone下建议不超过几百条,超过后建议用ipset。第二,GeoIP数据库的准确性大约在99%左右,存在少量误差,个别IP可能被错误归类。第三,不要在生产环境直接操作,先在测试机验证规则效果。第四,如果你使用的是CentOS 8以上且开启了nftables后端,firewalld的规则会通过nftables下发,ipset同样兼容,但要确认nftables版本支持ipset引用。

第五,如果服务器有多个网卡或多个zone,要确认规则添加到了正确的zone。比如你的对外服务在public zone,内部管理在trusted zone,别把封禁规则加错地方。第六,做好规则备份,定期导出当前规则:

firewall-cmd --zone=public --list-rich-rules > /backup/firewall_rules_$(date +%Y%m%d).txt

总结来说,firewalld富规则配合GeoIP实现国家级IP封禁是一套成熟、可靠、纯原生的方案。核心思路就是:获取国家IP段数据 → 用ipset或富规则批量添加 → 定期更新维护。掌握这套方法,你就能在CentOS上轻松实现精准的地理位置访问控制,大幅提升服务器安全性。