Files
haproxy-manager-base/scripts/show-tarpit-ips.sh
shadowdaoandClaude Sonnet 5.5 0075b2e0de feat(edge): unrouted hosts 404+noindex everywhere; access log moves into the container (2026.11.2)
Default splash (Josh 2026-11-08): unrouted hosts are 404 on EVERY path incl.
"/" (same static page), with cache-control: no-store, x-robots-tag: noindex,
nofollow, noarchive and nosniff; /robots.txt answers 200 "Disallow: /". So the
server IP/hostname is never indexed as a site. Routed sites, ACME, blocked
IPs, suspended hosts, rate limits, wp-* gates and WAF are unchanged.

Access log: the old target 172.18.0.1:514 (host rsyslog) had no listener on
the fleet. HAProxy now logs to a UNIX datagram socket in its chroot
(`log /log.sock`, i.e. /var/lib/haproxy/log.sock); scripts/haproxy-log-receiver.py
(started before init.py, supervised by a restart loop in start-up.sh) writes
/var/log/haproxy/access.log rotated at 40 MB + 4 gzip generations (<= 200 MB).
No network listener; sends are non-blocking, so a dead/stalled receiver or a
full disk only drops lines (DroppedLogs). Nothing is printed to docker logs
(WHP's AI monitor reads them). Same log-format (cip/host/status/Coraza fields).
HAPROXY_SYSLOG_TARGET still overrides. show-tarpit-ips.sh / monitor-attacks.sh
point at the new file.

Tests: test-default-splash.py (19, incl. log target), new
test-access-log-receiver.py (10), both in the image build; validate-rendered-
config.py scenarios all accepted by haproxy -c. Lab (pre-populated volume,
recreate): 404+headers on hostname/IP/unknown, robots 200, routed site 200,
attack on site 403, ACME 200, suspended 503; 2000/2000 burst lines logged;
receiver kill -9 -> requests 200, receiver back <3s; SIGSTOP + 2500 requests
-> all 200, 1475 DroppedLogs; rotation at 64 KB cap kept 1+2 gz; logs survive
recreate on the haproxy-logs volume.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0185ukXjXyosSkunDsdWz3mb
2026-11-08 08:05:58 -08:00

36 lines
1.7 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# DEPRECATED SHIM — kept so existing docs/runbooks/muscle memory keep working.
#
# This script used to print a "Tarpitted IPs Report" with a "Scan Count" and a
# BLOCKED / SILENT-DROP / TARPIT status per IP, all derived from gpc0 and gpc1
# stick-table columns. Those columns DO NOT EXIST: the `web` table
# (templates/hap_listener.tpl) stores only conn_cur, conn_rate, http_req_rate
# and http_err_rate. The old parser defaulted every missing field to 0, so the
# whole report was fabricated — every IP showed "Scan Count 0 / Normal"
# regardless of what it was actually doing.
#
# The stick table also keeps NO history, so nothing in it can identify who was
# tarpitted. Real enforcement events live in the access log ON THE DOCKER HOST
# at /var/log/haproxy/access.log (inside this container since 2026.11.2):
# grep -aE ' (PT|PR)--' /var/log/haproxy/access.log | tail -20 # tarpit / deny
# grep -aE ' (429|403) ' /var/log/haproxy/access.log | tail -20 # rate-limit / block
#
# What IS knowable from the stick table — the current per-IP rates — is printed
# by show-edge-ip-rates.sh, which this shim now runs.
set -euo pipefail
cat >&2 <<'NOTE'
NOTE: show-tarpit-ips.sh is deprecated and cannot report tarpits.
The HAProxy stick table stores no history and no gpc0/gpc1 counters, so
the old "Scan Count"/"BLOCKED" columns were fabricated numbers.
Actual tarpit/deny events are in /var/log/haproxy/access.log (in this container since 2026.11.2):
grep -aE ' (PT|PR)--' /var/log/haproxy/access.log | tail -20
Running show-edge-ip-rates.sh instead (current rates, real values):
NOTE
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
exec "$SCRIPT_DIR/show-edge-ip-rates.sh" "$@"