brazier/ip

Types

A request’s client address and scheme, from one parse of its forwarding header (see client).

pub type Client {
  Client(address: String, scheme: Result(String, String))
}

Constructors

  • Client(address: String, scheme: Result(String, String))

Which forwarding header the trusted proxies set.

pub type ForwardingHeader {
  Forwarded
  XForwardedFor
  NoForwardingHeader
}

Constructors

  • Forwarded

    RFC 7239 Forwarded: for=...;proto=....

  • XForwardedFor

    X-Forwarded-For: client, proxy1, ..., with X-Forwarded-Proto for the scheme.

  • NoForwardingHeader

    No header is read: the socket peer is always the client address and the scheme is always http. Proxies may still be listed: they are exempt from the per-IP connection cap.

A parsed list of trusted proxy networks, without a header. The listener uses it to exempt proxies from the per-IP connection cap.

pub type ProxyNetworks

What one request’s peer and forwarding headers resolved to, and the trusted proxies that resolution honoured. A request that carries one (server.Connection.resolved) is never resolved again against the same proxies, so a problem in its forwarding header is logged once.

pub type Resolved {
  Resolved(trusted: TrustedProxies, client: Client)
}

Constructors

Trusted proxy networks and the one forwarding header they set.

pub opaque type TrustedProxies

Values

pub fn client(
  peer: String,
  headers: List(#(String, String)),
  trusted: TrustedProxies,
) -> Client

client_address and client_scheme together, for a caller that needs both (the WebSocket upgrade: its per-IP cap and its Origin check). The configured header is parsed once, so a problem in it (a malformed hop, an unconfigured header from a trusted proxy) is logged once per request, not once per question asked about it.

pub fn client_address(
  peer: String,
  headers: List(#(String, String)),
  trusted: TrustedProxies,
) -> String

Resolve the client address for a request.

peer is the socket peer address. headers are the request headers (lower-case names). When the peer is not trusted the headers are ignored. When it is, only the configured header is read. The other forwarding header is never read; it is logged as a warning, because a proxy that sets one header passes the other through from the client.

pub fn client_scheme(
  peer: String,
  headers: List(#(String, String)),
  trusted: TrustedProxies,
) -> Result(String, String)

The scheme the client used to reach the outermost trusted proxy: http or https.

When the peer is not trusted, or no forwarding header is configured, no header is read and the scheme is http (Brazier does not terminate TLS). Otherwise only the configured header’s scheme is read:

  • Forwarded: the proto= of the element that names the client (the element client_address stops at), since the trusted proxy that appended it saw the client’s connection. An element without proto= is http. X-Forwarded-Proto is not read.
  • X-Forwarded-For: the left-most value of the one X-Forwarded-Proto header, as Django’s SECURE_PROXY_SSL_HEADER reads it, so the outermost proxy must overwrite that header. Forwarded is not read.

Errors when the scheme read is not http or https, the header is sent twice, or an element repeats proto= (RFC 7239 section 4).

pub fn forwarding_header_to_string(
  header: ForwardingHeader,
) -> String

The header name, for logs and error messages.

pub fn is_trusted(
  address: String,
  trusted: TrustedProxies,
) -> Bool

Is the (textual) address inside one of the trusted networks?

pub fn no_trusted_proxies() -> TrustedProxies

No trusted proxies: forwarding headers are never honoured.

pub fn parse_ip(text: String) -> Result(String, Nil)

Normalize a textual IP address (IPv4-mapped IPv6 becomes IPv4).

pub fn parse_proxy_networks(
  entries: List(String),
) -> Result(ProxyNetworks, String)

Parse trusted proxy networks without a header, for the listener’s per-IP cap exemption. Any invalid entry is an error, returned for the caller to log once (the listener logs “Invalid listener options”).

pub fn parse_trusted_proxies(
  entries: List(String),
  header: ForwardingHeader,
) -> Result(TrustedProxies, String)

Parse trusted proxy entries such as "10.0.0.0/8", "127.0.0.1", or "fd00::/8", with the header those proxies set. Any invalid entry is an error: a typo in a proxy list must never silently widen or narrow trust. A header with no proxies is an error too: it would never be read, and the operator plainly expected it to be.

A refusal is returned, not logged: the caller logs it once, with its own context (“Invalid security limits: trusted_proxies: …” when the listener checks its limits), so one refusal makes one log line.

✨ Search Document