lpf

lpf

Next-generation Linux firewall

PF-style syntax nftables first safe remote apply 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
$ mkdir -p "$OUT"
$ gh release download \
  --repo ingresslabs/lpf \
  --pattern 'lpf_*_amd64.deb' \
  --dir "$OUT"
$ sudo apt install "$OUT"/lpf_*_amd64.deb
RPM hosts
$ OUT=/tmp/lpf-release
$ mkdir -p "$OUT"
$ gh release download \
  --repo ingresslabs/lpf \
  --pattern 'lpf-*.x86_64.rpm' \
  --dir "$OUT"
$ sudo dnf install "$OUT"/lpf-*.x86_64.rpm
Source checkout
$ git clone \
  https://github.com/ingresslabs/lpf.git
$ cd lpf
$ opam switch create . \
  ocaml-base-compiler.5.2.1
$ opam install . \
  --deps-only --with-test
$ dune build
$ 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
$ lpf fmt --check /etc/lpf.conf
$ lpf plan --json /etc/lpf.conf
$ lpf diff --live /etc/lpf.conf
Guarded deploy
$ lpf apply --confirm 60s /etc/lpf.conf
# verify the session still reaches the host
$ 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 serverpublic HTTP/HTTPS, restricted SSH
set default deny

interface wan = "eth0"

table <admin> { 198.51.100.10, 203.0.113.0/24 }

pass in log on wan proto tcp from any to any port 80
pass in log on wan proto tcp from any to any port 443
pass in log (user) on wan proto tcp from <admin> to any port 22 keep state
block in log (all) on wan proto tcp from any to any port 22
pass out on wan proto udp from any to any port 53 keep state
pass out on wan proto tcp from any to any port 80 keep state
pass out on wan proto tcp from any to any port 443 keep state
block in log from any to any
Reverse proxypublic redirects to internal app listeners
set default deny

interface app = "eth1"
interface wan = "eth0"

table <admin> { 198.51.100.10, 203.0.113.0/24 }
table <apps> { 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
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
pass in log on wan proto tcp from any to any port 443
pass in on app proto tcp from <apps> to any port 8080 keep state
pass in on app proto tcp from <apps> to any port 8443 keep state
pass in log (user) on wan proto tcp from <admin> to any port 22 keep state
pass out on wan proto tcp from any to any port 443 keep state
block in log from any to any
NAT gatewayLAN masquerade and controlled egress
set default deny

interface lan = "eth1"
interface wan = "eth0"

table <blocked> { 10.0.0.66, 10.0.0.67 }
table <lan_hosts> { 10.0.0.0/24 }

nat on wan from <lan_hosts> to any -> wan

block in log (user) on lan from <blocked> to any
pass in on lan from <lan_hosts> to any
pass out on wan proto udp from <lan_hosts> to any port 53 keep state
pass out on wan proto tcp from <lan_hosts> to any port 80 keep state
pass out on wan proto tcp from <lan_hosts> to any port 443 keep state
pass out on wan proto icmp from <lan_hosts> to any keep state
block out log (all) on wan from any to any
Workstation egressdefault-deny client outbound policy
set default deny

interface uplink = "wlan0"

table <dns> { 1.1.1.1, 9.9.9.9 }
table <update_mirrors> { 198.51.100.20, 203.0.113.20 }

pass out on uplink proto udp from any to <dns> port 53 keep state
pass out on uplink proto tcp from any to any port 80 keep state
pass out on uplink proto tcp from any to any port 443 keep state
pass out on uplink proto udp from any to any port 123 keep state
pass out on uplink proto tcp from any to <update_mirrors> port 22 keep state
block in log (all) on uplink from any to any
block out log (user) on uplink from any to any
DNS resolverLAN clients, upstream DNS, admin SSH
set default deny

interface lan = "eth1"
interface wan = "eth0"

table <admin> { 10.0.0.10, 10.0.0.11 }
table <clients> { 10.0.0.0/24, 192.168.10.0/24 }
table <upstream_dns> { 1.1.1.1, 9.9.9.9 }

pass in log on lan proto udp from <clients> to any port 53
pass in log on lan proto tcp from <clients> to any port 53
pass out on wan proto udp from any to <upstream_dns> port 53 keep state
pass out on wan proto tcp from any to <upstream_dns> port 53 keep state
pass in on lan proto tcp from <admin> to any port 22 keep state
block in log (user) on wan proto udp from any to any port 53
block in log (user) on wan proto tcp from any to any port 53
block in log from any to any