#!/bin/bash

FWVER="20130910"

# this script provides a reasonably well locked-down firewall that can be used
# for any systems which only need to be accessed via SSH and which themselves
# will have unlimited TCP and UDP outbound access

# INPUT design:
# - early input hook (if /etc/rc.firewall.input_early exists include it as if it were coded inline here)
# - accept inbound SSH (port 22) traffic from the local network targetted to this specific box
# - allow any related (already established) TCP or UDP traffic coming back to the server in
# - late input hook (if /etc/rc.firewall.input_late exists include it as if it were coded inline here)
# - drop and log everything else
# FORWARD design:
# - there is only one interface, so no forwarding is necessary
# OUTPUT design:
# - loopback interface is valid
# - early output hook (if /etc/rc.firewall.output_early exists include it as if it were coded inline here)
# - any outbound TCP or UDP traffic is allowed
# - late output hook (if /etc/rc.firewall.output_late exists include it as if it were coded inline here)
# - catch all rule, all other outgoing is denied and logged

# program paths
IPTABLES=/sbin/iptables
LSMOD=/sbin/lsmod
DEPMOD=/sbin/depmod
MODPROBE=/sbin/modprobe
GREP=/bin/grep
AWK=/usr/bin/awk
SED=/bin/sed
CUT=/usr/bin/cut
IFCONFIG=/sbin/ifconfig

ME=$(/bin/hostname)

echo "Loading Golden Code Firewall - rc.firewall (version $FWVER on host $ME)."

# get the list of non-loopback network interfaces
eths=( $($IFCONFIG -s | while read iface_name; do iface_name=$(echo $iface_name | cut -d " " -f 1); if [ "$iface_name" != "lo" ] && [ "$iface_name" != "Iface" ]; then echo $iface_name; fi; done) )

echo "Waiting for at least one of ( ${eths[@]} ) to be initialized (max 30 seconds)..."

ok=0
INTIF=""
INTIP=""
for i in {1..30}; do
   # check all interfaces
   for eth in ${eths[@]}; do
      cfgline=$($IFCONFIG $eth | $GREP "inet addr:")
      ethip=$(echo ${cfgline##*inet addr:} | $CUT -d' ' -f 1)

      if [ "$ethip" ]; then
         INTIF="$eth"
         INTIP="$ethip"
         ok=1
         break 2
      fi

      echo "Interface $eth is not initialized..."
   done
   sleep 1
done

if [ $ok -eq 0 ]; then
   echo "No interface is initialized!"
   exit 1
fi

$IFCONFIG $INTIF

echo "  -----------------------------------------------"
echo "  Network Interface : $INTIF"
echo "  IP Address        : $INTIP"
echo "  -----------------------------------------------"

# everyone else
UNIVERSE="0.0.0.0/0"

# test our modules
echo "  Calculating module dependencies."
$DEPMOD -a

echo -n "  Loading kernel modules: "

echo -n "ip_tables, "
if [ -z "` $LSMOD | $GREP ip_tables | $AWK {'print $1'} `" ]; then
   $MODPROBE ip_tables
fi
echo ""

# clear all current rules
echo "  Clearing existing rules.  Set the default policy to DROP."
$IPTABLES -P INPUT DROP
$IPTABLES -F INPUT 
$IPTABLES -P OUTPUT DROP
$IPTABLES -F OUTPUT 
$IPTABLES -P FORWARD DROP
$IPTABLES -F FORWARD 

# clear the user chain
if [ -n "`$IPTABLES -L | $GREP drop-and-log-it`" ]; then
   $IPTABLES -F drop-and-log-it
fi

# delete all user chains
$IPTABLES -X

# reset all counters
$IPTABLES -Z

# setup a user chain for dropping/logging
echo "  Creating the DROP chain."
$IPTABLES -N drop-and-log-it
$IPTABLES -A drop-and-log-it -j LOG --log-level info 
$IPTABLES -A drop-and-log-it -j REJECT

# INPUT rules
echo "  Loading INPUT rulesets."

# loopback interfaces are valid
$IPTABLES -A INPUT -i lo -s $UNIVERSE -d $UNIVERSE -j ACCEPT

# early input hook (if it exists include it as if it were coded inline here)
if [ -f /etc/rc.firewall.input_early ]; then
    . /etc/rc.firewall.input_early
fi

# accept inbound SSH traffic from anyone targeted to this specific box
$IPTABLES -A INPUT -i $INTIF -s $UNIVERSE -d $INTIP -p tcp --dport 22 -j ACCEPT

# allow any related (UDP or TCP) traffic coming back to the server in
$IPTABLES -A INPUT -i $INTIF -s $UNIVERSE -d $INTIP -m state --state \
 ESTABLISHED,RELATED -j ACCEPT

# late input hook (if it exists include it as if it were coded inline here)
if [ -f /etc/rc.firewall.input_late ]; then
    . /etc/rc.firewall.input_late
fi
 
# catch all rule, all other incoming is denied and logged
$IPTABLES -A INPUT -s $UNIVERSE -d $UNIVERSE -j drop-and-log-it

# OUTPUT rules
echo "  Loading OUTPUT rulesets."

# loopback interface is valid
$IPTABLES -A OUTPUT -o lo -j ACCEPT

# early output hook (if it exists include it as if it were coded inline here)
if [ -f /etc/rc.firewall.output_early ]; then
    . /etc/rc.firewall.output_early
fi

# outbound TCP traffic is allowed
$IPTABLES -A OUTPUT -o $INTIF -p tcp -j ACCEPT

# outbound UDP traffic is allowed
$IPTABLES -A OUTPUT -o $INTIF -p udp -j ACCEPT

# late output hook (if it exists include it as if it were coded inline here)
if [ -f /etc/rc.firewall.output_late ]; then
    . /etc/rc.firewall.output_late
fi

# catch all rule, all other outgoing is denied and logged
$IPTABLES -A OUTPUT -s $UNIVERSE -d $UNIVERSE -j drop-and-log-it

echo "Loading rc.firewall complete."
