summaryrefslogtreecommitdiff
path: root/fw/man/fwrules
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/man/fwrules
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/man/fwrules')
-rw-r--r--fw/man/fwrules224
1 files changed, 224 insertions, 0 deletions
diff --git a/fw/man/fwrules b/fw/man/fwrules
new file mode 100644
index 0000000..143ac08
--- /dev/null
+++ b/fw/man/fwrules
@@ -0,0 +1,224 @@
+.TH FWRULES 6
+.SH NAME
+fwrules \- firewall rule files
+.SH DESCRIPTION
+.IR Fw (8)
+decides what may cross a network by matching traffic against a file of
+rules.
+The file is an
+.IR ndb (6)
+file: one entry is one rule.
+.PP
+A rule set for a machine that may look up names and fetch pages, and do
+nothing else:
+.IP
+.EX
+allow=out proto=udp port=53
+allow=out proto=tcp port=53
+allow=out proto=tcp port=80
+allow=out proto=tcp port=443
+
+deny=* log=yes
+.EE
+.PP
+Rules are matched from the top, the first one that matches decides, and
+traffic matching none of them is denied.
+A file with no rules therefore permits nothing, and the last rule above
+changes nothing about what is allowed - it exists so that the refusals
+are written down instead of happening silently.
+.PP
+There is no rule permitting the replies to any of this, and none is
+needed: see
+.B STATE
+below.
+.SH ACTION
+Every rule begins with an action, whose value is the direction it
+governs:
+.TP
+.BI allow= dir
+permit.
+.TP
+.BI deny= dir
+refuse.
+.PP
+.I Dir
+is
+.BR out ,
+.BR in ,
+or
+.B *
+for either.
+.B Out
+is traffic begun from the side being protected;
+.B in
+is traffic begun toward it.
+That holds wherever the rule is enforced: on a gateway the protected
+side is the inside, and in a namespace it is the program, whose
+.B connect
+is
+.B out
+and whose
+.B announce
+is
+.BR in .
+.B Connect
+and
+.B announce
+are accepted as older spellings of
+.B out
+and
+.BR in .
+.SH ATTRIBUTES
+The rest of a rule says what it matches.
+An attribute that is absent does not constrain, so there is never a
+.B *
+to write:
+.TP
+.BI proto= name
+a protocol:
+.BR tcp ,
+.BR udp ,
+.BR icmp ,
+and so on.
+.TP
+.BI port= n
+the port at the far end.
+.TP
+.BI lport= n
+the port at this end.
+A program announcing a port is naming this end, so
+.B announce 17019
+is matched by
+.BR lport=17019 ,
+and by
+.B port=
+never - at that moment nobody has called, so there is no far end.
+.TP
+.BI ip= address
+the address at the far end, optionally carrying a
+.BI / mask
+suffix.
+IPv4 and IPv6 are written the usual way and need no distinguishing.
+.TP
+.BI ipmask= mask
+the mask, written separately, as
+.B /24
+or in full.
+.TP
+.B log=yes
+note every match of this rule in
+.BR /sys/log/fw .
+.PP
+An unrecognised attribute is an error, and
+.I fw
+refuses to start rather than run with it ignored: a mistyped constraint
+would otherwise silently widen the rule it was meant to narrow.
+.PP
+An entry may be spread over indented continuation lines, as any
+.IR ndb (6)
+entry may:
+.IP
+.EX
+allow=out
+ proto=tcp
+ port=443
+.EE
+.SH ORDER
+The first match decides, so a rule carving an exception out of a
+broader rule must come above it.
+This is right:
+.IP
+.EX
+deny=out ip=1.1.1.1
+allow=out proto=tcp port=443
+.EE
+.PP
+and this is not, because 1.1.1.1:443 matches the allow first and the
+deny is never reached:
+.IP
+.EX
+allow=out proto=tcp port=443
+deny=out ip=1.1.1.1
+.EE
+.PP
+Rules are numbered from one in the order they appear, and that is the
+number a refusal names in
+.BR /sys/log/fw .
+.SH STATE
+Connections are tracked, so a rule permitting traffic one way permits
+the replies without a second rule.
+A permitted packet records the protocol and both addresses and ports;
+anything matching that, either way round, passes without consulting the
+rules again.
+.PP
+UDP has no connections, so a flow is that same tuple and lasts 60
+seconds after the last packet.
+TCP lasts 300 seconds, everything else 30.
+ICMP has no ports, so its flows are the two addresses alone, which is
+enough for a reply to an echo to be recognised, but does not tie an
+ICMP error to the connection it is about.
+.PP
+When the rules change, connections the new rules forbid are dropped
+rather than left to finish: a block blocks.
+.SH EXAMPLES
+A gateway.
+The machines behind it may reach the web, one host is refused outright,
+and the only thing the internet may reach is a web server:
+.IP
+.EX
+# the exception first, or the allows below would match
+# 1.1.1.1:443 before this was ever reached
+deny=out ip=1.1.1.1
+
+# out: what the machines behind me may reach
+allow=out proto=udp port=53
+allow=out proto=tcp port=53
+allow=out proto=tcp port=80
+allow=out proto=tcp port=443
+
+# in: what the internet may reach here
+allow=in proto=tcp lport=443
+
+# and note anything else that tries, either way
+deny=* log=yes
+.EE
+.PP
+On a gateway facing the internet that last rule will log a great deal,
+since the internet knocks on every door constantly.
+Narrow it to
+.B deny=out
+if only the traffic from your own machines is worth recording.
+.PP
+A program that may resolve names and fetch pages over TLS, and nothing
+else:
+.IP
+.EX
+allow=out proto=udp port=53
+allow=out proto=tcp port=443
+.EE
+.PP
+A service that answers on one port and never calls out:
+.IP
+.EX
+allow=in proto=tcp lport=17019
+.EE
+.PP
+No network at all.
+An empty file permits nothing, so this is a complete rule set:
+.IP
+.EX
+# nothing
+.EE
+.SH FILES
+.TP
+.B /lib/fw
+rule sets
+.TP
+.B /lib/fw/example.ndb
+every attribute, with comments
+.SH "SEE ALSO"
+.IR fw (8),
+.IR ndb (6)
+.SH BUGS
+A rule cannot name which network card it applies to, so a machine
+filtering two cards needs a file for each.