做過面向公網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
還能兼容上文中第1種直接訪問模式,不像 $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 <ge@zhangge.net> # # 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 <ge@zhangge.net> # # 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的一種手工拉黑方案。而自動化限制的方案可以參考博客之前的分享:
Nginx在CDN加速之后,獲取用戶真實IP做並發訪問限制的方法
好了,本文分享到此,希望對你有所幫助。
相關推薦:
騰訊存儲與CDN免費體驗
Nginx雙向認證配置指南
此文已由作者授權騰訊雲技術社區發布,轉載請注明文章出處,獲取更多雲計算技術干貨,可請前往騰訊雲技術社區
歡迎大家關注騰訊雲技術社區-博客園官方主頁,我們將持續在博客園為大家推薦技術精品文章哦~
傳播騰訊海量技術實踐經驗,www.qcloud.com