nginx 流量限制、限 request 數目、避免被 DDos
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 burst=10 nodelay;
:::
}
:::
}
burst=10 nodelay
參考: Day09-流量限制(四)
當有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 即可
參考: