1
Files
2025-07-04 14:28:23 +02:00

288 lines
9.8 KiB
INI

# Authors: jfou & tchv
# License: MIT
#############
### GLOBAL
global
log /dev/log local0 info
log /dev/log local1 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
user haproxy
group haproxy
daemon
# Increase max stick-tables (requires haproxy 2.8 or newer)
tune.stick-counters 9
#limited-quic
maxconn 20000
# Default SSL material locations
ca-base /etc/ssl/certs
crt-base /etc/ssl/private
# See: https://ssl-config.mozilla.org/#server=haproxy&version=3.0.7&config=intermediate&openssl=3.3.2&guideline=5.7
# intermediate configuration
ssl-default-bind-curves X25519:prime256v1:secp384r1
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-bind-options prefer-client-ciphers ssl-min-ver TLSv1.2 no-tls-tickets
ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305
ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-server-options ssl-min-ver TLSv1.2 no-tls-tickets
# curl https://ssl-config.mozilla.org/ffdhe2048.txt > /etc/haproxy/dhparam
ssl-dh-param-file /etc/haproxy/dhparam
###############
### DEFAULTS
defaults
log global
mode http
option httplog
option dontlognull
maxconn 2000
timeout connect 10s
timeout client 30s
timeout server 120s
timeout queue 1m
timeout tarpit 2s
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http
################
### FRONTENDS
frontend stats
mode http
option httplog
bind :8404
stats enable
stats uri /stats
stats refresh 10s
http-request set-log-level silent
http-request use-service prometheus-exporter if { path /metrics }
frontend main
mode http
option httplog
bind :80,:::80 v6only
bind :443,:::443 v6only ssl crt /etc/haproxy/ssl/ alpn h2,http/1.1
# Enable Compression
filter compression
compression algo gzip
compression type text/html text/plain text/css application/javascript application/json
compression offload
# Headers
http-response set-header X-Server-Id haproxywafpocpoc
http-response set-header Strict-Transport-Security max-age=63072000
http-response del-header Server
http-response del-header X-Powered-By
http-response del-header X-AspNetMvc-Version
http-response del-header X-AspNet-Version
http-response del-header X-Drupal-Cache
http-response del-header X-Drupal-Dynamic-Cache
http-response del-header X-Generator
http-response del-header X-Runtime
http-response del-header X-Rack-Cache
# Letsencrypt
acl is_letsencrypt path_beg /.well-known/acme-challenge/
# X-Forwarded Headers and HTTPS redirect
acl cf_ip_hdr req.hdr(CF-Connecting-IP) -m found
acl bunny_ip_hdr req.hdr(CDN-ServerId) -m found
http-request redirect scheme https code 301 if !{ ssl_fc } !is_letsencrypt
http-request set-header X-Forwarded-Proto https if { ssl_fc }
http-request set-header X-Forwarded-Proto http if !{ ssl_fc }
http-request set-header X-Forwarded-For %[src] if !cf_ip_hdr !bunny_ip_hdr
http-request set-header X-Forwarded-For %[req.hdr(CF-Connecting-IP)] if cf_ip_hdr
http-request set-header X-Forwarded-For %[req.hdr(X-Real-IP)] if bunny_ip_hdr
# Log format
log-format "%ci %b/%s %ST %B %Tt %sq/%bq %{+Q}r %hr %hs"
# Capture request headers
capture request header Host len 64
capture request header Referer len 256
capture request header User-Agent len 256
# Check for static data request
acl static path_end -i .zst -i .gz -i .pkg -i .tar -i .db -i .sig -i .files -i .zip -i .jar
acl static path_end -i .css -i .js -i .html -i .htm -i .png -i .jpg -i .jpeg -i .gif -i .webp
acl static path_end -i .mp4 -i .webm
## WAF
# SQL Injection | XSS | LFI | RFI | SSRF
acl deny_400 path_reg -i [\"\'();]+|%20(select|union|insert|update|delete|drop|create|truncate|alter|grant|revoke)%20
acl deny_400 path_reg -i \<(script|img|embed|background|iframe|frame|frameset|meta|link|style|svg|object|video|audio|input|textarea|button|select|option|alert|prompt|confirm|expression)\>
acl deny_400 path_reg -i \.\./
acl deny_400 path_reg -i http://|https://
acl deny_400 path_reg -i localhost|127\.0\.0\.1
# File extensions
acl deny_404 path_end -i .sql -i .sql.bz2
# Paths
acl deny_404 path -i -m sub /xmlrpc.php /actuator/gateway/routes /mifs /directdata /user/scripts /overview.asp /actuator /adminer.php
acl deny_404 path -i -m sub /sftp-config.json /db.php /phpinfo.php /wlwmanifest.xml
acl deny_404 path_beg -i /.git /config /.env /backend /console /vendor /admin /.aws /aws /phpinfo /lib /rpc /restore.php /.vscode
## User-agent + empty + badbot + AI Robots + custom
acl deny_403 hdr_cnt(user-agent) eq 0
acl deny_403 hdr_sub(user-agent) -i -f /etc/haproxy/block-ua-badbot.txt
acl deny_403 hdr_sub(user-agent) -i -f /etc/haproxy/block-ua-custom.txt
acl in_src_queue hdr_sub(user-agent) -i -f /etc/haproxy/block-ua-ai-robots.txt
## Digitalocean block
# curl -s https://www.digitalocean.com/geo/google.csv | awk -F ',' '{ print $1 }' | grep -v "::/" > /etc/haproxy/do_ips.txt
acl deny_403 src -f /etc/haproxy/do_ips.txt
acl deny_403 hdr_ip(x-forwarded-for) -f /etc/haproxy/do_ips.txt
## Deny method
acl deny_405 method DELETE CONNECT PATCH
# Do block
http-request tarpit deny_status 400 if deny_400
http-request tarpit deny_status 403 if deny_403
http-request tarpit deny_status 404 if deny_404
http-request tarpit deny_status 405 if deny_405
# POST rate limit
http-request track-sc6 src table st6_src_post if { method POST }
acl rl_post sc_http_req_rate(6) gt 20
http-request track-sc7 src table st7_src_post_wp_login if { method POST } { path_beg -i /wp-login.php -i /wp-json -i /xmlrpc.php }
acl rl_wp_login sc_http_req_rate(7) gt 5
## Rate-limit
# Global maxconn (doit etre nettement superieur au maxconn additionné des backends)
maxconn 2000
http-request set-priority-class int(1) # Default queue priority
## IPs
# Enqueue IPs with too many open conn
http-request track-sc0 src table st0_src_conn_slow
acl in_src_queue sc_conn_cur(0) gt 80
# Enqueue IPs that made to many requests
http-request track-sc1 src table st1_src_req_slow
acl in_src_queue sc_http_req_rate(1) gt 600
# Deny IPs that made to many requests to php files
http-request track-sc2 src table st2_src_req_tarpit_php if { path_end -i .php }
acl rl_php_files sc_http_req_rate(2) gt 200
# Deny IPs that made way too many requests
http-request track-sc3 src table st3_src_req_tarpit
acl rl_global sc_http_req_rate(3) gt 8000
# Deny IPs that had too many 4XX responses
http-request track-sc8 src table st8_src_4xx
acl rl_4xx sc_http_err_rate(8) gt 200
## Hosts
# Enqueue hosts with too many open conn
http-request track-sc4 hdr(Host) table st4_hst_conn
acl in_hst_queue sc_conn_cur(4) gt 120
# Enqueue hosts that recieved to many requests
http-request track-sc5 hdr(Host) table st5_hst_rate
acl in_hst_queue sc_http_req_rate(5) gt 2000
## Priority Classes
# Set queue priority based on previous rules
http-request set-priority-class int(2) if in_hst_queue
http-request set-priority-class int(3) if in_src_queue
http-request set-priority-class int(4) if rl_global
http-request set-priority-class int(4) if rl_php_files
http-request set-priority-class int(4) if rl_4xx
http-request set-priority-class int(4) if rl_post
http-request set-priority-class int(4) if rl_wp_login
# Send to rate-limit backends
use_backend rl_global if rl_global
use_backend rl_php_files if rl_php_files
use_backend rl_4xx if rl_4xx
use_backend rl_post if rl_post
use_backend rl_wp_login if rl_wp_login
# Send to queue backends
use_backend nginx_src_queue if in_src_queue
use_backend nginx_hst_queue if in_hst_queue
# Send to normal backends
default_backend nginx
###############
### BACKENDS
# Normal backends
backend nginx
mode http
server nginx 127.0.0.1:8080 maxconn 180
# Queue backends
backend nginx_src_queue
mode http
tcp-request inspect-delay 200ms
tcp-request content accept if WAIT_END
server nginx 127.0.0.1:8080 maxconn 2
backend nginx_hst_queue
mode http
tcp-request inspect-delay 20ms
tcp-request content accept if WAIT_END
server nginx 127.0.0.1:8080 maxconn 20
# Logging backends
backend rl_wp_login
http-request tarpit deny_status 429
backend rl_post
http-request tarpit deny_status 429
backend rl_4xx
http-request tarpit deny_status 429
backend rl_php_files
http-request tarpit deny_status 429
backend rl_global
http-request tarpit deny_status 429
###################
### STICK-TABLES
# Stick-tables
backend st0_src_conn_slow
stick-table type ip size 1m expire 2m store conn_cur
backend st1_src_req_slow
stick-table type ip size 1m expire 20s store http_req_rate(10s)
backend st2_src_req_tarpit_php
stick-table type ip size 1m expire 20s store http_req_rate(10s)
backend st3_src_req_tarpit
stick-table type ip size 1m expire 1m store http_req_rate(30s)
backend st4_hst_conn
stick-table type string size 10k expire 2m store conn_cur
backend st5_hst_rate
stick-table type string size 10k expire 20s store http_req_rate(10s)
backend st6_src_post
stick-table type ip size 100k expire 20s store http_req_rate(10s)
backend st7_src_post_wp_login
stick-table type ip size 100k expire 1m store http_req_rate(30s)
backend st8_src_4xx
stick-table type ip size 100k expire 20s store gpc0_rate(10s)