記一次LVS/Nginx環境下的訪問控制
原文 http://huoding.com/2015/01/23/412
偶然間,我發現 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 模塊的例子,有一些不錯的 資料 可供參考,這里我就不多說了。
</div> </div>