feat(suspension): opt-in routing for suspended hosts via bk_suspended backend
All checks were successful
HAProxy Manager Build and Push / Build-and-Push (push) Successful in 56s

Adds a new env var HAPROXY_SUSPENSION_BACKEND (default unset). When set
(e.g. "whp-suspended:80"), generate_config() renders:
- A bk_suspended backend pointing at the configured upstream
- An ACL `acl is_suspended_domain hdr(host),lower -f /etc/haproxy/suspended_domains.list`
  + `use_backend bk_suspended if is_suspended_domain` in the frontend,
  sitting after IP-blocking and before any per-domain routing
- An empty /etc/haproxy/suspended_domains.list if missing (haproxy refuses
  to start with -f pointing at a non-existent file)

External tooling (e.g. WHP's site_disable.php) maintains the list via
`docker cp` and HUP-reloads the container.

Non-WHP deployments (home networks, standalone use) leave the env var
unset and see byte-identical haproxy.cfg output. Same opt-in shape as
the existing HAPROXY_CORAZA_SPOE_BACKEND integration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-13 11:46:18 -07:00
parent 3572c66fb7
commit 2ef582a3de
3 changed files with 53 additions and 0 deletions

View File

@@ -53,6 +53,19 @@ frontend web
acl is_blocked_ip var(txn.real_ip),map_ip(/etc/haproxy/blocked_ips.map,0) -m int gt 0
http-request set-path /blocked-ip if is_blocked_ip
use_backend default-backend if is_blocked_ip
{%- if suspension_enabled %}
# Site suspension routing. Any Host header listed in
# /etc/haproxy/suspended_domains.list is routed to bk_suspended (a
# backend serving a static 503 "site unavailable" page). External
# tooling (e.g. WHP's site_disable.php) maintains the list file via
# `docker cp`. An empty list is safe — the ACL simply doesn't match.
# Sits after IP-blocking (so 429/403 still trigger first) and before
# any per-domain use_backend rules, so suspension takes precedence
# over normal site routing.
acl is_suspended_domain hdr(host),lower -f /etc/haproxy/suspended_domains.list
use_backend bk_suspended if is_suspended_domain
{%- endif %}
{%- if coraza_spoe_backend %}
# Coraza WAF inspection via SPOE. Runs AFTER rate-limit and IP-block

View File

@@ -0,0 +1,13 @@
# Suspended-site backend. Used when external tooling adds a host to
# /etc/haproxy/suspended_domains.list (read by an ACL in the frontend).
# The backend points at a single upstream that serves a static 503
# "site temporarily unavailable" page. Only rendered when the
# HAPROXY_SUSPENSION_BACKEND env var is set on the haproxy-manager
# container; non-WHP deployments (home networks, standalone use) see
# no change to haproxy.cfg.
backend bk_suspended
mode http
option http-server-close
http-request set-header X-Forwarded-Proto https if { ssl_fc }
http-request set-header X-Forwarded-For %[src]
server suspended {{ target }} check inter 30s