Expand description
PROXY protocol acceptance, v1 and v2 (DW-030, feature analysis 4.20).
A listener configured with proxy_protocol: true expects a PROXY
protocol header (HAProxy specification; v1 text or v2 binary) as the
FIRST bytes of every connection — BEFORE the TLS handshake when the
listener terminates TLS, because the L4 load balancer in front wraps
the whole stream. The header’s source address then REPLACES the
accepted socket peer for everything downstream that consumes the
peer address: the authz IP ACL’s effective-client-IP base,
rate-limit keying, and the X-Forwarded-For / X-Real-IP values
stamped on the forwarded request (the one peer argument of
crate::dataplane::proxy::handle).
Security posture (frozen):
- Opt-in per listener. A listener without the flag never
interprets first bytes as a PROXY line; there is no sniffing. The
spoofing boundary is exactly the config: an address claim is
honored only on listeners explicitly declared to sit behind a
PROXY-protocol-speaking LB (the same trust model as
gateway.trusted_proxiesfor XFF). - Fail closed. A malformed header (bad signature, bad lengths,
Unix address family, datagram protocol on this stream listener,
trailing garbage) is answered with a
400JSON error envelope and the connection is closed — the bytes are NEVER handed to HTTP parsing (a desynced half-header must not become half a request line). A connection that stalls mid-header or drops is closed without a response (nothing parseable to answer, same as a TLS handshake timeout). - Bounded. The whole header must arrive within the caller’s deadline (the DW-023 slowloris header timeout — the same attack, one layer earlier) and within the protocol’s own size ceiling (107 bytes for v1, 16 + 65535 for v2). A trickle that never completes is cut by the deadline, not buffered forever.
- Spec fallbacks honored. A v2
LOCALcommand (the LB’s own health check, not a proxied client) and a v1UNKNOWNline keep the REAL peer address — the specification’s fallback for connections whose origin the LB declines to assert.
Parsing is delegated to the ppp crate (v2 TLV/bounds safety);
this module owns the async framing read, the deadline, the caps,
and the fail-closed policy. Passthrough listeners cannot combine
with PROXY acceptance (validation rejects it): a passthrough
listener splices raw bytes and never runs the HTTP pipeline that
would consume the address.
Structs§
- Proxy
Header - What the listener learned from one accepted connection’s header.
Enums§
- Proxy
Proto Error - Why a connection was not admitted with a PROXY-protocol-derived
address.
Malformedis answerable (the gateway was given bytes that claimed to be a header and were not); the IO variants are silent closes.
Functions§
- read_
client_ addr - Read one PROXY protocol header (v1 or v2) from the stream and decide the connection’s EFFECTIVE client address.
- reject_
malformed - Answer a malformed-header connection with the
400error envelope and close it (the fail-closed half of the policy: never fall through to HTTP parsing). The envelope is the gateway’s uniform error shape, with a freshly generated correlation ID (no request exists yet to inherit one from).