IPTABLES - NON-BASIC SECURITY

I like iptables, I use iptables, I have written about iptables previously (see Basic Security and Stop Scanning) and I keep getting asked to put together some settings and checks that can be used in any iptables firewall setup.  So I decided to give it a bash, I have broken down each section into a specific purpose which can be used by itself in any iptables firewall. I will also be going through some theory and some example firewall rules. Now I have tried to make the settings/rules as generic as possible in the order I would use them, but you should still doublecheck them as they relate to your setup. So without further ado...

Network Security Proc Settings
SETTING_0="/proc/sys/net/ipv4/conf/*/accept_redirects /proc/sys/net/ipv4/conf/*/accept_source_route /proc/sys/net/ipv4/conf/*/send_redirects /proc/sys/net/ipv4/conf/*/secure_redirects /proc/sys/net/ipv4/conf/*/proxy_arp"
SETTING_1="/proc/sys/net/ipv4/conf/*/log_martians"
for file_0 in $SETTING_0
 do
  for y in `ls $file_0`
   do
    echo 0 > $y
   done
 done
for file_1 in $SETTING_1
 do
  for i in `ls $file_1`
   do
    echo 1 > $i
   done
 done
echo 1 > /proc/sys/net/ipv4/conf/all/rp_filter

echo 1 > /proc/sys/net/ipv4/tcp_syncookies
echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all
echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts
echo 1 > /proc/sys/net/ipv4/icmp_ignore_bogus_error_responses
echo 1 > /proc/sys/net/ipv4/ip_forward                                  ##used if you are routing traffic##
echo 0 > /proc/sys/net/ipv4/ip_forward                                 ##used if you are not routing traffic##
echo 0 > /proc/sys/net/ipv4/conf/*/proxy_arp                     ##used if you are not bridging traffic##
echo 1 > /proc/sys/net/ipv4/conf/*/proxy_arp                      ##used if you are bridging traffic##

Performance Related Proc Settings
echo 45 > /proc/sys/net/ipv4/tcp_fin_timeout
echo 60 > /proc/sys/net/ipv4/netfilter/ip_conntrack_udp_timeout
echo 1 > /proc/sys/net/ipv4/tcp_rfc1337
echo 1800 > /proc/sys/net/ipv4/tcp_keepalive_intvl
echo 1280 > /proc/sys/net/ipv4/tcp_max_syn_backlog
echo 1 > /proc/sys/net/ipv4/tcp_window_scaling
echo 0 > /proc/sys/net/ipv4/tcp_sack
echo 1 > /proc/sys/net/ipv4/tcp_fack
echo 0 > /proc/sys/net/ipv4/tcp_timestamps

Flushing Old Rules
iptables -F
iptables -t nat -F
iptables -t mangle -F
iptables -X
iptables -t nat -X
iptables -t mangle -X

Restrictive Default Settings
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT DROP
iptables -t nat -P PREROUTING ACCEPT
iptables -t nat -P POSTROUTING DROP
iptables -t nat -P OUTPUT DROP

Ensure Loopback Functioning
ifconfig lo localhost up
iptables -A INPUT -i lo -p all -j ACCEPT
iptables -A OUTPUT -o lo -p all -j ACCEPT

Setup Statefiul Packet Inspection
iptables -A INPUT   -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -t nat -A PREROUTING -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -t nat -A POSTROUTING -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -t nat -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

Check For Port Scans
##If you use this ruleblock, you will need to use the Adaptive Dropping ruleblock as well. ##
##Otherwise remove the
-m recent --set switches from this ruleblock##
iptables -N SCAN_CHK
iptables -A SCAN_CHK -m psd -m limit --limit 5/minute -m recent --set -j DROP
iptables -A SCAN_CHK -j RETURN
iptables -A INPUT -i <external_interface> -p tcp -j SCAN_CHK
iptables -A FORWARD -i <external_interface> -p tcp -j SCAN_CHK

