偶然间,我发现 Graphite 显示服务器网卡流量呈锯齿状,于是查了一下 Nginx 日志,发现有人在周期性抓我们的接口数据。我这爆脾气自然不能容忍这种行径。
简单分析一下访问日志,很容易就能拿到了可疑的 IP 段,直接用 iptables 封杀:
shell> iptables -A INPUT -s x.y.z.0/24 -j DROP
本以为世界会就此清净,可没想到一点儿用都没有。莫非小偷已经突破锁头的限制?不能够啊!直觉告诉我问题应该和 LVS 有关,可惜我对 LVS 的了解极其匮乏,唯一知道的就是项目用的是 FULLNAT 模式,那就以此为切入点开始挖掘:
LVS FULLNAT
所谓 FULLNAT 模式,是指当用户请求经由 LVS 转发给 RS 服务器的时候,其来源 IP 会从用户 IP 改成 LVS 内网 IP,目标 IP 会从 LVS 的 VIP 改成 RS 服务器的 IP;当 RS 服务器生成响应数据经由 LVS 返回给用户的时候,其来源 IP 会从 RS 服务器 IP 改成 LVS 的 VIP,目标 IP 会从 LVS 内网 IP 改成用户 IP。
说明:关于 LVS 更详细的介绍请参考「 从一个开发的角度看负载均衡和LVS 」一文。
对于 RS 服务器而言,实际上它看到的是 LVS。可我们明明在 Nginx 日志里看到了客户端的 IP,而不是 LVS 的 IP,这又是什么原因呢?原来 LVS 为了解决 FULLNAT 模式下传递用户 IP 的问题,引入了一个名为 TOA 的补丁机制,在 TCP 的三次握手阶段,通过 TCP 的 options 来传递用户 IP 和端口等信息,继而覆盖 socket 的 IP 和端口数据。
换句话说,在 RS 服务器上,从 iptables 的角度看,因为 NAT 的缘故,来源 IP 都是 LVS 的 IP;而从 Nginx 的角度看,因为 TOA 的缘故,来源 IP 都是用户的 IP。关于这一点也可以通过 tcpdump 命令抓包来印证:
tcpdump
说明:如上图所示那一堆 254 开头的字符串里保存的就是用户 IP 和端口等信息。
于是乎可以得出结论:在 RS 服务器上通过 iptables 来封杀用户 IP 无疑是没有意义的,如果一定要用 iptables,应该在 LVS 服务器上用,但实际情况是我无权操作 LVS 服务器,只能在 RS 服务器上想办法。既然 Nginx 能拿到用户 IP,那么我们就可以在 Nginx 上解决问题,有 Access , GEO 等模块可供选择,这里我们选择的是 GEO 模块:
geo $bad { default 0; x.y.z.0/24 1; } location / { if ($bad) { return 403; } }
关于 GEO 模块的例子,有一些不错的 资料 可供参考,这里我就不多说了。