Skip to content

maxuru ~ % cat haproxy/secure-the-stats-page

Secure the HAProxy stats page

Severity: highApplies to: HAProxy 2.xApplies to: HAProxy 3.x
The fix/etc/haproxy/haproxy.cfg
listen stats
bind 127.0.0.1:8404
mode http
stats enable
stats uri /stats
stats refresh 10s
stats auth admin:CHANGE_THIS_TO_SOMETHING_RANDOM
# deliberately absent: stats admin

Bound to loopback, reached over an SSH tunnel. If it must be reachable remotely, the bind line is the control — not the password.

The stats page is a live HTML dashboard of the proxy. It lists every frontend, every backend, and every server in each backend by name, address and port, together with health-check state, current sessions, error counters and queue depth.

For an attacker who has reached your load balancer and nothing else, that page is a free internal network map: the addresses of your application servers, how many there are, which are unhealthy, and which backend serves what. None of that is secret in the sense of being a credential, and all of it shortens the distance between a foothold and a target.

Worth stating plainly, because it is a point in HAProxy’s favour and the genre tends to imply otherwise. In HAProxy’s own words: “By default, statistics page is read-only for security reasons.”

Turning it into a control surface takes a second, explicit directive:

stats admin if LOCALHOST

stats admin allows enabling and disabling servers from the web interface — that is, taking production servers out of rotation from a browser. It is condition-gated by design (if/unless is required, not optional), which is HAProxy nudging you toward scoping it. If you enable it, scope the condition tightly and understand you have converted a disclosure problem into a control problem.

Why stats auth is not the answer on its own

Section titled “Why stats auth is not the answer on its own”

stats auth adds HTTP Basic authentication. Basic auth sends the password base64-encoded on every request, so over plain HTTP it is transmitted in the clear to anyone on the path. If the stats page is reachable over anything other than loopback or a private network, it needs TLS before the password means anything.

There is also no lockout, no rate limiting and no logging of failures on that form, which makes it a poor thing to leave facing the internet regardless of password strength. Use stats auth as a second layer behind a network restriction, not as the restriction itself.

If it genuinely must be exposed, restrict by source as well:

listen stats
bind 10.0.0.5:8404 ssl crt /etc/haproxy/certs/stats.pem
mode http
acl trusted_net src 10.0.0.0/24
http-request deny unless trusted_net
stats enable
stats uri /stats
stats auth admin:CHANGE_THIS_TO_SOMETHING_RANDOM

Changing stats uri to something unguessable is worth doing, but it is obscurity — it raises the cost of finding the page and does nothing once somebody has.