Check For Bad Packet Flags
##If you use this ruleblock, you will need to use the Adaptive Dropping ruleblock as well. ##
##Otherwise remove the
-m recent --set switches from this ruleblock##
iptables -N BAD_FLAGS
iptables -A BAD_FLAGS -p tcp --tcp-option 64 -m recent --set -j DROP
iptables -A BAD_FLAGS -p tcp --tcp-option 128 -m recent --set -j DROP
iptables -A BAD_FLAGS -p tcp --tcp-flags ACK,FIN FIN -m recent --set -j DROP
iptables -A BAD_FLAGS -p tcp --tcp-flags ACK,PSH PSH -m recent --set -j DROP
iptables -A BAD_FLAGS -p tcp --tcp-flags ACK,URG URG -m recent --set -j DROP
iptables -A BAD_FLAGS -p tcp --tcp-flags FIN,RST FIN,RST -m recent --set -j DROP
iptables -A BAD_FLAGS -p tcp --tcp-flags SYN,FIN SYN,FIN -m recent --set -j DROP
iptables -A BAD_FLAGS -p tcp --tcp-flags SYN,RST SYN,RST -m recent --set -j DROP
iptables -A BAD_FLAGS -p tcp --tcp-flags ALL ALL -m recent --set -j DROP
iptables -A BAD_FLAGS -p tcp --tcp-flags ALL NONE -m recent --set -j DROP
iptables -A BAD_FLAGS -p tcp --tcp-flags ALL FIN -m recent --set -j DROP
iptables -A BAD_FLAGS -p tcp --tcp-flags ALL FIN,PSH,URG -m recent --set -j DROP
iptables -A BAD_FLAGS -p tcp --tcp-flags ALL SYN,FIN,PSH,URG -m recent --set -j DROP
iptables -A BAD_FLAGS -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG -m recent --set -j DROP
iptables -A BAD_FLAGS -p tcp --tcp-flags ALL URG,ACK,PSH,RST,SYN,FIN -m recent --set -j DROP
iptables -A BAD_FLAGS -j RETURN
iptables -A INPUT -p tcp -j BAD_FLAGS
iptables -A FORWARD -p tcp -j BAD_FLAGS

Check For Bad Packet Options
##If you use this ruleblock, you will need to use the Adaptive Dropping ruleblock as well. ##
##Otherwise remove the
-m recent --set switches from this ruleblock##
iptables -N BAD_OPT
iptables -A BAD_OPT -m ipv4options --ssrr -m recent --set -j DROP
iptables -A BAD_OPT
-m ipv4options --lsrr -m recent --set -j DROP
iptables -A BAD_OPT -m ipv4options --rr -m recent --set -j DROP
iptables -A BAD_OPT -j RETURN
iptables -A INPUT -i <external_interface> -p tcp -j BAD_OPT
iptables -A FORWARD -i <external_interface> -p tcp -j BAD_OPT

Check For Bad Connection States
##If you use this ruleblock, you will need to use the Adaptive Dropping ruleblock as well. ##
##Otherwise remove the
-m recent --set switches from this ruleblock##
iptables -N BAD_CONN
iptables -A BAD_CONN -m conntrack --ctstate INVALID -m recent --set -j DROP
iptables -A BAD_CONN -j RETURN
iptables -A INPUT -j BAD_CONN
iptables -A FORWARD -j BAD_CONN

Check For Packets of the Wrong Size
iptables -N BAD_SMALL
iptables -A BAD_SMALL -p udp -m length --length 0:27 -m recent --set -j DROP
iptables -A BAD_SMALL -p tcp -m length --length 0:39 -m recent --set -j DROP
iptables -A BAD_SMALL -p 30 -m length --length 0:31 -m recent --set -j DROP
iptables -A BAD_SMALL -p 47 -m length --length 0:39 -m recent --set -j DROP
iptables -A BAD_SMALL -p 50 -m length --length 0:49 -m recent --set -j DROP
iptables -A BAD_SMALL -p 51 -m length --length 0:35 -m recent --set -j DROP
iptables -A BAD_SMALL -m length --length 0:19 -m recent --set -j DROP
iptables -A BAD_SMALL -j RETURN
iptables -A INPUT -p tcp -j BAD_SMALL
iptables -A FORWARD -p tcp -j BAD_SMALL

Checks for Peer-2-Peer Network Traffic
iptables -N P2P_CHK
iptables -A P2P_CHK -m ipp2p --edk -j DROP
iptables -A P2P_CHK -m ipp2p --dc -j DROP
iptables -A P2P_CHK -m ipp2p --kazaa -j DROP
iptables -A P2P_CHK -m ipp2p --gnu -j DROP
iptables -A P2P_CHK -m ipp2p --bit -j DROP
iptables -A P2P_CHK -m ipp2p --apple -j DROP
iptables -A P2P_CHK -m ipp2p --winmx -j DROP
iptables -A P2P_CHK -m ipp2p --soul -j DROP
iptables -A P2P_CHK -m ipp2p --ares -j DROP
iptables -A P2P_CHK -j RETURN
iptables -A INPUT -j P2P_CHK
iptables -A FORWARD -j P2P_CHK

