用 iptables 屏蔽来自某个国家的 IP

星期六我们一位客户受到攻击,我们的网络监测显示有连续6小时的巨大异常流量,我们立即联系了客户,没有得到回应,我们修改和限制了客户的 VPS,使得个别 VPS 受攻击不会对整个服务器和其他 VPS 用户造成任何影响,我们一直保持这个 VPS 为开通状态(尽管一直受攻击),攻击又持续了24小时,星期天攻击仍在继续,我们忍无可忍,但是仍然无法联系到客户,我们向客户网站的另一负责人询问是否需要我们介入来帮助解决,这位负责人答应后我们立即投入到与 DDoS 的战斗中(我们动态扫描屏蔽坏 IP,现在客户网站已恢复。整个过程很有意思,以后有时间再写一篇博客来描述)。登录到客户 VPS 第一件事情就是查当前连接和 IP,来自中国的大量 IP 不断侵占80端口,典型的 DDoS. 所以第一件事是切断攻击源,既然攻击只攻80端口,那有很多办法可以切断,直接关闭网站服务器、直接用防火墙/iptables 切断80端口或者关闭所有连接、把 VPS 网络关掉、换一个 IP,⋯,等等。因为攻击源在国内,所以我们决定切断来自国内的所有访问,这样看上去网站好像是被墙了而不是被攻击了,有助于维护客户网站的光辉形象:D,那么如何屏蔽来自某个特定国家的 IP 呢?

方法很容易,先到 IPdeny 下载以国家代码编制好的 IP 地址列表,比如下载 cn.zone:

# wget http://www.ipdeny.com/ipblocks/data/countries/cn.zone

有了国家的所有 IP 地址,要想屏蔽这些 IP 就很容易了,直接写个脚本逐行读取 cn.zone 文件并加入到 iptables 中:

#!/bin/bash
# Block traffic from a specific country
# written by vpsee.com

COUNTRY="cn"
IPTABLES=/sbin/iptables
EGREP=/bin/egrep

if [ "$(id -u)" != "0" ]; then
   echo "you must be root" 1>&2
   exit 1
fi

resetrules() {
$IPTABLES -F
$IPTABLES -t nat -F
$IPTABLES -t mangle -F
$IPTABLES -X
}

resetrules

for c in $COUNTRY
do
        country_file=$c.zone

        IPS=$($EGREP -v "^#|^$" $country_file)
        for ip in $IPS
        do
           echo "blocking $ip"
           $IPTABLES -A INPUT -s $ip -j DROP
        done
done

exit 0

好 IP 和坏 IP 都被屏蔽掉了,这种办法当然不高明,屏蔽 IP 也没有解决被攻击的问题,但是是解决问题的第一步,屏蔽了攻击源以后我们才有带宽、时间和心情去检查 VPS 的安全问题。公布一份我们客户被攻击的网络流量图,在18点到0点所有带宽都被攻击流量占用,这时候客户无法登录 VPS,访问者也无法访问网站:

ddos

在 pfSense 上安装 DDclient

接着昨天的来,现在我们需要给我们刚建的小组内部网络设个动态域名以便外面可以用 ssh 等访问到内部的服务器,首先需要到 DynDNS.com 注册一个免费二级域名,然后在防火墙上安装动态域名客户端,每隔一段时间自动读取 ADSL 路由器的动态 IP 地址然后更新到 DynDNS.com 的域名服务就可以了。

pfSense 有自带的动态域名客户端,打开 pfSense 管理界面后配置 pfSense 自带的 Dynamic DNS 服务(pfSense->Services->Dynamic DNS),发现这个客户端有问题,只能在 save 的时候更新一次 IP 地址,不能自己自动更新 IP 地址,而且第一次提交的 IP 地址是内部的网络地址而不是我们想要的 ADSL 路由器对外的 IP 地址,google 了一下发现 pfSense 自带的动态域名客户端确实有问题:

Only works if pfSense is the public IP assigned to one of its interfaces. If you have a modem that gets your public IP and pfSense is a private IP, private IP address is registered with the provider.

解决办法是 disable pfSense 自带的 Dynamic DNS,安装第三方动态域名客户端 ddclient:

# pkg_add -r http://files.pfsense.org/packages/dns/ddclient-3.7.0.tbz
# cp /usr/local/etc/ddclient.conf.sample /usr/local/etc/ddclient.conf

# chmod +w /usr/local/etc/ddclient.conf
# vi /usr/local/etc/ddclient.conf
use=web, web=checkip.dyndns.com/, web-skip='IP Address'
server=members.dyndns.org
protocol=dyndns2
login=vpsee
password=your-password
vpsee.dyndns.org

修改完上面的 ddclient 的配置文件后,运行 ddclient 测试一下是否能运行:

# /usr/local/sbin/ddclient -daemon=0 -debug -verbose -noquiet

...
Can't load '/usr/local/lib/perl5/site_perl/5.8.8/mach/auto/Net/SSLeay/SSLeay.so' for module Net::SSLeay: Shared object "libssl.so.4" not found, required by "SSLeay.so" at /usr/local/lib/perl5/5.8.8/mach/DynaLoader.pm line 230.
 at /usr/local/lib/perl5/site_perl/5.8.8/IO/Socket/SSL.pm line 17
