Unrouted hosts (server hostname, bare IP, unknown names) now get a tiny static page served by HAProxy before the WAF: 404 on every path, with x-robots-tag noindex/nofollow/noarchive and no-store; /robots.txt returns 200 Disallow: /. ACME challenges, blocked IPs, suspended hosts, rate limits and the WAF for routed sites are unchanged. HAPROXY_DEFAULT_SPLASH=off restores the old behaviour.
Access log moves into the container: HAProxy logs to a unix socket and a supervised in-container receiver writes a size-capped rotating file (40MB x 4 generations) to a volume; logging can never block HAProxy. The host-side 172.18.0.1:514 target is removed.
Tests: 15 splash tests + 10 receiver tests in the image build; rendered configs validated with haproxy -c; lab proof recorded in the README (Default Splash / Access Log sections).
Deploy: image build by CI, then container-manager.sh recreate haproxy-manager (test host first). WHP side lives on feat/go-edge-access-log (new run args: haproxy-logs volume + net.unix.max_dgram_qlen sysctl).
Unrouted hosts (server hostname, bare IP, unknown names) now get a tiny static page served by HAProxy before the WAF: 404 on every path, with x-robots-tag noindex/nofollow/noarchive and no-store; /robots.txt returns 200 Disallow: /. ACME challenges, blocked IPs, suspended hosts, rate limits and the WAF for routed sites are unchanged. HAPROXY_DEFAULT_SPLASH=off restores the old behaviour.
Access log moves into the container: HAProxy logs to a unix socket and a supervised in-container receiver writes a size-capped rotating file (40MB x 4 generations) to a volume; logging can never block HAProxy. The host-side 172.18.0.1:514 target is removed.
Tests: 15 splash tests + 10 receiver tests in the image build; rendered configs validated with haproxy -c; lab proof recorded in the README (Default Splash / Access Log sections).
Deploy: image build by CI, then container-manager.sh recreate haproxy-manager (test host first). WHP side lives on feat/go-edge-access-log (new run args: haproxy-logs volume + net.unix.max_dgram_qlen sysctl).
Visiting a server's own hostname (or its IP, or any unrouted name) on 80/443
could show the Coraza WAF block page. `use_backend default-backend` does not
short-circuit http-request rules, so the catch-all was WAF-inspected with the
full server-wide rule set like customer traffic; a browser on the server
hostname also carries the control panel's cookies (not port-scoped).
Unrouted requests are now answered by HAProxy itself with a tiny static page
(errors/default-splash.html via `http-request return ... file`; 200 on "/",
404 elsewhere) placed before the SPOE call. "Routed" comes from
routed_hosts.list / routed_wildcards.list, written by generate_config() from
the same rows as the routing ACLs, port-stripped + lowercased (superset of the
raw-Host routing ACLs), and part of the rollback set. ACME http-01, blocked
IPs and suspended hosts keep their existing handling; rate limits and gates
still run first. HAPROXY_DEFAULT_SPLASH=off restores the old behaviour.
Page and template live in the image outside the /etc/haproxy named volume;
the lists are generated on start, so image bump + recreate is the deploy.
Tests: scripts/test-default-splash.py (in the image build), new splash-off
scenario in validate-rendered-config.py.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0185ukXjXyosSkunDsdWz3mb
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
jknapp
merged commit 50c58c4e9e into main2026-11-08 20:03:24 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Unrouted hosts (server hostname, bare IP, unknown names) now get a tiny static page served by HAProxy before the WAF: 404 on every path, with x-robots-tag noindex/nofollow/noarchive and no-store; /robots.txt returns 200 Disallow: /. ACME challenges, blocked IPs, suspended hosts, rate limits and the WAF for routed sites are unchanged. HAPROXY_DEFAULT_SPLASH=off restores the old behaviour.
Access log moves into the container: HAProxy logs to a unix socket and a supervised in-container receiver writes a size-capped rotating file (40MB x 4 generations) to a volume; logging can never block HAProxy. The host-side 172.18.0.1:514 target is removed.
Tests: 15 splash tests + 10 receiver tests in the image build; rendered configs validated with haproxy -c; lab proof recorded in the README (Default Splash / Access Log sections).
Deploy: image build by CI, then container-manager.sh recreate haproxy-manager (test host first). WHP side lives on feat/go-edge-access-log (new run args: haproxy-logs volume + net.unix.max_dgram_qlen sysctl).