Nginx網站使用CDN之後禁止使用者真實IP訪問的方法
做過面向公網 WEB 運維的苦逼們肯定見識過各種惡意掃描、拉取、注入等圖謀不軌行為吧?對於直接對外的 WEB 伺服器,我們可以直接通過 iptables 、 Nginx 的 deny 指令或者是程式來 ban 掉這些惡意請求。
而對於套了一層 CDN 或代理的網站,這些方法可能就失效了。尤其是個人網站,可能就一臺 VPS,然後套一個免費的 CDN 就行走在網際網路了。並不是每個 CDN 都能精準的攔截各種惡意請求的,更鬧心的是很多 CDN 還不支援使用者在 CDN 上新增 BAN 規則,比如騰訊雲 CDN。。。
因此,就有了本文的折騰分享。
一、真假難辨
如何禁止訪問,我們先了解下常見的 3 種網站訪問模式:
①、使用者直接訪問對外服務的普通網站 瀏覽器 --> DNS 解析 --> WEB 資料處理 --> 資料吐到瀏覽器渲染展示 ②、使用者訪問使用了 CDN 的網站 瀏覽器 --> DNS 解析 --> CDN 節點 --> WEB 資料處理 --> 資料吐到瀏覽器渲染展示 ③、使用者通過代理上網訪問了我們的網站 瀏覽器 --> 代理上網 --> DNS 解析 --> 上述 2 種模式均可能
對於第一種模式,我要禁止這個使用者的訪問很簡單,可以直接通過 iptables 或者 Nginx 的 deny 指令來禁止均可:
iptabels: iptables -I INPUT -s 使用者 ip -j DROP Nginx 的 deny 指令: 語 法: deny address | CIDR | unix: | all; 預設值: — 配置段: http, server, location, limit_except 順 序:從上往下 Demo: location / { deny 使用者 IP 或 IP 段; }
但對於後面 2 種模式就無能為力了,因為 iptables 和 deny 都只能針對直連 IP,而後面 2 種模式中,WEB 伺服器直連 IP 是 CDN 節點或者代理伺服器,此時使用 iptable 或 deny 就只能把 CDN 節點 或代理 IP 給封了,可能誤殺一大片正常使用者了,而真正的罪魁禍首輕輕鬆鬆換一個代理 IP 又能繼續請求了。
那該怎麼辦?
二、火眼金睛
如果長期關注張戈部落格的朋友,應該還記得之前轉載過一篇分享 Nginx 在 CDN 加速之後,獲取使用者真實 IP 做併發訪問限制的方法。說明 Nginx 還是可以實實在在的拿到使用者真實 IP 地址的,那麼事情就好辦了。
要拿到使用者真實 IP,只要在 Nginx 的 http 模組內加入如下配置:
#獲取使用者真實IP,並賦值給變數$clientRealIP
map $http_x_forwarded_for $clientRealIp {
"" $remote_addr;
~^(?P<firstAddr>[0-9.]+),?.*$ $firstAddr;
}
那麼,$clientRealIP 就是使用者真實 IP 了,其實就是匹配了 $http_x_forwarded_for 的第一個值,具體原理前文也簡單分享過:
其實,當一個 CDN 或者透明代理伺服器把使用者的請求轉到後面伺服器的時候,這個 CDN 伺服器會在 Http 的頭中加入一個記錄 X-Forwarded-For : 使用者 IP, 代理伺服器 IP 如果中間經歷了不止一個代理伺服器,這個記錄會是這樣 X-Forwarded-For : 使用者 IP, 代理伺服器 1-IP, 代理伺服器 2-IP, 代理伺服器 3-IP, …. 可以看到經過好多層代理之後, 使用者的真實 IP 在第一個位置, 後面會跟一串中間代理伺服器的 IP 地址,從這裡取到使用者真實的 IP 地址,針對這個 IP 地址做限制就可以了。
而且程式碼中還配合使用了 $remote_addr,因此$clientRealIP 還能相容上文中第①種直接訪問模式,不像 $http_x_forwarded_for 在直接訪問模式中將會是空值!
所以,$clientRealIP 還能配置到 Nginx 日誌格式中,替代傳統的 $remote_addr 使用,推薦!
三、隔山打牛
既然已經拿到了真實 IP,卻不能使用 iptables 和 deny 指令,是否無力感油然而生?
哈哈,在強大的 Nginx 面前只要想得到,你就做得到!通過對 $clientRealIP 這個變數的判斷,Nginx 就能實現隔山打牛的目的,而且規則簡單易懂:
#如果真實IP為 121.42.0.18、121.42.0.19,那麼返回403
if ($clientRealIp ~* "121.42.0.18|121.42.0.19") {
#如果你的nginx安裝了echo模組,還能如下輸出語言,狠狠的發洩你的不滿(但不相容返回403,試試200吧)!
#add_header Content-Type text/plain;
#echo "son of a bitch,you mother fucker,go fuck yourself!";
return 403;
break;
}
把這個儲存為 deny_ip.conf ,上傳到 Nginx 的 conf 資料夾,然後在要生效的網站 server 模組中引入這個配置檔案,並 Reload 過載 Nginx 即可生效:
#禁止某些使用者訪問 include deny_ip.conf;
如果再想新增其他要禁止的 IP,只需要編輯這個檔案,插入要禁止的 IP,使用分隔符 | 隔開即可,記得每次修改都需要 reload 過載 Nginx 才能生效。
四、奇淫巧計
為了更方便的新增和刪除這些黑名單 IP,昨晚熬夜寫了一個小指令碼,一鍵新增和刪除,懶人有福了!
#!/bin/bash
###################################################################
# Deny Real IP for Nginx; Author: Jager <[email protected]> #
# For more information please visit https://zhangge.net/5096.html #
#-----------------------------------------------------------------#
# Copyright ©2016 zhangge.net. All rights reserved. #
###################################################################
NGINX_BIN=/usr/local/nginx/sbin/nginx
DENY_CONF=/usr/local/nginx/conf/deny_ip.conf
COLOR_RED=$( echo -e "e[31;49m" )
COLOR_GREEN=$( echo -e "e[32;49m" )
COLOR_RESET=$( echo -e "e[0m" )
rep_info() { echo;echo -e "${COLOR_GREEN}$*${COLOR_RESET}";echo; }
rep_error(){ echo;echo -e "${COLOR_RED}$*${COLOR_RESET}";echo;exit 1; }
show_help()
{
printf "
###################################################################
# Deny Real IP for Nginx; Author: Jager <[email protected]> #
# For more information please visit https://zhangge.net/5096.html #
#-----------------------------------------------------------------#
# Copyright ©2016 zhangge.net. All rights reserved. #
###################################################################
Usage: $0 [OPTIONS]
OPTIONS:
-h | --help : Show help of this script
-a | --add : Add a deny ip to nginx, for example: ./$0 -a 192.168.1.1
-c | --create : Create deny config file($DENY_CONF) for Nginx
-d | --del : Delete a ip from deny list, for example: ./$0 -d 192.168.1.1
-s | --show : Show current deny list
"
}
reload_nginx()
{
$NGINX_BIN -t >/dev/null 2>&1 &&
$NGINX_BIN -s reload &&
return 0
}
show_list()
{
awk -F '["){|]' '/if/ {for(i=2;i<=NF;i++) if ($i!="") printf $i"n"}' $DENY_CONF
}
pre_check()
{
test -f $NGINX_BIN || rep_error "$NGINX_BIN not found,Plz check and edit."
test -f $DENY_CONF || rep_error "$DENY_CONF not found,Plz check and edit."
MATCH_COUNT=$(show_list | grep -w $1 | wc -l)
return $MATCH_COUNT
}
create_rule()
{
test -f $DENY_CONF &&
rep_error "$DENY_CONF already exist!."
cat >$DENY_CONF<<EOF
if ($clientRealIp ~* "8.8.8.8") {
#add_header Content-Type text/plain;
#echo "son of a bitch,you mother fucker,go fuck yourself!";
return 403;
break;
}
EOF
test -f $DENY_CONF &&
rep_info "$DENY_CONF create success!" &&
cat $DENY_CONF &&
exit 0
rep_error "$DENY_CONF create failed!" &&
exit 1
}
add_ip()
{
pre_check $1
if [[ $? -eq 0 ]];then
sed -i "s/")/|$1&/g" $DENY_CONF &&
reload_nginx &&
rep_info "add $1 to deny_list success." ||
rep_error "add $1 to deny_list failed."
else
rep_error "$1 has been in deny list!"
exit
fi
}
del_ip()
{
pre_check $1
if [[ $? -ne 0 ]];then
sed -ie "s/(|$1|$1|)//g" $DENY_CONF &&
reload_nginx &&
rep_info "del $1 from deny_list success." ||
rep_error "del $1 from deny_list failed."
else
rep_error "$1 not found in deny list!"
exit
fi
}
case $1 in
"-s"|"--show" )
show_list
exit
;;
"-h"|"--help" )
show_help
exit
;;
"-c"|"--create" )
create_rule
;;
esac
while [ $2 ];do
case $1 in
"-a"|"--add" )
add_ip $2;
;;
"-d"|"--del" )
del_ip $2
;;
* )
show_help
;;
esac
exit
done
show_help
使用方法:
①、根據實際情況修改第 9、10 行 Nginx 二進位制檔案及其 deny 配置檔案路徑
②、然後將此指令碼儲存為 deny_ctrl.sh 上傳到伺服器任意目錄,比如放到 /root
③、給指令碼賦予可執行許可權:chmod +x deny_ctrl.sh 即可使用
④、使用引數:
Usage: deny_ctrl.sh [OPTIONS] OPTIONS: -h | --help : 顯示幫助資訊 -a | --add : 新增一個黑名單 IP, 例如: ./deny_ctrl.sh -a 192.168.1.1 -c | --create : 初始化建立一個禁止 IP 的配置檔案,需要自行 include 到需要的網站 server 模組 -d | --del : 刪除一個黑名單 IP,例如: ./deny_ctrl.sh -d 192.168.1.1 -s | --show : 顯示當前已拉黑 IP 清單
初次使用,先執行 ./deny_ctrl.sh -c 建立一下 Nginx 相關配置檔案:deny_ip.conf,預設內容如下:
if ($clientRealIp ~* "8.8.8.8") {
#add_header Content-Type text/plain;
#echo "son of a bitch,you mother fucker,go fuck yourself!";
return 403;
break;
}
8.8.8.8 是為了佔位,規避為空的坑爹情況,實際使用中也請注意,必須要有一個 IP 佔位,否則可能導致誤殺哦!
生成這個檔案之後,編輯網站對應的配置檔案,比如 zhangge.net.conf
在 server {} 模組內部插入 include deny_ip.conf; (注意有英文分號)即可
比如:
server
{
listen 80;
server_name zhangge.net;
index index.html index.htm index.php default.html default.htm default.php;
root /home/wwwroot/zhangge.net;
include agent_deny.conf; #新增此行
#其他略 ...
最後,使用 nginx -s reload 過載 nginx 即可。
後面需要新增黑名單或刪除黑名單都可以使用 deny_ctrl.sh 指令碼來操作了!
最後,順便說明一下,本文分享的方法僅作為使用 CDN 網站遇到惡意 IP 的一種手工拉黑方案。而自動化限制的方案可以參考部落格之前的分享:
好了,本文分享到此,希望對你有所幫助。