Skip to main content

Nginx ddos

DDOS protection

Nginx frees 1 slot per time set in rate: 1r/s = 1 slot free per second, 10r/s = 10 slots per second;

Burst - requests excess of the rate;

Nodelay - drop extra requests;

10m - 10 megabytes of RAM for IP list

Limit requests for specific location

# set 4 request per minute limit
limit_req_zone $binary_remote_addr zone=limit_req_by_remote_ip_sign_in:10m rate=4r/m;
limit_req_status 429;

location /sign-in {
# apply limit to location
limit_req zone=limit_req_by_remote_ip_sign_in burst=4 nodelay;
proxy_pass http://backend;
}

Limit requests and connections with whitelist

When the first parameter to the limit_req_zone directory (the key) is an empty string, the limit is not applied, so allowlisted IP addresses ( 192.168.0.0/24 subnet) are not limited.


# Whitelisted IP (e.g internal networks)
# 0 to $limit for IP addresses in the allowlist and 1 for all others.
geo $limit {
default 1;
192.168.0.0/24 0;
# any your IP 0;
}


# If $limit is 0, $limit_key is set to the empty string
# If $limit is 1, $limit_key is set to the client’s IP address in binary format

map $limit $limit_key {
0 "";
1 $binary_remote_addr;
}

#Limit requests:
limit_req_zone $limit_key zone=limit_req_by_remote_ip:10m rate=1r/s;
limit_req_status 429;

#Limit connections:
limit_conn_zone $limit_key zone=limit_conn_by_remote_ip:10m;
limit_conn_status 429;

# Apply limits:
# 1 RPS with 1st 100 request ignored
limit_req zone=limit_req_by_remote_ip burst=100 nodelay;
# 15 TCP connetions from 1 IP
limit_conn limit_conn_by_remote_ip 15;


Analyze limit amount needed:

# Count requests per IP for 1 (sign-in) location
DOMAIN="your.domain"
grep "sign-in" /var/log/nginx/$DOMAIN.access.log |awk '{print $1}' |sort | uniq -c | sort -n | tail -n20
# Get current connections to 443, COUNT - IP
netstat -natupl | grep ":443 " |awk '{print $5}' | cut -f1 -d":" | sort |uniq -c | sort -n | tail -n20

usefull links:

https://bobcares.com/blog/nginx-ddos-prevention/
https://nginx.org/en/docs/http/ngx_http_limit_req_module.html
http://nginx.org/ru/docs/http/ngx_http_limit_conn_module.html
https://www.nginx.com/blog/rate-limiting-nginx/

Block attacker

Identify attacker among regular user:

  • User Agent
  • Attack URI
  • GEO

Block IP:

requests on sign-in

# Take top 100 IPs by requests
DOMAIN="your.domain"
grep "sign-in" /var/log/nginx/$DOMAIN.access.log | grep "400 " |awk '{print $1}' |sort | uniq -c | sort -n | tail -n 100 > /tmp/list.txt

# Create nginx deny list (format: 'deny IP;')
sed 's/$/;/g; s/^\s\+[0-9]\+\s/deny /g' /tmp/list.txt > /etc/nginx/banned-ip-list-`date +%d.%m.%Y`.txt

Add to nginx config include of generated file and reload nginx

include /etc/nginx/banned-ip-list-*.txt;


Block XFF:

requests on sign-in

# Take top 100 IPs by requests
DOMAIN="your.domain"
grep "sign-in" /var/log/nginx/$DOMAIN.access.log | grep "400 " |awk '{print $1}' |sort | uniq -c | sort -n | tail -n 100 > /tmp/list.txt

# Create nginx deny list for XFF (format: 'IP 1;')
sed 's/$/ 1;/g; s/^\s\+[0-9]\+\s/ /g' /tmp/list.txt > /etc/nginx/banned-XFF-MAP-`date +%d.%m.%Y`.txt

Add to nginx config include of generated file and reload nginx

map $http_x_forwarded_for $banned_xff {
default 0;
include /etc/nginx/banned-XFF-MAP-*.txt;
}

server {
...
if ($banned_xff) {
return 403;
}
}

Block User Agent:

requests on sign-in

# Take top 50 User Agents by requests
DOMAIN="your.domain"
grep "sign-in" /var/log/nginx/$DOMAIN.access.log | grep "400 " |awk -F '"' '{print $6}' | sort | uniq -c | sort -n | tail -n 50
# Block array of User Agents
map $http_user_agent $bot_agents {
default 0;
'Mozilla/5.0 (iPhone; CPU iPhone OS 12_0_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Mobile/15E148 Safari/604.1' 1;
'Mozilla/5.0 (iPhone; CPU iPhone OS 12_2_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Mobile/15E148 Safari/604.1' 1;
'Mozilla/5.0 (iPhone; CPU iPhone OS 12_1_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Mobile/15E148 Safari/604.1' 1;
'Mozilla/5.0 (iPhone; CPU iPhone OS 12_2_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Mobile/15E148 Safari/604.1' 1;
'Mozilla/5.0 (iPhone; CPU iPhone OS 12_4_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Mobile/15E148 Safari/604.1' 1;
'Mozilla/5.0 (iPhone; CPU iPhone OS 12_0_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Mobile/15E148 Safari/604.1' 1;
}

if ($bot_agents) {
return 403;
}

# Block 1 User Agent
if ($http_user_agent = "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko"){
return 403;
}