Initial commit of fancy firewall infrastructure.
authorMark Wooding <mdw@distorted.org.uk>
Wed, 10 Dec 2008 10:00:35 +0000 (10:00 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Wed, 10 Dec 2008 10:00:35 +0000 (10:00 +0000)
15 files changed:
.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
base.m4 [new file with mode: 0644]
bookends.m4 [new file with mode: 0644]
classify.m4 [new file with mode: 0644]
config.m4 [new file with mode: 0644]
filter.m4 [new file with mode: 0644]
functions.m4 [new file with mode: 0644]
icmp.m4 [new file with mode: 0644]
local.m4 [new file with mode: 0644]
local.mk [new file with mode: 0644]
metalzone.m4 [new file with mode: 0644]
numbers.m4 [new file with mode: 0644]
prologue.m4 [new file with mode: 0644]
vampire.m4 [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..7ba5f27
--- /dev/null
@@ -0,0 +1,2 @@
+*.sh
+cruft
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..04c9a4a
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,46 @@
+### Makefile for firewall scripts
+
+MAIN_M4_SOURCES                 =
+HOSTS                   =
+
+###--------------------------------------------------------------------------
+### Local configuration.
+
+## Should set up HOSTS and add stuff to MAIN_M4_SOURCES if necessary.
+include        local.mk
+
+###--------------------------------------------------------------------------
+### Configuration.
+
+## The main m4 inputs which construct the firewall.  These are read in last
+## to allow local configuration to change their environments.
+MAIN_M4_SOURCES                += config.m4
+MAIN_M4_SOURCES                += prologue.m4
+MAIN_M4_SOURCES                += functions.m4
+MAIN_M4_SOURCES                += numbers.m4
+MAIN_M4_SOURCES                += bookends.m4
+MAIN_M4_SOURCES                += classify.m4
+MAIN_M4_SOURCES                += icmp.m4
+
+## All of our m4 inputs.  The base gets read first to set things up.
+M4_SOURCES              = base.m4
+M4_SOURCES             += $(MAIN_M4_SOURCES)
+
+###--------------------------------------------------------------------------
+### Hosts.
+
+TARGETS                         = $(addsuffix .sh,$(HOSTS))
+
+###--------------------------------------------------------------------------
+### Building.
+
+all: $(TARGETS)
+
+%.sh: %.m4 $(M4_SOURCES)
+       m4 -P base.m4 $*.m4 $(MAIN_M4_SOURCES) >$@.new
+       chmod +x $@.new
+       mv $@.new $@
+
+clean:; rm -f $(TARGETS) *.new
+
+###----- That's all, folks --------------------------------------------------
diff --git a/base.m4 b/base.m4
new file mode 100644 (file)
index 0000000..5ae9d48
--- /dev/null
+++ b/base.m4
@@ -0,0 +1,95 @@
+m4_divert(-1)
+### -*-m4-*-
+###
+### Failsafe prologue for firewall scripts
+###
+### (c) 2008 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This program is free software; you can redistribute it and/or modify
+### it under the terms of the GNU General Public License as published by
+### the Free Software Foundation; either version 2 of the License, or
+### (at your option) any later version.
+###
+### This program is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+### GNU General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with this program; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+m4_changequote(<:, :>)
+m4_changecom(<:##:>)
+
+###--------------------------------------------------------------------------
+### Overall structure.
+###
+###  0 File header: shebang, do-not-edit warning.              [base]
+###  5 Configuration.                                          [config]
+### 10 Prologue: command-line parsing and failsafe.            [prologue]
+### 20 Function definitions.                                   [functions]
+### 25 Port numbers etc.                                       [numbers]
+### 30 Initialization.                                         [bookends]
+### 30   Clear existing rules.                                 [bookends]
+### 32   Set safe IP options.                                  [bookends]
+### 34   Error chains.                                         [bookends]
+### 36   Give loopback traffic a free pass.                    [bookends]
+### 40 Address classification.                                 [classify]
+### 42   Definition of address class policies.                 [local]
+### 44   Definition of interfaces and addresses.               [local]
+### 46   Handling of default interface.                        [classify]
+### 50 ICMP filtering.                                         [icmp]
+### 52           Local configuration.                                  [local]
+### 58    Finally accept ICMP, hook onto INPUT and FORWARD.    [icmp]
+### 60 Local configuration.                                    [local]
+### 90 Finishing touches.                                      [bookends]
+### 94   Set final policies.                                   [bookends]
+### 99 File footer: do-not-edit warning.                       [base]
+
+###--------------------------------------------------------------------------
+### Headers and footers.
+
+m4_divert(0)m4_dnl
+#! /bin/sh
+### *** GENERATED FILE: DO NOT EDIT ***
+
+set -e
+PATH=/bin:/sbin:/usr/bin:/usr/sbin; export PATH
+
+m4_divert(99)m4_dnl
+### *** GENERATED FILE: DO NOT EDIT ***
+m4_divert(-1)
+
+###--------------------------------------------------------------------------
+### Unpleasant m4 hacking.
+
+## dolist(VAR, LIST, BODY)
+##
+## LIST is a parenthesized list of comma-separated items.  For each item,
+## set VAR to expand to the item and emit the BODY.
+m4_define(<:dolist:>, <:m4_pushdef(<:$1:>)__loop($@)m4_popdef(<:$1:>):>)
+m4_define(<:__loop:>, <:m4_ifelse(<:$2:>, <:():>, ,m4_dnl
+<:m4_define(<:$1:>, __first$2)$3<::>__loop(<:$1:>,(m4_shift$2),<:$3:>):>):>)
+m4_define(<:__first:>, <:$1:>)
+
+## split(DELIM, TEXT)
+##
+## Split TEXT at characters in DELIM; stash result in positional parameters.
+m4_define(<:split:>, <:IFS=$1; set -- $2; IFS=$STDIFS:>)
+
+## defconf(CONF, DEFAULT)
+##
+## Define config variable CONF, assigning it the DEFAULT value if not
+## overridden by setconf.
+m4_define(<:defconf:>, <:: ${$1=m4_ifdef(<:conf_$1:>, conf_$1, $2)}:>)
+
+## setconf(CONF, VALUE)
+##
+## Set config variable VALUE.
+m4_define(<:setconf:> <:m4_define(<:conf_$1:>, <:$2:>):>)
+
+###----- That's all, folks --------------------------------------------------
diff --git a/bookends.m4 b/bookends.m4
new file mode 100644 (file)
index 0000000..f99066c
--- /dev/null
@@ -0,0 +1,107 @@
+### -*-m4-*-
+###
+### Initialization and finishing touches for firewall scripts
+###
+### (c) 2008 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This program is free software; you can redistribute it and/or modify
+### it under the terms of the GNU General Public License as published by
+### the Free Software Foundation; either version 2 of the License, or
+### (at your option) any later version.
+###
+### This program is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+### GNU General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with this program; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+m4_divert(30)m4_dnl
+###--------------------------------------------------------------------------
+### Clear existing firewall rules.
+
+## The main chains: set policy to drop, and then clear the rules.  For a
+## while, incoming packets will be silently dropped, but we should have got
+## everything going before anyone actually hits a timeout.
+for t in mangle filter; do
+  for i in PREROUTING INPUT FORWARD OUTPUT POSTROUTING; do
+    run iptables -t $t -P $i DROP 2>/dev/null || :
+    run iptables -t $t -F $i 2>/dev/null || :
+  done
+  run iptables -t $t -F
+  run iptables -t $t -X
+done
+
+m4_divert(32)m4_dnl
+###--------------------------------------------------------------------------
+### Set safe IP options.
+
+## Set forwarding options.  Apparently setting ip_forward clobbers other
+## settings, so put this first.
+setopt ip_forward $forward
+setdevopt forwarding $forward
+
+## Set dynamic port allocation.
+setopt ip_local_port_range $open_port_min $open_port_max
+
+## Deploy SYN-cookies if necessary.
+setopt tcp_syncookies 1
+
+## Turn on the reverse-path filter, and log weird things.
+setdevopt rp_filter 1
+setdevopt log_martians 1
+
+## Turn off things which can mess with our routing decisions.
+setdevopt accept_source_route 0
+setdevopt accept_redirects 0
+
+## If we're maent to stop the firewall, then now is the time to do it.
+$exit_after_clearing
+
+m4_divert(34)m4_dnl
+###--------------------------------------------------------------------------
+### Establish error chains.
+
+errorchain forbidden REJECT --reject-with icmp-host-prohibited
+## Generic `not allowed' chain.  Rejects with ICMP host-prohibited.
+
+errorchain tcp-fragment REJECT --reject-with icmp-host-prohibited
+## Chain for logging fragmented TCP segements.  Rejects with ICMP
+## host-prohibited.
+
+errorchain bad-tcp REJECT -p tcp --reject-with tcp-reset
+## Bad TCP segments (e.g., for unknown connections).  Sends a TCP reset.
+
+errorchain mangle:bad-source-address DROP
+## Packet arrived on wrong interface for its source address.  Drops the
+## packet, since there's nowhere sensible to send an error.
+
+errorchain interesting ACCEPT
+## Not an error, just log interesting packets.
+
+m4_divert(36)m4_dnl
+###--------------------------------------------------------------------------
+### Don't clobber local traffic.
+
+run iptables -A INPUT -i lo -j ACCEPT
+
+m4_divert(90)m4_dnl
+###--------------------------------------------------------------------------
+### Finishing touches.
+
+m4_divert(94)m4_dnl
+## Locally generated packets are all OK.
+run iptables -P OUTPUT ACCEPT
+
+## Other incoming things are forbidden.
+for chain in INPUT FORWARD; do
+  run iptables -A $chain -g forbidden
+done
+
+m4_divert(-1)
+###----- That's all, folks --------------------------------------------------
diff --git a/classify.m4 b/classify.m4
new file mode 100644 (file)
index 0000000..c1365b6
--- /dev/null
@@ -0,0 +1,133 @@
+### -*-m4-*-
+###
+### Initialization and finishing touches for firewall scripts
+###
+### (c) 2008 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This program is free software; you can redistribute it and/or modify
+### it under the terms of the GNU General Public License as published by
+### the Free Software Foundation; either version 2 of the License, or
+### (at your option) any later version.
+###
+### This program is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+### GNU General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with this program; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+m4_divert(40)m4_dnl
+###--------------------------------------------------------------------------
+### Address classification.
+###
+### The objective of address classification is to work out what kind of
+### networks a packet is travelling between, in order to make filtering
+### decisions easier.
+###
+### Address classification is done in the mangle table, by attaching
+### appropriate marks to the packet.  We split the Internet into a number of
+### address classes, and make forwarding decisions based on the classes of
+### the source and destination addresses.
+###
+### The mark word is split into three fields: the FROM and TO fields simply
+### record the source and destination classes numerically; the MASK field is
+### used to determine whether forwarding should occur.  There is a mask bit
+### for each address class.  Source classification sets mask bits according
+### to the forwarding policy for the source address class.  Destination
+### classification clears all of the mask bits except for the one
+### corresponding to the actual destination class.  Therefore, forwarding is
+### permitted if and only if the mask bits are not all zero.
+###
+### The mangle chains are arranged as follows.
+###
+### The PREROUTING hook simply invokes in-classify and out-classify chains as
+### subroutines.  These will tail-call appropriate classification chains.
+###
+### The in-classify chain is responsible for both source address
+### classification and verifying that the packet arrived from the correct
+### interface.  It does an initial dispatch on the source interface, to
+### in-IFACE.  The in-IFACE chain dispatches to mark-from-CLASS when it
+### recognizes an address belonging to the CLASS; if no matches succeed, it
+### goes to bad-source-address, which logs a message and drops the packet.
+### The default interface is special.  If no explicit matches are found, it
+### dispatches to in-default which forbids a few obviously evil things and
+### finally dispatches to mark-from-untrusted.
+###
+### The out-classify is simpler because it doesn't care about the interface.
+### It simply checks each network range in turn, dispatching to mark-to-CLASS
+### on a match or mark-to-DEFAULT (probably untrusted) if there is no match.
+
+clearchain mangle:in-classify mangle:in-default mangle:out-classify
+clearchain mangle:local-source
+
+## Packets over the loopback interface are automatically trusted.  All manner
+## of weird stuff happens on lo, and it's best not to second-guess it.
+run iptables -t mangle -A in-classify -i lo -j ACCEPT
+
+## Local bootp packets have bizarre addresses.  Don't block them just because
+## of this.
+run iptables -t mangle -A in-classify -j RETURN \
+       -s 0.0.0.0 -d 255.255.255.255 \
+       -p udp --source-port $port_bootpc --destination-port $port_bootps
+
+## Since packets with source and destination addresses both local will go
+## over the loopback interface, I shouldn't see a packet from me over any
+## other interface.  Except that I will if I sent a broadcast or multicast.
+## Allow the broadcasts, and remember not to trust them.
+run iptables -t mangle -A local-source -j RETURN \
+       -m addrtype --dst-type BROADCAST
+run iptables -t mangle -A local-source -j RETURN \
+       -m addrtype --dst-type MULTICAST
+run iptables -t mangle -A local-source -g bad-source-address
+run iptables -t mangle -A in-classify -j local-source \
+       -m addrtype --src-type LOCAL
+
+m4_divert(41)m4_dnl
+## Define the important networks.
+for pass in 1 2; do
+  netclassindex=0
+m4_divert(42)m4_dnl
+done
+
+m4_divert(46)m4_dnl
+## Mark addresses reachable on non-default interfaces as not reachable on the
+## default interface.
+trace "nets = $allnets"
+for net in $allnets; do
+  case $net in
+    "$defaultiface":*)
+      ;;
+    *)
+      run iptables -t mangle -A in-$defaultiface \
+             -s ${net#*:} -g bad-source-address
+      ;;
+  esac
+done
+
+## Fill in the black holes in the network.
+for addr in \
+       10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 \
+       127.0.0.0/8 192.0.2.0/24
+do
+  run iptables -t mangle -A in-default -s $addr -g bad-source-address
+done
+
+m4_divert(92)m4_dnl
+## Put the final default decision on the in-default chain, and attach the
+## classification chains to the PREROUTING hook.
+run iptables -t mangle -A in-$defaultiface -g mark-from-$defaultclass
+run iptables -t mangle -A PREROUTING -j in-classify
+run iptables -t mangle -A PREROUTING -j out-classify
+
+## Now it's safe to let stuff through.
+for i in PREROUTING INPUT FORWARD OUTPUT POSTROUTING; do
+  run iptables -t mangle -P $i ACCEPT
+done
+
+m4_divert(-1)
+###----- That's all, folks --------------------------------------------------
diff --git a/config.m4 b/config.m4
new file mode 100644 (file)
index 0000000..c48a92a
--- /dev/null
+++ b/config.m4
@@ -0,0 +1,43 @@
+### -*-m4-*-
+###
+### Configuration for firewall scripts
+###
+### (c) 2008 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This program is free software; you can redistribute it and/or modify
+### it under the terms of the GNU General Public License as published by
+### the Free Software Foundation; either version 2 of the License, or
+### (at your option) any later version.
+###
+### This program is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+### GNU General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with this program; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+m4_divert(5)m4_dnl
+###--------------------------------------------------------------------------
+### Configuration.
+
+## Name of this script.
+defconf(firewall_script, /etc/init.d/firewall)
+
+## Name of emergency fallback sceipt.
+defconf(firewall_failsafe, /etc/init.d/firewall.safe)
+
+## Set the range of dynamically allocated ports.  Access to these from
+## outside is permitted.
+defconf(open_port_min, 32000)
+defconf(open_port_max, 54999)
+
+## Whether to permit forwarding.
+defconf(forward, 1)
+
+m4_divert(-1)
+###----- That's all, folks --------------------------------------------------
diff --git a/filter.m4 b/filter.m4
new file mode 100644 (file)
index 0000000..2800b41
--- /dev/null
+++ b/filter.m4
@@ -0,0 +1,36 @@
+### -*-m4-*-
+###
+### Main packet filtering
+###
+### (c) 2008 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This program is free software; you can redistribute it and/or modify
+### it under the terms of the GNU General Public License as published by
+### the Free Software Foundation; either version 2 of the License, or
+### (at your option) any later version.
+###
+### This program is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+### GNU General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with this program; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+m4_divert(60)m4_dnl
+###--------------------------------------------------------------------------
+### Main packet filtering.
+
+## Incoming packets from untrusted sources need special inspection.
+clearchain inbound
+run iptables -A INPUT -m mark --mark $from_untrusted/$MASK_FROM -g inbound
+
+## Locally generated packets are all OK.
+run iptables -P OUTPUT ACCEPT
+
+m4_divert(-1)
+###----- That's all, folks --------------------------------------------------
diff --git a/functions.m4 b/functions.m4
new file mode 100644 (file)
index 0000000..66a83f3
--- /dev/null
@@ -0,0 +1,361 @@
+### -*-m4-*-
+###
+### Utility functions for firewall scripts
+###
+### (c) 2008 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This program is free software; you can redistribute it and/or modify
+### it under the terms of the GNU General Public License as published by
+### the Free Software Foundation; either version 2 of the License, or
+### (at your option) any later version.
+###
+### This program is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+### GNU General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with this program; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+m4_divert(20)m4_dnl
+###--------------------------------------------------------------------------
+### Utility functions.
+
+## doit COMMAND ARGS...
+##
+## If debugging, print the COMMAND and ARGS.  If serious, execute them.
+run () {
+  set -e
+  if [ "$FW_DEBUG" ]; then echo "* $*"; fi
+  if ! [ "$FW_NOACT" ]; then "$@"; fi
+}
+
+## trace MESSAGE...
+##
+## If debugging, print the MESSAGE.
+trace () {
+  set -e
+  if [ "$FW_DEBUG" ]; then echo "$*"; fi
+}
+
+## defport NAME NUMBER
+##
+## Define $port_NAME to be NUMBER.
+defport () {
+  name=$1 number=$2
+  eval port_$name=$number
+}
+
+m4_divert(22)m4_dnl
+###--------------------------------------------------------------------------
+### Basic chain constructions.
+
+## clearchain CHAIN CHAIN ...
+##
+## Ensure that the named chains exist and are empty.
+clearchain () {
+  set -e
+  for chain; do
+    case $chain in
+      *:*) table=${chain%:*} chain=${chain#*:} ;;
+      *) table=filter ;;
+    esac
+    run iptables -t $table -N $chain
+  done
+}
+
+## errorchain CHAIN ACTION ARGS ...
+##
+## Make a chain which logs a message and then invokes some other action,
+## typically REJECT.  Log messages are prefixed by `fw: CHAIN'.
+errorchain () {
+  set -e
+  chain=$1; shift
+  case $chain in
+    *:*) table=${chain%:*} chain=${chain#*:} ;;
+    *) table=filter ;;
+  esac
+  clearchain $table:$chain
+  run iptables -t $table -A $chain -j LOG \
+         -m limit --limit 3/minute --limit-burst 10 \
+         --log-prefix "new fw: $chain " --log-level notice
+  run iptables -t $table -A $chain -j "$@"
+}
+
+m4_divert(24)m4_dnl
+###--------------------------------------------------------------------------
+### Basic option setting.
+
+## setopt OPTION VALUE
+##
+## Set an IP sysctl.
+setopt () {
+  set -e
+  opt=$1; shift; val=$*
+  run sysctl -q net/ipv4/$opt="$val"
+}
+
+## setdevopt OPTION VALUE
+##
+## Set an IP interface-level sysctl.
+setdevopt () {
+  set -e
+  opt=$1; shift; val=$*
+  for i in /proc/sys/net/ipv4/conf/*; do
+    [ -f $i/$opt ] &&
+      run sysctl -q net/ipv4/conf/${i#/proc/sys/net/ipv4/conf/}/$opt="$val"
+  done
+}
+
+m4_divert(26)m4_dnl
+###--------------------------------------------------------------------------
+### Packet filter construction.
+
+## conntrack CHAIN
+##
+## Add connection tracking to CHAIN, and allow obvious stuff.
+conntrack () {
+  set -e
+  chain=$1
+  run iptables -A $chain -p tcp -m state \
+         --state ESTABLISHED,RELATED -j ACCEPT
+  run iptables -A $chain -p tcp ! --syn -g bad-tcp
+}
+
+## allowservices CHAIN PROTO SERVICE ...
+##
+## Add rules to allow the SERVICES on the CHAIN.
+allowservices () {
+  set -e
+  chain=$1 proto=$2; shift 2
+  count=0
+  list=
+  for svc; do
+    case $svc in
+      *:*)
+        n=2
+       left=${svc%:*} right=${svc#*:}
+       case $left in *[!0-9]*) eval left=\$port_$left ;; esac
+       case $right in *[!0-9]*) eval right=\$port_$right ;; esac
+       svc=$left:$right
+       ;;
+      *)
+        n=1
+       case $svc in *[!0-9]*) eval svc=\$port_$svc ;; esac
+       ;;
+    esac
+    case $svc in
+      *: | :* | "" | *[!0-9:]*)
+        echo >&2 "Bad service name"
+       exit 1
+       ;;
+    esac
+    count=$(( $count + $n ))
+    if [ $count -gt 15 ]; then
+      run iptables -A $chain -p $proto -m multiport -j ACCEPT \
+            --destination-ports ${list#,}
+      list= count=$n
+    fi
+    list=$list,$svc
+  done
+  case $list in
+    "")
+      ;;
+    ,*,*)
+      run iptables -A $chain -p $proto -m multiport -j ACCEPT \
+             --destination-ports ${list#,}
+      ;;
+    *) 
+      run iptables -A $chain -p $proto -j ACCEPT \
+             --destination-port ${list#,}
+      ;;
+  esac
+}
+
+## ntpclient CHAIN NTPSERVER ...
+##
+## Add rules to CHAIN to allow NTP with NTPSERVERs.
+ntpclient () {
+  set -e
+  chain=$1; shift
+  for ntp; do
+    run iptables -A $chain -s $ntp -j ACCEPT \
+           -p udp --source-port 123 --destination-port 123
+  done
+}
+
+## dnsresolver CHAIN
+##
+## Add rules to allow CHAIN to be a DNS resolver.
+dnsresolver () {
+  set -e
+  chain=$1
+  for p in tcp udp; do
+    run iptables -A $chain -j ACCEPT \
+            -m state --state ESTABLISHED \
+            -p $p --source-port 53
+  done
+}
+
+## openports CHAIN [MIN MAX]
+##
+## Add rules to CHAIN to allow the open ports.
+openports () {
+  set -e
+  chain=$1; shift
+  [ $# -eq 0 ] && set -- $open_port_min $open_port_max
+  run iptables -A $chain -p tcp -g interesting --destination-port $1:$2
+  run iptables -A $chain -p udp -g interesting --destination-port $1:$2
+}
+
+m4_divert(28)m4_dnl
+###--------------------------------------------------------------------------
+### Packet classification.
+
+## defbitfield NAME WIDTH
+##
+## Defines MASK_NAME and BIT_NAME symbolic constants for dealing with
+## bitfields: x << BIT_NAME yields the value x in the correct position, and
+## ff & MASK_NAME extracts the corresponding value.
+defbitfield () {
+  set -e
+  name=$1 width=$2
+  eval MASK_$name=$(( (1 << $width) - 1 << $bitindex ))
+  eval BIT_$name=$bitindex
+  bitindex=$(( $bitindex + $width ))
+}
+
+## Define the layout of the bitfield.
+bitindex=0
+defbitfield MASK 16
+defbitfield FROM 4
+defbitfield TO 4
+
+## defnetclass NAME FORWARD-TO...
+##
+## Defines a netclass called NAME, which is allowed to forward to the
+## FORWARD-TO netclasses.
+##
+## For each netclass, constants from_NAME and to_NAME are defined as the
+## appropriate values in the FROM and TO fields (i.e., not including any mask
+## bits).
+##
+## This function also establishes mangle chains mark-from-NAME and
+## mark-to-NAME for applying the appropriate mark bits to the packet.
+##
+## Because it needs to resolve forward references, netclasses must be defined
+## in a two-pass manner, using a loop of the form
+##
+##   for pass in 1 2; do netclassindex=0; ...; done
+netclassess=
+defnetclass () {
+  set -e
+  name=$1; shift
+  case $pass in
+    1)
+
+      ## Pass 1.  Establish the from_NAME and to_NAME constants, and the
+      ## netclass's mask bit.
+      eval from_$name=$(( $netclassindex << $BIT_FROM ))
+      eval to_$name=$(( $netclassindex << $BIT_TO ))
+      eval _mask_$name=$(( 1 << ($netclassindex + $BIT_MASK) ))
+      nets="$nets $name"
+      ;;
+    2)
+
+      ## Pass 2.  Compute the actual from and to values.  We're a little bit
+      ## clever during source classification, and set the TO field to
+      ## all-bits-one, so that destination classification needs only a single
+      ## AND operation.
+      from=$(( ($netclassindex << $BIT_FROM) + (0xf << $BIT_TO) ))
+      for net; do
+       eval bit=\$_mask_$net
+       from=$(( $from + $bit ))
+      done
+      to=$(( ($netclassindex << $BIT_TO) + \
+            (0xf << $BIT_FROM) + \
+            (1 << ($netclassindex + $BIT_MASK)) ))
+      trace "from $name --> set $(printf %x $from)"
+      trace "  to $name --> and $(printf %x $from)"
+
+      ## Now establish the mark-from-NAME and mark-to-NAME chains.
+      clearchain mangle:mark-from-$name mangle:mark-to-$name
+      run iptables -t mangle -A mark-from-$name -j MARK --set-mark $from
+      run iptables -t mangle -A mark-to-$name -j MARK --and-mark $to
+      ;;
+  esac
+  netclassindex=$(( $netclassindex + 1 ))
+}
+
+## defiface NAME NETCLASS:NETWORK/MASK...
+##
+## Declares a network interface NAME and associates with it a number of
+## reachable networks.  During source classification, a packet arriving on
+## interface NAME from an address in NETWORK/MASK is classified as coming
+## from to NETCLASS.  During destination classification, all packets going to
+## NETWORK/MASK are classified as going to NETCLASS, regardless of interface
+## (which is good, because the outgoing interface hasn't been determined
+## yet).
+##
+## As a special case, the NETWORK/MASK can be the string `default', which
+## indicates that all addresses not matched elsewhere should be considered.
+ifaces=:
+defaultiface=none
+allnets=
+defiface () {
+  set -e
+  name=$1; shift
+  case $ifaces in
+    *:"$name":*) ;;
+    *)
+      clearchain mangle:in-$name
+      run iptables -t mangle -A in-classify -i $name -g in-$name
+      ;;
+  esac
+  ifaces=$ifaces$name:
+  for item; do
+    netclass=${item%:*} addr=${item#*:}
+    case $addr in
+      default)
+       defaultiface=$name
+       defaultclass=$netclass
+       run iptables -t mangle -A out-classify -g mark-to-$netclass
+       ;;
+      *)
+       run iptables -t mangle -A in-$name -s $addr -g mark-from-$netclass
+       run iptables -t mangle -A out-classify -d $addr -g mark-to-$netclass
+       allnets="$allnets $name:$addr"
+       ;;
+    esac
+  done
+}
+
+## defvpn IFACE CLASS NET HOST:ADDR ...
+##
+## Defines a VPN interface.  If the interface has the form `ROOT+' (i.e., a
+## netfilter wildcard) then define a separate interface ROOTHOST routing to
+## ADDR; otherwise just write a blanket rule allowing the whole NET.  All
+## addresses concerned are put in the named CLASS.
+defvpn () {
+  set -e
+  iface=$1 class=$2 net=$3; shift 3
+  case $iface in
+    *-+)
+      root=${iface%+}
+      for host; do
+       name=${host%:*} addr=${host#*:}
+       defiface $root$name $class:$addr
+      done
+      ;;
+    *)
+      defiface $iface $class:$net
+      ;;
+  esac
+}
+
+m4_divert(-1)
+###----- That's all, folks --------------------------------------------------
diff --git a/icmp.m4 b/icmp.m4
new file mode 100644 (file)
index 0000000..004d583
--- /dev/null
+++ b/icmp.m4
@@ -0,0 +1,50 @@
+### -*-m4-*-
+###
+### ICMP filtering for firewall scripts
+###
+### (c) 2008 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This program is free software; you can redistribute it and/or modify
+### it under the terms of the GNU General Public License as published by
+### the Free Software Foundation; either version 2 of the License, or
+### (at your option) any later version.
+###
+### This program is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+### GNU General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with this program; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+m4_divert(50)m4_dnl
+###--------------------------------------------------------------------------
+### ICMP filtering.
+
+clearchain check-icmp
+
+## Ping needs inspecting on a host-by-host basis.
+for type in echo-request echo-reply; do
+  run iptables -A check-icmp -p icmp --icmp-type $type -j RETURN
+done
+
+## Certainly don't allow ping to broadcast addresses.
+run iptables -A check-icmp -g forbidden \
+       -p icmp --icmp-type echo-request \
+       -m addrtype --dst-type BROADCAST
+
+m4_divert(58)m4_dnl
+## Other ICMP is basically benign, we claim.
+run iptables -A check-icmp -j ACCEPT
+
+## Done.
+for i in INPUT FORWARD; do
+  run iptables -A $i -p icmp -j check-icmp
+done
+
+m4_divert(-1)
+###----- That's all, folks --------------------------------------------------
diff --git a/local.m4 b/local.m4
new file mode 100644 (file)
index 0000000..56c2253
--- /dev/null
+++ b/local.m4
@@ -0,0 +1,109 @@
+### -*-m4-*-
+###
+### Local firewall configuration
+###
+### (c) 2008 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This program is free software; you can redistribute it and/or modify
+### it under the terms of the GNU General Public License as published by
+### the Free Software Foundation; either version 2 of the License, or
+### (at your option) any later version.
+###
+### This program is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+### GNU General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with this program; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+###--------------------------------------------------------------------------
+### Packet classification.
+
+## Define the available network classes.
+m4_divert(42)m4_dnl
+defnetclass untrusted untrusted trusted
+defnetclass trusted untrusted trusted safe noloop
+defnetclass safe trusted safe noloop
+defnetclass noloop trusted safe
+m4_divert(-1)m4_dnl
+
+###--------------------------------------------------------------------------
+### Network layout.
+
+m4_divert(46)m4_dnl
+## Networks and routing.
+
+defiface $if_trusted \
+       trusted:172.29.199.0/26 \
+       safe:172.29.199.64/27 \
+       untrusted:default
+defiface $if_untrusted \
+       untrusted:172.29.198.0/24
+defvpn $if_vpn safe 172.29.199.128/27 \
+       crybaby:172.29.199.129
+defiface $if_its_mz safe:172.29.199.160/30
+defiface $if_its_pi safe:192.168.0.0/24
+
+m4_divert(60)m4_dnl
+###--------------------------------------------------------------------------
+### Special forwarding exemptions.
+
+## Allow ping from safe/noloop to untrusted networks.
+run iptables -A FORWARD -j ACCEPT \
+       -p icmp --icmp-type echo-request \
+       -m mark --mark $to_untrusted/$MASK_TO
+run iptables -A FORWARD -j ACCEPT \
+       -p icmp --icmp-type echo-reply \
+       -m mark --mark $from_untrusted/$MASK_FROM \
+       -m state --state ESTABLISHED
+
+## Allow SSH from safe/noloop to untrusted networks.
+run iptables -A FORWARD -j ACCEPT \
+       -p tcp --destination-port $port_ssh \
+       -m mark --mark $to_untrusted/$MASK_TO
+run iptables -A FORWARD -j ACCEPT \
+       -p tcp --source-port $port_ssh \
+       -m mark --mark $from_untrusted/$MASK_FROM \
+       -m state --state ESTABLISHED
+
+m4_divert(80)m4_dnl
+###--------------------------------------------------------------------------
+### Locally-bound packet inspection.
+
+clearchain inbound
+
+## Track connections.
+conntrack inbound
+
+## Allow incoming bootp.  Bootp won't be forwarded, so this is obviously a
+## local request.
+run iptables -A inbound -j ACCEPT \
+       -s 0.0.0.0 -d 255.255.255.255 \
+       -p udp --source-port $port_bootpc --destination-port $port_bootps
+run iptables -A inbound -j ACCEPT \
+       -s 172.29.198.0/23 \
+       -p udp --source-port $port_bootpc --destination-port $port_bootps
+
+## Allow incoming ping.  This is the only ICMP left.
+run iptables -A inbound -j ACCEPT -p icmp
+
+m4_divert(88)m4_dnl
+## Allow unusual things.
+openports inbound
+
+## Inspect inbound packets from untrusted sources.
+run iptables -A inbound -j forbidden
+run iptables -A INPUT -m mark --mark $from_untrusted/$MASK_FROM -g inbound
+
+## Otherwise process as indicated by the mark.
+for i in INPUT FORWARD; do
+  run iptables -A $i -m mark ! --mark 0/$MASK_MASK -j ACCEPT
+done
+
+m4_divert(-1)
+###----- That's all, folks --------------------------------------------------
diff --git a/local.mk b/local.mk
new file mode 100644 (file)
index 0000000..ccdd6a4
--- /dev/null
+++ b/local.mk
@@ -0,0 +1,6 @@
+### Local configuration makefile.
+
+MAIN_M4_SOURCES                += local.m4
+
+HOSTS                  += metalzone
+HOSTS                  += vampire
diff --git a/metalzone.m4 b/metalzone.m4
new file mode 100644 (file)
index 0000000..62804c6
--- /dev/null
@@ -0,0 +1,66 @@
+### -*-m4-*-
+###
+### Firewall configuration for metalzone
+###
+### (c) 2008 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This program is free software; you can redistribute it and/or modify
+### it under the terms of the GNU General Public License as published by
+### the Free Software Foundation; either version 2 of the License, or
+### (at your option) any later version.
+###
+### This program is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+### GNU General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with this program; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+###--------------------------------------------------------------------------
+### Network interfaces.
+
+m4_divert(44)m4_dnl
+## Interface definitions.
+if_untrusted=eth0
+if_trusted=eth0
+if_vpn=eth0
+if_its_mz=its-mz
+if_its_pi=its-pi
+
+m4_divert(-1)
+###--------------------------------------------------------------------------
+### metalzone-specific rules.
+
+m4_divert(82)m4_dnl
+## Externally visible services.
+allowservices inbound tcp \
+       finger ident \
+       ssh \
+       smtp \
+       gnutella_svc \
+       ftp ftp_data \
+       rsync \
+       http https \
+       git     
+allowservices inbound udp \
+       tripe \
+       gnutella_svc
+
+## Provide DNS resolution to local untrusted hosts.
+for p in tcp udp; do
+  run iptables -A inbound -j ACCEPT \
+         -s 172.29.198.0/24 \
+         -p $p --destination-port $port_dns
+done
+
+## Other interesting things.
+dnsresolver inbound
+ntpclient inbound 158.152.1.76 158.152.1.204 194.159.253.2
+
+m4_divert(-1)
+###----- That's all, folks --------------------------------------------------
diff --git a/numbers.m4 b/numbers.m4
new file mode 100644 (file)
index 0000000..a87bf9c
--- /dev/null
@@ -0,0 +1,48 @@
+### -*-m4-*-
+###
+### Useful numbers for firewall configuration
+###
+### (c) 2008 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This program is free software; you can redistribute it and/or modify
+### it under the terms of the GNU General Public License as published by
+### the Free Software Foundation; either version 2 of the License, or
+### (at your option) any later version.
+###
+### This program is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+### GNU General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with this program; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+m4_divert(25)m4_dnl
+###--------------------------------------------------------------------------
+### Port numbers.
+
+defport ftp_data 20
+defport ftp 21
+defport ssh 22
+defport smtp 25
+defport dns 53
+defport bootps 67
+defport bootpc 68
+defport finger 79
+defport http 80
+defport ident 113
+defport https 443
+defport syslog 514                     # UDP only!
+defport rsync 873
+defport squid 3128
+defport tripe 4070
+defport postgresql 5432
+defport gnutella_svc 6346
+defport git 9418
+
+m4_divert(-1)
+###----- That's all, folks --------------------------------------------------
diff --git a/prologue.m4 b/prologue.m4
new file mode 100644 (file)
index 0000000..60fcf4d
--- /dev/null
@@ -0,0 +1,82 @@
+### -*-m4-*-
+###
+### Failsafe prologue for firewall scripts
+###
+### (c) 2008 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This program is free software; you can redistribute it and/or modify
+### it under the terms of the GNU General Public License as published by
+### the Free Software Foundation; either version 2 of the License, or
+### (at your option) any later version.
+###
+### This program is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+### GNU General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with this program; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+m4_divert(10)m4_dnl
+###--------------------------------------------------------------------------
+### Failsafe prologue.
+
+revert () {
+  echo "$1!  Retreating to safe version..."
+  if ! "$firewall_failsafe" revert; then
+    echo >&2 "Safe firewall failed.  You're screwed.  Good luck."
+    exit 1
+  fi
+  echo >&2 "Phew!  Fallback to safe version successful."
+  exit 1
+}
+
+finished () {
+  echo "Done."
+  exit 0
+}
+
+exit_after_clearing=:
+export FWCOOKIE=magical
+case "${1-update}" in
+  start | restart | reload | force-reload)
+    echo -n "Starting up firewall... "
+    "$firewall_script" install || revert "Failed"
+    finished
+    ;;
+  stop)
+    echo -n "Shutting down firewall... "
+    exit_after_clearing=finished
+    ;;
+  update)
+    echo -n "Installing new firewall... "
+    "$firewall_script" install || revert "Failed"
+    echo "Done."
+    echo "Can you hear me?"
+    parent=$$
+    (sleep 5; kill $parent; revert "Timeout")&
+    child=$!
+    read answer
+    kill $child
+    case "$answer" in
+      y* | Y*)
+       echo "Cool.  We're done here."
+       exit 0
+       ;;
+    esac
+    revert "Bogus"
+    ;;
+  install | revert)
+    ;;
+  *)
+    echo >&2 "Usage: firewall start|stop|reload|restart|force-reload|update|install|revert"
+    exit 1
+    ;;
+esac
+
+m4_divert(-1)
+###----- That's all, folks --------------------------------------------------
diff --git a/vampire.m4 b/vampire.m4
new file mode 100644 (file)
index 0000000..82439d2
--- /dev/null
@@ -0,0 +1,78 @@
+### -*-m4-*-
+###
+### Firewall configuration for vampire
+###
+### (c) 2008 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This program is free software; you can redistribute it and/or modify
+### it under the terms of the GNU General Public License as published by
+### the Free Software Foundation; either version 2 of the License, or
+### (at your option) any later version.
+###
+### This program is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+### GNU General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with this program; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+###--------------------------------------------------------------------------
+### Network interfaces.
+
+m4_divert(44)m4_dnl
+## Interface definitions.
+if_untrusted=eth0.1
+if_trusted=eth0.0
+if_vpn=vpn-+
+if_its_mz=eth0.0
+if_its_pi=eth0.0
+
+m4_divert(-1)
+###--------------------------------------------------------------------------
+### vampire-specific rules.
+
+m4_divert(82)m4_dnl
+## Externally visible services.
+allowservices inbound tcp \
+       finger ident \
+       dns \
+       ssh \
+       smtp \
+       gnutella_svc \
+       ftp ftp_data \
+       rsync \
+       http https \
+       git     
+allowservices inbound udp \
+       dns \
+       tripe \
+       gnutella_svc
+
+## Provide DNS resolution to local untrusted hosts.
+for p in tcp udp; do
+  run iptables -A inbound -j ACCEPT \
+         -s 172.29.198.0/24 \
+         -p $p --destination-port $port_dns
+done
+
+## Provide syslog for evolution.
+run iptables -A inbound -j ACCEPT \
+       -s 172.29.198.2 \
+       -p udp --destination-port $port_syslog
+
+## Provide a web cache to local untrusted hosts.
+run iptables -A inbound -j ACCEPT \
+       -s 172.29.198.0/24 \
+       -p tcp --destination-port $port_squid
+
+## Other interesting things.
+dnsresolver inbound
+ntpclient inbound 158.152.1.76 158.152.1.204 194.159.253.2
+
+m4_divert(-1)
+###----- That's all, folks --------------------------------------------------