Anti DDoS with Nginx

I want to share bash script that will install hosting-firewall.tpl (nginx template) that will:

  • allow 1 http request per second per IP address (sent to your server, more precisely to your PHP-FPM)
  • allow 2 parallel http connections per IP address (sent to your PHP-FPM)
  • allow burst for 7 additional http requests (they will enter queue), first 3 will be processed immediately, 4 others will processed each request each second (so this will handle natural peaks that CMS can generate to itself)
  • if client fills allowed queue, additional requests will be denied

Generally, this nginx template will prevent bad bots to run hundreds parallel http requests against your site, which will probably cause denial-of-service on your server.

There are also 3 additional templates, with larger limit values (for example: 2 req/sec, 14 allowed requests in queue, 7 burst, 8 parallel connections).

#!/bin/bash

# This script will install hosting-firewall.tpl (nginx template) that will:
# + allow 1 http request per second per IP address (sent to your server, more precisely to your PHP-FPM)
# + allow 2 parallel http connections per IP address (sent to your PHP-FPM)
# + allow burst for 7 additional http requests (they will enter queue), first 3 will be processed immediately, 4 others will processed each request each second (so this will handle natural peaks that CMS can generate to itself)
# + if client fills allowed queue, additional requests will be denied
#
# Generally, this nginx template will prevent bad bots to run hundreds parallel http requests against your site, which will probably cause denial-of-service on your server.
#
# There are also 3 additional templates, with larger limit values (for example: 2 req/sec, 14 allowed requests in queue, 7 burst, 8 parallel connections).

grepc=$(grep -c 'limit_conn_zone' /etc/nginx/nginx.conf)
if [ "$grepc" -eq 0 ]; then
    sed -i 's|server_names_hash_bucket_size   512;|server_names_hash_bucket_size   512;\n    limit_conn_zone $binary_remote_addr zone=addr:1m;\n    limit_conn_zone $server_name zone=zone_site:1m;\n    limit_req_zone $scheme zone=wfone:1m rate=1r/s;\n    limit_req_zone $binary_remote_addr zone=one:1m rate=1r/s;\n    limit_req_zone $binary_remote_addr zone=two:1m rate=2r/s;\n    limit_conn_log_level error;\n    limit_req_log_level error;\n    limit_conn_status 429;\n    limit_req_status 429;|g' /etc/nginx/nginx.conf
    echo "=== Added rate_limit to nginx.conf"
fi

grepc=$(grep -c 'zone=addr:10m' /etc/nginx/nginx.conf)
if [ "$grepc" -eq 1 ]; then
    sed -i 's|zone=addr:10m|zone=addr:1m|g' /etc/nginx/nginx.conf
    echo "=== Decrease addr zone to 1mb to nginx.conf"
fi

grepc=$(grep -c 'zone=zone_site:1m' /etc/nginx/nginx.conf)
if [ "$grepc" -eq 0 ]; then
    sed -i 's| zone=addr:1m;| zone=addr:1m;\n    limit_conn_zone $server_name zone=zone_site:1m;|g' /etc/nginx/nginx.conf
    echo "=== Added rate_limit 'zone_site' to nginx.conf"
fi

grepc=$(grep -c 'zone=wfone:1m' /etc/nginx/nginx.conf)
if [ "$grepc" -eq 0 ]; then
    sed -i 's| zone=addr:1m;| zone=addr:1m;\n    limit_req_zone $scheme zone=wfone:1m rate=1r/s;|g' /etc/nginx/nginx.conf
    echo "=== Added rate_limit 'wfone' to nginx.conf"
fi

grepc=$(grep -c 'zone=one:10m' /etc/nginx/nginx.conf)
if [ "$grepc" -eq 1 ]; then
    sed -i 's|zone=one:10m|zone=one:1m|g' /etc/nginx/nginx.conf
    echo "=== Decrease one zone to 1mb to nginx.conf"
fi

grepc=$(grep -c 'zone=two' /etc/nginx/nginx.conf)
if [ "$grepc" -eq 0 ]; then
    sed -i 's|zone=one:1m rate=1r/s;|zone=one:1m rate=1r/s;\n    limit_req_zone $binary_remote_addr zone=two:1m rate=2r/s;|g' /etc/nginx/nginx.conf
    echo "=== Added rate_limit for 2 req/sec to nginx.conf"
fi

wget -nv -O /usr/local/hestia/data/templates/web/nginx/force-https-firewall.tpl http://c.myvestacp.com/tools/rate-limit-tpl/force-https-firewall.tpl
wget -nv -O /usr/local/hestia/data/templates/web/nginx/force-https-firewall.stpl http://c.myvestacp.com/tools/rate-limit-tpl/force-https-firewall.stpl
wget -nv -O /usr/local/hestia/data/templates/web/nginx/hosting-firewall.tpl http://c.myvestacp.com/tools/rate-limit-tpl/hosting-firewall.tpl
wget -nv -O /usr/local/hestia/data/templates/web/nginx/hosting-firewall.stpl http://c.myvestacp.com/tools/rate-limit-tpl/hosting-firewall.stpl

