summaryrefslogtreecommitdiff
path: root/fw/man
diff options
context:
space:
mode:
authorCalvin Morrison <calvin@pobox.com>2026-08-19 10:41:03 -0400
committerCalvin Morrison <calvin@pobox.com>2026-08-19 10:41:03 -0400
commitf05383bb3447f7fbd44c9051c4e273caeb33352f (patch)
treed2d2da9c9c5596bc42574a036b79d23faad20351 /fw/man
parentdee31d66b705734be68c1bb21ff5dfc7bdaae23b (diff)
fw: a dead firewall leaves the network down, and can be restarted into it
Two things can happen when a firewall dies: the traffic it was filtering carries on unfiltered, or it stops. Only the second is defensible. A machine briefly off the network is a machine somebody notices and fixes; a machine briefly on the network with no rules is the thing the firewall was installed to prevent, and nobody notices it at all. fw already does the second, in all three modes, and by mechanism rather than by care. Measured rather than assumed: before fw device: /net/ether1 addr: 10.9.9.1 route: 10.9.9.254 fw running device: pkt0 addr: 10.9.9.1 route: 10.9.9.254 fw killed device: addr: route: pktmedium is unbindonclose, so the interface and the address go when fw's fds close, and the card is left bound to nothing with nothing reading it. In a namespace it is harder still: /net answers "i/o on hungup channel" and bind -a '#I' /net answers "mount/attach disallowed", because the device mask was dropped before the program started. So todo item 1 -- "a dead fw takes the network with it", open since the first commit -- was the requirement written down as a defect. It is now design.md and a FAILURE section in fw(8), and the tests assert it, which is the point: this is exactly the property a later helpful change reverses without meaning to. putback() was that change, written and never run; deleting it removed a fail-open path, not just dead code. What was actually broken is recovery, and in a way nobody had reached: a fw that dies leaves its control filesystem mounted, and a corpse of a mount fails everything asked of it -- including the access() check fw makes before touching a card, which then refuses the restart: fw: /tmp/rdbg/ctl: clone failed; not touching /net/ether1 until it exists That check exists so fw does not take a card it cannot then serve, and it was keeping fw from ever coming back. Now the dead mount is cleared first, the same way reclaim() clears the pkt interface the same dead fw left behind: both are its own wreckage. With that and -a/-g in the service file -- so a restart does not need the address it just lost -- the whole cycle works and never passes through open: crash, network down, restart, network up and filtered. Ten checks, three of which fail against the previous fw.c. 87 pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'fw/man')
-rw-r--r--fw/man/fw89
1 files changed, 61 insertions, 28 deletions
diff --git a/fw/man/fw b/fw/man/fw
index 9279c60..0b11ac9 100644
--- a/fw/man/fw
+++ b/fw/man/fw
@@ -348,6 +348,44 @@ everything else instead.
.PP
.B /lib/fw/fw.rc
does this in the right order: private namespace, mount, drop, exec.
+.SH FAILURE
+When
+.I fw
+dies, the traffic it was filtering stops.
+It does not carry on unfiltered.
+.PP
+Filtering a card, the interface that replaced the card is unbound when
+.I fw
+stops and the address goes with it, so the machine has no network at
+all: the card is bound to nothing and nothing is reading it.
+Between two networks, the same happens on both sides and nothing
+crosses.
+Filtering a namespace, the mount is hung up and every path into
+.B /net
+fails; the program cannot put the real one back either, because it was
+denied
+.B #I
+before it started \- see
+.BR CONTAINMENT .
+.PP
+This is deliberate, and it is the reason
+.I fw
+does not try to give the card back as it exits.
+A machine that is briefly off the network is a machine somebody
+notices; a machine that is briefly on the network with no rules is the
+thing the firewall was installed to prevent.
+.PP
+The cost is that
+.I fw
+cannot restart unaided: the address it would read off the card is the
+address that has just gone.
+Give
+.B -a
+and
+.B -g
+so that it does not have to read them, and it can be restarted into a
+machine it has itself left bare \- see
+.BR SUPERVISION .
.SH SUPERVISION
.I Fw
posts its filesystem on
@@ -373,6 +411,10 @@ svc=fw.ether0
args=/mnt/fw/ether0
args=-e
args=/net/ether0
+ args=-a
+ args=10.0.2.15/24
+ args=-g
+ args=10.0.2.2
args=/lib/fw/host.ndb
needs=ipconfig
ready=srv:fw.ether0
@@ -395,17 +437,20 @@ What such a mode would buy is a pid to watch, and the
name is the better signal: it survives the process that made it and
answers the question that matters.
.PP
-Restarting is not free.
-.B Restart=always
-will bring
+The address and gateway are given here rather than read off the card,
+which is the one place that is worth doing.
+A
.I fw
-back, but taking a card is destructive and is not undone, so the card
-must be configured again before the new
+that has died has taken the address with it, so a restarted
.I fw
-can read an address off it \- see
-.B BUGS
-and
-.IR ipconfig (8).
+has nothing to read; told them, it can restart into the bare card it
+left behind, and does \- including clearing away its own control mount,
+which a dead
+.I fw
+leaves behind as a mount that fails everything asked of it.
+Without them the restart fails and the machine stays off the network,
+which is the safe direction but not a working one.
+.PP
A copy of this file per card is the shape to use; one
.I fw
per card is the only shape there is.
@@ -549,26 +594,14 @@ so a fragmented IPv6 datagram does not cross.
IPv4 fragments do: the first one carries the transport header and
decides, and the rest of the datagram inherits what it decided.
.PP
-Taking a card is destructive and is not undone.
-The interface that replaces it is unbound when
-.I fw
-stops, and the address goes with it, so a
-.I fw
-that is killed leaves the card bound to nothing and the machine with no
-network.
-It cannot be restarted unaided either: the address it would have read
-off the card is the address that has just been lost, so it must be told
-one with
-.BR -a ,
-or the card configured again with
-.IR ipconfig (8)
-first.
-Nothing puts the card back.
-Whatever restarts
-.I fw
-must configure the card first, with the address it had, which
+Taking a card is destructive and is not undone, on purpose; see
+.BR FAILURE .
+A
.I fw
-no longer knows.
+that is killed therefore leaves the machine with no network, and must
+be told an address with
+.B -a
+to start again.
.PP
The first packet to an unresolved next hop is dropped while
.I fw