Remove 40X rate limiting from HAProxy to prevent false positives
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 1m54s

- Removed all 40X error tracking and rate limiting from HAProxy templates
- Preserved critical IP forwarding headers (X-CLIENT-IP, X-Real-IP, X-Forwarded-For)
- Kept stick table and IP blocking infrastructure for potential future use
- Rate limiting can now be implemented at container level with proper context

This change prevents legitimate developers from being rate-limited during
normal development activities while maintaining proper client IP forwarding
for container-level security and logging.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-08-30 08:54:55 -07:00
parent 8c7031fd6d
commit 402c48b4a0
4 changed files with 10 additions and 131 deletions

View File

@@ -2,39 +2,12 @@
backend {{ name }}-backend backend {{ name }}-backend
option forwardfor option forwardfor
# Pass the real client IP to backend (from proxy headers or direct connection) # Pass the real client IP to backend (from proxy headers or direct connection)
# This is crucial for container-level logging and security tools
http-request add-header X-CLIENT-IP %[var(txn.real_ip)] http-request add-header X-CLIENT-IP %[var(txn.real_ip)]
http-request set-header X-Real-IP %[var(txn.real_ip)] http-request set-header X-Real-IP %[var(txn.real_ip)]
http-request set-header X-Forwarded-For %[var(txn.real_ip)]
{% if ssl_enabled %}http-request set-header X-Forwarded-Proto https if { ssl_fc }{% endif %} {% if ssl_enabled %}http-request set-header X-Forwarded-Proto https if { ssl_fc }{% endif %}
# Define error status codes
acl is_404_error status 404
acl is_403_error status 403
acl is_401_error status 401
acl is_400_error status 400
# Define suspicious scan patterns - only these count as scan attempts
# Script/config files that shouldn't exist on most sites
acl scan_scripts path_reg -i \.(php|asp|aspx|jsp|cgi|pl|py|rb|sh|bash)$
acl scan_admin path_reg -i /(wp-admin|wp-login|phpmyadmin|adminer|manager|admin-console)
acl scan_configs path_reg -i \.(env|git|svn|htaccess|htpasswd|ini|conf|config|yml|yaml|toml)
acl scan_backups path_reg -i \.(backup|bak|old|orig|save|swp|sql|db|dump|tar|zip|rar|7z)
acl scan_vulns path_reg -i /(cgi-bin|fckeditor|tiny_mce|ckfinder|userfiles|filemanager)
# Define legitimate static assets that should NOT count as scan attempts
acl legitimate_assets path_reg -i \.(css|js|jpg|jpeg|png|gif|svg|ico|woff|woff2|ttf|eot|otf|map|webp|mp4|webm|pdf)$
acl legitimate_paths path_beg /static/ /assets/ /media/ /images/ /fonts/ /css/ /js/
# Track scan attempts in the frontend stick table
# Only count suspicious 404s and auth failures
# Multiple ACL conditions on same line = AND, multiple lines = OR
http-response sc-inc-gpc0(0) if scan_scripts is_404_error !legitimate_assets !legitimate_paths
http-response sc-inc-gpc0(0) if scan_admin is_404_error !legitimate_assets !legitimate_paths
http-response sc-inc-gpc0(0) if scan_configs is_404_error !legitimate_assets !legitimate_paths
http-response sc-inc-gpc0(0) if scan_backups is_404_error !legitimate_assets !legitimate_paths
http-response sc-inc-gpc0(0) if scan_vulns is_404_error !legitimate_assets !legitimate_paths
http-response sc-inc-gpc0(0) if is_403_error !legitimate_assets !legitimate_paths
http-response sc-inc-gpc0(0) if is_401_error
{% for server in servers %} {% for server in servers %}
server {{ server.server_name }} {{ server.server_address }}:{{ server.server_port }} {{ server.server_options }} server {{ server.server_name }} {{ server.server_address }}:{{ server.server_port }} {{ server.server_options }}

View File

