PROXY Protocol

When Ghostunnel terminates TLS, the backend only sees a plaintext connection from Ghostunnel itself. It has no idea who the original client was, what TLS version was negotiated, or whether a client certificate was presented. The PROXY protocol fixes this: Ghostunnel prepends a small binary header to each forwarded connection carrying the original client metadata. Backends can then do logging, access control, or auditing based on client identity without needing their own TLS stack.

Enabling

See Command-Line Flags for the full flag reference.

Pass --proxy-protocol in server mode to enable PROXY protocol v2 with connection info (source/destination IP and port):

ghostunnel server \
  --listen=:8443 \
  --target=localhost:8080 \
  --keystore=server.p12 \
  --cacert=ca.crt \
  --allow-ou=my-service \
  --proxy-protocol

To also include TLS metadata and/or client certificate details, use the --proxy-protocol-mode flag (available since v1.10.0):

ModeWhat is sent
connConnection info only (src/dst IP+port). Same as bare --proxy-protocol.
tlsConnection info + TLS metadata (version, ALPN, SNI). No client cert details.
tls-fullConnection info + TLS metadata + full client certificate details.
# TLS metadata without client cert:
ghostunnel server ... --proxy-protocol-mode=tls

# Everything, including client cert:
ghostunnel server ... --proxy-protocol-mode=tls-full

Using --proxy-protocol-mode implies --proxy-protocol; you do not need to pass both.

The backend will receive a PROXY protocol v2 binary header on each new connection, followed by the normal application data stream.

What Ghostunnel Sends

Ghostunnel sends a version 2 (binary format) header with the PROXY command. The address family (IPv4 or IPv6) is detected from the incoming connection.

Address Fields (All Modes)

FieldValue
Source address/portOriginal client IP and port
Destination address/portGhostunnel’s listen IP and port

TLV Extensions (tls and tls-full Modes)

When using --proxy-protocol-mode=tls or --proxy-protocol-mode=tls-full, Ghostunnel includes TLV (Type-Length-Value) extensions with TLS connection metadata:

TLVTypeDescription
PP2_TYPE_SSL0x20Container for SSL/TLS metadata (see below)
PP2_TYPE_AUTHORITY0x02SNI hostname the client requested (if set)
PP2_TYPE_ALPN0x01Negotiated ALPN protocol, e.g. h2 (if set)

SSL Sub-TLVs

The PP2_TYPE_SSL TLV contains a 5-byte sub-header followed by nested sub-TLVs:

Sub-header:

FieldSizeDescription
Client flags1 byteBitfield: 0x01 = SSL used, 0x02 = client cert on connection, 0x04 = client cert on session
Verify result4 bytes0 = certificate verified successfully

Nested sub-TLVs (always present in tls and tls-full modes):

Sub-TLVTypeExample value
PP2_SUBTYPE_SSL_VERSION0x21TLS 1.3

Nested sub-TLVs (tls-full mode only, when a client certificate was provided):

Sub-TLVTypeDescription
PP2_SUBTYPE_SSL_CN0x22Client certificate Common Name
PP2_SUBTYPE_SSL_CLIENT_CERT0x28Full client certificate in DER (ASN.1) encoding

The tls-full mode is useful when backends need to perform their own access control or auditing based on client certificate identity. See Access Control Flags for how Ghostunnel itself verifies client certificates before forwarding.

Note: PP2_SUBTYPE_SSL_CLIENT_CERT (0x28) is not part of the original HAProxy spec but is supported by the go-proxyproto library and others. The spec requires receivers to ignore unknown TLV types, so this is safe.

Backend Requirements

Your backend must be configured to expect PROXY protocol headers. It needs to parse the binary header before reading application data. Most servers and frameworks support this:

  • nginx: proxy_protocol parameter on listen directive
  • Apache: mod_remoteip with RemoteIPProxyProtocol
  • HAProxy: accept-proxy on bind lines
  • Custom apps: use a PROXY protocol parsing library for your language

Backends that aren’t expecting PROXY protocol will see the binary header as garbage at the start of the stream and will reject the connection.

References