Index | Thread | Search

From:
Christoph Liebender <christoph@liebender.dev>
Subject:
Re: relayd: add support for PROXY protocol in TCP relays
To:
tech@openbsd.org
Cc:
reyk@openbsd.org
Date:
Sat, 8 Nov 2025 22:00:21 +0100

Download raw body.

Thread
On 11/8/25 20:20, Christoph Liebender wrote:
> Hi tech@,
> 
> I wrote a patch to add support for the PROXY protocol (version 1) [1] to 
> relayd(8). Works in my usecase where I have a host in a DMZ where hosts 
> outside of the DMZ connect via a router that NATs their IP into the DMZs 
> subnet.
> 
> Essentially, what I am trying to mimic is the behavior of the 
> proxy_protocol directive of nginx's stream proxy module [2].
> 
> I'm more than happy to hear any feedback or comments you have for me :)
> 
> [1] https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
> [2] https://nginx.org/en/docs/stream/ 
> ngx_stream_proxy_module.html#proxy_protocol
> 
> PS: I appended my name and email to the copyright notices - also let me 
> know if that is correct!

Whoops! Updated patch with bugfix is attached.

comments, ok?
diff --git a/usr.sbin/relayd/parse.y b/usr.sbin/relayd/parse.y
index fcdfb8e92e3..0d78cb72123 100644
--- a/usr.sbin/relayd/parse.y
+++ b/usr.sbin/relayd/parse.y
@@ -1,6 +1,7 @@
 /*	$OpenBSD: parse.y,v 1.258 2024/10/28 19:56:18 tb Exp $	*/
 
 /*
+ * Copyright (c) 2025 Christoph Liebender <christoph@liebender.dev>
  * Copyright (c) 2007 - 2014 Reyk Floeter <reyk@openbsd.org>
  * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
  * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -179,13 +180,13 @@ typedef struct {
 %token	TIMEOUT TLS TO ROUTER RTLABEL TRANSPARENT URL WITH TTL RTABLE
 %token	MATCH PARAMS RANDOM LEASTSTATES SRCHASH KEY CERTIFICATE PASSWORD ECDHE
 %token	EDH TICKETS CONNECTION CONNECTIONS CONTEXT ERRORS STATE CHANGES CHECKS
-%token	WEBSOCKETS PFLOG CLIENT
+%token	WEBSOCKETS PFLOG CLIENT PROXYPROTO
 %token	<v.string>	STRING
 %token  <v.number>	NUMBER
 %type	<v.string>	context hostname interface table value path
 %type	<v.number>	http_type loglevel quick
 %type	<v.number>	dstmode flag forwardmode retry
-%type	<v.number>	opttls opttlsclient
+%type	<v.number>	opttls opttlsclient optproxyproto
 %type	<v.number>	redirect_proto relay_proto match pflog
 %type	<v.number>	action ruleaf key_option
 %type	<v.port>	port
@@ -1103,6 +1104,10 @@ proto		: relay_proto PROTO STRING	{
 		}
 		;
 
+optproxyproto	: /*empty*/	{ $$ = 0; }
+		| PROXYPROTO	{ $$ = 1; }
+		;
+
 protopts_n	: /* empty */
 		| '{' '}'
 		| '{' optnl protopts_l '}'
@@ -1946,7 +1951,7 @@ relayoptsl	: LISTEN ON STRING port opttls {
 			tableport = h->port.val[0];
 			host_free(&al);
 		}