wget -nv -O /usr/local/hestia/data/templates/web/nginx/force-https-firewall-burst-2.tpl http://c.myvestacp.com/tools/rate-limit-tpl/force-https-firewall-burst-2.tpl
wget -nv -O /usr/local/hestia/data/templates/web/nginx/force-https-firewall-burst-2.stpl http://c.myvestacp.com/tools/rate-limit-tpl/force-https-firewall-burst-2.stpl
wget -nv -O /usr/local/hestia/data/templates/web/nginx/hosting-firewall-burst-2.tpl http://c.myvestacp.com/tools/rate-limit-tpl/hosting-firewall-burst-2.tpl
wget -nv -O /usr/local/hestia/data/templates/web/nginx/hosting-firewall-burst-2.stpl http://c.myvestacp.com/tools/rate-limit-tpl/hosting-firewall-burst-2.stpl

wget -nv -O /usr/local/hestia/data/templates/web/nginx/force-https-firewall-burst-2-speed-2.tpl http://c.myvestacp.com/tools/rate-limit-tpl/force-https-firewall-burst-2-speed-2.tpl
wget -nv -O /usr/local/hestia/data/templates/web/nginx/force-https-firewall-burst-2-speed-2.stpl http://c.myvestacp.com/tools/rate-limit-tpl/force-https-firewall-burst-2-speed-2.stpl
wget -nv -O /usr/local/hestia/data/templates/web/nginx/hosting-firewall-burst-2-speed-2.tpl http://c.myvestacp.com/tools/rate-limit-tpl/hosting-firewall-burst-2-speed-2.tpl
wget -nv -O /usr/local/hestia/data/templates/web/nginx/hosting-firewall-burst-2-speed-2.stpl http://c.myvestacp.com/tools/rate-limit-tpl/hosting-firewall-burst-2-speed-2.stpl

wget -nv -O /usr/local/hestia/data/templates/web/nginx/force-https-firewall-burst-2-speed-2-conn-4.tpl http://c.myvestacp.com/tools/rate-limit-tpl/force-https-firewall-burst-2-speed-2-conn-4.tpl
wget -nv -O /usr/local/hestia/data/templates/web/nginx/force-https-firewall-burst-2-speed-2-conn-4.stpl http://c.myvestacp.com/tools/rate-limit-tpl/force-https-firewall-burst-2-speed-2-conn-4.stpl
wget -nv -O /usr/local/hestia/data/templates/web/nginx/hosting-firewall-burst-2-speed-2-conn-4.tpl http://c.myvestacp.com/tools/rate-limit-tpl/hosting-firewall-burst-2-speed-2-conn-4.tpl
wget -nv -O /usr/local/hestia/data/templates/web/nginx/hosting-firewall-burst-2-speed-2-conn-4.stpl http://c.myvestacp.com/tools/rate-limit-tpl/hosting-firewall-burst-2-speed-2-conn-4.stpl

wget -nv -O /usr/local/hestia/data/templates/web/nginx/force-https-firewall-wordpress.tpl http://c.myvestacp.com/tools/rate-limit-tpl/force-https-firewall-wordpress.tpl
wget -nv -O /usr/local/hestia/data/templates/web/nginx/force-https-firewall-wordpress.stpl http://c.myvestacp.com/tools/rate-limit-tpl/force-https-firewall-wordpress.stpl
wget -nv -O /usr/local/hestia/data/templates/web/nginx/hosting-firewall-wordpress.tpl http://c.myvestacp.com/tools/rate-limit-tpl/hosting-firewall-wordpress.tpl
wget -nv -O /usr/local/hestia/data/templates/web/nginx/hosting-firewall-wordpress.stpl http://c.myvestacp.com/tools/rate-limit-tpl/hosting-firewall-wordpress.stpl

systemctl restart nginx
1 Like

Thanks a lot, I’m looking for anti-DDoS solution now. I was going to ask for help setting request limits, but as I understand this script will do everything for me. That’s great!

I just want to ask Hestia representatinves to confirm that this solution is working and templates from another vesta fork will work fine here.

Maybe someone need a solution. So I’ve added to /etc/nginx/nginx.conf

	# Anti-DDoS ngx_http_limit_conn_module
    limit_conn_zone $binary_remote_addr zone=addr:2m;
    limit_conn_zone $server_name zone=zone_site:2m;
    limit_req_zone $scheme zone=wfone:1m rate=4r/m;
    limit_req_zone $binary_remote_addr zone=one:2m rate=4r/m;
    limit_req_zone $binary_remote_addr zone=two:2m rate=8r/m;
    limit_conn_log_level notice;
    limit_req_log_level notice;
    limit_conn_status 429;
    limit_req_status 429;

As only root of my site is under DDoS so I made nginx template out of default adding.

	location = / {
		limit_conn addr 3;
		limit_conn zone_site 5;
		limit_req zone=one burst=3 nodelay;
		proxy_pass http://%ip%:%web_port%;
		try_files  $uri @fallback;

		root       %docroot%;
		access_log /var/log/%web_system%/domains/%domain%.log combined;
		access_log /var/log/%web_system%/domains/%domain%.bytes bytes;
		expires    max;

	}

So it limits request to / from one IP to 5 times in a minute, it works fine with me, and no problems with legitimate requests.