From bfdc045deb6149808d309b4ac3c292d9c57a8b38 Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Wed, 10 Dec 2008 10:00:35 +0000 Subject: [PATCH] Initial commit of fancy firewall infrastructure. --- .gitignore | 2 + Makefile | 46 ++++++++ base.m4 | 95 ++++++++++++++++ bookends.m4 | 107 ++++++++++++++++++ classify.m4 | 133 ++++++++++++++++++++++ config.m4 | 43 +++++++ filter.m4 | 36 ++++++ functions.m4 | 361 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ icmp.m4 | 50 +++++++++ local.m4 | 109 ++++++++++++++++++ local.mk | 6 + metalzone.m4 | 66 +++++++++++ numbers.m4 | 48 ++++++++ prologue.m4 | 82 ++++++++++++++ vampire.m4 | 78 +++++++++++++ 15 files changed, 1262 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 base.m4 create mode 100644 bookends.m4 create mode 100644 classify.m4 create mode 100644 config.m4 create mode 100644 filter.m4 create mode 100644 functions.m4 create mode 100644 icmp.m4 create mode 100644 local.m4 create mode 100644 local.mk create mode 100644 metalzone.m4 create mode 100644 numbers.m4 create mode 100644 prologue.m4 create mode 100644 vampire.m4 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 -------------------------------------------------- -- 2.11.0