-		| forwardmode opttlsclient TO forwardspec dstaf {
+		| forwardmode opttlsclient TO forwardspec dstaf optproxyproto {
 			rlay->rl_conf.fwdmode = $1;
 			if ($1 == FWD_ROUTE) {
 				yyerror("no route for relays");
@@ -1956,6 +1961,9 @@ relayoptsl	: LISTEN ON STRING port opttls {
 				rlay->rl_conf.flags |= F_TLSCLIENT;
 				conf->sc_conf.flags |= F_TLSCLIENT;
 			}
+			if ($6) {
+				rlay->rl_conf.flags |= F_PROXYPROTO;
+			}
 		}
 		| SESSION TIMEOUT NUMBER		{
 			if ((rlay->rl_conf.timeout.tv_sec = $3) < 0) {
@@ -2479,6 +2487,7 @@ lookup(char *s)
 		{ "prefork",		PREFORK },
 		{ "priority",		PRIORITY },
 		{ "protocol",		PROTO },
+		{ "proxy-protocol",	PROXYPROTO },
 		{ "query",		QUERYSTR },
 		{ "quick",		QUICK },
 		{ "random",		RANDOM },
diff --git a/usr.sbin/relayd/relay.c b/usr.sbin/relayd/relay.c
index 6d0970802c5..6e414eafb77 100644
--- a/usr.sbin/relayd/relay.c
+++ b/usr.sbin/relayd/relay.c
@@ -1,6 +1,7 @@
 /*	$OpenBSD: relay.c,v 1.260 2024/10/28 19:56:18 tb Exp $	*/
 
 /*
+ * Copyright (c) 2025 Christoph Liebender <christoph@liebender.dev>
  * Copyright (c) 2006 - 2014 Reyk Floeter <reyk@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
@@ -682,7 +683,7 @@ relay_socket_listen(struct sockaddr_storage *ss, in_port_t port,
 void
 relay_connected(int fd, short sig, void *arg)
 {
-	char			 obuf[128];
+	char			 ibuf[128], obuf[128];
 	struct rsession		*con = arg;
 	struct relay		*rlay = con->se_relay;
 	struct protocol		*proto = rlay->rl_proto;
@@ -690,6 +691,7 @@ relay_connected(int fd, short sig, void *arg)
 	evbuffercb		 outwr = relay_write;
 	struct bufferevent	*bev;
 	struct ctl_relay_event	*out = &con->se_out;
+	const char		*proxyproto;
 	char			*msg;
 	socklen_t		 len;
 	int			 error;
@@ -767,6 +769,31 @@ relay_connected(int fd, short sig, void *arg)
 		    "failed to allocate output buffer event", 0);
 		return;
 	}
+
+	if ((rlay->rl_conf.flags & F_PROXYPROTO) &&
+	    rlay->rl_proto->type == RELAY_PROTO_TCP) {
+		memset(&ibuf, 0, sizeof(ibuf));
+		memset(&obuf, 0, sizeof(obuf));
+		print_host(&con->se_in.ss, ibuf, sizeof(ibuf));
+		print_host(&rlay->rl_conf.ss, obuf, sizeof(obuf));
+
+		switch (con->se_in.ss.ss_family) {
+		case AF_INET:
+			proxyproto = "TCP4";
+			break;
+		case AF_INET6:
+			proxyproto = "TCP6";
+			break;
+		default:
+			proxyproto = "UNKNOWN";
+			break;
+		}
+
+		evbuffer_add_printf(bev->output,
+			"PROXY %s %s %s %d %d\r\n", proxyproto, ibuf, obuf,
+			ntohs(con->se_in.port), ntohs(rlay->rl_conf.port));
+	}
+
 	/* write pending output buffer now */
 	if (bufferevent_write_buffer(bev, con->se_out.output)) {
 		relay_abort_http(con, 500, strerror(errno), 0);
diff --git a/usr.sbin/relayd/relayd.conf.5 b/usr.sbin/relayd/relayd.conf.5
index 29a13efbe8a..97f604bd0cb 100644
--- a/usr.sbin/relayd/relayd.conf.5
+++ b/usr.sbin/relayd/relayd.conf.5
@@ -1,5 +1,6 @@
 .\"	$OpenBSD: relayd.conf.5,v 1.213 2025/07/08 14:26:45 schwarze Exp $
 .\"
+.\" Copyright (c) 2025 Christoph Liebender <christoph@liebender.dev>
 .\" Copyright (c) 2006 - 2016 Reyk Floeter <reyk@openbsd.org>
 .\" Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
 .\"
@@ -673,6 +674,13 @@ option will be used as a tolerance for failed
 host connections; the connection will be retried for
 .Ar number
 more times.
+.It Ic proxy-protocol
+Upon connection to the destination,
+.Xr relayd 8
+will prepend a PROXY protocol header of version 1 to the relayed data,
+with the IP address and port of the remote host as well as the
+.Xr relayd 8
+server.
 .El
 .It Xo
 .Ic forward to
diff --git a/usr.sbin/relayd/relayd.h b/usr.sbin/relayd/relayd.h
index 3b5c3987f93..4c56895f395 100644
--- a/usr.sbin/relayd/relayd.h
+++ b/usr.sbin/relayd/relayd.h
@@ -1,6 +1,7 @@
 /*	$OpenBSD: relayd.h,v 1.276 2024/10/28 19:56:18 tb Exp $	*/
 
 /*
+ * Copyright (c) 2025 Christoph Liebender <christoph@liebender.dev>
  * Copyright (c) 2006 - 2016 Reyk Floeter <reyk@openbsd.org>
  * Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -402,6 +403,7 @@ union hashkey {
 #define F_HASHKEY		0x08000000
 #define F_AGENTX_TRAPONLY	0x10000000
 #define F_PFLOG			0x20000000
+#define F_PROXYPROTO		0x40000000
 
 #define F_BITS								\
 	"\10\01DISABLE\02BACKUP\03USED\04DOWN\05ADD\06DEL\07CHANGED"	\