Using Proxyprotocol with sslh
HAProxy defined the Proxy protocol, where a proxy adds external connection information directly inside the TCP connection, for the backend server to get. This achieves the same goal as transparent proxying, while requiring no networking knowledge and no administration rights.
Instead, the protocol needs to be supported in the servers. Many do,
e.g. Apache (with the RemoteIPProxyProtocol
setting).
sslh
supports this protocol both on the client and the server side.
Proxyprotocol on the server side
Presumably that’s the most common setup, with sslh
being the
client-facing proxy in front of the backend server. Support is enabled
by adding the proxyprotocol: [1|2]
setting in the target protocol:
listen:
(
{ host: "localhost"; port: "443"; }
);
protocols:
(
{ name: "ssh"; host: "localhost"; port: "2222"; },
{ name: "tls"; host: "localhost"; port: "8080"; proxyprotocol: 2; }
);
);
Here sslh
listens on port 443. It forwards ssh
connections to port
2222 without touching them (which means sshd does not see the external
data) (Apparently openssh does not as of september 2025 support
Proxyprotocol). It forwards tls
to port 8080, adding a proxy-protocol
v2 header which allows Apache (configured with RemoteIPProxyProtocol) to
log the external IP and port.
203.0.113.4:443 :2222
Client -----------------> sslh -------------> sshd
198.51.100.42:2342 | logs: 127.0.0.1:<local port>
|
| :8080
\--pp v2-------> apache
logs: 198.51.100.42:2342
The proxyprotocol
setting can be set to 1 or 2, which selects the
appropriate protocol version. Version 2 being more efficient, this is
what you should use unless you have good reasons to use version 1
(e.g. your backend server only supports v1, an unlikely scenario).
Proxyprotocol on the client side
sslh
also supports Proxy-protocol on incoming connections. These are
so-called “client side”, but you should not enable proxy-protocol on an
Internet-facing server. Instead, this can be useful if sslh
is located
behind another proxy that adds proxy-protocol headers, e.g. HAProxy.
This is enabled by adding proxyprotocol: true
to a listen
statement:
listen:
(
{ host: "localhost"; port: "443"; proxyprotocol: true; }
);
protocols:
(
{ name: "ssh"; host: "localhost"; port: "2222"; proxyprotocol: 0; },
{ name: "tls"; host: "localhost"; port: "8080"; }
);
);
In that case, sslh
expects to find a proxyprotocol header on all
incoming connections. It ignores the header to perform its usual
protocol detection.
If the target protocol specified nothing, the connection is passed untouched, i.e. the proxyprotocol header is forwarded as usual: here, Apache on 8080 will receive the proxyprotocol header.
If the target protocol specifies proxyprotocol: 0
, then sslh
removes
the proxyprotocol header, and logs an additional message with the
connection information extracted from the header. Here, sslh
logs the
external information for ssh connections, removes the header, and
forwards the connection to sshd. sshd
does not support proxyprotocol,
and logs a connection coming from the local IP address.
203.0.113.4:443 :2222
Client ---------> HaProxy ----pp v2---------> sslh -------------> sshd
198.51.100.42:2342 | logs: 127.0.0.1:<local port>
| sslh logs: 198.51.100.42:2342 to localhost:2222
|
| :8080
\--pp v2-------> apache
logs: 198.51.100.42:2342
sslh logs: local connection
sslh
always logs the ‘real’ network information, here the local
addresses, and only logs the proxyprotocol information when it removes
the header. It is not necessary to log the proxyprotocol information
when the target server will receive it (as it is then the target
server’s job to log the external IP address).