-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhaproxy_tarpit_config.txt
More file actions
187 lines (157 loc) · 8.46 KB
/
Copy pathhaproxy_tarpit_config.txt
File metadata and controls
187 lines (157 loc) · 8.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# =============================================================================
# NOT IMPLEMENTED. THIS FILE IS A DESIGN SKETCH THAT WAS NEVER SHIPPED.
# =============================================================================
#
# Nothing here is deployed, has ever been deployed, or is rendered into
# haproxy.cfg. The real edge config is generated from templates/*.tpl. Compare:
#
# THIS FILE proposes: store gpc0,gpc1,gpc2,http_err_rate(30s),...
# plus sc-inc-gpc0/1/2 scan-escalation rules
# templates/hap_listener.tpl ACTUALLY has:
# store conn_cur,conn_rate(10s),http_req_rate(10s),http_err_rate(30s)
#
# There is no gpc0, no gpc1, no gpc2, and no scan-escalation state anywhere on
# the edge, and never has been.
#
# WHY THE BANNER. This file was mistaken for the shipped config. Two consumers
# -- /api/security/stats in haproxy_manager.py and scripts/show-tarpit-ips.sh --
# were written to parse gpc0/gpc1 out of `show table web`, defaulting missing
# fields to 0. The result was a "Scan Count" column and BLOCKED/TARPITTED
# statuses that were pure fabrication, presented to an operator as fact, for
# the entire life of both tools. Fixed 2026-08-22; scripts/test-stick-table-
# contract.py now fails if any consumer's field expectations and the templates'
# `store` clauses ever drift apart again.
#
# IF YOU WANT TO REVIVE ANY OF THIS: the counters must be added to a template
# `store` clause and to STICK_TABLE_FIELD_CONTRACT in haproxy_manager.py first.
# Adding them to a consumer alone produces confident zeros, not data. Note also
# that sc0/sc1/sc2 are all in use and HAProxy's tune.stick-counters defaults to
# 3, and that since 2026.08.8 the edge has real per-request access logging on
# the host at /var/log/haproxy.log -- which records what was actually denied,
# tarpitted and rate-limited, with request references. That log is a better
# source for most of what this sketch was reaching for.
# =============================================================================
global
daemon
log stdout local0 info
chroot /var/lib/haproxy
stats socket /var/run/haproxy.sock mode 600 level admin
stats timeout 30s
user haproxy
group haproxy
# SSL/TLS settings if using HTTPS
ssl-default-bind-ciphers ECDHE+AESGCM:ECDHE+CHACHA20:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
timeout tarpit 60s # Maximum tarpit time
option httplog
option dontlognull
option log-health-checks
retries 3
# Error files (optional - customize as needed)
errorfile 400 /usr/local/etc/haproxy/errors/400.http
errorfile 403 /usr/local/etc/haproxy/errors/403.http
errorfile 408 /usr/local/etc/haproxy/errors/408.http
errorfile 500 /usr/local/etc/haproxy/errors/500.http
errorfile 502 /usr/local/etc/haproxy/errors/502.http
errorfile 503 /usr/local/etc/haproxy/errors/503.http
errorfile 504 /usr/local/etc/haproxy/errors/504.http
frontend web_frontend
bind *:80
bind *:443 ssl crt /etc/ssl/certs/haproxy.pem
# Stick table for tracking attacks with escalating timeouts
# gpc0 = total scan attempts
# gpc1 = escalation level (0=none, 1=level1, 2=level2, 3=level3)
# gpc2 = total tarpit/block actions taken
stick-table type ip size 200k expire 2h store gpc0,gpc1,gpc2,http_err_rate(30s),http_err_rate(300s),http_err_rate(3600s)
# Whitelist trusted networks and monitoring systems
acl trusted_networks src 127.0.0.1 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12
acl monitoring_systems src -f /etc/haproxy/monitoring_ips.txt
acl health_check path_beg /health /ping /status /.well-known/
# Allow trusted traffic to bypass all protection
http-request allow if trusted_networks or monitoring_systems or health_check
# Define threat levels based on scan attempts and rates
acl low_threat sc_get_gpc0(0) ge 3 sc_get_gpc0(0) lt 10
acl medium_threat sc_get_gpc0(0) ge 10 sc_get_gpc0(0) lt 25
acl high_threat sc_get_gpc0(0) ge 25 sc_get_gpc0(0) lt 50
acl critical_threat sc_get_gpc0(0) ge 50
# Rate-based detection (burst attacks)
acl burst_attack sc_http_err_rate(0,30s) gt 8 # >8 errors in 30 seconds
acl sustained_attack sc_http_err_rate(0,300s) gt 3 # >3 errors/min for 5 minutes
acl persistent_attack sc_http_err_rate(0,3600s) gt 1 # >1 error/min for 1 hour
# Escalation levels (tracks how many times we've escalated this IP)
acl escalation_level_0 sc_get_gpc1(0) eq 0
acl escalation_level_1 sc_get_gpc1(0) eq 1
acl escalation_level_2 sc_get_gpc1(0) eq 2
acl escalation_level_3 sc_get_gpc1(0) ge 3
# ESCALATING TARPIT RULES
# Level 1: Short tarpit (2-5 seconds) for first offense
http-request tarpit timeout 2s if low_threat escalation_level_0
http-request tarpit timeout 3s if medium_threat escalation_level_0
http-request tarpit timeout 5s if burst_attack escalation_level_0
# Level 2: Medium tarpit (8-15 seconds) for second offense
http-request tarpit timeout 8s if low_threat escalation_level_1
http-request tarpit timeout 12s if medium_threat escalation_level_1
http-request tarpit timeout 15s if high_threat escalation_level_1
http-request tarpit timeout 10s if sustained_attack escalation_level_1
# Level 3: Long tarpit (20-45 seconds) for repeat offenders
http-request tarpit timeout 20s if low_threat escalation_level_2
http-request tarpit timeout 30s if medium_threat escalation_level_2
http-request tarpit timeout 45s if high_threat escalation_level_2
http-request tarpit timeout 25s if persistent_attack escalation_level_2
# Level 4: Maximum tarpit (60 seconds) for persistent attackers
http-request tarpit timeout 60s if escalation_level_3
# Complete block for critical threats regardless of escalation level
http-request deny deny_status 429 if critical_threat
# Increment escalation level when we apply tarpit/block
http-request sc-inc-gpc1(0) if low_threat or medium_threat or high_threat or burst_attack or sustained_attack or persistent_attack
http-request sc-inc-gpc2(0) if low_threat or medium_threat or high_threat or critical_threat or burst_attack or sustained_attack or persistent_attack
# Capture useful headers for logging
capture request header User-Agent len 150
capture request header X-Forwarded-For len 30
capture request header Referer len 100
# Enhanced logging format with protection metrics
log-format "%ci:%cp [%t] %ft %b/%s %Tq/%Tw/%Tc/%Tr/%Ta %ST %B %CC %CS \"%r\" \"%[capture.req.hdr(0)]\" gpc0:%[sc_get_gpc0(0)] gpc1:%[sc_get_gpc1(0)] gpc2:%[sc_get_gpc2(0)] err_rate_30s:%[sc_http_err_rate(0,30s)]"
# Redirect HTTP to HTTPS (optional)
redirect scheme https if !{ ssl_fc }
default_backend web_servers
backend web_servers
balance roundrobin
option httpchk GET /health HTTP/1.1\r\nHost:\ localhost
# Define scanning attempt patterns
acl is_404_error status 404
acl is_403_error status 403
acl is_401_error status 401
acl is_400_error status 400
acl is_scan_attempt status 400 401 403 404
# Additional suspicious patterns (optional)
acl suspicious_path path_reg -i \.(php|asp|aspx|jsp|cgi)$
acl suspicious_path path_reg -i /(wp-admin|phpmyadmin|admin|login|xmlrpc)
acl suspicious_path path_reg -i \.(env|git|svn|backup|bak|old)
# Track scan attempts in the frontend stick table
http-response sc-inc-gpc0(0) if is_scan_attempt
# Optional: Track suspicious paths even if they return 200
# http-response sc-inc-gpc0(0) if suspicious_path
# Optional: Different weights for different error types
# http-response sc-add-gpc0(0) 2 if is_403_error # 403s count as 2 points
# http-response sc-add-gpc0(0) 3 if is_401_error # 401s count as 3 points
# Server definitions - adjust as needed
server web1 backend-server-1:80 check maxconn 200 weight 100
server web2 backend-server-2:80 check maxconn 200 weight 100
# server web3 backend-server-3:80 check maxconn 200 weight 100
# Optional: Stats page for monitoring
frontend stats
bind *:8404
# Restrict access to stats page
acl allowed_ips src 127.0.0.1 192.168.0.0/16 10.0.0.0/8
http-request allow if allowed_ips
http-request deny
stats enable
stats uri /stats
stats refresh 30s
stats show-legends
stats show-node