#! /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