什麼是remote_addr
remote_addr代表客戶端的IP,但它的值不是由客戶端提供的,而是服務端根據客戶端的ip指定的,當你
的瀏覽器訪問某個網站時,假設中間沒有任何代理,那麼網站的web服務器(Nginx,Apache等)就會把
remote_addr設爲你的機器IP,如果你用了某個代理,那麼你的瀏覽器會先訪問這個代理,然後再由這個
代理轉發到網站,這樣web服務器就會把remote_addr設爲這臺代理機器的IP。
什麼是x_forwarded_for
正如上面所述,當你使用了代理時,web服務器就不知道你的真實IP了,爲了避免這個情況,代理服務器通
常會增加一個叫做x_forwarded_for的頭信息,把連接它的客戶端IP(即你的上網機器IP)加到這個頭信息
裏,這樣就能保證網站的web服務器能獲取到真實IP
註釋:客戶端的IP地址作爲鍵。注意,這裏使用的是$binary_remote_addr變量,而不是$remote_addr變量。$remote_addr變量的長度爲7字節到15字節,而存儲狀態在32位平臺中佔用32字節或64字節,在64位平臺中佔用64字節。$binary_remote_addr變量的長度是固定的4字節,存儲狀態在32位平臺中佔用32字節或64字節,在64位平臺中佔用64字節。1M共享空間可以保存3.2萬個32位的狀態,1.6萬個64位的狀態。如果共享內存空間被耗盡,服務器將會對後續所有的請求返回 503 (Service Temporarily Unavailable) 錯誤。limit_zone 指令和limit_conn_zone指令同等意思,已經被棄用,就不再做說明了。
geo: 定義從指定的變量獲取客戶端的IP地址。默認情況下,nginx從$remote_addr變量取得客戶端IP地址,但也可以從其他變量獲得(0.7.27版),如果該變量的值不能代表一個合法的IP地址,那麼nginx將使用地址“255.255.255.255”。比如:
delete
刪除指定網絡(0.7.23版)。
default
如果客戶端地址不能匹配任意一個定義的地址,nginx將使用此值。 如果使用CIDR,可以用“0.0.0.0/0”代替default。
include
包含一個定義地址和值的文件。可以多次包含。
proxy
定義可信地址(0.8.7,0.7.63版)。 如果請求來自可信地址,nginx將使用其“X-Forwarded-For”頭來獲得地址。 相對於普通地址,可信地址是順序檢測的。
map: 在配置的參數中,第一個是要創建新的變量,它的值取決於後面一個或多個源變量。在 map 塊裏的參數指定了源變量值和結果值的對應關係。源變量值可以使用字符串或者正則表達式 (0.9.6)。
http://tengine.taobao.org/nginx_docs/cn/docs/http/ngx_http_geo_module.html
經過多層CDN之後取得原始用戶的IP地址,nginx 配置取得用戶的原始地址
線上的配置如下:
geo $clientRealIp $white_ip { #$white_ip白名單ip
default 1;
127.0.0.1 0;
122.132.49.241 0;
123.217.40.142 0;
123.217.40.143 0;
}
map $http_x_forwarded_for $clientRealIp {
## 沒有通過代理,直接用 remote_addr
""$remote_addr; #remote_addr客戶端ip
## 用正則匹配,從 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地址
map $white_ip $limit {
1 $binary_remote_addr;
0 "";
}
這裏使用的是$binary_remote_addr變量,而不是$remote_addr變量。$remote_addr變量的長度爲7字節到15字節,而存儲狀態在32位平臺中佔用32字節或64字節,在64位平臺中佔用64字節。$binary_remote_addr變量的長度是固定的4字節,存儲狀態在32位平臺中佔用32字節或64字節,在64位平臺中佔用64字節。1M共享空間可以保存3.2萬個32位的狀態,1.6萬個64位的狀態。如果共享內存空間被耗盡,服務器將會對後續所有的請求返回 503 (Service Temporarily Unavailable) 錯誤。limit_zone 指令和limit_conn_zone指令同等意思,已經被棄用,就不再做說明了。
limit_req_zone $limit zone=tlcy_com:10m rate=50r/s; #$limit爲空不限制 限制平均每秒不超過一個請求50個
limit_req_log_level info;
limit_conn_zone $limit zone=addr:10m;
limit_conn_log_level info;
server
{
listen 80;
server_name xxx;
location / {
proxy_next_upstream http_502 http_504 error timeout invalid_header;
proxy_pass http://xxx;
index index.html index.htm index.jsp;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_ignore_client_abort on;
}
#if ($http_user_agent ~ "M8")
#{
# return 504;
#}
if ($request_method !~ ^(GET|HEAD|POST)$ ) {
return 444;
}
####過濾方法post get
set $flag 0;
if ($request_method ~ "POST" ){
set $flag "${flag}1";
}
if ($request_uri ~* "/tlcysns/user/getMsg.action.*$" ) {
set $flag "${flag}1";
}
if ($flag = "011"){
return 444;
}
set $flag 1;
if ($request_method ~ "GET" ){
set $flag "${flag}1";
}
if ($request_uri ~* "/tlcysns/user/getMsg.action.*$" ) {
set $flag "${flag}1";
}
if ($flag = "111"){
return 200 '{"result":1,"msgs":[],"h_msgs":[]}';
}
#limit_req zone=tlcy_com burst=20 ; #限制平均每秒不超過一個請求50個,同時允許超過頻率限制的請求數不多於20個
#limit_req zone=ttlsa_com burst=5 nodelay; #如果不希望超過的請求被延遲,可以用nodelay參數。
#limit_conn addr 100; #是限制每個IP只能發起100個連接 (addr 要跟 limit_conn_zone 的變量對應)
#limit_rate 100k; #是對每個連接限速100k。這裏是對連接限速,而不是對IP限速!如果一個IP允許兩個併發連接,那麼這個IP就是限速limit_rate * 2
if ($http_user_agent ~ "i500")
{
return 504;
}
access_log /data/logs/nginx/sns_access.log main;
}
現在我們面對的最直接的問題就是, 經過這麼多層加速,我怎麼得到“最前面普通用戶的 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 地址做限制就可以了,
2.2 經過多層CDN之後取得原始用戶的IP地址,nginx 配置取得用戶的原始地址
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地址
2.3 測試、測試
很多時候,你在網上搜到一堆配置,你照着做了,但是你怎麼知道這個配置真的正確 ?是的,我們需要自己做一個有效的真實的測試,驗證它是正確的之後才真的採用它
Nginx 這種配置怎麼測試呢? 用 Echo 模塊,如果你知道 Nginx 這個模塊的話。
以 www.bzfshop.net 網站爲例, 我們首先測試這個 $clientRealIp 是否真的是我們客戶機的 IP 地址,在網站上增加一個訪問地址,比如 www.bzfshop.net/nginx-test,配置如下:
給 Nginx 增加一個測試地址Shell
server {
listen 80;
server_name www.bzfshop.net;
## 當用戶訪問 /nginx-test 的時候,我們輸出 $clientRealIp 變量,看看這個變量
## 值是不是真的 用戶源IP 地址
location /nginx-test {
echo $clientRealIp;
}
}
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地址
2.4 根據用戶的真實 IP 做連接限制
下面是修改之後的 Nginx 配置:
CDN環境下 Nginx 的安全配置Shell
## 這裏取得原始用戶的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;
includefastcgi_params;
}
}
## 這裏取得原始用戶的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;
}
}
後記:
通過上面的配置,現在你的網站可以完美的配合任何 網絡加速服務(CDN)的使用,並且同時能保證對“最終用戶的限制”。
寫這篇文章的原因是因爲 我們最近把 www.bzfshop.net 遷移到 360網站衛士(wangzhan.360.cn) 上了,使用 360網站衛士 做我們的加速服務器和安全保護,同時我們網站自身 nginx 本身也配置了防止***的安全措施, 結果我們的安全配置把 360網站衛士的加速服務器給 盾 掉了,因爲所有用戶的訪問都通過加速服務器過來,很明顯加速服務器超過了我們的“連接限制”。經過上面的改造之後,現在我們的 Nginx 安全配置能夠和 360加速服務器 完美配合,同時能對終端的用戶訪問作限制。
寫下這些文字,希望對看到這篇文章的朋友會有用。
Nginx 有 2 個模塊用於控制訪問“數量”和“速度”,簡單的說,控制你最多同時有 多少個訪問,並且控制你每秒鐘最多訪問多少次, 你的同時併發訪問不能太多,也不能太快,不然就“殺無赦”。
HttpLimitZoneModule 限制同時併發訪問的數量
HttpLimitReqModule 限制訪問數據,每秒內最多幾個請求
limit_conn_zone語法: limit_conn_zone $variable zone=name:size;默認值: none配置段: http該指令描述會話狀態存儲區域。鍵的狀態中保存了當前連接數,鍵的值可以是特定變量的任何非空值(空值將不會被考慮)。$variable定義鍵,zone=name定義區域名稱,後面的limit_conn指令會用到的。size定義各個鍵共享內存空間大小。如:
limit_conn_zone $binary_remote_addr zone=addr:10m;
註釋:客戶端的IP地址作爲鍵。注意,這裏使用的是$binary_remote_addr變量,而不是$remote_addr變量。$remote_addr變量的長度爲7字節到15字節,而存儲狀態在32位平臺中佔用32字節或64字節,在64位平臺中佔用64字節。$binary_remote_addr變量的長度是固定的4字節,存儲狀態在32位平臺中佔用32字節或64字節,在64位平臺中佔用64字節。1M共享空間可以保存3.2萬個32位的狀態,1.6萬個64位的狀態。如果共享內存空間被耗盡,服務器將會對後續所有的請求返回 503 (Service Temporarily Unavailable) 錯誤。limit_zone 指令和limit_conn_zone指令同等意思,已經被棄用,就不再做說明了。
limit_conn_log_level語法:limit_conn_log_level info | notice | warn | error默認值:error配置段:http, server, location當達到最大限制連接數後,記錄日誌的等級。
limit_conn語法:limit_conn zone_name number默認值:none配置段:http, server, location指定每個給定鍵值的最大同時連接數,當超過這個數字時被返回503 (Service Temporarily Unavailable)錯誤。如:
limit_conn_zone $binary_remote_addr zone=addr:10m;
server {
location /www.2cto.com/ {
limit_conn addr 1;
}
}
同一IP同一時間只允許有一個連接。當多個 limit_conn 指令被配置時,所有的連接數限制都會生效。比如,下面配置不僅會限制單一IP來源的連接數,同時也會限制單一虛擬服務器的總連接數:
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;
server {
limit_conn perip 10;
limit_conn perserver 100;
}
[warning]limit_conn指令可以從上級繼承下來。
limit_conn_status語法: limit_conn_status code;默認值: limit_conn_status 503;配置段: http, server, location該指定在1.3.15版本引入的。指定當超過限制時,返回的狀態碼。默認是503。
limit_rate語法:limit_rate rate默認值:0配置段:http, server, location, if in location對每個連接的速率限制。參數rate的單位是字節/秒,設置爲0將關閉限速。 按連接限速而不是按IP限制,因此如果某個客戶端同時開啓了兩個連接,那麼客戶端的整體速率是這條指令設置值的2倍。
limit_req_zone語法: limit_req_zone $variable zone=name:size rate=rate;默認值: none配置段: http設置一塊共享內存限制域用來保存鍵值的狀態參數。 特別是保存了當前超出請求的數量。 鍵的值就是指定的變量(空值不會被計算)。如
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
說明:區域名稱爲one,大小爲10m,平均處理的請求頻率不能超過每秒一次。鍵值是客戶端IP。使用$binary_remote_addr變量, 可以將每條狀態記錄的大小減少到64個字節,這樣1M的內存可以保存大約1萬6千個64字節的記錄。如果限制域的存儲空間耗盡了,對於後續所有請求,服務器都會返回 503 (Service Temporarily Unavailable)錯誤。速度可以設置爲每秒處理請求數和每分鐘處理請求數,其值必須是整數,所以如果你需要指定每秒處理少於1個的請求,2秒處理一個請求,可以使用 “30r/m”。
limit_req_log_level語法: limit_req_log_level info | notice | warn | error;默認值: limit_req_log_level error;配置段: http, server, location設置你所希望的日誌級別,當服務器因爲頻率過高拒絕或者延遲處理請求時可以記下相應級別的日誌。 延遲記錄的日誌級別比拒絕的低一個級別;比如, 如果設置“limit_req_log_level notice”, 延遲的日誌就是info級別。
limit_req_status語法: limit_req_status code;默認值: limit_req_status 503;配置段: http, server, location該指令在1.3.15版本引入。設置拒絕請求的響應狀態碼。
limit_req語法: limit_req zone=name [burst=number] [nodelay];默認值: —配置段: http, server, location設置對應的共享內存限制域和允許被處理的最大請求數閾值。 如果請求的頻率超過了限制域配置的值,請求處理會被延遲,所以所有的請求都是以定義的頻率被處理的。 超過頻率限制的請求會被延遲,直到被延遲的請求數超過了定義的閾值,這時,這個請求會被終止,並返回503 (Service Temporarily Unavailable) 錯誤。這個閾值的默認值爲0。如:
limit_req_zone $binary_remote_addr zone=limit_com:10m rate=1r/s;
server {
location /www.2cto.com/ {
limit_req zone=limit_com burst=5;
}
}
限制平均每秒不超過一個請求,同時允許超過頻率限制的請求數不多於5個。如果不希望超過的請求被延遲,可以用nodelay參數,如:
limit_req zone=ttlsa_com burst=5 nodelay;
--------完成配置事例--------------------------------------------
## 用戶的 IP 地址 $limit 作爲 Key,每個 IP 地址最多有 50 個併發連接
## 你想開 幾千個連接 刷死我? 超過 50 個連接,直接返回 503 錯誤給你,根本不處理你的請求了
當,然這是都是ngin來處理,不會影響後端的tomcat等WEB 應用 ,如果nginx網卡流量堵塞和單臺壓力問題就的想別的辦法了。後面會說在解決的
limit_req_zone $limit zone=tlcy_com:10m rate=10r/s; limit_req_log_level info; limit_conn_zone $limit zone=addr:10m; limit_conn_log_level info;
## 用戶的 IP 地址 $limit 作爲 Key,每個 IP 地址每秒處理 10 個請求
## 你想用程序每秒幾百次的刷我,沒戲,再快了就不處理了,直接返回 503 錯誤給你
## 具體服務器配置
http{....
limit_req_zone $limit zone=tlcy_com:10m rate=10r/s;
limit_req_log_level info;
limit_conn_zone $limit zone=addr:10m;
limit_conn_log_level info;
server
{
listen 80;
server_name www.2cto.com;
if ($http_user_agent ~* LWP::Simple|BBBike|wget|Sosospider|YodaoBot) {
return 403;
}
## root /data/www/;
## index hou.txt;
location /mp4/
{
if ($request_method !~ ^(GET|HEAD|POST)$ ) {
return 444;
}
}
location / {
if ($request_method !~ ^(GET|HEAD)$ ) {
return 444;
}
proxy_next_upstream http_502 http_504 error timeout invalid_header;
proxy_pass http://tlcy;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
allow all;
}
## 最多 5 個排隊, 由於每秒處理 10 個請求 + 5個排隊,你一秒最多發送 15 個請求過來,
再多就直接返回 503 錯誤給你了
limit_req zone=tlcy_com burst=5 nodelay;
limit_conn addr 10;
location ~* \.(gif|jpg|png|swf|flv)$ {
valid_referers none blocked www.2cto.com ;
if ($invalid_referer) {
rewrite ^/ http://www.2cto.com/403.html;
#return 404;
}
}
}
事務都具有兩面性的。ngx_http_limit_conn_module 模塊雖說可以解決當前面臨的併發問題,但是會引入另外一些問題的。如前端如果有做LVS或反代,而我們後端啓用了該模塊功能,那不是非常多503錯誤了? 這樣的話,可以在前端啓用該模塊,要麼就是設置白名單。
------------------------------白名單設置-----------------------------------------
## 具體服務器配置
http{....
geo $white_ip {
default 1;
127.0.0.1 0;
10.0.0.0/8 0;
}
#白名單
map $white_ip $limit {
1 $binary_remote_addr;
0 "";
}
limit_req_zone $limit zone=tlcy_com:10m rate=10r/s;
limit_req_log_level info;
limit_conn_zone $limit zone=addr:10m;
limit_conn_log_level info;
server
{
listen 80;
server_name www.hzcsky.com;
if ($http_user_agent ~* LWP::Simple|BBBike|wget|Sosospider|YodaoBot) {
return 403;
}
## root /data/www/;
## index hou.txt;
location /mp4/
{
if ($request_method !~ ^(GET|HEAD|POST)$ ) {
return 444;
}
}
location / {
if ($request_method !~ ^(GET|HEAD)$ ) {
return 444;
}
proxy_next_upstream http_502 http_504 error timeout invalid_header;
proxy_pass http://tlcy;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
allow all;
}
## 最多 5 個排隊, 由於每秒處理 10 個請求 + 5個排隊,你一秒最多發送 15 個請求過來,
再多就直接返回 503 錯誤給你了
limit_req zone=tlcy_com burst=5 nodelay;
limit_conn addr 10;
location ~* \.(gif|jpg|png|swf|flv)$ {
valid_referers none blocked www.2cto.com ;
if ($invalid_referer) {
rewrite ^/ http://www.2cto.com/403.html;
#return 404;
}
}
}
#################如果 單臺nginx解決不了前面需要LVS或者haproxy 了做4層 然後nginx多個 來解決這個問題,如果多臺的 因爲過來的全是LVS 的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 地址做限制就可以了,
nginx 配置
取得用戶的原始地址
日誌開啓顯示 :
log_format main '$http_x_forwarded_for $remote_addr - - $time_iso8601 "$request_method $scheme://$host$request_uri $server_protocol" $status $bytes_sent "$http_referer" "$http_user_agent" $request_time $upstream_cache_status:TCP';
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 服務器
~^(?PfirstAddr>[0-9\.]+),?.*$ $firstAddr;
}
## 通過 map 指令,我們爲 nginx 創建了一個變量 $clientRealIp ,這個就是 原始用戶的真實 IP 地址,
## 不論用戶是直接訪問,還是通過一串 CDN 之後的訪問,我們都能取得正確的原始IP地址
完整配置事例:
## 具體服務器配置
http{
map $http_x_forwarded_for $limit {
"" $remote_addr;
~^(?P[0-9\.]+),?.*$ $firstAddr;
}
# map $white_ip $limit {
# 1 $clientRealIp;
# 0 "";
# }
limit_req_zone $limit zone=tlcy_com:10m rate=5r/s;
limit_req_log_level info;
limit_conn_zone $limit zone=addr:10m;
limit_conn_log_level info;
server
{
listen 80;
server_name www.2cto.com;
if ($http_user_agent ~* LWP::Simple|BBBike|wget|Sosospider|YodaoBot) {
return 403;
}
## root /data/www/;
## index hou.txt;
location /mp4/
{
if ($request_method !~ ^(GET|HEAD|POST)$ ) {
return 444;
}
}
location / {
if ($request_method !~ ^(GET|HEAD)$ ) {
return 444;
}
proxy_next_upstream http_502 http_504 error timeout invalid_header;
proxy_pass http://tlcy;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
allow all;
}
## 最多 5 個排隊, 由於每秒處理 10 個請求 + 5個排隊,你一秒最多發送 15 個請求過來,
再多就直接返回 503 錯誤給你了
limit_req zone=tlcy_com burst=5 nodelay;
limit_conn addr 10;
location ~* \.(gif|jpg|png|swf|flv)$ {
valid_referers none blocked www.2cto.com ;
if ($invalid_referer) {
rewrite ^/ http://www.2cto.com/403.html;
#return 404;
}
}
}
因爲他限制的是 原始IP 不用設置白名單之類了就。自己人可以直接訪問nginx層,是原始IP地址沒有限制就。
還要合理的封掉IP 就需要日誌配合腳本了:
#!/bin/bash
#取得參數$1爲併發閾值,若留空則默認允許單IP最大200併發!
if [[ -z $1 ]];then
num=200
else
num=$1
fi
LOG=/root/log/nginx/sns_access.log
STATUS=503
#請求檢查、判斷及拉黑主功能函數
function check(){
iplist=`cat $LOG |grep -i $STATUS |grep -i "," |awk '{print $2}' |grep -v "-" |sed "s#,##g" |sort |uniq -cd |sort -rn| awk -v str=$num '{if ($1>str){print $2}}'`
if [[ ! -z $iplist ]];
then
>/data/shell/black_ip.txt
for black_ip in $iplist
do
#白名單過濾中已取消IP段的判斷功能,可根據需要自行修改以下代碼
#exclude_ip=`echo $black_ip | awk -F"." '{print $1"."$2"."$3}'`
#grep -q $exclude_ip ./white_ip.txt
grep -q $black_ip /data/shell/white_ip.txt
if [[ $? -eq 0 ]];then
echo "$black_ip (white_ip)" >>/data/shell/black_ip.txt
else
echo $black_ip >> /data/shell/black_ip.txt
# iptables -nL | grep $black_ip ||(iptables -I INPUT -s $black_ip -j DROP & echo "$black_ip `date +%Y-%m-%H:%M:%S`">>/data/shell/denylog.txt )
fi
done
#存在併發超過閾值的單IP就發送郵件
# if [[ `cat ./sendmail` == 1 ]];then sendmsg;fi
fi
}
function checka(){
iplist=`cat $LOG |grep -i $STATUS |grep -v "," |awk '{print $1}' |sort |uniq -cd |sort -rn | awk -v str=$num '{if ($1>str){print $2}}'`
if [[ ! -z $iplist ]];
then
>/data/shell/black_ip.txt
for black_ip in $iplist
do
#白名單過濾中已取消IP段的判斷功能,可根據需要自行修改以下代碼
#exclude_ip=`echo $black_ip | awk -F"." '{print $1"."$2"."$3}'`
#grep -q $exclude_ip ./white_ip.txt
grep -q $black_ip /data/shell/white_ip.txt
if [[ $? -eq 0 ]];then
echo "$black_ip (white_ip)" >>/data/shell/black_ip.txt
else
echo $black_ip >> /data/shell/black_ip.txt
# iptables -nL | grep $black_ip ||(iptables -I INPUT -s $black_ip -j DROP & echo "$black_ip `date +%Y-%m-%H:%M:%S`">>/data/shell
#/denylog.txt )
fi
done
#存在併發超過閾值的單IP就發送郵件
# if [[ `cat ./sendmail` == 1 ]];then sendmsg;fi
fi
}
#發郵件函數
function sendmsg(){
netstat -nutlp | grep "sendmail" >/dev/null 2>&1 || /etc/init.d/sendmail start >/dev/null 2>&1
echo -e "From: 發郵件地@qq.com\nTo:收郵件地@qq.com\nSubject:Someone Attacking your system!!\nIts Ip is" >./message
cat ./black_ip.txt >>./message
/usr/sbin/sendmail -f 發郵件地@qq.com -t 收郵件地@qq.com -i /message
>./sendmail
}
##間隔10s無限循環檢查函數
#while true
#do
# check
# #每隔10s檢查一次,時間可根據需要自定義
# sleep 10
#done
#
check #處理沒有代理的IP
checka #處理多層的IP
我們日誌是5分鐘切割一次。所以就檢查5分鐘內非法的IP 給封掉,在寫個2小時重置IPtables的計劃任務 就行了。
設置兩個域名
server {
listen 80;
server_name test1.magedu.com;
root /test1/;
}
location /status{
stub_status on;
}
location /req-status {
req_status_show on;
}
}
server {
listen 80;
server_name test2.magedu.com;
root /test2/;
location / {
index index.html index.htm;
}
}
代理設置
upstream backend {
server backend1.example.com weight=5;
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
server unix:/tmp/backend3;
server backup1.example.com backup;
}
max_fails=3一共做三次健康狀態檢測fail_timeout=30s每30秒做一個健康狀態檢測 三次都失敗就標記爲不可用
格式:server address [parameters];可以是ip:port hostname unix套接字(本地通信)
listen 80;
server_name sns.audiocn.org;
location / {
proxy_next_upstream http_502 http_504 error timeout invalid_header;#指定在何種情況下一個失敗的請求應該被髮送到下一臺後端服務器:
proxy_pass http://tlcysns;
index index.html index.htm index.jsp;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_ignore_client_abort on;
}
設置後端服務器“Location”響應頭和“Refresh”響應頭的替換文本。 假設後端服務器返回的響應頭是 “Location: http://localhost:8000/two/some/uri/”,那麼指令
proxy_redirect http://localhost:8000/two/ http://frontend/one/;
將把字符串改寫爲 “Location: http://frontend/one/some/uri/”。
NGINX的proxy_redirect功能比較強大,其作用是對發送給客戶端的URL進行修改。以例子說明:
server {
listen 80;
server_name test.abc.com;
location / {
proxy_pass http://10.10.10.1:9080;
}
}這段配置一般情況下都正常,但偶爾會出錯, 錯誤在什麼地方呢? 抓包發現服務器給客戶端的跳轉指令里加了端口號,如 Location: http://test.abc.com:9080/abc.html 。因爲nginx服務器偵聽的是80端口,所以這樣的URL給了客戶端,必然會出錯.針對這種情況, 加一條proxy_redirect指令: proxy_redirect http://test.abc.com:9080/ / ,把所有“http://test.abc.com:9080/”的內容替換成“/”再發給客戶端,就解決了。
server {
listen 80;
server_name test.abc.com;
proxy_redirect http://test.abc.com:9080/ /;
location / {
proxy_pass http://10.10.10.1:9080;
}
}