edgeproxy

package
v0.2.31 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 30, 2026 License: MPL-2.0 Imports: 8 Imported by: 0

Documentation

Overview

Package edgeproxy is the chassis side of the trusted-edge contract:

trusted edge | PROXY v2 (+ AUTHORITY, SSL, ALPN TLVs) → TCP inlet

An edge that terminates TLS in front of the chassis (Caddy today) has already seen everything an inlet would learn from its own handshake — the real client address, the SNI hostname, the negotiated ALPN and TLS version. It hands them over in the PROXY protocol header it writes before the first byte of the stream, and this package is the one place that header is accepted and read.

The trust boundary is the peer address: a header is REQUIRED from the operator's trusted CIDRs and never honoured from anyone else (their connection is served as-is, header bytes and all). So a parsed header is proof the connection came through the front door, and its contents are chassis facts, not client input.

An HTTP head behind HTTP proxies gets the same fact from the same boundary, in a header instead (clientip.go): X-Forwarded-For counts only when the socket peer is one of those CIDRs.

Leaf package: no chassis imports, so any head (imap, tcp, web, …) can use it.

Index

Constants

View Source
const DefaultHeaderTimeout = 5 * time.Second

DefaultHeaderTimeout bounds how long a trusted peer may take to send its header. The edge writes it with the first packet; anything slower is a broken or hostile peer holding a socket open.

Variables

View Source
var ErrNoHeader = errors.New("edgeproxy: trusted peer sent no valid PROXY header")

ErrNoHeader is Read's answer for a trusted peer that sent no (or an unparseable) PROXY header. The stream is unusable — every read on it fails — so the caller closes.

Functions

func Fronted

func Fronted(conn net.Conn) bool

Fronted reports whether a trusted edge presented a PROXY header on conn — the cheap form of Read for heads that only need the yes/no.

func ParseTrusted

func ParseTrusted(entries []string) (nets []*net.IPNet, bad []string)

ParseTrusted turns operator entries (CIDRs, or bare IPs meaning /32 or /128) into networks. Blank entries are dropped; entries that parse as neither come back in bad so the caller decides whether that is a warning (imap, web) or a refusal to start (tcp).

An entry may itself hold several, separated by commas or spaces. A list flag read from one environment variable arrives split one way or the other depending on who wrote it, and the wrong guess used to leave "a,b" as one unparseable entry — a trust list that silently trusted nobody.

func Trusted

func Trusted(nets []*net.IPNet, a net.Addr) bool

Trusted reports whether a socket peer address is inside nets.

func Wrap

func Wrap(ln net.Listener, trusted []*net.IPNet, headerTimeout time.Duration) net.Listener

Wrap makes ln accept a PROXY header (v1 or v2) from trusted peers: REQUIRE from inside trusted, SKIP for everyone else — their connection is handed back untouched and a header they send is just stream bytes. Wrap BEFORE tls.NewListener: the header precedes the handshake. headerTimeout <= 0 means DefaultHeaderTimeout. With no trusted networks ln is returned as is.

Types

type Clients

type Clients struct {
	// contains filtered or unexported fields
}

Clients resolves the client address of HTTP requests for one trust list. The zero value and nil trust nobody: the client is the socket peer.

func NewClients

func NewClients(entries []string) (c *Clients, bad []string)

NewClients builds a resolver from operator entries (--web-trusted-proxies; see ParseTrusted for the forms). Unparseable entries come back in bad and are not trusted.

func (*Clients) IP

func (c *Clients) IP(r *http.Request) string

IP returns the address of the client that sent r, without a port.

  • The peer is not a trusted proxy (or none are configured): the peer. X-Forwarded-For is not read.
  • The peer is a trusted proxy: the first X-Forwarded-For address from the right that is not a trusted proxy.
  • Every address in it is a trusted proxy — the request began inside, say one app calling another: the leftmost, the one that began it.
  • The header is missing, or the address a proxy recorded cannot be read ("unknown", an obfuscated node name): the peer. Nothing to the left of an unreadable hop is trustworthy, and the proxy's own address is the conservative key for a throttle — shared, never forgeable.

func (*Clients) Networks

func (c *Clients) Networks() []string

Networks lists the trusted proxies as CIDRs — what was parsed, not what was asked for — for a start-up log line.

func (*Clients) Trusts

func (c *Clients) Trusts() bool

Trusts reports whether any proxy is trusted: whether IP can ever answer with something other than the socket peer.

type Facts

type Facts struct {
	Present bool

	// The real client and the address it dialled, from the header. The
	// wrapped conn's RemoteAddr/LocalAddr already report these too.
	ClientIP   string
	ClientPort int
	LocalIP    string
	LocalPort  int

	Authority  string // PP2_TYPE_AUTHORITY: the hostname the client asked for (SNI), raw
	ALPN       string // PP2_TYPE_ALPN
	TLS        bool   // PP2_TYPE_SSL says the client leg was TLS
	TLSVersion string // PP2_SUBTYPE_SSL_VERSION, in crypto/tls spelling ("TLS 1.3")
}

Facts is what a trusted edge said about one connection. The zero value (Present false) means nobody vouched for it: an untrusted peer, an unwrapped listener, or the edge's own LOCAL health probe.

func Read

func Read(conn net.Conn) (Facts, error)

Read reports what the edge said about conn, blocking until the header has arrived (bounded by Wrap's headerTimeout). Call it off the accept loop. A conn from a listener Wrap never touched, or from an untrusted peer, yields the zero Facts and no error.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL