Skip to content

maxuru ~ % cat haproxy/protect-the-runtime-api

Protect the HAProxy runtime API socket

Severity: criticalApplies to: HAProxy 2.xApplies to: HAProxy 3.x
The fix/etc/haproxy/haproxy.cfg
global
stats socket /run/haproxy/admin.sock mode 660 level operator
stats timeout 30s
user haproxy
group haproxy

Note level operator, not level admin — and note that the socket is a filesystem path, not an address and port.

“Stats socket” undersells it. It is HAProxy’s runtime API: a live control channel into the running process. Depending on level, a client connected to it can read every counter and, at higher levels, change how traffic is routed right now — without touching the configuration file and without a reload.

The commands that matter are the ones that alter behaviour: taking servers in and out of rotation, changing weights, and editing the contents of maps and ACLs that your routing rules consult. Anyone who can talk to an admin-level socket can quietly point a backend somewhere else.

The level parameter is what decides how much of that is available. HAProxy’s own documentation defines them:

Level What it permits
user Least privileged. Only non-sensitive stats can be read; no change is allowed.
operator The default. All data can be read; only non-sensitive changes are permitted.
admin Everything is permitted.

Two things follow from that table.

First, the default is operator, not admin — HAProxy did not ship you the worst case. A great many tutorials nonetheless write level admin into the example config, because at some point the author hit a command that operator refused and reached for the biggest hammer. If your config says level admin, the question worth asking is which command needed it.

Second, even user — the most restricted level — is not nothing, and operator reads all data. That includes backend server addresses and per-server health and traffic counters: a map of your internal topology. The socket is not a public surface at any level.

HAProxy will bind the runtime API to a TCP address if you ask it to. Almost nobody should.

A Unix socket is protected by filesystem permissions — mode 660 plus an owner and group means the check is done by the kernel against the connecting process, and it cannot be reached from another host at all. A TCP socket replaces that with whatever your firewall happens to be doing, on a channel that has no authentication of its own. There is no password on the runtime API. Reachability is the entire access control model.

If you need remote access — for a metrics collector or an automation tool — put it behind something that does authenticate: an SSH session, or a local agent that reads the socket and exposes only what it should.