Documentation
¶
Overview ¶
Package proxyproto implements Proxy Protocol (v1 and v2) parser and writer, as per specification: https://www.haproxy.org/download/3.4/doc/proxy-protocol.txt
Index ¶
- Variables
- func JoinTLVs(tlvs []TLV) ([]byte, error)
- func SetReadHeaderTimeout(t time.Duration) func(*Conn)
- func ValidateHeader(v Validator) func(*Conn)
- func WithBufferSize(length int) func(*Conn)
- func WithPolicy(p Policy) func(*Conn)
- type AddressFamilyAndProtocol
- func (ap AddressFamilyAndProtocol) IsDatagram() bool
- func (ap AddressFamilyAndProtocol) IsIPv4() bool
- func (ap AddressFamilyAndProtocol) IsIPv6() bool
- func (ap AddressFamilyAndProtocol) IsStream() bool
- func (ap AddressFamilyAndProtocol) IsUnix() bool
- func (ap AddressFamilyAndProtocol) IsUnspec() bool
- type Conn
- func (p *Conn) Close() error
- func (p *Conn) LocalAddr() net.Addr
- func (p *Conn) ProxyHeader() *Header
- func (p *Conn) Raw() net.Conn
- func (p *Conn) Read(b []byte) (int, error)
- func (p *Conn) ReadFrom(r io.Reader) (int64, error)
- func (p *Conn) RemoteAddr() net.Addr
- func (p *Conn) SetDeadline(t time.Time) error
- func (p *Conn) SetReadDeadline(t time.Time) error
- func (p *Conn) SetWriteDeadline(t time.Time) error
- func (p *Conn) TCPConn() (conn *net.TCPConn, ok bool)
- func (p *Conn) UDPConn() (conn *net.UDPConn, ok bool)
- func (p *Conn) UnixConn() (conn *net.UnixConn, ok bool)
- func (p *Conn) Write(b []byte) (int, error)
- func (p *Conn) WriteTo(w io.Writer) (int64, error)
- type ConnPolicyFunc
- func ConnLaxWhiteListPolicy(allowed []string) (ConnPolicyFunc, error)deprecated
- func ConnMustLaxWhiteListPolicy(allowed []string) ConnPolicyFuncdeprecated
- func ConnMustStrictWhiteListPolicy(allowed []string) ConnPolicyFuncdeprecated
- func ConnSkipProxyHeaderForCIDR(skipHeaderCIDR *net.IPNet, def Policy) ConnPolicyFunc
- func ConnStrictWhiteListPolicy(allowed []string) (ConnPolicyFunc, error)deprecated
- func IgnoreProxyHeaderNotOnInterface(allowedIP net.IP) ConnPolicyFunc
- func MustPolicyFromRanges(ranges []string, matched, unmatched Policy) ConnPolicyFunc
- func PolicyFromRanges(ranges []string, matched, unmatched Policy) (ConnPolicyFunc, error)
- func TrustProxyHeaderFrom(trustedIPs ...net.IP) ConnPolicyFunc
- func TrustProxyHeaderFromRanges(trusted []string) (ConnPolicyFunc, error)
- type ConnPolicyOptions
- type Header
- func HeaderProxyFromAddrs(version byte, sourceAddr, destAddr net.Addr) *Header
- func ParseUDPDatagram(datagram []byte) (*Header, []byte, error)
- func Read(reader *bufio.Reader) (*Header, error)
- func ReadHeaderTimeout(conn net.Conn, reader *bufio.Reader, timeout time.Duration) (*Header, error)
- func ReadTimeout(reader *bufio.Reader, timeout time.Duration) (*Header, error)deprecated
- func (header *Header) EqualTo(otherHeader *Header) bool
- func (header *Header) EqualsTo(otherHeader *Header) bool
- func (header *Header) Format() ([]byte, error)
- func (header *Header) FormatUDPDatagram(payload []byte) ([]byte, error)
- func (header *Header) IPs() (sourceIP, destIP net.IP, ok bool)
- func (header *Header) Ports() (sourcePort, destPort int, ok bool)
- func (header *Header) SetTLVs(tlvs []TLV) error
- func (header *Header) TCPAddrs() (sourceAddr, destAddr *net.TCPAddr, ok bool)
- func (header *Header) TLVs() ([]TLV, error)
- func (header *Header) UDPAddrs() (sourceAddr, destAddr *net.UDPAddr, ok bool)
- func (header *Header) UnixAddrs() (sourceAddr, destAddr *net.UnixAddr, ok bool)
- func (header *Header) WriteTo(w io.Writer) (int64, error)
- type Listener
- type PP2Type
- type Policy
- type PolicyFuncdeprecated
- func LaxWhiteListPolicy(allowed []string) (PolicyFunc, error)deprecated
- func MustLaxWhiteListPolicy(allowed []string) PolicyFuncdeprecated
- func MustStrictWhiteListPolicy(allowed []string) PolicyFuncdeprecated
- func SkipProxyHeaderForCIDR(skipHeaderCIDR *net.IPNet, def Policy) PolicyFuncdeprecated
- func StrictWhiteListPolicy(allowed []string) (PolicyFunc, error)deprecated
- type ProtocolVersionAndCommand
- type TLV
- type Validator
Examples ¶
- Header.FormatUDPDatagram
- Listener (Default)
- Listener (PolicyRequire)
- Listener (ReadBufferSize)
- Listener (ReadHeaderTimeout)
- Listener (Tls)
- Listener (TlsHeaderInsideTLS)
- Listener (ValidateHeader)
- NewConn (Combined)
- NewConn (Default)
- NewConn (DisableReadHeaderTimeout)
- NewConn (WithBufferSize)
- NewConn (WithPolicy)
- NewConn (WithReadHeaderTimeout)
- ParseUDPDatagram
- ReadHeaderTimeout
Constants ¶
This section is empty.
Variables ¶
var ( // SIGV1 is the signature for PROXY protocol v1. SIGV1 = []byte{'\x50', '\x52', '\x4F', '\x58', '\x59'} // SIGV2 is the signature for PROXY protocol v2. SIGV2 = []byte{'\x0D', '\x0A', '\x0D', '\x0A', '\x00', '\x0D', '\x0A', '\x51', '\x55', '\x49', '\x54', '\x0A'} // ErrCantReadVersion1Header indicates a v1 header could not be read. ErrCantReadVersion1Header = errors.New("proxyproto: can't read version 1 header") // ErrVersion1HeaderTooLong indicates a v1 header is too long. ErrVersion1HeaderTooLong = errors.New("proxyproto: version 1 header must be 107 bytes or less") // ErrLineMustEndWithCrlf indicates a v1 header is invalid, must end with \r\n. ErrLineMustEndWithCrlf = errors.New("proxyproto: version 1 header is invalid, must end with \\r\\n") // ErrCantReadProtocolVersionAndCommand indicates a protocol version and command could not be read. ErrCantReadProtocolVersionAndCommand = errors.New("proxyproto: can't read proxy protocol version and command") // ErrCantReadAddressFamilyAndProtocol indicates an address family and protocol could not be read. ErrCantReadAddressFamilyAndProtocol = errors.New("proxyproto: can't read address family or protocol") // ErrCantReadLength indicates a length could not be read. ErrCantReadLength = errors.New("proxyproto: can't read length") // ErrCantResolveSourceUnixAddress indicates a source Unix address could not be resolved. ErrCantResolveSourceUnixAddress = errors.New("proxyproto: can't resolve source Unix address") // ErrCantResolveDestinationUnixAddress indicates a destination Unix address could not be resolved. ErrCantResolveDestinationUnixAddress = errors.New("proxyproto: can't resolve destination Unix address") // ErrNoProxyProtocol indicates a proxy protocol signature is not present. ErrNoProxyProtocol = errors.New("proxyproto: proxy protocol signature not present") // ErrUnknownProxyProtocolVersion indicates an unknown proxy protocol version. ErrUnknownProxyProtocolVersion = errors.New("proxyproto: unknown proxy protocol version") // ErrUnsupportedProtocolVersionAndCommand indicates an unsupported protocol version and command. ErrUnsupportedProtocolVersionAndCommand = errors.New("proxyproto: unsupported proxy protocol version and command") // ErrUnsupportedAddressFamilyAndProtocol indicates an unsupported address family and protocol. ErrUnsupportedAddressFamilyAndProtocol = errors.New("proxyproto: unsupported address family and protocol") // ErrInvalidLength indicates an invalid length. ErrInvalidLength = errors.New("proxyproto: invalid length") // ErrInvalidAddress indicates an invalid address. ErrInvalidAddress = errors.New("proxyproto: invalid address") // ErrInvalidPortNumber indicates an invalid port number. ErrInvalidPortNumber = errors.New("proxyproto: invalid port number") // ErrSuperfluousProxyHeader indicates an upstream connection sent a PROXY header but isn't allowed to send one. ErrSuperfluousProxyHeader = errors.New("proxyproto: upstream connection sent PROXY header but isn't allowed to send one") )
var ( // DefaultReadHeaderTimeout is how long header processing waits for header to // be read from the wire, if Listener.ReaderHeaderTimeout is not set. // It's kept as a global variable so to make it easier to find and override, // e.g. go build -ldflags -X "github.com/pires/go-proxyproto.DefaultReadHeaderTimeout=1s". DefaultReadHeaderTimeout = 10 * time.Second // ErrInvalidUpstream should be returned (possibly wrapped) by a policy // function when an upstream connection address is not trusted or cannot be // classified. Listener.Accept closes that connection and keeps listening; // a policy error that does not wrap ErrInvalidUpstream is returned by // Accept itself, which typically stops the caller's accept loop. All // built-in policies wrap address-classification failures in // ErrInvalidUpstream so a single unclassifiable peer cannot stop the // listener. ErrInvalidUpstream = fmt.Errorf("proxyproto: upstream connection address not trusted for PROXY information") )
var ( // ErrTruncatedTLV indicates a TLV was truncated. ErrTruncatedTLV = errors.New("proxyproto: truncated TLV") // ErrMalformedTLV indicates a TLV has malformed data. ErrMalformedTLV = errors.New("proxyproto: malformed TLV Value") // ErrIncompatibleTLV indicates a TLV is of an unexpected type. ErrIncompatibleTLV = errors.New("proxyproto: incompatible TLV type") )
var DefaultPolicy = REQUIRE
DefaultPolicy is the policy applied when none is configured explicitly: by NewConn when no WithPolicy option is given, and by Listener.Accept when neither Policy nor ConnPolicy is set.
It defaults to REQUIRE, per the spec's mandate that a receiver "MUST not try to guess whether the protocol header is present or not": a connection that does not open with a PROXY header fails its first Read/Write with ErrNoProxyProtocol. Note REQUIRE alone still honors headers from any peer; restricting who may send one needs a policy such as TrustProxyHeaderFrom or TrustProxyHeaderFromRanges. (The deprecated *WhiteListPolicy helpers are NOT equivalent: they return USE for allowed peers, which replaces this default and makes the header optional again — that is why they were deprecated in favor of the explicit PolicyFromRanges.)
Deployments that relied on the historical optional-header behavior can restore it process-wide with:
proxyproto.DefaultPolicy = proxyproto.USE
Like DefaultReadHeaderTimeout, this is a package-level variable to keep it easy to override. Set it at program init; it must not be modified concurrently with accepting connections.
var MaxV2HeaderSize uint16 = 4096
MaxV2HeaderSize is the maximum accepted value of a v2 header's 16-bit length field, i.e. the number of bytes following the fixed 16-byte prefix (address block plus TLVs).
The spec allows up to 65535, but parseVersion2 allocates this many bytes before reading, so a lower limit mitigates memory-allocation DoS from untrusted peers while allowing real-world legitimate headers. PP2_SUBTYPE_SSL_CLIENT_CERT (a DER-encoded certificate) is typically between 1 and 2KiB, so the 4KiB default leaves room for other TLVs; deployments expecting larger headers may raise it.
Like DefaultReadHeaderTimeout, this is a package-level variable to keep it easy to override. Set it at program init; it must not be modified concurrently with parsing.
var V1AcceptIPv4InTCP6 = false
V1AcceptIPv4InTCP6 permits plain IPv4 literals in the address fields of a v1 TCP6 line, promoting them to v4-mapped IPv6 (::ffff:x.x.x.x). Some proxies (notably the nginx OSS stream module) emit such lines when the client and backend address families differ. The spec forbids them — "the advertised protocol family dictates what format to use" — so this compatibility mode is disabled by default.
Note the promotion is lossy: round-trip serialization renders the address as "::ffff:x.x.x.x" rather than the original "x.x.x.x".
Like DefaultReadHeaderTimeout, this is a package-level variable to keep it easy to override. Set it at program init; it must not be modified concurrently with parsing.
Functions ¶
func SetReadHeaderTimeout ¶ added in v0.8.0
SetReadHeaderTimeout sets the readHeaderTimeout for a connection when passed as option to NewConn(). A value of 0 disables the header read timeout; negative values are ignored, leaving the connection's current timeout (the NewConn default) in place.
func ValidateHeader ¶
ValidateHeader adds given validator for proxy headers to a connection when passed as option to NewConn().
func WithBufferSize ¶ added in v0.10.0
WithBufferSize sets the size of the read buffer used for proxy header detection. Values <= 0 are ignored and the default (256 bytes) is used. Values < 16 are effectively 16 due to bufio's minimum. The default is tuned for typical proxy protocol header lengths.
The buffer must be able to hold an entire v1 header line (up to 107 bytes): v1 parsing requires the line to be available without refilling the buffer (the slow-loris defense), so a smaller buffer rejects every v1 connection with ErrCantReadVersion1Header even from well-behaved senders. v2 parsing refills freely and works with any size.
func WithPolicy ¶
WithPolicy adds given policy to a connection when passed as option to NewConn().
Types ¶
type AddressFamilyAndProtocol ¶
type AddressFamilyAndProtocol byte
AddressFamilyAndProtocol represents address family and transport protocol.
const ( UNSPEC AddressFamilyAndProtocol = '\x00' TCPv4 AddressFamilyAndProtocol = '\x11' UDPv4 AddressFamilyAndProtocol = '\x12' TCPv6 AddressFamilyAndProtocol = '\x21' UDPv6 AddressFamilyAndProtocol = '\x22' UnixStream AddressFamilyAndProtocol = '\x31' UnixDatagram AddressFamilyAndProtocol = '\x32' )
AddressFamilyAndProtocol enum values.
func (AddressFamilyAndProtocol) IsDatagram ¶
func (ap AddressFamilyAndProtocol) IsDatagram() bool
IsDatagram returns true if the transport protocol is UDP or DGRAM (SOCK_DGRAM), false otherwise.
func (AddressFamilyAndProtocol) IsIPv4 ¶
func (ap AddressFamilyAndProtocol) IsIPv4() bool
IsIPv4 returns true if the address family is IPv4 (AF_INET4), false otherwise.
func (AddressFamilyAndProtocol) IsIPv6 ¶
func (ap AddressFamilyAndProtocol) IsIPv6() bool
IsIPv6 returns true if the address family is IPv6 (AF_INET6), false otherwise.
func (AddressFamilyAndProtocol) IsStream ¶
func (ap AddressFamilyAndProtocol) IsStream() bool
IsStream returns true if the transport protocol is TCP or STREAM (SOCK_STREAM), false otherwise.
func (AddressFamilyAndProtocol) IsUnix ¶
func (ap AddressFamilyAndProtocol) IsUnix() bool
IsUnix returns true if the address family is UNIX (AF_UNIX), false otherwise.
func (AddressFamilyAndProtocol) IsUnspec ¶
func (ap AddressFamilyAndProtocol) IsUnspec() bool
IsUnspec returns true if the transport protocol or address family is unspecified, false otherwise.
type Conn ¶
type Conn struct {
ProxyHeaderPolicy Policy
Validate Validator
// contains filtered or unexported fields
}
Conn is used to wrap and underlying connection which may be speaking the Proxy Protocol. If it is, the RemoteAddr() will return the address of the client instead of the proxy address. Each connection will have its own readHeaderTimeout and readDeadline set by the Accept() call.
func NewConn ¶
NewConn is used to wrap a net.Conn that may be speaking the PROXY protocol into a proxyproto.Conn.
Conn is stream-oriented; see the note on Listener about the PROXY protocol over UDP datagrams.
By default the returned Conn applies DefaultPolicy (REQUIRE, so the peer must open with a PROXY header; override with the WithPolicy option) and DefaultReadHeaderTimeout (10s) while detecting the PROXY protocol header, so a client that connects but never sends data cannot make header detection block forever.
The timeout bounds header detection only, not the first Read end-to-end: under a non-REQUIRE policy, when no header is present Read falls through to a normal read of the underlying connection, which can still block on a silent client (pinning a goroutine and file descriptor). For an end-to-end bound, set a read deadline on the connection, or keep the REQUIRE policy, which makes the first Read fail when no header arrives within the timeout.
Override the timeout with the SetReadHeaderTimeout option; pass SetReadHeaderTimeout(0) to disable it entirely.
NOTE: NewConn may interfere with previously set ReadDeadline on the provided net.Conn, because it sets a temporary deadline when detecting and reading the PROXY protocol header. If you need to enforce a specific ReadDeadline on the connection, be sure to call Conn.SetReadDeadline again after NewConn returns, to restore your desired deadline.
Example (Combined) ¶
serverConn, clientConn := net.Pipe()
defer func() { _ = serverConn.Close() }()
defer func() { _ = clientConn.Close() }()
go func() {
_, _ = clientConn.Write([]byte(proxyV1Line))
_, _ = clientConn.Write([]byte("c"))
_ = clientConn.Close()
}()
conn := proxyproto.NewConn(serverConn,
proxyproto.WithBufferSize(2048),
proxyproto.SetReadHeaderTimeout(2*time.Second),
)
buf := make([]byte, 1)
_, _ = conn.Read(buf)
Example (Default) ¶
serverConn, clientConn := net.Pipe()
defer func() { _ = serverConn.Close() }()
defer func() { _ = clientConn.Close() }()
go func() {
_, _ = clientConn.Write([]byte(proxyV1Line))
_, _ = clientConn.Write([]byte("x"))
_ = clientConn.Close()
}()
// By default a connection must open with a PROXY header (DefaultPolicy is
// REQUIRE): a headerless connection fails its first Read with
// ErrNoProxyProtocol. Detection is bounded by DefaultReadHeaderTimeout, so
// a silent client cannot block it forever. RemoteAddr then reports the
// client address carried by the header.
conn := proxyproto.NewConn(serverConn)
buf := make([]byte, 1)
if _, err := conn.Read(buf); err != nil {
fmt.Println("read error:", err)
return
}
fmt.Println(conn.RemoteAddr())
Output: 192.168.1.1:12345
Example (DisableReadHeaderTimeout) ¶
serverConn, clientConn := net.Pipe()
defer func() { _ = serverConn.Close() }()
defer func() { _ = clientConn.Close() }()
go func() {
_, _ = clientConn.Write([]byte(proxyV1Line))
_, _ = clientConn.Write([]byte("d"))
_ = clientConn.Close()
}()
// NewConn applies DefaultReadHeaderTimeout by default; pass
// SetReadHeaderTimeout(0) to opt out and manage read deadlines yourself.
conn := proxyproto.NewConn(serverConn, proxyproto.SetReadHeaderTimeout(0))
buf := make([]byte, 1)
_, _ = conn.Read(buf)
Example (WithBufferSize) ¶
serverConn, clientConn := net.Pipe()
defer func() { _ = serverConn.Close() }()
defer func() { _ = clientConn.Close() }()
go func() {
_, _ = clientConn.Write([]byte(proxyV1Line))
_, _ = clientConn.Write([]byte("y"))
_ = clientConn.Close()
}()
conn := proxyproto.NewConn(serverConn, proxyproto.WithBufferSize(4096))
buf := make([]byte, 1)
_, _ = conn.Read(buf)
Example (WithPolicy) ¶
serverConn, clientConn := net.Pipe()
defer func() { _ = serverConn.Close() }()
defer func() { _ = clientConn.Close() }()
go func() {
_, _ = clientConn.Write([]byte(proxyV1Line))
_, _ = clientConn.Write([]byte("p"))
_ = clientConn.Close()
}()
conn := proxyproto.NewConn(serverConn, proxyproto.WithPolicy(proxyproto.REQUIRE))
buf := make([]byte, 1)
_, _ = conn.Read(buf)
Example (WithReadHeaderTimeout) ¶
serverConn, clientConn := net.Pipe()
defer func() { _ = serverConn.Close() }()
defer func() { _ = clientConn.Close() }()
go func() {
_, _ = clientConn.Write([]byte(proxyV1Line))
_, _ = clientConn.Write([]byte("z"))
_ = clientConn.Close()
}()
conn := proxyproto.NewConn(serverConn, proxyproto.SetReadHeaderTimeout(time.Second))
buf := make([]byte, 1)
_, _ = conn.Read(buf)
func (*Conn) LocalAddr ¶
LocalAddr returns the address of the server if the proxy protocol is being used, otherwise just returns the address of the socket server. In case an error happens on reading the proxy header the original LocalAddr is returned, not the one from the proxy header even if the proxy header itself is syntactically correct.
func (*Conn) ProxyHeader ¶ added in v0.3.2
ProxyHeader returns the proxy protocol header, if any. If an error occurs while reading the proxy header, nil is returned.
func (*Conn) Raw ¶ added in v0.3.3
Raw returns the underlying connection which can be casted to a concrete type, allowing access to specialized functions.
Use this ONLY if you know exactly what you are doing.
func (*Conn) Read ¶
Read checks for the proxy protocol header on the first call, then reads from the connection. If there is an error processing the header, it is returned by this and every subsequent call. The connection is NOT closed by this package; the caller should close it.
func (*Conn) RemoteAddr ¶
RemoteAddr returns the address of the client if the proxy protocol is being used, otherwise just returns the address of the socket peer. In case an error happens on reading the proxy header the original RemoteAddr is returned, not the one from the proxy header even if the proxy header itself is syntactically correct.
func (*Conn) SetDeadline ¶
SetDeadline wraps original conn.SetDeadline.
func (*Conn) SetReadDeadline ¶
SetReadDeadline wraps original conn.SetReadDeadline.
func (*Conn) SetWriteDeadline ¶
SetWriteDeadline wraps original conn.SetWriteDeadline.
func (*Conn) TCPConn ¶ added in v0.3.1
TCPConn returns the underlying TCP connection, allowing access to specialized functions.
Use this ONLY if you know exactly what you are doing.
func (*Conn) UDPConn ¶ added in v0.3.1
UDPConn returns the underlying UDP connection, allowing access to specialized functions.
Use this ONLY if you know exactly what you are doing.
type ConnPolicyFunc ¶ added in v0.8.0
type ConnPolicyFunc func(connPolicyOptions ConnPolicyOptions) (Policy, error)
ConnPolicyFunc can be used to decide whether to trust the PROXY info based on connection policy options. If set, the connecting addresses (remote and local) are passed in as argument.
See below for the different policies.
In case an error is returned the connection is denied: an error wrapping ErrInvalidUpstream denies just that connection while Listener.Accept keeps listening; any other error is returned by Accept itself, which typically stops the caller's accept loop.
func ConnLaxWhiteListPolicy
deprecated
added in
v0.9.0
func ConnLaxWhiteListPolicy(allowed []string) (ConnPolicyFunc, error)
ConnLaxWhiteListPolicy returns a ConnPolicyFunc which decides whether the upstream ip is allowed to send a proxy header based on a list of allowed IP addresses and IP ranges. In case upstream IP is not in list the proxy header will be ignored. If one of the provided IP addresses or IP ranges is invalid it will return an error instead of a ConnPolicyFunc.
Deprecated: the name hides that the header stays optional (USE) for allowed peers, overriding the REQUIRE default. Use the equivalent, explicit PolicyFromRanges(allowed, USE, IGNORE), or TrustProxyHeaderFromRanges for the spec-strict header-mandatory posture.
func ConnMustLaxWhiteListPolicy
deprecated
added in
v0.9.0
func ConnMustLaxWhiteListPolicy(allowed []string) ConnPolicyFunc
ConnMustLaxWhiteListPolicy returns a ConnLaxWhiteListPolicy but will panic if one of the provided IP addresses or IP ranges is invalid.
Deprecated: use MustPolicyFromRanges(allowed, USE, IGNORE) instead; see ConnLaxWhiteListPolicy for why.
func ConnMustStrictWhiteListPolicy
deprecated
added in
v0.9.0
func ConnMustStrictWhiteListPolicy(allowed []string) ConnPolicyFunc
ConnMustStrictWhiteListPolicy returns a ConnStrictWhiteListPolicy but will panic if one of the provided IP addresses or IP ranges is invalid.
Deprecated: use MustPolicyFromRanges(allowed, USE, REJECT) instead; see ConnStrictWhiteListPolicy for why.
func ConnSkipProxyHeaderForCIDR ¶ added in v0.9.0
func ConnSkipProxyHeaderForCIDR(skipHeaderCIDR *net.IPNet, def Policy) ConnPolicyFunc
ConnSkipProxyHeaderForCIDR returns a ConnPolicyFunc which can be used to accept a connection from a skipHeaderCIDR without requiring a PROXY header, e.g. Kubernetes pods local traffic. The def is a policy to use when an upstream address doesn't match the skipHeaderCIDR.
func ConnStrictWhiteListPolicy
deprecated
added in
v0.9.0
func ConnStrictWhiteListPolicy(allowed []string) (ConnPolicyFunc, error)
ConnStrictWhiteListPolicy returns a ConnPolicyFunc which decides whether the upstream ip is allowed to send a proxy header based on a list of allowed IP addresses and IP ranges. In case upstream IP is not in list reading on the connection will be refused: the first read returns ErrSuperfluousProxyHeader and every subsequent read returns the same error, so the connection should be closed. If one of the provided IP addresses or IP ranges is invalid it will return an error instead of a ConnPolicyFunc.
Deprecated: "strict" only refers to refusing headers from unlisted peers; the header stays optional (USE) for allowed peers, overriding the REQUIRE default. Use the equivalent, explicit PolicyFromRanges(allowed, USE, REJECT), or TrustProxyHeaderFromRanges for the spec-strict header-mandatory posture.
func IgnoreProxyHeaderNotOnInterface ¶ added in v0.8.0
func IgnoreProxyHeaderNotOnInterface(allowedIP net.IP) ConnPolicyFunc
IgnoreProxyHeaderNotOnInterface returns a ConnPolicyFunc which can be used to decide whether to use or ignore PROXY headers depending on the connection being made on specific interfaces. This policy can be used when the server is bound to multiple interfaces but wants to allow on one or more interfaces.
func MustPolicyFromRanges ¶ added in v0.15.0
func MustPolicyFromRanges(ranges []string, matched, unmatched Policy) ConnPolicyFunc
MustPolicyFromRanges returns PolicyFromRanges and panics if any entry in ranges is invalid. Intended for static configuration known at program init.
func PolicyFromRanges ¶ added in v0.15.0
func PolicyFromRanges(ranges []string, matched, unmatched Policy) (ConnPolicyFunc, error)
PolicyFromRanges returns a ConnPolicyFunc that applies the matched policy to connections whose upstream address belongs to ranges, and the unmatched policy to every other connection. Each entry in ranges may be an individual IP address ("10.0.0.10") or a CIDR range ("10.0.0.0/24"); an invalid entry returns an error instead of a ConnPolicyFunc. Connections whose upstream address cannot be classified are dropped by Listener.Accept via an ErrInvalidUpstream-wrapping error.
It is the explicit replacement for the deprecated *WhiteListPolicy helpers:
PolicyFromRanges(ranges, USE, IGNORE) // ConnLaxWhiteListPolicy PolicyFromRanges(ranges, USE, REJECT) // ConnStrictWhiteListPolicy
Both of those combinations leave the header optional for matched peers. For the spec-strict posture — trusted sources MUST send a header, everything else is dropped — use TrustProxyHeaderFromRanges instead.
func TrustProxyHeaderFrom ¶ added in v0.9.0
func TrustProxyHeaderFrom(trustedIPs ...net.IP) ConnPolicyFunc
TrustProxyHeaderFrom returns a ConnPolicyFunc implementing the spec's receiver posture end to end: connections from trusted IPs MUST carry a PROXY header (REQUIRE — absence fails the first Read with ErrNoProxyProtocol, so header presence is never guessed), and connections from any other source are dropped by Listener.Accept without stopping the listener.
Note the REJECT policy alone cannot provide the second half: REJECT refuses connections that DO send a header, but a headerless connection from an untrusted peer would still be served raw, sharing the port between PROXY and non-PROXY traffic — exactly what the spec's security model forbids.
func TrustProxyHeaderFromRanges ¶ added in v0.15.0
func TrustProxyHeaderFromRanges(trusted []string) (ConnPolicyFunc, error)
TrustProxyHeaderFromRanges is the CIDR-capable variant of TrustProxyHeaderFrom, with the same spec posture: connections from the trusted set MUST carry a PROXY header (REQUIRE), and connections from any other source are dropped by Listener.Accept without stopping the listener. Each entry in trusted may be an individual IP address ("10.0.0.10") or a CIDR range ("10.0.0.0/24"); an invalid entry returns an error instead of a ConnPolicyFunc.
Unlike the *WhiteListPolicy helpers — which make the header optional (USE) for allowed peers and merely ignore or refuse the header for denied ones — this helper never guesses whether a header is present.
type ConnPolicyOptions ¶ added in v0.8.0
ConnPolicyOptions contains the remote and local addresses of a connection.
type Header ¶
type Header struct {
Version byte
Command ProtocolVersionAndCommand
TransportProtocol AddressFamilyAndProtocol
SourceAddr net.Addr
DestinationAddr net.Addr
// contains filtered or unexported fields
}
Header is the placeholder for proxy protocol header.
func HeaderProxyFromAddrs ¶ added in v0.2.0
HeaderProxyFromAddrs creates a new PROXY header from a source and a destination address. If version is zero, the latest protocol version is used.
The header is filled on a best-effort basis: if hints cannot be inferred from the provided addresses, the header will be left unspecified.
func ParseUDPDatagram ¶ added in v0.15.0
ParseUDPDatagram parses a PROXY protocol header at the start of a UDP datagram and returns the header together with the proxied payload that follows it. The returned payload aliases the datagram slice.
Per spec (section 2), when the PROXY protocol is carried over UDP the header and the proxied payload MUST be sent in the same datagram, and the receiver MUST parse the header independently for each received datagram. Call this for every datagram received; there is no connection state to carry over. Conn and Listener are stream-oriented and cannot provide those semantics.
A datagram that does not begin with a complete, valid header fails with the same errors Read returns — ErrNoProxyProtocol when the signature is absent. The spec forbids guessing whether a header is present, so on a receiver configured for the PROXY protocol such datagrams must be dropped, not treated as raw payload.
Example ¶
package main
import (
"fmt"
"net"
"github.com/pires/go-proxyproto"
)
func main() {
// A receiver behind a proxy that speaks the PROXY protocol over UDP. The
// spec (section 2) requires the header and the proxied payload to share a
// single datagram and the header to be parsed independently for each
// datagram, so the socket stays a plain net.PacketConn and every ReadFrom
// is followed by ParseUDPDatagram.
server, _ := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)})
defer func() { _ = server.Close() }()
go func() {
// The proxy side: one FormatUDPDatagram plus one Write keeps header
// and payload in the same datagram.
header := &proxyproto.Header{
Version: 2,
Command: proxyproto.PROXY,
TransportProtocol: proxyproto.UDPv4,
SourceAddr: &net.UDPAddr{IP: net.ParseIP("192.168.1.1"), Port: 12345},
DestinationAddr: &net.UDPAddr{IP: net.ParseIP("192.168.1.2"), Port: 443},
}
datagram, _ := header.FormatUDPDatagram([]byte("ping"))
c, _ := net.Dial("udp", server.LocalAddr().String())
if c != nil {
_, _ = c.Write(datagram)
_ = c.Close()
}
}()
buf := make([]byte, 65535)
n, _, _ := server.ReadFrom(buf)
header, payload, err := proxyproto.ParseUDPDatagram(buf[:n])
if err != nil {
// The spec forbids guessing: a datagram without a valid header is
// dropped, never treated as raw payload.
fmt.Println("drop:", err)
return
}
fmt.Printf("%s says %q\n", header.SourceAddr, payload)
}
Output: 192.168.1.1:12345 says "ping"
func Read ¶
Read identifies the proxy protocol version and reads the remaining of the header, accordingly.
If proxy protocol header signature is not present, the reader buffer remains untouched and is safe for reading outside of this code.
If proxy protocol header signature is present but an error is raised while processing the remaining header, assume the reader buffer to be in a corrupt state. Also, this operation will block until enough bytes are available for peeking.
func ReadHeaderTimeout ¶ added in v0.14.0
ReadHeaderTimeout reads the PROXY protocol header from conn, giving up after timeout. It is the cancellable replacement for the deprecated ReadTimeout: because it is given the net.Conn, it sets a read deadline so a stalled read is actually interrupted instead of leaking a blocked goroutine and the connection's file descriptor. If the timeout is reached it returns ErrNoProxyProtocol, assuming no header is present.
reader must be buffered over conn (for example bufio.NewReader(conn)); it is used for the header read so that any bytes buffered past the header remain available for the caller to read afterwards. A timeout <= 0 reads without a deadline.
ReadHeaderTimeout overwrites conn's read deadline and restores the zero (no) deadline before returning; re-apply your own read deadline afterwards if you had one set.
Example ¶
ExampleReadHeaderTimeout demonstrates the cancellable, low-level way to read a PROXY protocol header directly from a net.Conn you manage yourself. Unlike the deprecated ReadTimeout, it is given the conn and sets a real read deadline, so a peer that connects but never sends the header cannot block the read forever.
serverConn, clientConn := net.Pipe()
defer func() { _ = serverConn.Close() }()
go func() {
// A PROXY v1 header naming the real client, followed by application data.
_, _ = clientConn.Write([]byte(proxyV1Line))
_, _ = clientConn.Write([]byte("HELO"))
_ = clientConn.Close()
}()
// reader must be buffered over conn: any bytes read past the header remain
// available for the caller to consume afterwards.
reader := bufio.NewReader(serverConn)
header, err := proxyproto.ReadHeaderTimeout(serverConn, reader, time.Second)
if err != nil && err != proxyproto.ErrNoProxyProtocol {
fmt.Println("error:", err)
return
}
if header != nil {
fmt.Println("client:", header.SourceAddr)
}
// Continue reading the application data from the same buffered reader.
data, _ := io.ReadAll(reader)
fmt.Printf("data: %s\n", data)
Output: client: 192.168.1.1:12345 data: HELO
func ReadTimeout
deprecated
ReadTimeout acts as Read but takes a timeout. If that timeout is reached, it's assumed there's no proxy protocol header.
Deprecated: ReadTimeout cannot cancel the read it starts. It only receives a *bufio.Reader, so on timeout it has no way to set a deadline on or close the underlying connection: the goroutine it spawns stays blocked in Read, peeking at the stalled connection, until the peer sends data or the connection is closed elsewhere. Each timed-out call therefore leaks that goroutine and the connection's file descriptor. Use ReadHeaderTimeout instead, which also takes the net.Conn and sets a real read deadline so the read is actually cancelled on timeout; or wrap the connection with NewConn or a Listener and configure the header timeout via the SetReadHeaderTimeout option or Listener.ReadHeaderTimeout.
func (*Header) EqualTo ¶
EqualTo returns true if headers are equivalent, false otherwise. Deprecated: use EqualsTo instead. This method will eventually be removed.
func (*Header) FormatUDPDatagram ¶ added in v0.15.0
FormatUDPDatagram renders the header followed by the proxied payload, producing the exact bytes to send as one UDP datagram. Per spec (section 2) the header and the payload MUST share a single datagram, which a single write of the returned slice guarantees; formatting the header and payload separately risks a sender splitting them across datagrams.
Example ¶
package main
import (
"fmt"
"net"
"github.com/pires/go-proxyproto"
)
func main() {
header := &proxyproto.Header{
Version: 2,
Command: proxyproto.PROXY,
TransportProtocol: proxyproto.UDPv4,
SourceAddr: &net.UDPAddr{IP: net.ParseIP("192.168.1.1"), Port: 12345},
DestinationAddr: &net.UDPAddr{IP: net.ParseIP("192.168.1.2"), Port: 443},
}
// The returned slice is the exact bytes to hand to a single WriteTo;
// writing header and payload separately risks splitting them across
// datagrams, which the spec forbids.
datagram, err := header.FormatUDPDatagram([]byte("ping"))
if err != nil {
fmt.Println("format error:", err)
return
}
parsed, payload, _ := proxyproto.ParseUDPDatagram(datagram)
fmt.Printf("%d bytes on the wire: %s -> %s carrying %q\n",
len(datagram), parsed.SourceAddr, parsed.DestinationAddr, payload)
}
Output: 32 bytes on the wire: 192.168.1.1:12345 -> 192.168.1.2:443 carrying "ping"
func (*Header) SetTLVs ¶ added in v0.2.0
SetTLVs sets the TLVs stored in this header. This method replaces any previous TLV.
func (*Header) TCPAddrs ¶ added in v0.3.0
TCPAddrs returns TCP source/destination addresses if the header is stream-based.
func (*Header) TLVs ¶
TLVs returns the TLVs stored into this header, if they exist. TLVs are optional for v2 of the protocol.
func (*Header) UDPAddrs ¶ added in v0.3.0
UDPAddrs returns UDP source/destination addresses if the header is datagram-based.
type Listener ¶
type Listener struct {
// Listener is the underlying listener.
Listener net.Listener
// Deprecated: use ConnPolicyFunc instead. This will be removed in future release.
Policy PolicyFunc
// ConnPolicy is the policy function for accepted connections.
ConnPolicy ConnPolicyFunc
// ValidateHeader is the validator function for the proxy header.
ValidateHeader Validator
// ReadHeaderTimeout is the timeout for reading the proxy header.
ReadHeaderTimeout time.Duration
// ReadBufferSize is the read buffer size for accepted connections. When > 0,
// each accepted connection uses this size for proxy header detection; 0 means default.
// See the sizing note on WithBufferSize: values below 107 bytes break v1
// header parsing.
ReadBufferSize int
}
Listener is used to wrap an underlying listener, whose connections may be using the HAProxy Proxy Protocol. If the connection is using the protocol, the RemoteAddr() will return the correct client address. ReadHeaderTimeout will be applied to all connections in order to prevent blocking operations. If no ReadHeaderTimeout is set, a default of 10s will be used. This can be disabled by setting the timeout to < 0.
When neither Policy nor ConnPolicy is set, DefaultPolicy applies: REQUIRE, so connections that do not open with a PROXY header fail their first Read/Write with ErrNoProxyProtocol. Headers are still honored from ANY peer under REQUIRE; a listener reachable by untrusted clients should set a trusted-source policy (e.g. TrustProxyHeaderFrom or TrustProxyHeaderFromRanges).
Listener is stream-oriented (TCP, Unix stream): the header is read once at the start of the byte stream. It cannot implement the PROXY protocol over UDP, where the spec requires a header parsed independently in each datagram; use ParseUDPDatagram and Header.FormatUDPDatagram for that.
Note that ReadHeaderTimeout only bounds how long a single slow connection can hold a goroutine and file descriptor during header detection; it is not a connection count or accept-rate limit. Deployments exposed to untrusted clients should keep ReadHeaderTimeout low and enforce connection/rate limits upstream (or around Accept).
Only one of Policy or ConnPolicy should be provided. If both are provided then a panic would occur during accept.
Example (Default) ¶
package main
import (
"fmt"
"net"
"github.com/pires/go-proxyproto"
)
// proxyV1Line is a minimal PROXY protocol v1 header for examples.
const proxyV1Line = "PROXY TCP4 192.168.1.1 192.168.1.2 12345 443\r\n"
func main() {
l, _ := net.Listen("tcp", "127.0.0.1:0")
// By default every connection must open with a PROXY header (DefaultPolicy
// is REQUIRE); RemoteAddr then reports the address the header carries.
pl := &proxyproto.Listener{Listener: l}
defer func() { _ = pl.Close() }()
go func() {
c, _ := net.Dial("tcp", pl.Addr().String())
if c != nil {
_, _ = c.Write([]byte(proxyV1Line))
_, _ = c.Write([]byte("x"))
_ = c.Close()
}
}()
conn, _ := pl.Accept()
if conn != nil {
buf := make([]byte, 1)
if _, err := conn.Read(buf); err != nil {
fmt.Println("read error:", err)
return
}
fmt.Println(conn.RemoteAddr())
_ = conn.Close()
}
}
Output: 192.168.1.1:12345
Example (PolicyRequire) ¶
package main
import (
"net"
"github.com/pires/go-proxyproto"
)
// proxyV1Line is a minimal PROXY protocol v1 header for examples.
const proxyV1Line = "PROXY TCP4 192.168.1.1 192.168.1.2 12345 443\r\n"
func main() {
l, _ := net.Listen("tcp", "127.0.0.1:0")
pl := &proxyproto.Listener{
Listener: l,
Policy: func(net.Addr) (proxyproto.Policy, error) { return proxyproto.REQUIRE, nil },
}
defer func() { _ = pl.Close() }()
go func() {
c, _ := net.Dial("tcp", pl.Addr().String())
if c != nil {
_, _ = c.Write([]byte(proxyV1Line))
_, _ = c.Write([]byte("p"))
_ = c.Close()
}
}()
conn, _ := pl.Accept()
if conn != nil {
buf := make([]byte, 1)
_, _ = conn.Read(buf)
_ = conn.Close()
}
}
Output:
Example (ReadBufferSize) ¶
package main
import (
"net"
"github.com/pires/go-proxyproto"
)
// proxyV1Line is a minimal PROXY protocol v1 header for examples.
const proxyV1Line = "PROXY TCP4 192.168.1.1 192.168.1.2 12345 443\r\n"
func main() {
l, _ := net.Listen("tcp", "127.0.0.1:0")
pl := &proxyproto.Listener{
Listener: l,
ReadBufferSize: 4096,
}
defer func() { _ = pl.Close() }()
go func() {
c, _ := net.Dial("tcp", pl.Addr().String())
if c != nil {
_, _ = c.Write([]byte(proxyV1Line))
_, _ = c.Write([]byte("b"))
_ = c.Close()
}
}()
conn, _ := pl.Accept()
if conn != nil {
buf := make([]byte, 1)
_, _ = conn.Read(buf)
_ = conn.Close()
}
}
Output:
Example (ReadHeaderTimeout) ¶
package main
import (
"net"
"time"
"github.com/pires/go-proxyproto"
)
// proxyV1Line is a minimal PROXY protocol v1 header for examples.
const proxyV1Line = "PROXY TCP4 192.168.1.1 192.168.1.2 12345 443\r\n"
func main() {
l, _ := net.Listen("tcp", "127.0.0.1:0")
pl := &proxyproto.Listener{
Listener: l,
ReadHeaderTimeout: 2 * time.Second,
}
defer func() { _ = pl.Close() }()
go func() {
c, _ := net.Dial("tcp", pl.Addr().String())
if c != nil {
_, _ = c.Write([]byte(proxyV1Line))
_, _ = c.Write([]byte("a"))
_ = c.Close()
}
}()
conn, _ := pl.Accept()
if conn != nil {
_ = conn.SetReadDeadline(time.Now().Add(time.Second))
buf := make([]byte, 1)
_, _ = conn.Read(buf)
_ = conn.Close()
}
}
Output:
Example (Tls) ¶
ExampleListener_tls shows how to combine the PROXY protocol with TLS.
The only real decision is the listener wrapping order, and it depends on where the upstream places the PROXY header relative to the TLS handshake:
Header in cleartext, BEFORE the handshake. This is the common case (e.g. AWS NLB with proxy protocol v2, or HAProxy "send-proxy" in front of a TLS listener). proxyproto must read the header first, so it goes INSIDE the TLS listener: tls.NewListener(&proxyproto.Listener{...}, cfg).
Header INSIDE the TLS session, AFTER the handshake. TLS must be decrypted first, so proxyproto goes OUTSIDE the TLS listener: &proxyproto.Listener{Listener: tls.NewListener(l, cfg)}.
This example demonstrates the first (cleartext-header) ordering, which is what most deployments need. Because the header is parsed before TLS, conn.RemoteAddr() reports the real client address carried by the PROXY header rather than the immediate peer (the proxy). See ExampleListener_tlsHeaderInsideTLS for the second ordering.
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"fmt"
"math/big"
"net"
"time"
proxyproto "github.com/pires/go-proxyproto"
)
func main() {
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
return
}
// proxyproto INNER, tls OUTER: the PROXY header is read from cleartext, then
// the TLS handshake runs on the remaining stream.
proxyListener := &proxyproto.Listener{Listener: l}
tlsListener := tls.NewListener(proxyListener, exampleSelfSignedConfig())
defer func() { _ = tlsListener.Close() }()
go func() {
// Client side: open a raw TCP connection, write the PROXY header in
// cleartext, and only THEN start the TLS handshake.
raw, err := net.Dial("tcp", l.Addr().String())
if err != nil {
return
}
defer func() { _ = raw.Close() }()
header := &proxyproto.Header{
Version: 2,
Command: proxyproto.PROXY,
TransportProtocol: proxyproto.TCPv4,
SourceAddr: &net.TCPAddr{IP: net.ParseIP("10.1.1.1"), Port: 1000},
DestinationAddr: &net.TCPAddr{IP: net.ParseIP("20.2.2.2"), Port: 2000},
}
if _, err := header.WriteTo(raw); err != nil {
return
}
client := tls.Client(raw, &tls.Config{
InsecureSkipVerify: true, //nolint:gosec // example uses a throwaway self-signed cert.
})
_ = client.Handshake()
_ = client.Close()
}()
conn, err := tlsListener.Accept()
if err != nil {
return
}
defer func() { _ = conn.Close() }()
// conn is a *tls.Conn whose underlying connection is a *proxyproto.Conn that
// has already parsed the header, so RemoteAddr() is the real client. From
// here, use conn like any other tls.Conn (Read/Write) to serve the client.
fmt.Println(conn.RemoteAddr())
}
// exampleSelfSignedConfig returns a *tls.Config with a throwaway self-signed
// certificate, so the example is self-contained. Real servers load a real
// certificate, e.g. via tls.LoadX509KeyPair.
func exampleSelfSignedConfig() *tls.Config {
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
panic(err)
}
template := &x509.Certificate{
SerialNumber: big.NewInt(1),
NotBefore: time.Now().Add(-time.Hour),
NotAfter: time.Now().Add(time.Hour),
}
der, err := x509.CreateCertificate(rand.Reader, template, template, &priv.PublicKey, priv)
if err != nil {
panic(err)
}
return &tls.Config{
MinVersion: tls.VersionTLS12,
Certificates: []tls.Certificate{{
Certificate: [][]byte{der},
PrivateKey: priv,
}},
}
}
Output: 10.1.1.1:1000
Example (TlsHeaderInsideTLS) ¶
ExampleListener_tlsHeaderInsideTLS shows the second wrapping order, where the upstream completes the TLS handshake first and only then sends the PROXY header inside the encrypted session. TLS must be decrypted before the header can be read, so proxyproto wraps the TLS listener: proxyproto OUTER, tls INNER (the inverse of ExampleListener_tls). Use this only when you control the upstream and it deliberately speaks the header after the handshake.
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"fmt"
"math/big"
"net"
"time"
proxyproto "github.com/pires/go-proxyproto"
)
func main() {
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
return
}
// tls INNER, proxyproto OUTER: TLS is decrypted first, then the PROXY header
// is parsed from the decrypted stream.
proxyListener := &proxyproto.Listener{
Listener: tls.NewListener(l, exampleSelfSignedConfig()),
}
defer func() { _ = proxyListener.Close() }()
go func() {
// Client side: complete the TLS handshake first, then write the PROXY
// header inside the encrypted session.
raw, err := net.Dial("tcp", l.Addr().String())
if err != nil {
return
}
defer func() { _ = raw.Close() }()
client := tls.Client(raw, &tls.Config{
InsecureSkipVerify: true, //nolint:gosec // example uses a throwaway self-signed cert.
})
if err := client.Handshake(); err != nil {
return
}
header := &proxyproto.Header{
Version: 2,
Command: proxyproto.PROXY,
TransportProtocol: proxyproto.TCPv4,
SourceAddr: &net.TCPAddr{IP: net.ParseIP("10.1.1.1"), Port: 1000},
DestinationAddr: &net.TCPAddr{IP: net.ParseIP("20.2.2.2"), Port: 2000},
}
_, _ = header.WriteTo(client)
_ = client.Close()
}()
conn, err := proxyListener.Accept()
if err != nil {
return
}
defer func() { _ = conn.Close() }()
// conn is a *proxyproto.Conn wrapping a *tls.Conn. Reading the header (here
// via RemoteAddr) transparently runs the TLS handshake, decrypts the stream,
// and then parses the PROXY header, so RemoteAddr() is the real client.
fmt.Println(conn.RemoteAddr())
}
// exampleSelfSignedConfig returns a *tls.Config with a throwaway self-signed
// certificate, so the example is self-contained. Real servers load a real
// certificate, e.g. via tls.LoadX509KeyPair.
func exampleSelfSignedConfig() *tls.Config {
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
panic(err)
}
template := &x509.Certificate{
SerialNumber: big.NewInt(1),
NotBefore: time.Now().Add(-time.Hour),
NotAfter: time.Now().Add(time.Hour),
}
der, err := x509.CreateCertificate(rand.Reader, template, template, &priv.PublicKey, priv)
if err != nil {
panic(err)
}
return &tls.Config{
MinVersion: tls.VersionTLS12,
Certificates: []tls.Certificate{{
Certificate: [][]byte{der},
PrivateKey: priv,
}},
}
}
Output: 10.1.1.1:1000
Example (ValidateHeader) ¶
package main
import (
"net"
"github.com/pires/go-proxyproto"
)
// proxyV1Line is a minimal PROXY protocol v1 header for examples.
const proxyV1Line = "PROXY TCP4 192.168.1.1 192.168.1.2 12345 443\r\n"
func main() {
l, _ := net.Listen("tcp", "127.0.0.1:0")
pl := &proxyproto.Listener{
Listener: l,
ValidateHeader: func(*proxyproto.Header) error { return nil },
}
defer func() { _ = pl.Close() }()
go func() {
c, _ := net.Dial("tcp", pl.Addr().String())
if c != nil {
_, _ = c.Write([]byte(proxyV1Line))
_, _ = c.Write([]byte("v"))
_ = c.Close()
}
}()
conn, _ := pl.Accept()
if conn != nil {
buf := make([]byte, 1)
_, _ = conn.Read(buf)
_ = conn.Close()
}
}
Output:
type PP2Type ¶
type PP2Type byte
PP2Type is the proxy protocol v2 type.
const ( // Section 2.2. PP2_TYPE_ALPN PP2Type = 0x01 PP2_TYPE_AUTHORITY PP2Type = 0x02 PP2_TYPE_CRC32C PP2Type = 0x03 PP2_TYPE_NOOP PP2Type = 0x04 PP2_TYPE_UNIQUE_ID PP2Type = 0x05 PP2_TYPE_SSL PP2Type = 0x20 PP2_SUBTYPE_SSL_VERSION PP2Type = 0x21 PP2_SUBTYPE_SSL_CN PP2Type = 0x22 PP2_SUBTYPE_SSL_CIPHER PP2Type = 0x23 PP2_SUBTYPE_SSL_SIG_ALG PP2Type = 0x24 PP2_SUBTYPE_SSL_KEY_ALG PP2Type = 0x25 PP2_SUBTYPE_SSL_GROUP PP2Type = 0x26 PP2_SUBTYPE_SSL_SIG_SCHEME PP2Type = 0x27 PP2_SUBTYPE_SSL_CLIENT_CERT PP2Type = 0x28 PP2_TYPE_NETNS PP2Type = 0x30 // Section 2.2.8, reserved types. PP2_TYPE_MIN_CUSTOM PP2Type = 0xE0 PP2_TYPE_MAX_CUSTOM PP2Type = 0xEF PP2_TYPE_MIN_EXPERIMENT PP2Type = 0xF0 PP2_TYPE_MAX_EXPERIMENT PP2Type = 0xF7 PP2_TYPE_MIN_FUTURE PP2Type = 0xF8 PP2_TYPE_MAX_FUTURE PP2Type = 0xFF )
TLV type constants defined by the PROXY protocol spec.
func (PP2Type) App ¶
App is true if the type is reserved for application specific data, see section 2.2.8.
func (PP2Type) Experiment ¶
Experiment is true if the type is reserved for temporary experimental use by application developers, see section 2.2.8.
func (PP2Type) Registered ¶
Registered is true if the type is registered in the spec, see section 2.2.
type Policy ¶
type Policy int
Policy defines how a connection with a PROXY header address is treated.
const ( // USE address from PROXY header. USE Policy = iota // IGNORE address from PROXY header, but accept connection. IGNORE // REJECT connection when PROXY header is sent // Note: if a PROXY header is present the first read returns // ErrSuperfluousProxyHeader, and every subsequent read returns the same // error, so the connection should be closed. REJECT // REQUIRE connection to send PROXY header, reject if not present // Note: if no PROXY header is present the first read returns // ErrNoProxyProtocol, and every subsequent read returns the same error, // so the connection should be closed. REQUIRE // SKIP accepts a connection without requiring the PROXY header. // Note: an example usage can be found in the SkipProxyHeaderForCIDR // function. // // On a Listener, SKIP short-circuits Accept and returns the raw, unwrapped // connection. On a Conn (via WithPolicy), a PROXY header that is present is // still consumed from the stream but discarded: ProxyHeader returns nil and // no validation runs. SKIP )
type PolicyFunc
deprecated
PolicyFunc can be used to decide whether to trust the PROXY info from upstream. If set, the connecting address is passed in as an argument.
See below for the different policies.
In case an error is returned the connection is denied: an error wrapping ErrInvalidUpstream denies just that connection while Listener.Accept keeps listening; any other error is returned by Accept itself, which typically stops the caller's accept loop.
Deprecated: use ConnPolicyFunc instead.
func LaxWhiteListPolicy
deprecated
func LaxWhiteListPolicy(allowed []string) (PolicyFunc, error)
LaxWhiteListPolicy returns a PolicyFunc which decides whether the upstream ip is allowed to send a proxy header based on a list of allowed IP addresses and IP ranges. In case upstream IP is not in list the proxy header will be ignored. If one of the provided IP addresses or IP ranges is invalid it will return an error instead of a PolicyFunc.
Deprecated: use PolicyFromRanges(allowed, USE, IGNORE) instead; see ConnLaxWhiteListPolicy for why.
func MustLaxWhiteListPolicy
deprecated
func MustLaxWhiteListPolicy(allowed []string) PolicyFunc
MustLaxWhiteListPolicy returns a LaxWhiteListPolicy but will panic if one of the provided IP addresses or IP ranges is invalid.
Deprecated: use MustPolicyFromRanges(allowed, USE, IGNORE) instead; see ConnLaxWhiteListPolicy for why.
func MustStrictWhiteListPolicy
deprecated
func MustStrictWhiteListPolicy(allowed []string) PolicyFunc
MustStrictWhiteListPolicy returns a StrictWhiteListPolicy but will panic if one of the provided IP addresses or IP ranges is invalid.
Deprecated: use MustPolicyFromRanges(allowed, USE, REJECT) instead; see ConnStrictWhiteListPolicy for why.
func SkipProxyHeaderForCIDR
deprecated
added in
v0.7.0
func SkipProxyHeaderForCIDR(skipHeaderCIDR *net.IPNet, def Policy) PolicyFunc
SkipProxyHeaderForCIDR returns a PolicyFunc which can be used to accept a connection from a skipHeaderCIDR without requiring a PROXY header, e.g. Kubernetes pods local traffic. The def is a policy to use when an upstream address doesn't match the skipHeaderCIDR.
Deprecated: use ConnSkipProxyHeaderForCIDR instead.
func StrictWhiteListPolicy
deprecated
func StrictWhiteListPolicy(allowed []string) (PolicyFunc, error)
StrictWhiteListPolicy returns a PolicyFunc which decides whether the upstream ip is allowed to send a proxy header based on a list of allowed IP addresses and IP ranges. In case upstream IP is not in list reading on the connection will be refused: the first read returns ErrSuperfluousProxyHeader and every subsequent read returns the same error, so the connection should be closed. If one of the provided IP addresses or IP ranges is invalid it will return an error instead of a PolicyFunc.
Deprecated: use PolicyFromRanges(allowed, USE, REJECT) instead; see ConnStrictWhiteListPolicy for why.
type ProtocolVersionAndCommand ¶
type ProtocolVersionAndCommand byte
ProtocolVersionAndCommand represents the command in proxy protocol v2. Command doesn't exist in v1 but it should be set since other parts of this library may rely on it for determining connection details.
const ( // LOCAL represents the LOCAL command in v2 or UNKNOWN transport in v1, // in which case no address information is expected. LOCAL ProtocolVersionAndCommand = '\x20' // PROXY represents the PROXY command in v2 or transport is not UNKNOWN in v1, // in which case valid local/remote address and port information is expected. PROXY ProtocolVersionAndCommand = '\x21' )
func (ProtocolVersionAndCommand) IsLocal ¶
func (pvc ProtocolVersionAndCommand) IsLocal() bool
IsLocal returns true if the command in v2 is LOCAL or the transport in v1 is UNKNOWN, i.e. when no address information is expected, false otherwise.
func (ProtocolVersionAndCommand) IsProxy ¶
func (pvc ProtocolVersionAndCommand) IsProxy() bool
IsProxy returns true if the command in v2 is PROXY or the transport in v1 is not UNKNOWN, i.e. when valid local/remote address and port information is expected, false otherwise.
func (ProtocolVersionAndCommand) IsUnspec ¶
func (pvc ProtocolVersionAndCommand) IsUnspec() bool
IsUnspec returns true if the command is unspecified, false otherwise.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
client
command
Package main provides a proxyproto client example.
|
Package main provides a proxyproto client example. |
|
httpserver
command
Package main provides a proxyproto HTTP server example.
|
Package main provides a proxyproto HTTP server example. |
|
server
command
Package main provides a proxyproto server example.
|
Package main provides a proxyproto server example. |
|
tlsclient
command
Package main provides a proxyproto + TLS client example.
|
Package main provides a proxyproto + TLS client example. |
|
tlsserver
command
Package main provides a proxyproto + TLS server example.
|
Package main provides a proxyproto + TLS server example. |
|
udpclient
command
Package main provides a proxyproto UDP client example.
|
Package main provides a proxyproto UDP client example. |
|
udppacketconn
command
Package main sketches a net.PacketConn wrapper for the PROXY protocol over UDP.
|
Package main sketches a net.PacketConn wrapper for the PROXY protocol over UDP. |
|
udpserver
command
Package main provides a proxyproto UDP server example.
|
Package main provides a proxyproto UDP server example. |
|
helper
|
|
|
http2
Package http2 provides helpers for HTTP/2.
|
Package http2 provides helpers for HTTP/2. |
|
Package tlvparse provides helpers for PROXY protocol TLVs.
|
Package tlvparse provides helpers for PROXY protocol TLVs. |