plugin/proxyproto: expose PPv2 TLVs to downstream plugins as metadata (#8641)

The proxyproto plugin parsed PROXY protocol v2 headers only to recover the real
client address; the TLV vector was discarded, so downstream plugins had no way to
read values such as the AWS VPC endpoint ID, SSL client CN, or vendor-specific
custom TLVs.

Add an opt-in `tlv_metadata` option. When set, the connection's raw PPv2 TLV
vector is captured and the plugin, acting as a metadata.Provider, publishes it
under the `proxyproto/tlv` label as a hex-encoded string. Consumers decode and
split it with proxyproto.SplitTLVs. The option is off by default, so existing
deployments skip the extra per-connection processing and behave exactly as before.
PROXY protocol v1 connections, which carry no TLVs, are unaffected.

Capture spans all transports: dns:// (UDP and TCP), tls:// (DoT), https:// (DoH),
grpc://, quic:// (DoQ), and https3:// (DoH3). QUIC-based transports require
udp_session_tracking, since the PPv2 header arrives as a standalone first datagram
ahead of the QUIC handshake.

Includes unit tests for capture, the metadata provider (direct and through the
real metadata chain), v1 compatibility, and option parsing, plus README and man
page updates.

Fixes #8633

Signed-off-by: Mekias Yohannes <mekias@amazon.com>
Co-authored-by: Mekias Yohannes <mekias@amazon.com>
This commit is contained in:
myohannes
2026-10-15 22:28:43 -04:00
committed by GitHub
parent 21660a66b5
commit 3909cc187c
18 changed files with 1209 additions and 20 deletions

View File

@@ -1,9 +1,9 @@
.\" Generated by Mmark Markdown Processer - mmark.miek.nl
.\" Generated by Mmark Markdown Processor - mmark.miek.nl
.TH "COREDNS-PROXYPROTO" 7 "October 2026" "CoreDNS" "CoreDNS Plugins"
.SH "NAME"
.PP
\fIproxyproto\fP - add PROXY protocol
\fIproxyproto\fP \- add PROXY protocol
\[la]https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt\[ra] support.
.SH "DESCRIPTION"
@@ -21,6 +21,7 @@ proxyproto {
allow <CIDR...>
default <use|ignore|reject|skip>
udp\_session\_tracking <duration> [max\_sessions]
tlv\_metadata
}
.fi
@@ -47,12 +48,42 @@ The \fB\fCudp_session_tracking <duration> [max_sessions]\fR option enables UDP s
for Cloudflare Spectrum's PROXY Protocol v2 over UDP. Spectrum sends the PPv2 header as a
standalone first datagram (with no DNS payload). Subsequent datagrams from the same client arrive
without any header. When this option is set to a positive duration, the real client address from
the header-only datagram is cached (keyed by the Spectrum-side remote address) for that duration
the header\-only datagram is cached (keyed by the Spectrum\-side remote address) for that duration
and automatically applied to all subsequent headerless datagrams within that window. The TTL is
refreshed on each matching packet. The optional \fB\fCmax_sessions\fR argument caps the number of
concurrent sessions in the LRU cache (default: 10240). This option has no effect for TCP
connections.
.PP
The \fB\fCtlv_metadata\fR option exposes the PROXY Protocol v2 TLV
\[la]https://www.haproxy.org/download/2.3/doc/proxy-protocol.txt\[ra]
(Type\-Length\-Value) vector from the header to downstream plugins via the
\fImetadata\fP
\[la]../metadata/README.md\[ra] plugin. When set, and the \fImetadata\fP plugin is enabled, the
raw TLV vector is published under the label \fB\fCproxyproto/tlv\fR as a hex\-encoded string. Consumers
decode the hex and split it with \fB\fCproxyproto.SplitTLVs\fR from
\fB\fCgithub.com/pires/go-proxyproto\fR to read individual TLVs (e.g. the AWS VPC endpoint ID, SSL
client CN, or vendor\-specific custom TLVs). This option is opt\-in: without it, TLVs are not
captured and no extra per\-connection processing occurs. It applies to all transports: \fB\fCdns://\fR
(UDP and TCP), \fB\fCtls://\fR (DoT), \fB\fChttps://\fR (DoH), \fB\fCgrpc://\fR, \fB\fCquic://\fR (DoQ), and \fB\fChttps3://\fR
(DoH3).
.PP
For the QUIC\-based transports (\fB\fCquic://\fR and \fB\fChttps3://\fR), the PPv2 header arrives as a
standalone first datagram ahead of the QUIC handshake, so TLV capture additionally requires
\fB\fCudp_session_tracking\fR to be enabled; the cached session links the subsequent headerless QUIC
datagrams to the header's source and TLVs.
.SH "METADATA"
.PP
When the \fB\fCtlv_metadata\fR option and the \fImetadata\fP plugin are both enabled, the following metadata
label is made available:
.IP \(bu 4
\fB\fCproxyproto/tlv\fR: the raw PPv2 TLV vector, hex\-encoded. Only set when the request's connection
carried a PPv2 header with at least one TLV.
.SH "EXAMPLES"
.PP
In this configuration, we allow PROXY protocol connections from all IP addresses:
@@ -107,8 +138,8 @@ connections without valid PROXY protocol headers from those sources:
.RE
.PP
In this configuration, we enable UDP session tracking for Cloudflare Spectrum's PPv2-over-UDP
with a 28-second TTL (slightly shorter than Spectrum's 30-second UDP idle timeout) and the
In this configuration, we enable UDP session tracking for Cloudflare Spectrum's PPv2\-over\-UDP
with a 28\-second TTL (slightly shorter than Spectrum's 30\-second UDP idle timeout) and the
default session cap of 10240:
.PP
@@ -144,3 +175,24 @@ In this configuration, the session cap is raised to 20480:
.fi
.RE
.PP
In this configuration, PPv2 TLVs are exposed to downstream plugins as metadata. The \fImetadata\fP
plugin must also be enabled. Here the \fIlog\fP plugin records the raw TLV vector:
.PP
.RS
.nf
\&. {
metadata
proxyproto {
allow 192.168.1.1/32
tlv\_metadata
}
forward . /etc/resolv.conf
log . "{remote} {/proxyproto/tlv}"
}
.fi
.RE