1. 程式人生 > 其它 >Nginx網站使用CDN之後禁止使用者真實IP訪問的方法

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 的一種手工拉黑方案。而自動化限制的方案可以參考部落格之前的分享:

Nginx 在 CDN 加速之後,獲取使用者真實 IP 做併發訪問限制的方法

好了,本文分享到此,希望對你有所幫助。