#!/bin/bash
# This firewall was written by Robert Delahunt <delahunt@puresimplicity.net>
# It is not copyrighted.
# Permission to distribute must be obtained by the author.
# It may be included in Slackware Linux by Patrick Volkerding free of charge.

function drop_all {
   iptables -P INPUT DROP
   iptables -P OUTPUT ACCEPT
   iptables -P FORWARD DROP
}

function accept_all {
   iptables -P INPUT ACCEPT
   iptables -P OUTPUT ACCEPT
   iptables -P FORWARD DROP
}

function clear_tables {
   iptables -F
   iptables -X
   iptables -Z
   # This line is needed at all times
   iptables -A INPUT -i lo -j ACCEPT
}

# Drop known-bad traffic before it even hits stateful
function drop_known_bad {
   iptables -A INPUT ! -i lo -p igmp -j DROP
   iptables -A INPUT ! -i lo -s 244.0.0.1 -j DROP
   iptables -A INPUT ! -i lo -m pkttype --pkt-type broadcast -j DROP
   iptables -A INPUT ! -i lo -p icmp -f -j DROP
   iptables -A INPUT ! -i lo -m state --state INVALID -j DROP
   # Drop packets that are state NEW but not a SYN packet
   iptables -A INPUT ! -i lo -p tcp ! --syn -m state --state NEW -j DROP
   # Drop the various xmastree scans
   iptables -A INPUT ! -i lo -p tcp --tcp-flags ALL NONE -j DROP
   iptables -A INPUT ! -i lo -p tcp --tcp-flags ALL ALL -j DROP
   iptables -A INPUT ! -i lo -p tcp --tcp-flags ALL FIN,URG,PSH -j DROP
   iptables -A INPUT ! -i lo -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG -j DROP
   iptables -A INPUT ! -i lo -p tcp --tcp-flags SYN,RST SYN,RST -j DROP
   iptables -A INPUT ! -i lo -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
}

# Drop traffic across ports that we don't we want
function drop_unwanted_ports {
   iptables -A INPUT ! -i lo -p tcp -m multiport --dports 0,137,138,139,445 -j DROP
   iptables -A INPUT ! -i lo -p udp -m multiport --dports 0,137,138,139,445 -j DROP
}

# Set up logic trees for blocking DNS not from ISP DNS
function dns_filter {
   for i in `cat /etc/resolv.conf | grep -v '#' | awk '{print $2}' `
   do
      iptables -A INPUT ! -i lo -p udp --dport 53 --sport 53 -s $i -j ACCEPT
   done
   iptables -A INPUT ! -i lo -p udp --dport 53 --sport 53 -j DROP
}   

# Set up logic trees for allowing DHCP from only my ISP
function dhcp_filter {
   for i in /etc/dhcpc/dhcpcd-*.info
   do
      a=`grep DHCPSID $i | sed 's/DHCPSID=//g'`
      iptables -A INPUT ! -i lo -p udp --sport 67 --dport 68 -s $a -j ACCEPT
   done
   iptables -A INPUT ! -i lo -p udp --sport 67 --dport 68 -j DROP
}

# Here are rules for allowing state NEW across ports
# AUTH ports:
function allow_auth_hosts {
   if [ -e /root/trusted.auth ]; then
      for i in `cat /root/trusted.auth`
      do
         iptables -A INPUT ! -i lo -p tcp --dport 113 --syn -s $i -j ACCEPT
      done
   else
      echo "Function allow_auth_hosts called but no /root/trusted.auth exists"
      echo "Blocking port 113..."
      iptables -A INPUT ! -i lo -p tcp --dport 113 --syn -j DROP
   fi

}

# Instead, allow rate-filtered auth:
function allow_filtered_auth {
   iptables -A INPUT -p tcp --dport 113 -m limit --limit 1/second -j ACCEPT
   # iptables -A INPUT  -p tcp --dport 113 -j LOG
}

function allow_ssh_hosts {
   if [ -e /root/trusted.ssh ]; then
      for i in `cat /root/trusted.ssh`
      do
         iptables -A INPUT  -p tcp --dport 22 --syn -s $i -j ACCEPT
      done
   else
      echo "Function allow_ssh_hosts called but /root/trusted.ssh does not exist"
      echo "Blocking port 22..."
      iptables -A INPUT -p tcp --dport 22 --syn -j DROP
   fi
}

function stateful {
   iptables -A INPUT  -p tcp -m state --state ESTABLISHED,RELATED -j ACCEPT
   iptables -A INPUT  -p udp -m state --state ESTABLISHED,RELATED -j ACCEPT
   iptables -A INPUT  -p icmp -m state --state ESTABLISHED,RELATED -j ACCEPT
}

function allow_filtered_ping {
   iptables -A INPUT  -p icmp --icmp-type 8 -m limit --limit 1/second -j ACCEPT
}

case "$1" in
'start')
   $0 wireless
;;
'alone')
   echo "Starting Firewall..."
   drop_all
   clear_tables
   drop_known_bad
   drop_unwanted_ports
   dns_filter
   dhcp_filter
   allow_auth_hosts
   allow_ssh_hosts
   allow_filtered_ping
   stateful
;;
'stop')
   echo "CAUTION: LOWERING SHIELDS!"
   accept_all
   clear_tables
;;
'restart')
   $0 stop
   $0 start
;;
'panic')
   echo "PANIC MODE ACTIVATED, INTERNET OFF!"
   drop_all
   clear_tables
;;
'simple')
   drop_all
   clear_tables
   drop_known_bad
   allow_filtered_ping
   allow_filtered_auth
   stateful
;;
'lan')
   drop_all
   clear_tables
   drop_known_bad
# TODO: find a way to script "where am i?" type request so that
# we can accept state NEW from our own LAN.
#   iptables -A INPUT  -p tcp -m state --state NEW -s 192.168.1.0 -j ACCEPT
   allow_filtered_ping
   stateful
   allow_auth_hosts
;;
'wireless')
   drop_all
   clear_tables
   drop_known_bad
   stateful
   allow_filtered_ping
   allow_filtered_auth
;;
*)
   echo "USAGE: " $0 " start|alone|stop|restart|panic|simple|lan|wireless"
esac

