IPv6 firewall support.
authorMark Wooding <mdw@distorted.org.uk>
Sun, 22 May 2011 20:43:32 +0000 (21:43 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sun, 22 May 2011 22:47:39 +0000 (23:47 +0100)
Introduce half-hearted IPv6 support.  A surprising amount of the
firewall structure carries over unchanged.  The way fragmentation is
handled differs between IPv4 and IPv6, which is annoying.  And
ip6tables(8) doesn't have the `addrtype' match which was so useful in
IPv4.

bookends.m4
classify.m4
functions.m4
local.m4

index 6b4f5f4..1844139 100644 (file)
@@ -30,11 +30,11 @@ m4_divert(30)m4_dnl
 ## 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 || :
+    run ip46tables -t $t -P $i DROP 2>/dev/null || :
+    run ip46tables -t $t -F $i 2>/dev/null || :
   done
-  run iptables -t $t -F
-  run iptables -t $t -X
+  run ip46tables -t $t -F
+  run ip46tables -t $t -X
 done
 
 m4_divert(32)m4_dnl
@@ -67,23 +67,22 @@ 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 forbidden REJECT
+## Generic `not allowed' chain.
 
-errorchain tcp-fragment REJECT --reject-with icmp-host-prohibited
-## Chain for logging fragmented TCP segements.  Rejects with ICMP
-## host-prohibited.
+errorchain tcp-fragment REJECT
+## Chain for logging fragmented TCP segements.
 
 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
+errorchain 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 bad-destination-address REJECT --reject-with icmp-host-prohibited
-## Packet arrived on non-loopback interface with loopback destination.  Sends
-## a rude note back.
+errorchain bad-destination-address REJECT
+## Packet arrived on non-loopback interface with loopback destination.
 
 errorchain interesting ACCEPT
 ## Not an error, just log interesting packets.
@@ -93,12 +92,24 @@ m4_divert(36)m4_dnl
 ### Standard loopback stuff.
 
 ## Don't clobber local traffic
-run iptables -A INPUT -i lo -j ACCEPT
+run ip46tables -A INPUT -i lo -j ACCEPT
 
 ## We really shouldn't see packets destined for localhost on any interface
 ## other than the loopback.
 run iptables -A INPUT -g bad-destination-address \
        -d 127.0.0.0/8
+run ip6tables -A INPUT -g bad-destination-address \
+       -d ::1
+
+## We shouldn't be asked to forward things with link-local addresses.
+run iptables -A FORWARD -g bad-source-address \
+       -s 169.254.0.0/16
+run iptables -A FORWARD -g bad-destination-address \
+       -d 169.254.0.0/16
+run ip6tables -A FORWARD -g bad-source-address \
+       -s fe80::/10
+run ip6tables -A FORWARD -g bad-destination-address \
+       -d fe80::/10
 
 m4_divert(90)m4_dnl
 ###--------------------------------------------------------------------------
index 148a72f..27eb376 100644 (file)
@@ -67,7 +67,7 @@ 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
+run ip46tables -t mangle -A in-classify -i lo -j ACCEPT
 
 ## Local bootp packets have bizarre addresses.  Don't block them just because
 ## of this.
@@ -83,9 +83,15 @@ 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 ip6tables -t mangle -A local-source -j RETURN \
+       -d ff00::/8
+run ip46tables -t mangle -A local-source -g bad-source-address
 run iptables -t mangle -A in-classify -j local-source \
        -m addrtype --src-type LOCAL
+for addr in $host_6addrs; do
+  run ip6tables -t mangle -A in-classify -j local-source \
+         -s $addr
+done
 
 m4_divert(41)m4_dnl
 ## Define the important networks.
@@ -97,7 +103,7 @@ done
 m4_divert(46)m4_dnl
 ## Mark addresses reachable on non-default interfaces as not reachable on the
 ## default interface.
-trace "nets = $allnets"
+trace "nets = $allnets $allnets6"
 for net in $allnets; do
   case $net in
     "$defaultiface":*)
@@ -108,6 +114,16 @@ for net in $allnets; do
       ;;
   esac
 done
+for net in $allnets6; do
+  case $net in
+    "$defaultiface":*)
+      ;;
+    *)
+      run ip6tables -t mangle -A in-$defaultiface \
+             -s ${net#*:} -g bad-source-address
+      ;;
+  esac
+done
 
 ## Fill in the black holes in the network.
 for addr in \
@@ -116,17 +132,23 @@ for addr in \
 do
   run iptables -t mangle -A in-default -s $addr -g bad-source-address
 done
+for addr in \
+       fc00::/7 \
+       2001:0db8::/32
+do
+  run ip6tables -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
+run ip46tables -t mangle -A in-$defaultiface -g mark-from-$defaultclass
+run ip46tables -t mangle -A PREROUTING -j in-classify
+run ip46tables -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
+  run ip46tables -t mangle -P $i ACCEPT
 done
 
 m4_divert(-1)
index 680df6d..3214483 100644 (file)
@@ -54,6 +54,15 @@ m4_divert(22)m4_dnl
 ###--------------------------------------------------------------------------
 ### Basic chain constructions.
 
+## ip46tables ARGS ...
+##
+## Do the same thing for `iptables' and `ip6tables'.
+ip46tables () {
+  set -e
+  iptables "$@"
+  ip6tables "$@"
+}
+
 ## clearchain CHAIN CHAIN ...
 ##
 ## Ensure that the named chains exist and are empty.
@@ -64,7 +73,7 @@ clearchain () {
       *:*) table=${chain%:*} chain=${chain#*:} ;;
       *) table=filter ;;
     esac
-    run iptables -t $table -N $chain
+    run ip46tables -t $table -N $chain
   done
 }
 
@@ -80,10 +89,10 @@ errorchain () {
     *) table=filter ;;
   esac
   clearchain $table:$chain
-  run iptables -t $table -A $chain -j LOG \
+  run ip46tables -t $table -A $chain -j LOG \
          -m limit --limit 3/minute --limit-burst 10 \
          --log-prefix "fw: $chain " --log-level notice
-  run iptables -t $table -A $chain -j "$@"
+  run ip46tables -t $table -A $chain -j "$@"
 }
 
 m4_divert(24)m4_dnl
@@ -121,9 +130,9 @@ m4_divert(26)m4_dnl
 conntrack () {
   set -e
   chain=$1
-  run iptables -A $chain -p tcp -m state \
+  run ip46tables -A $chain -p tcp -m state \
          --state ESTABLISHED,RELATED -j ACCEPT
-  run iptables -A $chain -p tcp ! --syn -g bad-tcp
+  run ip46tables -A $chain -p tcp ! --syn -g bad-tcp
 }
 
 ## commonrules CHAIN
@@ -138,6 +147,10 @@ commonrules () {
   ## fragmented.
   run iptables -A $chain -p tcp -f -g tcp-fragment
   run iptables -A $chain -f -j ACCEPT
+  run ip6tables -A $chain -p tcp -g tcp-fragment \
+         -m ipv6header --soft --header frag
+  run ip6tables -A $chain -j ACCEPT \
+         -m frag ! --fragfirst
 }
 
 ## allowservices CHAIN PROTO SERVICE ...
@@ -170,7 +183,7 @@ allowservices () {
     esac
     count=$(( $count + $n ))
     if [ $count -gt 15 ]; then
-      run iptables -A $chain -p $proto -m multiport -j ACCEPT \
+      run ip46tables -A $chain -p $proto -m multiport -j ACCEPT \
             --destination-ports ${list#,}
       list= count=$n
     fi
@@ -180,11 +193,11 @@ allowservices () {
     "")
       ;;
     ,*,*)
-      run iptables -A $chain -p $proto -m multiport -j ACCEPT \
+      run ip46tables -A $chain -p $proto -m multiport -j ACCEPT \
              --destination-ports ${list#,}
       ;;
     *)
-      run iptables -A $chain -p $proto -j ACCEPT \
+      run ip46tables -A $chain -p $proto -j ACCEPT \
              --destination-port ${list#,}
       ;;
   esac
@@ -209,7 +222,7 @@ dnsresolver () {
   set -e
   chain=$1
   for p in tcp udp; do
-    run iptables -A $chain -j ACCEPT \
+    run ip46tables -A $chain -j ACCEPT \
             -m state --state ESTABLISHED \
             -p $p --source-port 53
   done
@@ -222,8 +235,8 @@ 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
+  run ip46tables -A $chain -p tcp -g interesting --destination-port $1:$2
+  run ip46tables -A $chain -p udp -g interesting --destination-port $1:$2
 }
 
 m4_divert(28)m4_dnl
@@ -298,8 +311,8 @@ defnetclass () {
 
       ## 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
+      run ip46tables -t mangle -A mark-from-$name -j MARK --set-mark $from
+      run ip46tables -t mangle -A mark-to-$name -j MARK --and-mark $to
       ;;
   esac
   netclassindex=$(( $netclassindex + 1 ))
@@ -319,7 +332,7 @@ defnetclass () {
 ## indicates that all addresses not matched elsewhere should be considered.
 ifaces=:
 defaultiface=none
-allnets=
+allnets= allnets6=
 defiface () {
   set -e
   name=$1; shift
@@ -327,7 +340,7 @@ defiface () {
     *:"$name":*) ;;
     *)
       clearchain mangle:in-$name
-      run iptables -t mangle -A in-classify -i $name -g in-$name
+      run ip46tables -t mangle -A in-classify -i $name -g in-$name
       ;;
   esac
   ifaces=$ifaces$name:
@@ -337,7 +350,12 @@ defiface () {
       default)
        defaultiface=$name
        defaultclass=$netclass
-       run iptables -t mangle -A out-classify -g mark-to-$netclass
+       run ip46tables -t mangle -A out-classify -g mark-to-$netclass
+       ;;
+      *:*)
+       run ip6tables -t mangle -A in-$name -s $addr -g mark-from-$netclass
+       run ip6tables -t mangle -A out-classify -d $addr -g mark-to-$netclass
+       allnets6="$allnets6 $name:$addr"
        ;;
       *)
        run iptables -t mangle -A in-$name -s $addr -g mark-from-$netclass
@@ -361,7 +379,7 @@ defvpn () {
     *-+)
       root=${iface%+}
       for host; do
-       name=${host%:*} addr=${host#*:}
+       name=${host%%:*} addr=${host#*:}
        defiface $root$name $class:$addr
       done
       ;;
index 27caa44..5e27449 100644 (file)
--- a/local.m4
+++ b/local.m4
@@ -63,6 +63,15 @@ run iptables -A FORWARD -j ACCEPT \
        -p icmp ! -f --icmp-type echo-reply \
        -m mark --mark $from_untrusted/$MASK_FROM \
        -m state --state ESTABLISHED
+run ip6tables -A FORWARD -j ACCEPT \
+       -p ipv6-icmp --icmpv6-type echo-request \
+       -m ipv6header --soft ! --header frag \
+       -m mark --mark $to_untrusted/$MASK_TO
+run ip6tables -A FORWARD -j ACCEPT \
+       -p ipv6-icmp --icmpv6-type echo-reply \
+       -m ipv6header --soft ! --header frag \
+       -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 \
@@ -72,6 +81,15 @@ run iptables -A FORWARD -j ACCEPT \
        -p tcp ! -f --source-port $port_ssh \
        -m mark --mark $from_untrusted/$MASK_FROM \
        -m state --state ESTABLISHED
+run ip6tables -A FORWARD -j ACCEPT \
+       -p tcp --destination-port $port_ssh \
+       -m ipv6header --soft ! --header frag \
+       -m mark --mark $to_untrusted/$MASK_TO
+run ip6tables -A FORWARD -j ACCEPT \
+       -p tcp --source-port $port_ssh \
+       -m ipv6header --soft ! --header frag \
+       -m mark --mark $from_untrusted/$MASK_FROM \
+       -m state --state ESTABLISHED
 
 m4_divert(80)m4_dnl
 ###--------------------------------------------------------------------------
@@ -93,19 +111,19 @@ run iptables -A inbound -j ACCEPT \
        -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
+run ip46tables -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
+run ip46tables -A inbound -j forbidden
+run ip46tables -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
+  run ip46tables -A $i -m mark ! --mark 0/$MASK_MASK -j ACCEPT
 done
 
 m4_divert(-1)