Compilation failed in require at /usr/local/lib/perl5/site_perl/5.8.8/IO/Socket/SSL.pm line 17.
BEGIN failed--compilation aborted at /usr/local/lib/perl5/site_perl/5.8.8/IO/Socket/SSL.pm line 17.
Compilation failed in require at /usr/local/sbin/ddclient line 1652.

上面错误显示缺少 libssl 库,这个好办,安装一个就可以了:

# pkg_add -r http://files.pfsense.org/packages/devel/openssl-stable-0.9.7l.tbz
# cp /usr/local/openssl/openssl.cnf.sample /usr/local/openssl/openssl.cnf

再次运行 ddclient 后无错误提示,运行成功:

# /usr/local/sbin/ddclient -daemon=0 -debug -verbose -noquiet

最后允许 ddclient 在后台(daemon)运行:

# vi /etc/rc.conf
ddclient_enable="YES"

登陆后 DynDNS.com 就会发现刚注册的二级域名的 IP 地址自动更新了:

dyndns domain

在 pfSense/FreeBSD 上安装 Squid

今年我们扯了一根自己的 4096 kbps ADSL 线路以便我们小组内部使用,不屏蔽端口,想访问什么就访问什么。为了同时访问工作网络和小组的网络,我们需要在不改变自己工作网络的网线和 IP 地址的情况下就能访问这根新的 ADSL 网络,所以需要在现有的工作网络上加一个 firwall/gateway 把 ADSL 和工作网络连接起来。简单的说我们现在有两套完全独立上网的网络,这样用户拿着笔记本进来插入工作网络网线就可以用工作网络上网,也可以改变代理服务器设置后用刚建的 ADSL 网络上网,或者不用网线,打开 WiFi 后直接进入 ADSL 所在的无线网络上网。下图中,如果 172.16.20.12 这台机器想上网,它可以使用 172.16.20.1 代理服务器通过工作网络上网,也可以通过使用代理服务器 172.16.20.10 通过 小组网络和 ADSL 上网。

小组:电话线 -- ADSL 路由器(10.0.0.1/8)
                 |
                 |
                 --------------  Firewall(10.0.0.2/172.16.20.10)
                                     |
                                     |
工作:Internet -- 路由器 -- 交换机 -- 工作网络(172.16.20.0/23)

为什么两个的网络要连起来呢?因为大家都不喜欢在网络之间切换,改 IP、网络设置、代理服务器等,很多时候大家需要同时在两个网络上,用工作网络(上网速度慢,很多端口被屏蔽,严格限制)访问内部所有的服务器,用新建的 ADSL 上网(网络速度快,没有屏蔽端口)。

要建这个 firewall/proxy/gateway 不需要很强大的硬件防火墙和服务器,所以 VPSee 决定把防火墙和 squid 服务安装在同一台机器上,节省资源。上个月对配置 Squid 服务器的硬件要求做了一些调查,接下来就是要在服务器上安装 Squid 服务。

pfSense 是一个基于 FreeBSD 的防火墙版本,从另一个 FreeBSD 防火墙 M0n0wall 发展而来,具有类似功能的 Linux 防火墙发行版本有 SmoothWall, Endian, IPCop, ClearOS, Untangle 等,试用了 一下 SmoothWall 发现非常简单易用,但是缺少一些我们需要的功能,SmoothWall 为了方便家庭用户使用把一些配置术语刻意简化了,比如配置网络的时候用到 Red/Green/Orange 让我摸不着头,我宁愿看到 nge0, bge0, eth0, 00:02:B3:CA:7B:92 等这些熟悉的术语,至少我能马上明白是什么意思。在测试 SmoothWall 的时候还遇到一些奇怪问题,比如启动了 DHCP 服务以后,客户端却不能及时得到 IP,要过几十秒钟,有时候甚至得不到 IP. pfSense 比 SmoothWall 强大很多,可自定义的模块很多,安装和配置第三方软件也很方便,使用起来特别顺手。

在 pfSense 上安装 Squid

安装 pfSense 过程很简单,这里省略,配置完 pfSense 网络接口后就开始安装 Squid,打开 pfSense 管理界面,在 System->packages 下面找到 squid 和 lightsquid(查看 squid 流量的工具)安装,成功后会在 Services 下看到 Proxy server 菜单,进去后可以配置 Squid 服务;在 Status->Proxy report 配置 Lighttpd 和查看 Squid 流量信息。
pfsense-squid

安装完 squid 后根据需要做一下配置,值得注意的是,默认安装后只有和这台 firewall/squid 服务器在同一个网段的机器才能通过这个 squid 访问 Internet,如果其他网段的电脑也想使用这个代理服务器的话除了在 Firewall: Rules 增加规则外,还需要在 Services->Proxy server: Access control 中增加网段到 Allowed subnets.

安装完 lightsquid 后要做如下配置:
1、配置 squid(Proxy server),enable /var/squid/log 路径;
2、配置 lightsquid(Proxy report)时需要点击 Refresh 按钮去创建 lightsquid reports,否则会得到一个出错页面。

在 FreeBSD 上安装 Squid

如果不用 pfSense,想在 FreeBSD 完整操作系统上安装 Squid:

# cd /usr/ports/www/squid
# make install

根据自己需要修改 squid.conf 配置文件:

# cp /usr/local/etc/squid/squid.conf.default /etc/squid/squid.conf
# vi /etc/squid/squid.conf

初始化 cache:

# /usr/local/sbin/squid -z

测试 squid 是否能正常运行:

# /usr/local/sbin/squid -NCd1