mirror of
https://gitgud.io/fatchan/haproxy-protection.git
synced 2025-05-09 02:05:37 +00:00
186 lines
10 KiB
INI
186 lines
10 KiB
INI
global
|
|
daemon
|
|
ca-base /etc/ssl/certs
|
|
crt-base /etc/ssl/private
|
|
master-worker
|
|
maxconn "${HAPROXY_MAXCONN}"
|
|
log stdout format raw local0 debug
|
|
lua-load /etc/haproxy/scripts/register-servers.lua
|
|
lua-load-per-thread /etc/haproxy/scripts/tfp.lua
|
|
lua-load-per-thread /etc/haproxy/scripts/register-bot-check.lua
|
|
stats socket /var/run/haproxy.sock mode 666 level admin
|
|
stats socket 127.0.0.1:1999 level admin
|
|
httpclient.ssl.verify none
|
|
# Allow larger buffer size for return-file of argon scripts
|
|
tune.bufsize 51200
|
|
expose-deprecated-directives
|
|
tune.ssl.capture-buffer-size 200
|
|
|
|
defaults
|
|
log global
|
|
mode http
|
|
option dontlognull
|
|
option httplog
|
|
timeout connect 5000ms
|
|
timeout client 50000ms
|
|
timeout server 50000ms
|
|
timeout tarpit 5000ms
|
|
http-error status 400 content-type "text/html; charset=utf-8" lf-file /etc/haproxy/errors/400.http
|
|
http-error status 403 content-type "text/html; charset=utf-8" lf-file /etc/haproxy/errors/403.http
|
|
http-error status 408 content-type "text/html; charset=utf-8" lf-file /etc/haproxy/errors/408.http
|
|
http-error status 429 content-type "text/html; charset=utf-8" lf-file /etc/haproxy/errors/429.http
|
|
http-error status 500 content-type "text/html; charset=utf-8" lf-file /etc/haproxy/errors/500.http
|
|
http-error status 502 content-type "text/html; charset=utf-8" lf-file /etc/haproxy/errors/502.http
|
|
http-error status 503 content-type "text/html; charset=utf-8" lf-file /etc/haproxy/errors/503.http
|
|
http-error status 504 content-type "text/html; charset=utf-8" lf-file /etc/haproxy/errors/504.http
|
|
|
|
program api
|
|
command dataplaneapi -f /etc/haproxy/dataplaneapi.yml --update-map-files
|
|
no option start-on-reload
|
|
|
|
frontend stats-frontend
|
|
bind 127.0.0.1:2000
|
|
option tcplog
|
|
mode tcp
|
|
acl white_list src 127.0.0.1
|
|
tcp-request connection reject unless white_list
|
|
default_backend stats-backend
|
|
|
|
backend stats-backend
|
|
mode tcp
|
|
server stats-localhost 127.0.0.1:1999
|
|
|
|
frontend http-in
|
|
|
|
# Clearnet http (you'll have to figure out https yourself)
|
|
bind *:80
|
|
bind *:443 ssl crt /etc/haproxy/certs/haproxy.pem alpn h2,http/1.1
|
|
# bind quic4@*:443 ssl crt /etc/haproxy/certs/haproxy.pem
|
|
# http-response set-header alt-svc "h3=\":443\";ma=900;"
|
|
|
|
# Or instead, for Tor, to use circuit IDs as "IP":
|
|
# bind 127.0.0.1:80 accept-proxy
|
|
option forwardfor
|
|
|
|
# tfp lua dev test
|
|
http-request lua.set_tfp
|
|
|
|
# custom log format
|
|
log-format "{\"cc\":%{+Q}[capture.req.hdr(1)],\"tp\":%{+Q}[var(txn.fp_custom)],\"cip\":\"%ci\",\"cp\":\"%cp\",\"hh\":%{+Q}[capture.req.hdr(0)],\"backend\":\"%b\",\"server\":\"%s\",\"timers\":\"%TR/%Tw/%Tc/%Tr/%Ta\",\"status\":\"%ST\",\"bytes\":\"%B\",\"bs\":\"%U\",\"conns\":\"%ac/%fc/%bc/%sc/%rc\",\"q\":\"%sq/%bq\",\"req\":%{+Q,+E}r,\"GeoIP_Source\":\"GeoIP_Source=%ci\"}"
|
|
|
|
# optional geoip handling (maps required) and alt-svc header addition
|
|
http-request set-var(req.xcc) src,map_ip(/etc/haproxy/map/geoip.map)
|
|
http-request set-var(req.asn) src,map_ip(/etc/haproxy/map/iptoasn.map)
|
|
http-request set-var(txn.xcn) var(req.xcc),map(/etc/haproxy/map/cctocn.map)
|
|
http-request set-header X-Country-Code %[var(req.xcc)]
|
|
http-request set-header X-Continent-Code %[var(txn.xcn)]
|
|
http-request set-header X-ASN %[var(req.asn)]
|
|
|
|
# drop requests with invalid host header
|
|
acl is_existing_vhost hdr(host),lower,map_str(/etc/haproxy/map/hosts.map) -m found
|
|
acl has_query query -m found
|
|
acl on_bot_check path /.basedflare/bot-check
|
|
http-request silent-drop unless is_existing_vhost
|
|
|
|
# debug information at /.basedflare/cgi/trace
|
|
http-request return status 200 content-type "text/plain; charset=utf-8" lf-file /etc/haproxy/template/trace.txt if { path /.basedflare/cgi/trace }
|
|
|
|
http-request track-sc1 query table count_qs_throttle if has_query !on_bot_check
|
|
|
|
# acl for blocked IPs/subnets/ASN/country
|
|
http-request lua.set-lang-json
|
|
acl found_in_blockedip_map src,map_ip(/etc/haproxy/map/blockedip.map) -m found
|
|
acl found_in_blockedasn_map var(req.asn),map(/etc/haproxy/map/blockedasn.map) -m found
|
|
acl found_in_blockedcc_map var(req.xcc),map(/etc/haproxy/map/blockedcc.map) -m found
|
|
acl found_in_blockedcn_map var(txn.xcn),map(/etc/haproxy/map/blockedcn.map) -m found
|
|
acl blocked_bool var(txn.blocked_bool) -m bool
|
|
http-request lua.set-ip-var blockedip txn.blocked_bool ip if found_in_blockedip_map
|
|
http-request lua.set-ip-var blockedasn txn.blocked_bool asn if found_in_blockedasn_map
|
|
http-request lua.set-ip-var blockedcc txn.blocked_bool cc if found_in_blockedcc_map
|
|
http-request lua.set-ip-var blockedcn txn.blocked_bool cn if found_in_blockedcn_map
|
|
http-request deny deny_status 403 if blocked_bool
|
|
|
|
# ratelimit (and for tor, kill circuit) on POST bot-check. legitimate users shouldn't hit this.
|
|
http-request track-sc0 src table bot_check_post_throttle if on_bot_check { method POST }
|
|
# http-request lua.kill-tor-circuit if { sc_http_req_rate(0) gt 1 }
|
|
# http-request tarpit if { sc_http_req_rate(0) gt 1 }
|
|
|
|
# acl for lua check whitelisted IPs/subnets and some excluded paths
|
|
acl found_in_whitelist_map src,map_ip(/etc/haproxy/map/whitelist.map) -m found
|
|
acl is_excluded var(txn.whitelist_ip_or_subnet) -m bool
|
|
http-request lua.set-ip-var whitelist txn.whitelist_ip_or_subnet ip if found_in_whitelist_map
|
|
acl is_excluded src -f /etc/haproxy/map/crawler-whitelist.map
|
|
acl is_excluded path /favicon.ico /.basedflare/pow-icon #add more
|
|
|
|
# acl ORs for when ddos_mode_enabled
|
|
acl ddos_mode_enabled_override str("true"),map(/etc/haproxy/map/ddos_global.map) -m found
|
|
acl ddos_mode_enabled hdr(host),lower,map(/etc/haproxy/map/ddos.map) -m found
|
|
acl ddos_mode_enabled base,map(/etc/haproxy/map/ddos.map) -m found
|
|
acl large_unique_query_count table_cnt(count_qs_throttle) -m int gt 1000
|
|
acl ddos_mode_enabled_override acl(large_unique_query_count,has_query,!on_bot_check)
|
|
|
|
# serve challenge page scripts directly from haproxy
|
|
http-request return file /etc/haproxy/js/auto.min.js status 200 content-type "application/javascript; charset=utf-8" hdr "Cache-Control" "public, max-age=86400" if { path /.basedflare/js/auto.min.js }
|
|
http-request return file /etc/haproxy/js/argon2.min.js status 200 content-type "application/javascript; charset=utf-8" hdr "Cache-Control" "public, max-age=86400" if { path /.basedflare/js/argon2.min.js }
|
|
http-request return file /etc/haproxy/js/challenge.js status 200 content-type "application/javascript; charset=utf-8" hdr "Cache-Control" "public, max-age=86400" if { path /.basedflare/js/challenge.min.js }
|
|
http-request return file /etc/haproxy/js/worker.min.js status 200 content-type "application/javascript; charset=utf-8" hdr "Cache-Control" "public, max-age=86400" if { path /.basedflare/js/worker.min.js }
|
|
http-request return file /etc/haproxy/js/bc.js status 200 content-type "application/javascript; charset=utf-8" hdr "Cache-Control" "public, max-age=86400" if { path /.basedflare/js/bc.js }
|
|
http-request return file /etc/haproxy/js/bm.min.js status 200 content-type "application/javascript; charset=utf-8" hdr "Cache-Control" "public, max-age=86400" if { path /.basedflare/js/bm.min.js }
|
|
|
|
|
|
# rewrite specific domain+path to domain or domain+path
|
|
http-request redirect location https://%[base,map(/etc/haproxy/map/rewrite.map)] code 302 if { base,map(/etc/haproxy/map/rewrite.map) -i -m found }
|
|
|
|
# redirect domain to domain or domain+path
|
|
http-request redirect location https://%[hdr(host),map(/etc/haproxy/map/redirect.map)] code 302 if { hdr(host),map(/etc/haproxy/map/redirect.map) -i -m found }
|
|
|
|
# create acl for bools updated by lua
|
|
acl captcha_passed var(txn.captcha_passed) -m bool
|
|
acl pow_passed var(txn.pow_passed) -m bool
|
|
acl validate_captcha var(txn.validate_captcha) -m bool
|
|
acl validate_pow var(txn.validate_pow) -m bool
|
|
|
|
# check pow/captcha and show page if necessary
|
|
http-request use-service lua.bot-check if on_bot_check !is_excluded
|
|
|
|
# challenge decisions, checking, and redirecting to /bot-check
|
|
http-request lua.decide-checks-necessary if !is_excluded !on_bot_check ddos_mode_enabled
|
|
http-request lua.captcha-check if !is_excluded !on_bot_check validate_captcha
|
|
http-request lua.pow-check if !is_excluded !on_bot_check validate_pow OR !is_excluded !on_bot_check ddos_mode_enabled_override
|
|
http-request redirect location /.basedflare/bot-check?%[capture.req.uri] code 302 if validate_captcha !captcha_passed !on_bot_check ddos_mode_enabled !is_excluded OR validate_pow !pow_passed !on_bot_check ddos_mode_enabled !is_excluded OR !pow_passed ddos_mode_enabled_override !on_bot_check !is_excluded
|
|
|
|
# acl for domains in maintenance mode to return maintenance page (after challenge page htp-request return rules, for the footerlogo)
|
|
acl maintenance_mode hdr(host),lower,map_str(/etc/haproxy/map/maintenance.map) -m found
|
|
http-request lua.set-lang-json
|
|
http-request return lf-file /etc/haproxy/template/maintenance.html status 200 content-type "text/html; charset=utf-8" hdr "Cache-Control" "private, max-age=30" if maintenance_mode
|
|
|
|
# optional alt-svc header (done after cache so not set in cached responses
|
|
acl match_server_continent var(txn.xcn) -m str "${HAPROXY_CONTINENT}"
|
|
http-response set-header X-Server-CN "${HAPROXY_CONTINENT}"
|
|
http-response set-header X-User-CN %[var(txn.xcn)]
|
|
http-response set-header Alt-Svc %[var(txn.xcn),map(/etc/haproxy/map/alt-svc.map)] if !match_server_continent
|
|
|
|
# varnish caching if available
|
|
acl varnish_available nbsrv(haproxy-to-varnish-cache) gt 0
|
|
acl is_varnish_methods method GET HEAD PURGE BAN
|
|
use_backend haproxy-to-varnish-cache if varnish_available is_varnish_methods
|
|
default_backend servers
|
|
|
|
frontend varnish-to-haproxy-internal
|
|
bind unix@/shared-sockets/varnish-to-haproxy-internal.sock user root mode 666
|
|
default_backend servers
|
|
|
|
backend haproxy-to-varnish-cache
|
|
server varnish unix@/shared-sockets/haproxy-to-varnish-cache.sock check observe layer7 inter 1s
|
|
|
|
backend servers
|
|
balance roundrobin
|
|
default-server ssl verify required ca-file ca-certificates.crt sni req.hdr(Host) check observe layer4 inter 30s
|
|
use-server %[lua.get_server_names] if TRUE
|
|
|
|
backend bot_check_post_throttle
|
|
stick-table type ipv6 size 100k expire 60s store http_req_rate(60s)
|
|
|
|
backend count_qs_throttle
|
|
stick-table type string size 100k expire 60s store http_req_rate(60s)
|