@@ -2,35 +2,10 @@
backend {{ name }}-backend backend {{ name }}-backend
option forwardfor option forwardfor
# Pass the real client IP to backend (from proxy headers or direct connection) # Pass the real client IP to backend (from proxy headers or direct connection)
# This is crucial for container-level logging and security tools
http-request add-header X-CLIENT-IP %[var(txn.real_ip)] http-request add-header X-CLIENT-IP %[var(txn.real_ip)]
http-request set-header X-Real-IP %[var(txn.real_ip)] http-request set-header X-Real-IP %[var(txn.real_ip)]
http-request set-header X-Forwarded-For %[var(txn.real_ip)]
# Define error status codes
acl is_404_error status 404
acl is_403_error status 403
acl is_401_error status 401
acl is_400_error status 400
# Define suspicious scan patterns - only these count as scan attempts
acl scan_scripts path_reg -i \.(php|asp|aspx|jsp|cgi|pl|py|rb|sh|bash)$
acl scan_admin path_reg -i /(wp-admin|wp-login|phpmyadmin|adminer|manager|admin-console)
acl scan_configs path_reg -i \.(env|git|svn|htaccess|htpasswd|ini|conf|config|yml|yaml|toml)
acl scan_backups path_reg -i \.(backup|bak|old|orig|save|swp|sql|db|dump|tar|zip|rar|7z)
acl scan_vulns path_reg -i /(cgi-bin|fckeditor|tiny_mce|ckfinder|userfiles|filemanager)
# Define legitimate static assets that should NOT count
acl legitimate_assets path_reg -i \.(css|js|jpg|jpeg|png|gif|svg|ico|woff|woff2|ttf|eot|otf|map|webp|mp4|webm|pdf)$
acl legitimate_paths path_beg /static/ /assets/ /media/ /images/ /fonts/ /css/ /js/
# Track scan attempts in the frontend stick table
# Only count suspicious 404s and auth failures
http-response sc-inc-gpc0(0) if scan_scripts is_404_error !legitimate_assets !legitimate_paths
http-response sc-inc-gpc0(0) if scan_admin is_404_error !legitimate_assets !legitimate_paths
http-response sc-inc-gpc0(0) if scan_configs is_404_error !legitimate_assets !legitimate_paths
http-response sc-inc-gpc0(0) if scan_backups is_404_error !legitimate_assets !legitimate_paths
http-response sc-inc-gpc0(0) if scan_vulns is_404_error !legitimate_assets !legitimate_paths
http-response sc-inc-gpc0(0) if is_403_error !legitimate_assets !legitimate_paths
http-response sc-inc-gpc0(0) if is_401_error
{% for server in servers %} {% for server in servers %}
server {{ server.server_name }} {{ server.server_address }}:{{ server.server_port }} {{ server.server_options }} server {{ server.server_name }} {{ server.server_address }}:{{ server.server_port }} {{ server.server_options }}

View File

@@ -3,37 +3,12 @@ backend {{ name }}-backend
option forwardfor option forwardfor
option httpchk option httpchk
# Pass the real client IP to backend (from proxy headers or direct connection) # Pass the real client IP to backend (from proxy headers or direct connection)
# This is crucial for container-level logging and security tools
http-request add-header X-CLIENT-IP %[var(txn.real_ip)] http-request add-header X-CLIENT-IP %[var(txn.real_ip)]
http-request set-header X-Real-IP %[var(txn.real_ip)] http-request set-header X-Real-IP %[var(txn.real_ip)]
http-request set-header X-Forwarded-For %[var(txn.real_ip)]
{% if ssl_enabled %}http-request set-header X-Forwarded-Proto https if { ssl_fc }{% endif %} {% if ssl_enabled %}http-request set-header X-Forwarded-Proto https if { ssl_fc }{% endif %}
# Define error status codes
acl is_404_error status 404
acl is_403_error status 403
acl is_401_error status 401
acl is_400_error status 400
# Define suspicious scan patterns - only these count as scan attempts
acl scan_scripts path_reg -i \.(php|asp|aspx|jsp|cgi|pl|py|rb|sh|bash)$
acl scan_admin path_reg -i /(wp-admin|wp-login|phpmyadmin|adminer|manager|admin-console)
acl scan_configs path_reg -i \.(env|git|svn|htaccess|htpasswd|ini|conf|config|yml|yaml|toml)
acl scan_backups path_reg -i \.(backup|bak|old|orig|save|swp|sql|db|dump|tar|zip|rar|7z)
acl scan_vulns path_reg -i /(cgi-bin|fckeditor|tiny_mce|ckfinder|userfiles|filemanager)
# Define legitimate static assets that should NOT count
acl legitimate_assets path_reg -i \.(css|js|jpg|jpeg|png|gif|svg|ico|woff|woff2|ttf|eot|otf|map|webp|mp4|webm|pdf)$
acl legitimate_paths path_beg /static/ /assets/ /media/ /images/ /fonts/ /css/ /js/
# Track scan attempts in the frontend stick table
# Only count suspicious 404s and auth failures
http-response sc-inc-gpc0(0) if scan_scripts is_404_error !legitimate_assets !legitimate_paths
http-response sc-inc-gpc0(0) if scan_admin is_404_error !legitimate_assets !legitimate_paths
http-response sc-inc-gpc0(0) if scan_configs is_404_error !legitimate_assets !legitimate_paths
http-response sc-inc-gpc0(0) if scan_backups is_404_error !legitimate_assets !legitimate_paths
http-response sc-inc-gpc0(0) if scan_vulns is_404_error !legitimate_assets !legitimate_paths
http-response sc-inc-gpc0(0) if is_403_error !legitimate_assets !legitimate_paths
http-response sc-inc-gpc0(0) if is_401_error
{% for server in servers %} {% for server in servers %}
server {{ server.server_name }} {{ server.server_address }}:{{ server.server_port }} {{ server.server_options }} server {{ server.server_name }} {{ server.server_address }}:{{ server.server_port }} {{ server.server_options }}
{% endfor %} {% endfor %}

