summaryrefslogtreecommitdiff
path: root/fw/lib/fw.rc
diff options
context:
space:
mode:
authorCalvin Morrison <calvin@pobox.com>2026-08-18 17:01:49 -0400
committerCalvin Morrison <calvin@pobox.com>2026-08-18 17:01:49 -0400
commit0f922552ad8cc73c0c3c3674d484c3d78dd8c557 (patch)
tree4bfc4222ccc202daa2d79d6a782727fac1fb9ae0 /fw/lib/fw.rc
parent441c64d81594bb521350dbee6348f552401a1a2e (diff)
fw: a firewall, at a card, between two networks, or in front of a namespace
One program with three modes, sharing one rule engine and one ndb rule language. Which mode it is depends on what you point it at, and it says so at startup rather than choosing silently. fw -e /net/ether0 rules.ndb a card: every packet in or out fw rules.ndb <side> <side> two networks: everything crossing fw rules.ndb one namespace: what programs ask for The first two filter packets on a wire, using the pkt medium: the stack gives up its card and gets a synthetic one with fw on the other end, so nothing reaches it that fw did not pass. Since the stack no longer has ethernet, fw answers ARP for the address it stands in for. The third serves a filtered /net and matches connect and announce before they reach the kernel, so a refusal comes back out of dial(2) with a reason. That is only a boundary if the program also loses #I, which /dev/drivers does and cannot be undone; fw.rc does it in the right order. Rules are ndb, matched top to bottom, first match wins, no match denies. Connections are tracked, so permitting traffic one way permits the replies. A rule change drops connections the new rules forbid rather than letting them finish: a block blocks. Logging is per rule, to /sys/log/fw. Tested on the init-test VM in all three modes: a page fetched through a real card, a TCP handshake across two networks, request filtering with the escape routes closed, live rule changes killing established connections, and one rule file working unchanged at both altitudes. doc/todo.md has what is not done. Item 1 is the one that matters: a fw that dies takes the card's address with it, so the machine loses its network and fw cannot restart unaided. That also blocks svc supervision. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'fw/lib/fw.rc')
-rwxr-xr-xfw/lib/fw.rc27
1 files changed, 27 insertions, 0 deletions
diff --git a/fw/lib/fw.rc b/fw/lib/fw.rc
new file mode 100755
index 0000000..09f2960
--- /dev/null
+++ b/fw/lib/fw.rc
@@ -0,0 +1,27 @@
+#!/bin/rc
+# fw.rc - run a command behind a firewall of its own.
+#
+# fw.rc rules.ndb cmd [arg ...]
+#
+# Order matters. The namespace is made private first, the firewall is
+# mounted second, and the devices are dropped last: once dropped they
+# cannot be regained by this process or any child, so fw has to
+# already be serving by then.
+#
+# The devices dropped are the ones that reach a network without going
+# through /net:
+# I the IP stack itself - bind -a '#I' /net undoes everything
+# l ethernet
+# u usb, which can carry an ether device
+# σ shr, where nusb publishes usbnet
+# Relax that set only if you know the machine has no other way out.
+rfork ne
+if(~ $#* 0 1){
+ echo usage: fw.rc rules.ndb cmd [arg ...] >[1=2]
+ exit usage
+}
+rules=$1
+shift
+fw $rules || exit 'fw failed'
+echo chdev '&~' 'Iluσ' >/dev/drivers || exit 'cannot drop devices'
+exec $*