summaryrefslogtreecommitdiff
path: root/fw/lib
diff options
context:
space:
mode:
Diffstat (limited to 'fw/lib')
-rw-r--r--fw/lib/example.ndb42
-rw-r--r--fw/lib/fw.ndb13
-rwxr-xr-xfw/lib/fw.rc27
-rwxr-xr-xfw/lib/fwstart61
-rw-r--r--fw/lib/local.ndb7
-rw-r--r--fw/lib/none.ndb3
-rw-r--r--fw/lib/web.ndb7
7 files changed, 160 insertions, 0 deletions
diff --git a/fw/lib/example.ndb b/fw/lib/example.ndb
new file mode 100644
index 0000000..60f5119
--- /dev/null
+++ b/fw/lib/example.ndb
@@ -0,0 +1,42 @@
+#
+# every attribute fw understands. an attribute that is absent does
+# not constrain, so there is never a "*" to write; it is accepted, but
+# leaving the attribute out says the same thing more quietly.
+#
+# allow=<dir> the rule permits. dir is in, out or *
+# deny=<dir> the rule refuses
+# proto=<name> a protocol: tcp, udp, icmp, ...
+# port=<n> the port at the far end
+# lport=<n> the port at this end
+# ip=<addr> the address at the far end, optionally with a /mask
+# ipmask=<mask> the mask, if you would rather write it separately
+#
+# in and out are directions, not layers: the same rule means the same
+# thing whether fw is matching it against a packet on a wire or against
+# a connection a program asked for. connect and announce are accepted
+# as older spellings of out and in.
+#
+# rules are matched top to bottom and the first one that matches wins.
+# nothing matching means deny, so a file with no rules permits nothing.
+#
+
+# deny before allow, since the first match wins
+deny=out ip=1.1.1.1
+deny=out ip=8.8.8.8
+
+# the two spellings of a masked address are the same rule
+allow=out ip=10.0.2.0/24
+allow=out ip=192.168.0.0 ipmask=/16
+
+# v6 needs no distinguishing: it is the same attribute
+allow=out ip=2001:db8::/32
+
+# an entry may also be spread over indented continuation lines
+allow=out
+ proto=tcp
+ port=443
+
+# answer on one port, but never call out on it. lport is our end, so
+# this is the port we serve; port would be the caller's, which we do not
+# get to know until they call.
+allow=in proto=tcp lport=17019
diff --git a/fw/lib/fw.ndb b/fw/lib/fw.ndb
new file mode 100644
index 0000000..0292f53
--- /dev/null
+++ b/fw/lib/fw.ndb
@@ -0,0 +1,13 @@
+#
+# Which cards are firewalled, and with what. Read by fwstart(8) at
+# boot. The address is not named here: fw reads it from the card it
+# takes over, so this file and ipconfig(8) cannot drift apart.
+#
+# One entry per card. Everything the card sends or receives is
+# filtered; see fwrules(6) for the rule files themselves.
+#
+
+fw=ether0 rules=/lib/fw/host.ndb
+
+# a second card, if this machine has one
+#fw=ether1 rules=/lib/fw/lan.ndb
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 $*
diff --git a/fw/lib/fwstart b/fw/lib/fwstart
new file mode 100755
index 0000000..00b4d3a
--- /dev/null
+++ b/fw/lib/fwstart
@@ -0,0 +1,61 @@
+#!/bin/rc
+# fwstart [cfg] - start a firewall for each card named in /lib/ndb/fw.
+#
+# Run this after the network is configured and before anything dials.
+# fw reads each card's address from the card itself, so the addresses
+# have to be there already; and a program that connects before fw is up
+# is a program that was never filtered.
+rfork e
+
+cfg=/lib/ndb/fw
+if(! ~ $#* 0)
+ cfg=$1
+if(! test -f $cfg){
+ echo fwstart: no $cfg, nothing to do >[1=2]
+ exit
+}
+
+# the cards named in the config
+fn cards {
+ awk '
+ /^[ \t]*#/ { next }
+ { for(i = 1; i <= NF; i++) if($i ~ /^fw=/) print substr($i, 4) }
+ ' $cfg
+}
+
+# the rule file for one card
+fn rulesfor {
+ awk -v 'want='^$1 '
+ /^[ \t]*#/ { next }
+ {
+ dev = ""; rules = ""
+ for(i = 1; i <= NF; i++){
+ if($i ~ /^fw=/) dev = substr($i, 4)
+ if($i ~ /^rules=/) rules = substr($i, 7)
+ }
+ if(dev == want && rules != ""){ print rules; exit }
+ }
+ ' $cfg
+}
+
+# each card gets its own control directory; mntgen makes them appear
+if(! test -d /mnt/fw)
+ mntgen /mnt/fw
+
+for(name in `{cards}){
+ dev=/net/$name
+ rules=`{rulesfor $name}
+ if(! test -e $dev)
+ echo fwstart: no $dev, skipped >[1=2]
+ if not if(~ $#rules 0)
+ echo fwstart: no rules given for $name, skipped >[1=2]
+ if not if(! test -f $rules)
+ echo fwstart: $rules missing, $name skipped >[1=2]
+ if not {
+ fw -m /mnt/fw/$name -e $dev $rules
+ if(~ $status '')
+ echo fwstart: $name filtered by $rules
+ if not
+ echo fwstart: $name failed to start >[1=2]
+ }
+}
diff --git a/fw/lib/local.ndb b/fw/lib/local.ndb
new file mode 100644
index 0000000..2b846c4
--- /dev/null
+++ b/fw/lib/local.ndb
@@ -0,0 +1,7 @@
+#
+# the local network, and one port to answer on.
+#
+allow=out ip=10.0.2.0/24
+allow=out ip=192.168.0.0/16
+allow=out ip=fe80::/10
+allow=in proto=tcp lport=17019
diff --git a/fw/lib/none.ndb b/fw/lib/none.ndb
new file mode 100644
index 0000000..c12c0aa
--- /dev/null
+++ b/fw/lib/none.ndb
@@ -0,0 +1,3 @@
+#
+# no rules. the default is deny, so this program gets no network at all.
+#
diff --git a/fw/lib/web.ndb b/fw/lib/web.ndb
new file mode 100644
index 0000000..709df98
--- /dev/null
+++ b/fw/lib/web.ndb
@@ -0,0 +1,7 @@
+#
+# a browser: name resolution and the web, nothing else.
+#
+allow=out proto=udp port=53
+allow=out proto=tcp port=53
+allow=out proto=tcp port=80
+allow=out proto=tcp port=443