Sanity Checks for External Traffic
##In this ruleblock replace the $EXT with the name of your external interface##
##If for some reason you are using one of these network address externally, uncomment it##
iptables -N EXT_SANITY
iptables -A EXT_SANITY -s 192.168.0.0/24 -i $EXT -j DROP
iptables -A EXT_SANITY -s 192.168.1.0/24 -i $EXT -j DROP
iptables -A EXT_SANITY -s 192.168.2.0/24 -i $EXT -j DROP
iptables -A EXT_SANITY -s 192.168.3.0/24 -i $EXT -j DROP
iptables -A EXT_SANITY -s 192.168.254.0/24 -i $EXT -j DROP
iptables -A EXT_SANITY -s 199.196.196.0/24 -i $EXT -j DROP
iptables -A EXT_SANITY -s 0.0.0.0/8 -i $EXT -j DROP
iptables -A EXT_SANITY -s 255.0.0.0/8 -i $EXT -j DROP
iptables -A EXT_SANITY -s 255.255.255.255 -i $EXT -j DROP
iptables -A EXT_SANITY -s 127.0.0.0/8 -i $EXT -j DROP
iptables -A EXT_SANITY -s 224.0.0.0/4 -i $EXT -j DROP
iptables -A EXT_SANITY -s 240.0.0.0/5 -i $EXT -j DROP
iptables -A EXT_SANITY -s 169.254.0.0/16 -i $EXT -j DROP
iptables -A EXT_SANITY -s 192.0.0.0/24 -i $EXT -j DROP
iptables -A EXT_SANITY -s 192.0.34.0/24 -i $EXT -j DROP
iptables -A EXT_SANITY -s 10.0.0.0/8 -i $EXT -j DROP
iptables -A EXT_SANITY -s 172.16.0.0/12 -i $EXT -j DROP
iptables -A EXT_SANITY -s 192.168.0.0/16 -i $EXT -j DROP
iptables -A EXT_SANITY -s <firewalls_own_ip> -i $EXT -j DROP
iptables -A EXT_SANITY -j RETURN
iptables -A INPUT -p tcp -j EXT_SANITY
iptables -A FORWARD -p tcp -j EXT_SANITY

Sanity Checks for Internal Traffic
##In this ruleblock replace $INT with the name of your internal interface##
iptables -N INT_SANITY
iptables -A INT_SANITY -d 255.255.255.255 -i $INT -j DROP
iptables -A INT_SANITY -s 255.255.255.255 -i $INT -j DROP
iptables -A INT_SANITY -s ! $INTIP -i $INT -j DROP
iptables -A INT_SANITY -j RETURN
iptables -A INPUT -p tcp -j INT_SANITY
iptables -A FORWARD -p tcp -j INT_SANITY

Check for Fragmented Traffic
iptables -N FRAG_CHK
iptables -A FRAG_CHK -j DROP
iptables -A INPUT -p ip -f -j FRAG_CHK
iptables -A FORWARD -p ip -f -j FRAG_CHK

Check for Proper Connection Status and Traffic Flooding
##If you use this ruleblock, you will need to use the Adaptive Dropping ruleblock as well. ##
##Otherwise remove the
-m recent --set switches from this ruleblock##
## Also the values of 50 and 120 work for me, you may need to change them in your setup##
iptables -N TCP_CHK
iptables -A TCP_CHK -p tcp --syn -m conntrack --ctstate NEW -m limit --limit 50/second --limit-burst 120 -j RETURN
iptables -A TCP_CHK -m recent --set -j DROP
iptables -A INPUT -p tcp -j TCP_CHK
iptables -A FORWARD -p tcp -j TCP_CHK

Adaptive Dropping
##Only use this ruleblock if you left used the
-m recent --set switches in other ruleblocks##
##It must also go in after the other ruleblocks using the
-m recent --set switches##
iptables -N SOD_OFF
iptables -A SOD_OFF -p tcp -j TARPIT       ## use this only if you feel nasty
iptables -A SOD_OFF -j DROP
iptables -A INPUT -m recent --rcheck --seconds 300 -j SOD_OFF
iptables -A FORWARD -m recent --rcheck --seconds 300 -j SOD_OFF

Reject Identd Traffic Politely
iptables -A INPUT -p tcp -m conntrack --ctstate NEW -m tcp --dport 113 --syn -j REJECT --reject-with tcp-reset
iptables -A FORWARD -p tcp -m conntrack --ctstate NEW -m tcp --dport 113 --syn -j REJECT --reject-with tcp-reset