View File

@@ -4,9 +4,8 @@ frontend web
# crt can now be a path, so it will load all .pem files in the path # crt can now be a path, so it will load all .pem files in the path
bind 0.0.0.0:443 ssl crt {{ crt_path }} alpn h2,http/1.1 bind 0.0.0.0:443 ssl crt {{ crt_path }} alpn h2,http/1.1
# Stick table for tracking attacks with escalating timeouts # Stick table kept for potential future use or custom implementations
# gpc0 = total scan attempts # Can be used by containers to track IPs if needed
# gpc1 = escalation level (0=none, 1=level1, 2=level2, 3=level3)
stick-table type ip size 200k expire 1h store gpc0,gpc1,http_err_rate(10s) stick-table type ip size 200k expire 1h store gpc0,gpc1,http_err_rate(10s)
# Whitelist trusted networks and monitoring systems # Whitelist trusted networks and monitoring systems
@@ -33,55 +32,12 @@ frontend web
http-request set-var(txn.real_ip) src if !has_cf_connecting_ip !has_x_real_ip !has_x_forwarded_for http-request set-var(txn.real_ip) src if !has_cf_connecting_ip !has_x_real_ip !has_x_forwarded_for
# Track the real client IP in stick table (not the proxy IP) # Track the real client IP in stick table (not the proxy IP)
# Kept for potential container-level tracking integrations
http-request track-sc0 var(txn.real_ip) http-request track-sc0 var(txn.real_ip)
# IP blocking using map file (no word limit, runtime updates supported) # IP blocking using map file (no word limit, runtime updates supported)
# Map file: /etc/haproxy/blocked_ips.map # Map file: /etc/haproxy/blocked_ips.map
# Runtime updates: echo "add map #0 IP_ADDRESS" | socat stdio /var/run/haproxy.sock # Runtime updates: echo "add map #0 IP_ADDRESS" | socat stdio /var/run/haproxy.sock
# Now checks the real client IP (from headers if present, otherwise src) # Checks the real client IP (from headers if present, otherwise src)
http-request set-path /blocked-ip if { var(txn.real_ip) -m ip -f /etc/haproxy/blocked_ips.map } http-request set-path /blocked-ip if { var(txn.real_ip) -m ip -f /etc/haproxy/blocked_ips.map }
use_backend default-backend if { var(txn.real_ip) -m ip -f /etc/haproxy/blocked_ips.map } use_backend default-backend if { var(txn.real_ip) -m ip -f /etc/haproxy/blocked_ips.map }
# Define threat levels based on accumulated error responses from backends
# These will be checked on subsequent requests after errors are tracked
acl scanner_low sc0_get_gpc0 ge 25 # 25+ errors = potential scanner
acl scanner_medium sc0_get_gpc0 ge 40 # 40+ errors = likely scanner
acl scanner_high sc0_get_gpc0 ge 60 # 60+ errors = confirmed scanner
acl scanner_critical sc0_get_gpc0 ge 100 # 100+ errors = aggressive scanner
# Rate-based detection (burst of errors)
acl burst_scanner sc0_http_err_rate gt 5 # >5 errors in 10 seconds
# Escalation levels (tracks how many times we've escalated this IP)
acl escalation_level_0 sc0_get_gpc1 eq 0 # First offense
acl escalation_level_1 sc0_get_gpc1 eq 1 # Second offense
acl escalation_level_2 sc0_get_gpc1 eq 2 # Third offense
acl escalation_level_3 sc0_get_gpc1 ge 3 # Repeat offender
# BLOCKING RULES - Progressive response based on threat level
# Level 4: Complete block for critical threats (50+ errors)
http-request deny deny_status 429 if scanner_critical
# Level 3: Silent drop for obvious scanners and burst attacks
# This immediately closes the connection without any response
http-request silent-drop if scanner_high # 35+ errors
http-request silent-drop if scanner_medium burst_scanner # 20+ errors with burst
http-request silent-drop if scanner_medium escalation_level_2 # Repeat medium scanner
http-request silent-drop if burst_scanner escalation_level_1 # Repeat burst scanner
# Level 2: Tarpit for medium scanners (first offense)
# 10 second delay before closing connection
http-request tarpit deny_status 429 if scanner_medium escalation_level_0
http-request tarpit deny_status 429 if scanner_medium escalation_level_1
# Level 1: Tarpit for low-level scanners
# 10 second delay to slow them down
http-request tarpit deny_status 429 if scanner_low
http-request tarpit deny_status 429 if burst_scanner escalation_level_0
# Increment escalation level when we apply any protection
# This tracks how many times this IP has been actioned
http-request sc-inc-gpc1(0) if scanner_low or scanner_medium or scanner_high or burst_scanner
# Note: The backend will increment sc0_get_gpc0 when it sees 400/401/403/404 responses