bIf - basic iptables firewall

This is a recipe for a simple persistent iptables firewall for GNU/Linux systems. The bIf script even though it was written for debian systems it has been used on Ubuntu and CentOS systems.


First let's look at some basic iptables commands

To list the iptables rules
# iptables -L -v -n
Use the -t option if you are using and other tables except filter. eg:
#iptables -L -n -t nat


Let 's create a very simple persistent iptables firewall that allows inbound traffic to the ssh daemon, drops all other inbound traffic, and does not pay attention to outbound traffic.
# iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# iptables -A INPUT -j DROP


To list the rules
# iptables -L -v
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
   96  7696 ACCEPT     tcp  --  any    any     anywhere             anywhere            tcp dpt:ssh 
    0     0 DROP       all  --  any    any     anywhere             anywhere            

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 17 packets, 2180 bytes)
 pkts bytes target     prot opt in     out     source               destination         


Save these iptables rules.
# iptables-save > /etc/rules.iptables


To make our iptables firewall persistent we need to make sure that our rules are set just before the network interface(s) come up. So we put a little shell script at /etc/network/if-pre-up.d/bif that all it does is to restore the saved iptables rules and set some ipv4 kernel parameters. Kernel parameter settings can stick with sysctl as well. sysctl is actually the debian "politically correct" way.
# cat /etc/network/if-pre-up.d/bif
#!/bin/bash
/sbin/iptables-restore < /etc/rules.iptables
echo 1 > /proc/sys/net/ipv4/tcp_syncookies
echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts
echo 1 > /proc/sys/net/ipv4/conf/all/log_martians
#echo 1 > /proc/sys/net/ipv4/ip_always_defrag
echo 1 > /proc/sys/net/ipv4/icmp_ignore_bogus_error_responses
echo 1 > /proc/sys/net/ipv4/conf/all/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/all/send_redirects
echo 0 > /proc/sys/net/ipv4/conf/all/accept_source_route

#Set to 1 if you are setting up a gateway
echo 0 > /proc/sys/net/ipv4/ip_forward 



Allow execution
# chmod 700 /etc/network/if-pre-up.d/iptables


OK, the very simple persistent iptables firewall is ready.

To disable the simple firewall --flush all the iptables chains.
# iptables -F
For iptables that are using chains in tables different than the default "filter" table we should use the -t flag to select a table.

However, useful firewalls need more rules than these two. We can set the iptables rules on the command line, edit the /etc/rules.iptables file , or create a shell script that sets the rules.

This is how a /etc/rules.iptables file looks like
# cat /etc/rules.iptables 
# Generated by iptables-save v1.4.8 on Fri May 25 17:58:27 2012
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [121:15892]
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT 
-A INPUT -j DROP 
COMMIT


One could edit the /etc/rules.iptables file, but using bash or some other programming language to configure an iptables firewall makes things easier. That's why I wrote bIf which is yet another well commented script that sets an iptables firewall.

bIf stands for basic Iptables firewall , duh!. bIf can be used to set a basic iptables firewall, to block a list of IP addresses from a URL or a local file , or to set the rules for a NAT gateway.

bIf:
#!/bin/bash
##bIf - basic IPtables firewall
##g0 - kod.ipduh.com  - 2011
##bIf protects from most and allows many but you should edit at least BAD_IP_URL and OPEN_INBOUND_TCP
##2012 - There is an entry about bIf at aLog.ipduh.com  and the latest bIf can be found at kod.ipduh.com

#Paths to the programs used
IPTABLES="/sbin/iptables"
IPTABLES_SAVE="/sbin/iptables-save"
#The following programs are not used if BIF_BAD_IP_FILE does not exist and BAD_IP_URL is set to ""
EGREP="/bin/egrep"
AWK="/usr/bin/awk"
WGET="/usr/bin/wget"
SORT="/usr/bin/sort"
UNIQ="/usr/bin/uniq"

#File that stores BAD IP addresses and sets of IP addresses in CIDR notation
#If BIF_BAD_IP_FILE does not exist this functionality is disabled
#an example of a BIF_BAD_IP_FILE file can be found at http://archimedes.ipduh.com/bad_ip.html
BIF_BAD_IP_FILE="/etc/bif.bad"

#URL of bad IP list , set to "" to disable
BAD_IP_URL="http://archimedes.ipduh.com/bad_ip.html"

#Open TCP ports List , TCP Services
OPEN_INBOUND_TCP="21 22 23 25 43 53 80 123 143 389 443 465 587 993 1352 1661 3306 3389"

#Open UDP ports List
OPEN_INBOUND_UDP="53"

#NAT port forwarding settings
#disabled by default , to enable set LAN , WAN , LAN_SRV_PORT ,  LAN_SRV_IP and uncomment the NAT paragraph
LAN="eth0"
WAN="eth1"
LAN_SRV_PORT="1661"
LAN_SRV_IP="192.168.1.101"

if [ -n "$BAD_IP_URL" ]; then
     ${WGET} ${BAD_IP_URL} -O ${BIF_BAD_IP_FILE}
fi

if ! [ -x ${IPTABLES} ]; then
     echo "bIf: I cannot use /sbin/iptables"
     exit 404
fi

#Flush iptables chains
$IPTABLES -F
$IPTABLES -X
$IPTABLES -t nat -F
$IPTABLES -t nat -X
$IPTABLES -t mangle -F
$IPTABLES -t mangle -X
$IPTABLES -t raw -F
$IPTABLES -t raw -X

#Set a liberal-permissive OUTPUT Policy -- Remember that firewalls were not invented to be liberal 
$IPTABLES -P OUTPUT ACCEPT
$IPTABLES -A OUTPUT -j ACCEPT -o lo

#Drop NEW tcp that does not start with SYN packets
$IPTABLES -A INPUT -p tcp ! --syn -m state --state NEW -j DROP

#Allow outbound connections -- You should disable or further specify this if you are a reasonable paranoid admin
$IPTABLES -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

#Drop second and further fragments of fragmented packets
$IPTABLES -A INPUT -f -j DROP

#Drop XMAS traffic
$IPTABLES -A INPUT -p tcp --tcp-flags ALL ALL -j DROP

#Drop Null packets
$IPTABLES -A INPUT -p tcp --tcp-flags ALL NONE -j DROP

#Allow all loopback traffic and drop politely all traffic to 127/8 that does not go through lo
$IPTABLES -A INPUT -i lo -j ACCEPT
$IPTABLES -A INPUT ! -i lo -d 127.0.0.0/8 -j REJECT

#Accept ICMP
$IPTABLES -A INPUT -p icmp -j ACCEPT

#Play ping pong only with 198.51.100.101 even when using a different OUPUT policy
#${IPTABLES} -A INPUT -p icmp -m icmp --icmp-type echo-request -s 198.51.100.101 -j ACCEPT
##${IPTABLES} -A OUTPUT -p icmp -m icmp --icmp-type echo-reply -s 198.51.100.101 -j ACCEPT

#Allow ipv6 ICMP and inbound proto 41 ipv6 tunnel traffic from the ipv6 tunnel PoP 198.51.100.101
#$IPTABLES -A INPUT -p icmpv6 -j ACCEPT
#$IPTABLES -A INPUT -p ipv6 -s 198.51.100.101/32 -j ACCEPT

#NAT
#Enable talking among LAN hosts
#$IPTABLES -A FORWARD -i ${LAN} -j ACCEPT
#$IPTABLES -A FORWARD -o ${LAN} -j ACCEPT
#Forward inbound traffic to a behing the NAT server
#$IPTABLES -t nat -A PREROUTING -i ${WAN} -p tcp --dport ${LAN_SRV_PORT} -j DNAT --to ${LAN_SRV_IP}:${LAN_SRV_PORT}
#Enable Network Address Translation
#$IPTABLES -t nat -A POSTROUTING -o ${WAN} -j MASQUERADE

#Allow inbound connections to the TCP daemons listening on the ports defined at the OPEN_INBOUND_TCP list
if [ -n "$OPEN_INBOUND_TCP" ] ; then
for TCP_PORT in $OPEN_INBOUND_TCP; do
        ${IPTABLES} -A INPUT -p tcp --dport ${TCP_PORT} -j ACCEPT
done
fi

#Allow inbound connections to the UDP daemons listening on the ports defined at the OPEN_INBOUND_UDP list
if [ -n "$OPEN_INBOUND_UDP" ] ; then
for UDP_PORT in $OPEN_INBOUND_UDP; do
        ${IPTABLES} -A INPUT -p tcp --dport ${UDP_PORT} -j ACCEPT
        ${IPTABLES} -A INPUT -m state --state NEW -p udp --dport ${UDP_PORT} -j ACCEPT
done
fi

#Block Bad IP addresses and sets of IP addresses in CIDR notation
if [ -e "$BIF_BAD_IP_FILE" ] ; then
for BAD_IP in `${EGREP} -v '^#|^$' ${BIF_BAD_IP_FILE} | ${AWK} -F "," '{print $1}' | ${SORT} | ${UNIQ}`; do
        ${IPTABLES} -A OUTPUT -d ${BAD_IP} -j DROP
        ${IPTABLES} -A INPUT -s ${BAD_IP} -j DROP
done
fi

#Accept SSH connections only from 198.51.100.101 , Take 22 out of the OPEN_INBOUND_TCP list
#${IPTABLES} -A INPUT -p tcp --dport 22 -s 198.51.100.101 -j ACCEPT

#Log outbound connections.
#${IPTABLES} -A OUTPUT -j LOG

#Log Inbound connections useful when debuging
#${IPTABLES} -A INPUT -j LOG

#Drop the rest. bIf is not polite
${IPTABLES} -A INPUT -j DROP

$IPTABLES_SAVE > /etc/rules.iptables


The BAD_IP_FILE can be used to set the path to an IP list to block. You can disable Block List by setting BAD_IP_FILE to a file that it does not exist.

This list can be downloaded of a URL as well. You can disable the pull of the list of a URL by setting BAD_IP_URL to "" --an empty string
BAD_IP_URL=""


The Blocked IP list should look like the one following:
#example bIf bad IP list
#1 IP address XOR 1 CIDR coma comment OR URL to information
# if a pound sign is used at the beginning of the line bIf ignores the line eg:
#5.0.0.0/8, 5.0.0.0/8 , oups RIPE got this one
# whatever follows a comma is ignored. eg:
192.0.2.245, some comment or URL
192.0.2.0/27, 192.0.2.0/27
192.0.2.111, 192.0.2.111
192.0.2.222, 192.0.2.222
192.0.2.212, 192.0.2.212




bif bASIC iPTABLES fIREWALL