nginx 流量限制、避免被 DDos

2024112109:16

Dos / DDos 阻斷服務攻擊 可能有兩種目的

一種是要讓你網站掛掉、無法正常服務。

另一是持續、大量下載你網站中圖片/影片,但又不讓你網站掛掉,然後你就得負擔大量的傳輸費用。

對於放在雲端的網站,如 GCP、AWS 等,用戶每下載 1GB 的網頁/圖片,站長得負擔約 USD 0.1 的費用

 

限制網頁/檔案的下載速度

將每個 request 下載的前 512KB 設定為不限速

超過 512KB 後的資料,降速為 256KB/s

這樣設定,對於需要播放、下載影片,可加快的首屏顯示速度

server {
    listen 80;
    server_name www.xxx.tw;
    root /var/www/html;
::

    location / {
       #任何的request,下載前 512KB 不限速, 超過後 降速為 256KB/s
       limit_rate_after 512k;
       limit_rate 256k;
    }

}

 

限制 Client 同時間的 request 數目

避免同時間被大量 httpd request

要設定兩個檔案

/etc/nginx.conf

http {
    #限制一個 IP 同時的 request 的數量
    # $binary_remote_addr 指 client IP
    #
    # zone=mylimit:10m 命名為 mylimit

    limit_req_zone $binary_remote_addr zone=mylimit:10m rate=100r/m;

    # 10M : 只以 10MB 記憶體來存每個 IP 的請求次數
    # rate=100r/m :每分鐘一百個請求。
    # 特別注意:
    # 並不是 一分鐘執行前100request 後,第 101、102..就失敗
    # nginx 會幫你換算成多少毫秒可以接受一個新請求,
    # 此例是 60/100 = 0.6秒,
    # 也就是 每0.6秒 nginx 只會幫你執行 1個 requst
    # 用戶若 0.6秒內同時發了10個 request請求,
    # 只有第1個會被執行,其它9個都會直接被拒絕掉(503)

      ::
}

關於儲存 IP 記憶體的數量

官方的說明:

The variable’s size is always 4 bytes for IPv4 addresses or 16 bytes for IPv6 addresses. The stored state always occupies 64 bytes on 32-bit platforms and 128 bytes on 64-bit platforms. One megabyte zone can keep about 16 thousand 64-byte states or about 8 thousand 128-byte states

大概就是: 在 64位元的系統,1MB 可以儲存 8000個 IP
 

網站的設定

www.xxx.tw.conf

server {
    listen 80;
    server_name www.xxx.tw;
    root /var/www/html'

   location / {
       #限制request速率
       limit_req zone=mylimit;
             :::
    }
:::
}


當有client碰觸此規則時,
nginx error記錄檔中,會有這樣的紀錄:

2024/11/14 13:33:45 [error] 6744#6744: *40250833 limiting requests, excess: 0.874 by zone "mylimit", client: 150.116.97.xxx, server: www.xxx.tw, request: "GET / HTTP/1.0", host: "www.xxx.tw"


 

$ ab -n 100 -c 3 http://hello3.xxx.tw/
  每次發出 3 requests,總共發出 100 requests

::
Server Software:        nginx
Server Hostname:        hello3.xxx.tw
Server Port:            80

Document Path:          /
Document Length:        11 bytes

Concurrency Level:      3
Time taken for tests:   2.672 seconds
Complete requests:      100
Failed requests:        95
   (Connect: 0, Receive: 0, Length: 95, Exceptions: 0)
Non-2xx responses:      95    **大量的 request 失敗
Total transferred:      34895 bytes
HTML transferred:       18105 bytes
Requests per second:    37.43 [#/sec] (mean)
Time per request:       80.149 [ms] (mean)
Time per request:       26.716 [ms] (mean, across all concurrent requests)
Transfer rate:          12.76 [Kbytes/sec] received


nginx log

150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 200 11 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:16 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:17 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:17 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:17 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:17 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:17 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:17 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:17 +0800] "GET / HTTP/1.0" 200 11 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:17 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:17 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:17 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:17 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-"
150.116.x.x - - [14/Nov/2024:14:01:17 +0800] "GET / HTTP/1.0" 503 190 "-" - "ApacheBench/2.3" "-"
::


 

有關 CDN 的 IP 問題

有一點要注意的是

如果網站是經由 CDN 保護時 (如 Cloudflare )

那 $binary_remote_addr 這個 IP 都會是 CDN 的 IP,

相當於 => 不同的人瀏覽你網站時,都會是 CDN IP

很容易就觸動 limit_req_zone 規則

 

需要讓 nginx 能記錄 (看到真正的用戶 IP)

以 Cloudflare 為例

新增一個檔案

/etc/nginx/conf.d/nginx-cloudflare-realip.conf

檔案內容:

# Cloudflare IP表: https://www.cloudflare.com/zh-tw/ips/
# 參考 https://github.com/ergin/nginx-cloudflare-real-ip

set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 131.0.72.0/22;

set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2a06:98c0::/29;
set_real_ip_from 2c0f:f248::/32;

# (repeat for all Cloudflare IPs listed at https://www.cloudflare.com/ips/)

# use any of the following two

real_ip_header CF-Connecting-IP;

重啟 nginx 即可


參考: