#! /bin/sh
# vim:syntax=sh
# this file maintained at http://git.mdcc.cx/uruk.git
# Copyright (C) 2003 Stichting LogReport Foundation logreport@logreport.org
# Copyright (C) 2003, 2004, 2010 Tilburg University http://www.uvt.nl/
# Copyright (C) 2003, 2004, 2005, 2007, 2010 Joost van Baal
# Copyright (C) 2012, 2013 Joost van Baal-Ilić
# Copyright © 2014,2015 Wessel Dankers
#
# This file 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 3 of the License, or (at your option)
# any later version.
#
# This file 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 GPL for more details.
#
# You should have received a copy of the GNU GPL along with this file, see
# e.g. the file named COPYING. If not, see .
#
# peeksheet: iptables predefined chains:
#
# - INPUT - - localhost - - OUTPUT -
# / \
# PREROUTING - - - - - - - - FORWARD - - - - - - - - POSTROUTING
#
iptables=${URUK_IPTABLES:-iptables}
ip6tables=${URUK_IP6TABLES:-ip6tables}
# Variables used: ip6_<...>, sources6_<...>, ip6tables.
interfaces_unprotect=${URUK_INTERFACES_UNPROTECT:-lo}
etcdir="/etc/uruk"
config=${URUK_CONFIG:-${etcdir}/rc}
# IPv4 ranges that should not send or receive packets unless specifically permitted
# See RFC 6890.
ip4_noroute_ranges='0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 169.254.0.0/16 172.16.0.0/12 192.0.0.0/24 192.0.2.0/24 192.88.99.0/24 192.168.0.0/16 198.18.0.0/15 198.51.100.0/24 203.0.113.0/24 224.0.0.0/3'
# IPv6 ranges that should not send or receive packets
# see http://www.iana.org/assignments/ipv6-address-space/
# and http://www.iana.org/assignments/ipv6-unicast-address-assignments/
# and RFC 6890.
# All IPv6 addresses in their canonical form.
ip6_noroute_ranges='64:ff9b::/96 ::ffff:0:0/96 100::/64 200::/7 2001:2::/48 2001:db8::/32 2001:10::/28 fc00::/7 fec0::/10 3ffe::/16 5f00::/8 ::1/128 ::/128'
uruk_version="@URUK_VERSION@"
test -r $config || {
echo >&2 "No readable rc file $config found. Please create one." && exit 1
}
. $config
case $version in ?*)
case $((version < 20040210)) in 1)
cat >&2 <&2
esac
if test -f $uruk_save_dir/$table
then
space=
for arg
do
case $arg in -[a-zA-Z0-9])
echo -n "$space-"
echo -n "${arg#-}"
;; *[!a-zA-Z0-9_!+,./:=@-]*)
echo -n "$space\""
echo -n "$arg" | sed 's/[\\\"'\'']/\\&/g'
echo -n \"
;; *)
echo -n "$space$arg"
esac
space=' '
done >>$uruk_save_dir/$table
echo >>$uruk_save_dir/$table
else
echo "Unknown table '$table'; skipping rule '" -t $table $* "'" >&2
fi
}
#
# bootstrap these rules
#
# 40 < 60 ( 50) medium: log denied non-broadcasts (default)
test -z "$loglevel" && loglevel=50
#
# traffic on interfaces_unprotect (lo, per default) is trusted
for iface in ${interfaces_unprotect}
do
$iptables -A INPUT -i $iface -j ACCEPT
$iptables -A OUTPUT -o $iface -j ACCEPT
$ip6tables -A INPUT -i $iface -j ACCEPT
$ip6tables -A OUTPUT -o $iface -j ACCEPT
done
uruk_hook "$rc_a"
if test $loglevel -ge 80
then
# 80 < 99 ( 90) fascist: log all packets
uruk_log
uruk6_log
fi
$iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
$ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
$iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
$ip6tables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# workaround bug(?) in linux kernel, see also
# http://serverfault.com/questions/309691/why-is-our-firewall-ubuntu-8-04-rejecting-the-final-packet-fin-ack-psh-wit
# first argument is the flags which we should examine, the second argument is
# the flags which must be set
$iptables -A INPUT -p tcp --tcp-flags SYN,ACK,FIN,RST FIN,ACK -j ACCEPT
$ip6tables -A INPUT -p tcp --tcp-flags SYN,ACK,FIN,RST FIN,ACK -j ACCEPT
$iptables -A INPUT -p tcp --tcp-flags SYN,ACK,FIN,RST RST -j ACCEPT
$ip6tables -A INPUT -p tcp --tcp-flags SYN,ACK,FIN,RST RST -j ACCEPT
uruk_hook "$rc_b"
#
# protect interfaces_public against spoofing
#
for iface in ${interfaces}
do
#
# don't allow anyone to spoof non-routeable addresses
#
eval "is=\$ips_${iface}"
case $is in '')
interfaces_x=$iface
;; *)
interfaces_x=
for i in $is
do
interfaces_x="$interfaces_x ${iface}_$i"
done
esac
# set of all addresses on this (physical) interface
blockips=
blockips6=
# set of all permitted "special" nets for this (physical) interface
eval "blocknet=\$net_${iface}"
eval "blocknet6=\$net6_${iface}"
for iface_x in $interfaces_x
do
eval "ips=\$ip_${iface_x}"
eval "ips6_defined=\${ip6_${iface_x}+DEFINED}"
case $ips6_defined in '')
ips6=$ips
;; *)
eval "ips6=\$ip6_${iface_x}"
esac
eval "net=\$net_${iface_x}"
eval "net6=\$net6_${iface_x}"
blocknet="$blocknet $net"
blocknet6="$blocknet6 $net6"
for ip in $ips
do
case $ip in *:*) ;; *) # if it doesn't look like an IPv6 address/range
for no_route_ip in $ip4_noroute_ranges
do
case " $net " in *[$IFS]$no_route_ip[$IFS]*) ;; *)
$iptables -A INPUT -i $iface -s $no_route_ip -d $ip -j DROP
$iptables -A OUTPUT -o $iface -s $ip -d $no_route_ip -j DROP
esac
done
blockips="$blockips $ip"
esac
done
for ip6 in $ips6
do
case $ip6 in *[!0-9/.]*) # if it doesn't look like an IPv4 address/range
for no_route_ip in $ip6_noroute_ranges
do
case " $net $net6 " in *[$IFS]$no_route_ip[$IFS]*) ;; *)
$ip6tables -A INPUT -i $iface -s $no_route_ip -d $ip6 -j DROP
$ip6tables -A OUTPUT -o $iface -s $ip6 -d $no_route_ip -j DROP
esac
done
blockips6="$blockips6 $ip6"
esac
done
done
case $blockips in *[!$IFS][$IFS]*[!$IFS]*)
# in multiple ip mode, we have to drop only if source is
# not _one_ of the nic's IPs
# supporting this for multiple ips would need multiple chains
# or, perhaps, some iptables extension.
# for now, we just block "known bad" addresses
for no_route_ip in $ip4_noroute_ranges
do
case " $blocknet " in *[$IFS]$no_route_ip[$IFS]*) ;; *)
$iptables -A INPUT -i $iface -d $no_route_ip -j DROP
$iptables -A OUTPUT -o $iface -s $no_route_ip -j DROP
esac
done
;; *)
# block outgoing packets that don't have our address as source,
# they are either spoofed or something is misconfigured (NAT disabled,
# for instance), we want to be nice and don't send out garbage.
for ip in $blockips
do
# drop all outgoing packets which don't have us as a source
$iptables -A OUTPUT -o $iface ! -s "$ip" -j DROP
# drop all incoming packets which don't have us as destination
$iptables -A INPUT -i $iface ! -d "$ip" -j DROP
done
esac
# in IPv6 we always have a multiple IP mode, because an interface
# always has a link-local address as well.
# in multiple ip mode, we have to drop only if source is
# not _one_ of the nic's IPs.
# supporting this for multiple ips would need multiple chains
# or, perhaps, some iptables extension.
# for now, we just block "known bad" addresses.
for no_route_ip in $ip6_noroute_ranges
do
case " $blocknet $blocknet6 " in *[$IFS]$no_route_ip[$IFS]*) ;; *)
$ip6tables -A INPUT -i $iface -d $no_route_ip -j DROP
$ip6tables -A OUTPUT -o $iface -s $no_route_ip -j DROP
esac
done
# Always allow outgoing connections
$iptables -A OUTPUT -m conntrack --ctstate NEW -o $iface -j ACCEPT
$ip6tables -A OUTPUT -m conntrack --ctstate NEW -o $iface -j ACCEPT
done
uruk_hook "$rc_c"
#
# allow traffic to offered services, from trusted sources
#
for iface in $interfaces
do
eval "is=\$ips_${iface}"
case $is in '')
interfaces_x=$iface
;; *)
interfaces_x=
for i in $is
do
interfaces_x="$interfaces_x ${iface}_$i"
done
esac
for iface_x in $interfaces_x
do
# tcp is special
eval "services_defined=\${services_${iface_x}_tcp+DEFINED}"
case $services_defined in '')
cat >&2 <&2 <&2 "WARNING: sources_${iface_x}_${proto}_${service} is undefined. (Processing uruk rc file nevertheless.)"
esac
eval "sources6_defined=\${sources6_${iface_x}_${proto}_${service}+DEFINED}"
eval "sources6=\$sources6_${iface_x}_${proto}_${service}"
case $sources6_defined in '')
eval "sources6=\$sources_${iface_x}_${proto}_${service}"
esac
eval "ports_defined=\${ports_${iface_x}_${proto}_${service}+DEFINED}"
eval "ports=\$ports_${iface_x}_${proto}_${service}"
case $ports_defined in '')
echo >&2 "WARNING: ports_${iface_x}_${proto}_${service} is undefined. (Processing uruk rc file nevertheless.)"
;; *)
for port in $ports
do
# port is e.g. www or 1023
for source in $sources
do
case $source in *:*) ;; *) # if it doesn't look like an IPv6 address/range
# source is e.g. 10.56.0.10/32
for ip in $ips
do
case $ip in *:*) ;; *) # if it doesn't look like an IPv6 address/range
$iptables \
--append INPUT \
--match conntrack \
--ctstate NEW \
--in-interface $iface \
--protocol $proto \
--source "$source" \
--destination "$ip" \
--destination-port "$port" \
--jump ACCEPT
esac
done
esac
done
for source6 in $sources6
do
case $source6 in *[!0-9/.]*) # if it doesn't look like an IPv4 address/range
for ip6 in $ips6
do
case $ip6 in *[!0-9/.]*) # if it doesn't look like an IPv4 address/range
$ip6tables \
--append INPUT \
--match conntrack \
--ctstate NEW \
--in-interface $iface \
--protocol $proto \
--source "$source6" \
--destination "$ip6" \
--destination-port "$port" \
--jump ACCEPT
esac
done
esac
done
done
esac
done
esac
done
done
done
uruk_hook "$rc_d"
#
# rc_e: backwards compatibility. should be removed one day.
#
uruk_hook "$rc_e"
#
# Don't answer broadcast and multicast packets
#
for iface in $interfaces_nocast
do
eval "is=\$bcasts_${iface}"
case $is in '')
interfaces_x=$iface
;; *)
interfaces_x=
for i in $is
do
interfaces_x="$interfaces_x ${iface}_$i"
done
esac
for iface_x in $interfaces_x
do
eval "bcast=\$bcast_${iface_x}"
$iptables -A INPUT -i $iface -d "$bcast" -j DROP
done
$iptables -A INPUT -i $iface -d 255.255.255.255 -j DROP
done
uruk_hook "$rc_f"
#
# icmp stuff. See RFC 1122 and also RFC 792, RFC 950, RFC 1812, RFC 1349,
# RFC 2474 and Stevens' TCP/IP Illustrated Chapter 6, p 69.
# The icmp types are even in %num2icmp_type in Lire::Firewall.
# Running "iptables -p icmp -h" gives iptables's idea of icmp types
#
#
# By default, we disallow
#
# source-quench
# redirect (
# network-redirect
# host-redirect
# TOS-network-redirect
# TOS-host-redirect
# )
# router-advertisement
# router-solicitation
#
# You might want to allow just
#
# echo-request echo-reply ttl-zero-during-transit \
# ttl-zero-during-reassembly ip-header-bad required-option-missing
#
# This makes pings succeed, as well as traceroute. However
# debugging network problems might be _much_ more difficult when disallowing
# lots of other icmp types. If you really want to do this, use rc_g.
#
for type in \
address-mask-reply \
address-mask-request \
destination-unreachable \
echo-reply \
echo-request \
parameter-problem \
timestamp-reply \
timestamp-request \
ttl-zero-during-reassembly \
ttl-zero-during-transit
do
$iptables -A INPUT -p icmp --icmp-type $type -j ACCEPT
done
# Drop echo replies which have a multicast address as a
# destination. See rfc4890-icmpv6-firewall.sh.
$ip6tables -A INPUT --protocol icmpv6 -d ff00::/8 \
--icmpv6-type echo-reply -j DROP
# See http://www.iana.org/assignments/icmpv6-parameters for ICMPv6 types
# Or run # ip6tables -p ipv6-icmp -h
for type in \
echo-request \
echo-reply \
destination-unreachable \
packet-too-big \
ttl-zero-during-transit \
ttl-zero-during-reassembly \
unknown-header-type \
unknown-option \
bad-header \
redirect \
144 \
145 \
146 \
147 \
router-solicitation \
router-advertisement \
neighbour-solicitation \
neighbour-advertisement \
141 \
142 \
130 \
131 \
132 \
143 \
148 \
149 \
151 \
152 \
153
do
$ip6tables -A INPUT --protocol icmpv6 --icmpv6-type $type -j ACCEPT
done
# Type 144 - Home Agent Address Discovery [RFC3775]
# Type 145 - Home Agent Address Discovery [RFC3775]
# Type 146 - Mobile Prefix Solicitation [RFC3775]
# Type 147 - Mobile Prefix Advertisement [RFC3775]
# We DROP, a.o.:
# Router renumbering messages: 138
# Node information queries (139) and replies (140): 139 140
#
$ip6tables -A INPUT --protocol icmpv6 -j DROP
uruk_hook "$rc_g"
#
# log packets which make it till here: denied packets (not denied broadcasts
# or spoofed stuff). take loglevel into account.
#
if test $loglevel -lt 20
then
# be silent
:
elif test $loglevel -lt 40
then
# log denied packets, targetted at our IPs
# INVALID: The packet is associated with no known connection. See iptables-extensions(8)
# may be due to the system running out of memory or ICMP error messages that do not
# respond to any known connections. It is helpfull to log these with explicitly
# mentioning reason of logging (and dropping).
$iptables -A INPUT -j LOG --log-level debug -m state --state INVALID --log-prefix 'iptables: REASON=invalid '
$ip6tables -A INPUT -j LOG --log-level debug -m state --state INVALID --log-prefix 'ip6tables: REASON=invalid '
for iface in $interfaces
do
eval "is=\$ips_${iface}"
case $is in '')
interfaces_x=$iface
;; *)
interfaces_x=
for i in $is
do
interfaces_x="$interfaces_x ${iface}_$i"
done
esac
for iface_x in $interfaces_x
do
eval "ip=\$ip_${iface_x}"
eval "ips6_defined=\${ip6_${iface_x}+DEFINED}"
case $ips6_defined in '')
ips6=$ips
;; *)
eval "ips6=\$ip6_${iface_x}"
esac
for ip in $ips
do
case $ip in *:*) ;; *) # if it doesn't look like an IPv6 address/range
uruk_log -i $iface -d $ip
esac
done
for ip6 in $ips6
do
case $ip6 in *[!0-9/.]*) # if it doesn't look like an IPv4 address/range
uruk6_log -i $iface -d $ip6
esac
done
done
done
elif test $loglevel -lt 60
then
# 40 < 60 ( 50) medium: log denied non-broadcasts (default)
uruk_log
uruk6_log
fi
# FIXME : yet to implement:
# 60 < 80 ( 70) high: log denied packets
uruk_hook "$rc_h"
#
# reject all others
#
$iptables -A INPUT -j REJECT --reject-with tcp-reset -p tcp
$iptables -A INPUT -j REJECT
# These ip6tables flags are supported since 2.4.5; we don't support older kernels
$ip6tables -A INPUT -j REJECT --reject-with tcp-reset -p tcp
$ip6tables -A INPUT -j REJECT --reject-with icmp6-adm-prohibited
uruk_hook "$rc_i"
# make sure we exit 0, even if last test failed
exit 0