From: Mark Wooding Date: Wed, 10 Dec 2008 10:00:35 +0000 (+0000) Subject: Initial commit of fancy firewall infrastructure. X-Git-Url: https://git.distorted.org.uk/~mdw/firewall/commitdiff_plain/bfdc045deb6149808d309b4ac3c292d9c57a8b38 Initial commit of fancy firewall infrastructure. --- bfdc045deb6149808d309b4ac3c292d9c57a8b38 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7ba5f27 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.sh +cruft diff --git a/Makefile b/Makefile new file mode 100644 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 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 index 0000000..f99066c --- /dev/null +++ b/bookends.m4 @@ -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 index 0000000..c1365b6 --- /dev/null +++ b/classify.m4 @@ -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 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 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 index 0000000..66a83f3 --- /dev/null +++ b/functions.m4 @@ -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 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 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 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 index 0000000..62804c6 --- /dev/null +++ b/metalzone.m4 @@ -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 index 0000000..a87bf9c --- /dev/null +++ b/numbers.m4 @@ -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 index 0000000..60fcf4d --- /dev/null +++ b/prologue.m4 @@ -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 index 0000000..82439d2 --- /dev/null +++ b/vampire.m4 @@ -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 --------------------------------------------------