Finally, found a good use-case for OCaml

akrylov2 pts1 comments

lpf

lpf

Next-generation Linux firewall

PF-style syntax<br>nftables first<br>safe remote apply<br>JSON ready

Why

Linux firewalling should feel like one system.

Linux packet policy is powerful, but it is split across nftables, policy routing, tc, conntrack, rollback files, and logging. lpf was created to make that surface readable, reviewable, testable, and safe to apply on remote machines.

One policy

PF-style rules describe filtering, NAT, routing intent, queues, tables, and logging in one file.

Safe changes

Plans, diffs, guarded apply, confirmation timers, history, and rollback are part of the operating model.

Explainable operations

Operators can ask what would change, why a packet matched, and what state must be restored before touching production networking.

Install

Install from GitHub Releases.

Use the native Debian or RPM package for a host install. Clone the repository only when you want the OCaml build, tests, and fixtures locally.

Debian / Ubuntu

$ OUT=/tmp/lpf-release<br>$ mkdir -p "$OUT"<br>$ gh release download \<br>--repo ingresslabs/lpf \<br>--pattern 'lpf_*_amd64.deb' \<br>--dir "$OUT"<br>$ sudo apt install "$OUT"/lpf_*_amd64.deb

RPM hosts

$ OUT=/tmp/lpf-release<br>$ mkdir -p "$OUT"<br>$ gh release download \<br>--repo ingresslabs/lpf \<br>--pattern 'lpf-*.x86_64.rpm' \<br>--dir "$OUT"<br>$ sudo dnf install "$OUT"/lpf-*.x86_64.rpm

Source checkout

$ git clone \<br>https://github.com/ingresslabs/lpf.git<br>$ cd lpf<br>$ opam switch create . \<br>ocaml-base-compiler.5.2.1<br>$ opam install . \<br>--deps-only --with-test<br>$ dune build<br>$ dune runtest

Usage

Check first. Diff live. Apply guarded.

Use policy files as code: validate before touching the host, inspect live drift, ask why a packet matches, then apply with a confirmation timer.

Policy loop

$ lpf check /etc/lpf.conf<br>$ lpf fmt --check /etc/lpf.conf<br>$ lpf plan --json /etc/lpf.conf<br>$ lpf diff --live /etc/lpf.conf

Guarded deploy

$ lpf apply --confirm 60s /etc/lpf.conf<br># verify the session still reaches the host<br>$ lpf confirm

Explain a packet

$ lpf explain --src 10.0.0.5 --dst 1.1.1.1 --dport 443 --tcp --in /etc/lpf.conf

Configs

Policy examples

Web server public HTTP/HTTPS, restricted SSH

set default deny

interface wan = "eth0"

table { 198.51.100.10, 203.0.113.0/24 }

pass in log on wan proto tcp from any to any port 80<br>pass in log on wan proto tcp from any to any port 443<br>pass in log (user) on wan proto tcp from to any port 22 keep state<br>block in log (all) on wan proto tcp from any to any port 22<br>pass out on wan proto udp from any to any port 53 keep state<br>pass out on wan proto tcp from any to any port 80 keep state<br>pass out on wan proto tcp from any to any port 443 keep state<br>block in log from any to any

Reverse proxy public redirects to internal app listeners

set default deny

interface app = "eth1"<br>interface wan = "eth0"

table { 198.51.100.10, 203.0.113.0/24 }<br>table { 10.20.0.10, 10.20.0.11 }

rdr on wan proto tcp from any to any port 80 -> 10.20.0.10 port 8080<br>rdr on wan proto tcp from any to any port 443 -> 10.20.0.10 port 8443

pass in log on wan proto tcp from any to any port 80<br>pass in log on wan proto tcp from any to any port 443<br>pass in on app proto tcp from to any port 8080 keep state<br>pass in on app proto tcp from to any port 8443 keep state<br>pass in log (user) on wan proto tcp from to any port 22 keep state<br>pass out on wan proto tcp from any to any port 443 keep state<br>block in log from any to any

NAT gateway LAN masquerade and controlled egress

set default deny

interface lan = "eth1"<br>interface wan = "eth0"

table { 10.0.0.66, 10.0.0.67 }<br>table { 10.0.0.0/24 }

nat on wan from to any -> wan

block in log (user) on lan from to any<br>pass in on lan from to any<br>pass out on wan proto udp from to any port 53 keep state<br>pass out on wan proto tcp from to any port 80 keep state<br>pass out on wan proto tcp from to any port 443 keep state<br>pass out on wan proto icmp from to any keep state<br>block out log (all) on wan from any to any

Workstation egress default-deny client outbound policy

set default deny

interface uplink = "wlan0"

table { 1.1.1.1, 9.9.9.9 }<br>table { 198.51.100.20, 203.0.113.20 }

pass out on uplink proto udp from any to port 53 keep state<br>pass out on uplink proto tcp from any to any port 80 keep state<br>pass out on uplink proto tcp from any to any port 443 keep state<br>pass out on uplink proto udp from any to any port 123 keep state<br>pass out on uplink proto tcp from any to port 22 keep state<br>block in log (all) on uplink from any to any<br>block out log (user) on uplink from any to any

DNS resolver LAN clients, upstream DNS, admin SSH

set default deny

interface lan = "eth1"<br>interface wan = "eth0"

table { 10.0.0.10, 10.0.0.11 }<br>table { 10.0.0.0/24, 192.168.10.0/24 }<br>table { 1.1.1.1, 9.9.9.9 }

pass in log on lan proto udp from to any port 53<br>pass in log on lan proto tcp from to any port 53<br>pass out on wan proto udp from any to port 53 keep state<br>pass out on wan proto tcp from any to port 53 keep state<br>pass in on lan proto tcp from to any port 22 keep...

from port proto pass state keep

Related Articles