很多時候,我們的網站不是簡單的 普通用戶IE瀏覽器 ——-> 你的服務器 的結構, 考慮到網絡訪問速度問題,我們中間可能會有各種 網絡加速(CDN)。以本網站 www.bzfshop.net 為例,考慮到網站的安全性和訪問加速,我們的架構是:
普通用戶瀏覽器 —–> 360網站衛士加速(CDN,360防 CC,DOS攻擊) ——> 阿里雲加速服務器(我們自己建的CDN,阿里雲盾) —-> 源服務器(PHP 程序部署在這里,iptables, nginx 安全配置)
可以看到,我們的網站中間經歷了好幾層的透明加速和安全過濾, 這種情況下,我們就不能用上面的“普通配置”。因為上面基於 源IP的限制 結果就是,我們把 360網站衛士 或者 阿里雲盾 給限制了,因為這里“源IP”地址不再是 普通用戶的IP,而是中間 網絡加速服務器 的IP地址。我們需要限制的是 最前面的普通用戶,而不是中間為我們做加速的 加速服務器。
1.1 現在我們面對的最直接的問題就是, 經過這么多層加速,我怎么得到“最前面普通用戶的 IP 地址”呢?
(這里只說明結果,不了解 Http 協議的人請自行 Google 或者 Wikipedia http://zh.wikipedia.org/zh-cn/X-Forwarded-For )
當一個 CDN 或者透明代理服務器把用戶的請求轉到后面服務器的時候,這個 CDN 服務器會在 Http 的頭中加入 一個記錄
X-Forwarded-For : 用戶IP, 代理服務器IP
如果中間經歷了不止一個 代理服務器,像 www.bzfshop.net 中間建立多層代理之后,這個 記錄會是這樣
X-Forwarded-For : 用戶IP, 代理服務器1-IP, 代理服務器2-IP, 代理服務器3-IP, ….
可以看到經過好多層代理之后, 用戶的真實IP 在第一個位置, 后面會跟一串 中間代理服務器的IP地址,從這里取到用戶真實的IP地址,針對這個 IP 地址做限制就可以了,
1.2 經過多層CDN之后取得原始用戶的IP地址,nginx 配置
1
2
3
4
5
6
7
8
9
10
11
|
map $http_x_forwarded_for $clientRealIp {
## 沒有通過代理,直接用 remote_addr
"" $remote_addr;
## 用正則匹配,從 x_forwarded_for 中取得用戶的原始IP
## 例如 X-Forwarded-For: 202.123.123.11, 208.22.22.234, 192.168.2.100,...
## 這里第一個 202.123.123.11 是用戶的真實 IP,后面其它都是經過的 CDN 服務器
~^(?P<firstAddr>[0-9\.]+),?.*$ $firstAddr;
}
## 通過 map 指令,我們為 nginx 創建了一個變量 $clientRealIp ,這個就是 原始用戶的真實 IP 地址,
## 不論用戶是直接訪問,還是通過一串 CDN 之后的訪問,我們都能取得正確的原始IP地址
|
1.3 測試、測試
很多時候,你在網上搜到一堆配置,你照着做了,但是你怎么知道這個配置真的正確 ?是的,我們需要自己做一個有效的真實的測試,驗證它是正確的之后才真的采用它
Nginx 這種配置怎么測試呢? 用 Echo 模塊,如果你知道 Nginx 這個模塊的話。
以 www.bzfshop.net 網站為例, 我們首先測試這個 $clientRealIp 是否真的是我們客戶機的 IP 地址,在網站上增加一個訪問地址,比如 www.bzfshop.net/nginx-test,配置如下:
1
2
3
4
5
6
7
8
9
10
|
server {
listen 80;
server_name www.bzfshop.net;
## 當用戶訪問 /nginx-test 的時候,我們輸出 $clientRealIp 變量,看看這個變量
## 值是不是真的 用戶源IP 地址
location /nginx-test {
echo $clientRealIp;
}
}
|
接下來,用你的瀏覽器訪問 www.bzfshop.net/nginx-test,這個時候會彈出框下載一個文件 nginx-test,下載完成用 notepad++ 打開,里面就是一個 IP 地址
訪問 www.ip138.com ,看看這個里面記錄的IP地址是否和 ip138 偵測的IP 一致?
通過這種方式,你就可以對 Nginx 的一些復雜配置做有效的測試。
經過測試,我們確認 通過多層CDN 之后,$clientRealIp 仍然是有效的原始用戶IP地址
1.4 根據用戶的真實 IP 做連接限制
下面是修改之后的 Nginx 配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
## 這里取得原始用戶的IP地址
map $http_x_forwarded_for $clientRealIp {
"" $remote_addr;
~^(?P<firstAddr>[0-9\.]+),?.*$ $firstAddr;
}
## 針對原始用戶 IP 地址做限制
limit_conn_zone $clientRealIp zone=TotalConnLimitZone:20m ;
limit_conn TotalConnLimitZone 50;
limit_conn_log_level notice;
## 針對原始用戶 IP 地址做限制
limit_req_zone $clientRealIp zone=ConnLimitZone:20m rate=10r/s;
#limit_req zone=ConnLimitZone burst=10 nodelay;
limit_req_log_level notice;
## 具體服務器配置
server {
listen 80;
location ~ \.php$ {
## 最多 5 個排隊, 由於每秒處理 10 個請求 + 5個排隊,你一秒最多發送 15 個請求過來,再多就直接返回 503 錯誤給你了
limit_req zone=ConnLimitZone burst=5 nodelay;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
}
}
|
原博客地址:https://yq.aliyun.com/articles/44945