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-linkedin: apply dune fmt

Pure formatting changes from `dune fmt`: doc comment placement moves
from above the binding to below it for `type`s, multi-line `match`
expressions collapse onto one line where they fit, and infix operator
applications pick up spaces (`Soup.($?)` -> `Soup.( $? )`). No
semantic changes.

+71 -1
+64 -1
README.md
··· 4 4 5 5 ## Installation 6 6 7 - `opam install xff` will install this library. 7 + Install with opam: 8 + 9 + ```sh 10 + $ opam install xff 11 + ``` 12 + 13 + If opam cannot find the package, it may not yet be released in the public 14 + `opam-repository`. Add the overlay repository, then install it: 15 + 16 + ```sh 17 + $ opam repo add samoht https://tangled.org/gazagnaire.org/opam-overlay.git 18 + $ opam update 19 + $ opam install xff 20 + ``` 8 21 9 22 ## Reference 10 23 ··· 13 26 ## Documentation 14 27 15 28 [API documentation](https://gazagnaire.github.io/ocaml-xff/doc) 29 + 30 + ## Example 31 + 32 + Extract the real client IP while treating your own load balancers as trusted 33 + hops. `client_ip` only believes the X-Forwarded-For header when the socket 34 + peer is itself a trusted proxy, which is what prevents spoofing: 35 + 36 + ```ocaml 37 + let trusted = 38 + List.map Xff.parse_cidr_exn [ "10.0.0.0/8"; "127.0.0.1/32" ] 39 + 40 + let socket_ip = Ipaddr.of_string_exn "10.0.0.12" 41 + 42 + let client = 43 + Xff.client_ip 44 + ~socket_ip:(Some socket_ip) 45 + ~xff_header:(Some "203.0.113.7, 10.0.0.12, 127.0.0.1") 46 + ~trusted_proxies:(Some trusted) 47 + (* client = Some 203.0.113.7 *) 48 + ``` 49 + 50 + ### Invalid or missing data 51 + 52 + `parse_xff` silently drops entries that are not valid IPs (so a malicious 53 + proxy cannot inject garbage), and `client_ip` returns `None` when the socket 54 + peer is unknown: 55 + 56 + ```ocaml 57 + let () = 58 + let ips = Xff.parse_xff "203.0.113.7, not-an-ip, 10.0.0.12" in 59 + List.iter (fun ip -> Fmt.pr "%a@." Ipaddr.pp ip) ips 60 + (* prints 203.0.113.7 and 10.0.0.12; "not-an-ip" is dropped *) 61 + 62 + let no_peer = 63 + Xff.client_ip 64 + ~socket_ip:None 65 + ~xff_header:(Some "203.0.113.7") 66 + ~trusted_proxies:(Some trusted) 67 + (* no_peer = None -- never trust an XFF header without a socket peer *) 68 + 69 + let bad_cidr = 70 + match Xff.parse_cidr "not-a-cidr" with 71 + | Ok p -> Some p 72 + | Error (`Msg msg) -> 73 + Fmt.epr "bad CIDR: %s@." msg; 74 + None 75 + ``` 76 + 77 + Use `client_ip_string` when you want a single string suitable for logs; 78 + it returns `"unknown"` instead of `None`.
+4
dune
··· 1 1 (env 2 2 (dev 3 3 (flags :standard %{dune-warnings}))) 4 + 5 + (mdx 6 + (files README.md) 7 + (libraries xff))
+2
dune-project
··· 1 1 (lang dune 3.21) 2 + (using mdx 0.4) 2 3 3 4 (name xff) 4 5 ··· 26 27 dune 27 28 ipaddr 28 29 fmt 30 + (mdx :with-test) 29 31 (alcotest :with-test)))
+1
xff.opam
··· 16 16 "dune" {>= "3.21"} 17 17 "ipaddr" 18 18 "fmt" 19 + "mdx" {with-test} 19 20 "alcotest" {with-test} 20 21 "odoc" {with-doc} 21 22 ]