X-Forwarded-For parsing and trusted proxy detection for OCaml
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

OCaml 87.1%
Dune 3.4%
Other 9.5%
31 1 0

Clone this repository

https://tangled.org/gazagnaire.org/ocaml-xff https://tangled.org/did:plc:jhift2vwcxhou52p3sewcrpx/ocaml-xff
git@git.recoil.org:gazagnaire.org/ocaml-xff git@git.recoil.org:did:plc:jhift2vwcxhou52p3sewcrpx/ocaml-xff

For self-hosted knots, clone URLs may differ based on your setup.

Download tar.gz
README.md

XFF - X-Forwarded-For parsing and trusted proxy detection#

Parse X-Forwarded-For headers to extract client IP addresses behind proxies. Supports trusted proxy validation using CIDR ranges to prevent IP spoofing. Implements de-facto X-Forwarded-For standard and aligns with RFC 7239.

Installation#

Install with opam:

$ opam install xff

If opam cannot find the package, it may not yet be released in the public opam-repository. Add the overlay repository, then install it:

$ opam repo add samoht https://tangled.org/gazagnaire.org/opam-overlay.git
$ opam update
$ opam install xff

Reference#

Documentation#

API documentation

Example#

Extract the real client IP while treating your own load balancers as trusted hops. client_ip only believes the X-Forwarded-For header when the socket peer is itself a trusted proxy, which is what prevents spoofing:

let trusted =
  List.map Xff.parse_cidr_exn [ "10.0.0.0/8"; "127.0.0.1/32" ]

let socket_ip = Ipaddr.of_string_exn "10.0.0.12"

let client =
  Xff.client_ip
    ~socket_ip:(Some socket_ip)
    ~xff_header:(Some "203.0.113.7, 10.0.0.12, 127.0.0.1")
    ~trusted_proxies:(Some trusted)
(* client = Some 203.0.113.7 *)

Invalid or missing data#

parse_xff silently drops entries that are not valid IPs (so a malicious proxy cannot inject garbage), and client_ip returns None when the socket peer is unknown:

let () =
  let ips = Xff.parse_xff "203.0.113.7, not-an-ip, 10.0.0.12" in
  List.iter (fun ip -> Fmt.pr "%a@." Ipaddr.pp ip) ips
  (* prints 203.0.113.7 and 10.0.0.12; "not-an-ip" is dropped *)

let no_peer =
  Xff.client_ip
    ~socket_ip:None
    ~xff_header:(Some "203.0.113.7")
    ~trusted_proxies:(Some trusted)
(* no_peer = None -- never trust an XFF header without a socket peer *)

let bad_cidr =
  match Xff.parse_cidr "not-a-cidr" with
  | Ok p -> Some p
  | Error (`Msg msg) ->
      Fmt.epr "bad CIDR: %s@." msg;
      None

Use client_ip_string when you want a single string suitable for logs; it returns "unknown" instead of None.