使用iptables结合ipset封锁国外的异常IP访问

最近遇到了网站不断被一些国外的IP扫描的问题,想了想,决定将所有非国内的客户端全部封锁,只允许国内的用户进行访问。

首先需要找到所有国内的IP段,这个比较简单,ipip.net的github提供了一个china_ip_list项目,记录了目前所有国内的IP地址段,每月更新。
这个列表可以通过wget https://raw.githubusercontent.com/17mon/china_ip_list/master/china_ip_list.txt直接进行下载。

有了IP列表,接下来的事情相对简单不少,虽然理论上可以通过iptables针对文件里的每一个IP段添加规则,但是这样会添加几千条规则,不是最优解,所以就借助ipset和iptables进行封锁。

首先,使用ipset创建一个集合,名字就叫whitelist-china

ipset create whitelist-china hash:net hashsize 10000 maxelem 1000000

然后,将文件中的所有IP段都加到这个集合里:

for i in $(cat china_ip_list.txt);do
        ipset add whitelist-china $i;
done

最后,在通过iptables封锁所有不在这个集合中的IP,因为不想影响我访问国内的地址,所以在这里,只需要将TCP协议的SYN包,以及ICMP包丢弃掉就可以:

iptables -A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m set ! --match-set whitelist-china src -j DROP
iptables -A INPUT -p icmp -m set ! --match-set whitelist-china src -j DROP

这么设置之后,出现了一些问题,我在本地无法通过127.0.0.1访问本机的MySQL服务了,为什么呢?因为连接本机,那么客户端的IP就是127.0.0.1,很显然这个IP也不在国内的地址段里,直接导致iptables错误的把本地的请求也丢弃了。于是还需要放行本地发起的请求:

iptables -A INPUT -s 127.0.0.1/8 -j ACCEPT

好了,经过这样的设置,世界清净了不少,如果想看看到底有多少的数据被iptables丢弃,只需要执行iptables -vnL查看命中数量:

# iptables -vnL
Chain INPUT (policy ACCEPT 70K packets, 3507K bytes)
 pkts bytes target     prot opt in     out     source               destination
70660 3505K DROP       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp flags:0x17/0x02 ! match-set whitelist-china src
   40  1738 DROP       icmp --  *      *       0.0.0.0/0            0.0.0.0/0            ! match-set whitelist-china src