Uhhhh... What About the "Normal Rules"?
Unfortunatly this section requires a bit more thought. While this is generally where I place the firewall rules for allowed traffic, I cannot hope to cover all the possible variations needed by everyone. So we'll try a quick crash course on rule writing. The first thing to understand is the way that a packet travels the iptables tables/chains. Lets start with a packet going to or from the firewall;

          <Packet>
                  |
mangle-PREROUTING
                  |
   nat-PREROUTING
                  |
     <routing decision>
                  |
      mangle-INPUT
                  |
           INPUT
                  |
      <local process>
                  |
    mangle-OUTPUT
                  |
       nat-OUTPUT
                  |
          OUTPUT
                  |
mangle-POSTROUTING
                  |
     <routing decision>
                  |
   nat-POSTROUTING
                  |
           <Packet>

Now lets look at how a packet travelling through a firewall (meaning that the firewall forward the packet between interfaces) goes
through the iptables tables/chains;

          <Packet>
                  |
mangle-PREROUTING
                  |
   nat-PREROUTING
                  |
     <routing decision>
                  |
           FORWARD
                  |
     <routing decision>
                  |
   nat-POSTROUTING
                  |
           <Packet>

This is stuff you need to know so that you can construct rules for use by your firewall. But to make rules you need to know some basic  (there is a lot more) syntax;
Switch
Desciption
-t
this specific the tablespace where the tables/chains reside which you want to work with
-A
this is used to append the rule to the targeted table/chain
-p
this is used to specify the protocol the rule must match
-i
this is used to specify the incoming network interface the rule must match
-o
this is used to specify the outgoing network interface the rule must match
-s
this is used to specify the source address the rule must match
-d
this is used to specify the destination address the rule must match
-j
this specifies the action the rule must take if the rule matches the packet

Now lets look at writing some rules (bear in mind these need the stateful packet inspection rules in place), lets start with enabling SSH (port 22) to the firewall..
iptables -t nat -A PREROUTING -p tcp -m tcp --syn --dport 22 -d <firewall_ip> -j ACCEPT
iptables -A INPUT -p tcp -m tcp --syn --dport 22 -d <firewall_ip> -j ACCEPT

Now lets enable our firewall to SSH (port 22) out from itself..
iptables -t nat -A OUTPUT -p tcp -m tcp --syn --dport 22 -s <firewall_ip> -j ACCEPT
iptables -A OUPUT -p tcp -m tcp --syn --dport 22 -s <firewall_ip> -j ACCEPT
iptables -t nat -A POSTROUTING -p tcp -m tcp --syn --dport 22 -s <firewall_ip> -j ACCEPT

Those were rules for packets going to and from the firewall, lets try one for allowing http (port 80) through the firewall..
iptables -t nat -A PREROUTING -i <incoming> -p tcp -m tcp --syn --dport 80 -d <web_server_ip> -j ACCEPT
iptables -A FORWARD -i <incoming> -o <outgoing> -p tcp -m tcp --syn --dport 80 -d <web_server_ip> -j ACCEPT
iptables -t nat -A POSTROUTING -o <outgoing> -p tcp -m tcp --syn --dport 80 -d <web_server_ip> -j ACCEPT

Lets try one for source network address translation, say for people accessing ftp (port 21)..
iptables -t nat -A PREROUTING -i <incoming> -p tcp -m tcp --syn --dport 21 -j ACCEPT
iptables -A FORWARD -i <incoming> -o <outgoing> -p tcp -m tcp --syn --dport 21 -j ACCEPT
iptables -t nat -A POSTROUTING -o <outgoing> -p tcp -m tcp --syn --dport 21 -d -j SNAT --to-source <external_ip>

Let try one for allowing smtp (port 25) into our network..
iptables -t nat -A PREROUTING -i <incoming> -p tcp -m tcp --syn --dport 25 -d <ext_smtp_ip> -j DNAT --to-dest <int_smtp_ip>
iptables -A FORWARD -i <incoming> -o <outgoing> -p tcp -m tcp --syn --dport 25 -d <int_smtp_ip> -j ACCEPT
iptables -t nat -A POSTROUTING -o <outgoing> -p tcp -m tcp --syn --dport 25 -d <int_smtp_ip> -j ACCEPT

Tarpit Them All
##Only use this ruleblock as the very last rule after all others##
##Also only use this rule if you feel like being nasty##
iptables -A INPUT -i $EXT -p tcp -j TARPIT
iptables -A FORWARD -i $EXT -p tcp -j TARPIT

Final Words
Iptables really is a good firewall to learn, stateful, extensible and free. So take the time to learn it a bit better. I hope the examples and snippets here help with that learning process. As always, have fun.