Index | Thread | Search

From:
Kirill A. Korinsky <kirill@korins.ky>
Subject:
Re: relayd: receive the PROXY protocol (v1 and v2) on listeners
To:
Romain FABBRI <romain.fabbri@alienconsulting.net>
Cc:
"tech@openbsd.org" <tech@openbsd.org>
Date:
Tue, 14 Jul 2026 01:27:06 +0200

Download raw body.

Thread
On Mon, 13 Jul 2026 19:16:44 +0200,
Romain FABBRI <romain.fabbri@alienconsulting.net> wrote:
> 
> Hi,
> 
> I am not subscribed to tech@, so please keep me in Cc on any
> replies.
> 
> relayd(8) can already PREPEND a PROXY protocol header towards a
> backend (proxy_protocol.c, "forward to ... proxy-protocol v1|v2").
> 
> The attached diff adds the symmetric direction: a listener can be
> told to EXPECT a PROXY header from the immediately upstream proxy and
> adopt the advertised client address.
> 
> Motivation:
> - When relayd sits behind a TCP SNI demultiplexer that forwards the
>   raw stream to a local relayd for TLS termination, every session
>   appears to come from the loopback address.
> - The real client address is then lost for the REMOTE_ADDR macro /
>   X-Forwarded-For, for source-hash stickiness, and for logging.
> - The PROXY protocol is the standard remedy; relayd could emit it but
>   not consume it.
> 
> Patch design:
> - one new per-listener field, relay_config.proxyproto (the F_* flag
>   word is full, and a receive flag is orthogonal to the emit flags).
> - the header is read before the TLS handshake via MSG_PEEK, then
>   exactly its bytes are consumed with recv(2), leaving the following
>   ClientHello untouched.
> - a partial header reschedules on EV_READ under the existing session
>   timeout (no userland reassembly).
> - the version is auto-detected; a LOCAL/UNSPEC header keeps the
>   accept(2) peer.
> - refused together with a transparent forward (FWD_TRANS), otherwise
>   the advertised address would become a bindany source (spoofing).
> - as with any PROXY receiver, the listener must only be reachable
>   from a trusted upstream (documented in relayd.conf(5)).
> 
> Configuration:
>   listen on <addr> [tls] port <port> [proxy-protocol]
> 
> Testing (diff is against -current; built and run on both 7.9-release,
> and -current, can send 7.9 patch if usefull to anyone):
> - builds with no new warnings.
> - "relayd -n" accepts the new keyword and rejects the FWD_TRANS
>   combination , on both.
> - a small unit harness around the parser (v1, v2, LOCAL, malformed
>   input, exact header consumption) passes.
> - verified end-to-end with sniproxy on 7.9 prepending a v2 header in front
>   of a TLS-terminating relayd: the backend sees the real client
>   address in X-Forwarded-For instead of the loopback address.
> 
> Feedback welcome, in particular on the naming and on whether a bare
> "proxy-protocol" (auto-detected) reads well next to the versioned
> emit directive.
> 
> Regards,
> Romain
> [2 relayd-proxyproto.diff <application/octet-stream (base64)>]

Thanks for a patch.

May I ask you to inline it or attach as 7bit plain/text? It will make review
by quoting the patch simpler.

Neverless here first points after fast reading of your diff.

1. I think that multiple listen on directives silently inherit the first
   listener's proxy setting.

2. Seems that fragmented v2 message will be treated as v1.

3. Not fully sure, but looks like that an IPv6 client advertised over an
   IPv4 proxy connection produces an invalid mixed family v1 header and a
   corrupted v2 source.

4. I think we need regression tests for this.

-- 
wbr, Kirill