#!/bin/bash

# +-------------------------------------------------+
# |                                                 |
# |     Korseby IP-TABLES FIREWALL   1.2.0-rc4      |
# |                                                 |
# | Configuration for a single computer             |
# |                                                 |
# +-------------------------------+-----------------+
# | (c) 2001-2004 Kristian Peters |
# +-------------------------------+

# ---------------------------- Preferences ---------------------------

VERSION="1.2.0-rc4"
IPTABLES="/sbin/iptables"
IFCONFIG="/sbin/ifconfig"
SYSCTL="/sbin/sysctl"

# LAN-side iface
LANIF="eth0"
LANIP="10.1.1.1"

# internal addresses LAN=10.1.1.[0-127]
LAN="10.1.1.0/255.255.255.128"
KORSEBY="10.1.1.1"

# erlaubte Verbindungen vom LAN
# Syntax:     IP#fromport#destport
LAN_ALLOW_HOST_TCP="${LAN}#32768:#21 ${LAN}#32768:#110 ${ADLIB}#1:1023#111 ${ADLIB}#111#1:1023 ${LAN}#32768:#487 ${LAN}#32768:#3128 ${LAN}#32768:#5901 ${LAN}#32768:#9999 ${LAN}#32768:#32768:"
LAN_ALLOW_HOST_UDP="${LAN}#32768:#53 ${ADLIB}#1:1023#111 ${ADLIB}#1:1023#1004 ${ADLIB}#1:1023#2049"

# gesamte Interfaces, die verwaltet werden sollen
INTERFACES="$LANIF"

# Alle Input/Output Chains festlegen
for iface in $INTERFACES ; do
	CHAINS="$CHAINS ${iface}_IN ${iface}_OUT"
done

# bogus addresses
CHAINS="$CHAINS LOGDROP"

# hier alle Ports eintragen, die geloggt werden sollen (portrange#DESCRIPTION)
LOGPORTS="20:21#FTP 22#SSH 23#TELNET 80#HTTP 111#PORTMAP 113#AUTH 123#NTP 135:136#MS_BAD 137:138#SAMBA 445#SMB_TCP 631#IPP 1080#SOCKS 1433:1434#MS_SQL 3128#PROXY 4661:4669#EDONKEY 6000:6101#X-WINDOW 6667#IRC 8080#PROXY 27374#ADDR_SRCH"

# hier alle ICMP-Typen, die explizit geloggt werden sollen (type#DESCRIPTION)
LOGICMP="0#echo-reply 5#redirect 8#echo-requ"

# hier alle Ports eintragen, die nicht geloggt werden sollen (sportrange#dportrange)
DONTLOG=""

# Zeitliche Limits setzen
LOG_FLOOD="20/m"
SYN_FLOOD="2/s"
PING_FLOOD="1/s"

# Loglevel Definitonen
info="6"
warning="4"
crit="2"



# ------------------------------ error -------------------------------
function error() {
	echo "$@"
	exit 1
}



# ------------------------------ sysctl ------------------------------
function sysctl() {
	if [ "$1" = "set" ] ; then
		# general things
		$SYSCTL -w net/ipv4/icmp_echo_ignore_all=0 1>/dev/null
		$SYSCTL -w net/ipv4/icmp_echo_ignore_broadcasts=1 1>/dev/null
		$SYSCTL -w net/ipv4/ip_autoconfig=0 1>/dev/null

		# amount of connection tracking in firewall rules
		$SYSCTL -w net/ipv4/ip_conntrack_max=2048 1>/dev/null

		# LOG packets with unknown sourceroutes
		$SYSCTL -w net/ipv4/conf/all/log_martians=1 1>/dev/null
		$SYSCTL -w net/ipv4/conf/${LANIF}/log_martians=1 1>/dev/null

		# syn & spoofing protection
		$SYSCTL -w net/ipv4/tcp_ecn=0 1>/dev/null
		$SYSCTL -w net/ipv4/net/ipv4/tcp_syncookies=0 2>/dev/null 1>/dev/null

		$SYSCTL -w net/ipv4/conf/all/rp_filter=1 1>/dev/null
		$SYSCTL -w net/ipv4/conf/${LANIF}/rp_filter=1 1>/dev/null
		
		# special things
		$SYSCTL -w net/ipv4/ip_local_port_range=32768\t59999 1>/dev/null
		$SYSCTL -w net/core/rmem_default=8318 1>/dev/null 
		$SYSCTL -w net/core/rmem_max=32768 1>/dev/null 
		$SYSCTL -w net/ipv4/tcp_rmem=1024\t8024\t87380 1>/dev/null 
	fi
}



# ------------------------------ logdrop -----------------------------
function logdrop() {
	# TCP-NULL
	$IPTABLES -A LOGDROP -p tcp --tcp-flags ALL NONE -m limit --limit $LOG_FLOOD -j LOG --log-level $crit --log-prefix "TCP NULL SCAN Dropped" --log-tcp-options --log-ip-options
	$IPTABLES -A LOGDROP -p tcp --tcp-flags ALL NONE -j DROP

	# TCP-NMAP
	$IPTABLES -A LOGDROP -p tcp --tcp-flags ALL FIN,URG,PSH -m limit --limit $LOG_FLOOD -j LOG --log-level $crit --log-prefix "TCP NMAP Dropped " --log-tcp-options --log-ip-options
	$IPTABLES -A LOGDROP -p tcp --tcp-flags ALL FIN,URG,PSH -j DROP

	# fragments, invalid states
	$IPTABLES -A LOGDROP -f -m limit --limit $LOG_FLOOD -j LOG --log-level $warning --log-prefix "FRAGMENT Dropped "
	$IPTABLES -A LOGDROP -f -j DROP
	$IPTABLES -A LOGDROP -m state --state INVALID -j LOG --log-level $info --log-prefix "INVALID Dropped "
	$IPTABLES -A LOGDROP -m state --state INVALID -j DROP

	# don't log these ports
	for i in $DONTLOG ; do
		echo "$i" | {
			IFS='#' read sport dport
			$IPTABLES -A LOGDROP -p tcp --sport $sport --dport $dport -j DROP
			$IPTABLES -A LOGDROP -p udp --sport $sport --dport $dport -j DROP
		}
	done

	# log specific ports
	for i in $LOGPORTS ; do
		echo "$i" | {
			IFS='#' read port descr
			$IPTABLES -A LOGDROP -p tcp --dport $port -m limit --limit $LOG_FLOOD -j LOG --log-level $warning --log-prefix "$descr TCP Requ Dropped "
			$IPTABLES -A LOGDROP -p tcp --dport $port -j DROP
			$IPTABLES -A LOGDROP -p udp --dport $port -m limit --limit $LOG_FLOOD -j LOG --log-level $warning --log-prefix "$descr UDP Requ Dropped "
			$IPTABLES -A LOGDROP -p udp --dport $port -j DROP
		}
	done

	# other TCP Rules
	$IPTABLES -A LOGDROP -p tcp --syn -j LOG -m limit --limit $LOG_FLOOD --log-level $warning --log-prefix "TCP SYN Dropped "
	$IPTABLES -A LOGDROP -p tcp --syn -j DROP

	$IPTABLES -A LOGDROP -p tcp -j LOG -m limit --limit $LOG_FLOOD --log-level $info --log-prefix "TCP Dropped "
	$IPTABLES -A LOGDROP -p tcp -j DROP

	# other UDP Rules
	$IPTABLES -A LOGDROP -p udp -m state --state NEW -j LOG -m limit --limit $LOG_FLOOD --log-level $warning --log-prefix "UDP NEW Dropped "
	$IPTABLES -A LOGDROP -p udp -m state --state NEW -j DROP

	$IPTABLES -A LOGDROP -p udp -m state --state ESTABLISHED -m limit --limit $LOG_FLOOD -j LOG --log-level $info --log-prefix "UDP Dropped "
	$IPTABLES -A LOGDROP -p udp -m state --state ESTABLISHED -j DROP

	# ICMP
	for i in $LOGICMP ; do
		echo "$i" | {
			IFS='#' read itype descr
			$IPTABLES -A LOGDROP -p icmp --icmp-type $itype -m limit --limit $LOG_FLOOD -j LOG --log-level $warning --log-prefix "ICMP $descr Dropped "
			$IPTABLES -A LOGDROP -p icmp --icmp-type $itype -j DROP
		}
	done

	# other ICMP
	$IPTABLES -A LOGDROP -p icmp -m limit --limit $LOG_FLOOD -j LOG --log-level $info --log-prefix "ICMP Dropped "
	$IPTABLES -A LOGDROP -p icmp -j DROP

	# not matched
	$IPTABLES -A LOGDROP -m limit --limit $LOG_FLOOD -j LOG --log-level $info --log-prefix "other Dropped "
	$IPTABLES -A LOGDROP -j DROP
}



# --------------------------- lanif_input ----------------------------
function lanif_input() {
	# DROP bad packets (NULL,ALL,fragments,invalid)
        $IPTABLES -A ${LANIF}_IN -p tcp --tcp-flags ALL NONE -j LOGDROP
	$IPTABLES -A ${LANIF}_IN -p tcp --tcp-flags ALL FIN,URG,PSH -j LOGDROP
	$IPTABLES -A ${LANIF}_IN -f -j LOGDROP
	$IPTABLES -A ${LANIF}_IN -m state --state INVALID -j DROP

	# Accept established & related connections
	$IPTABLES -A ${LANIF}_IN -p tcp ! --syn --dport 32768: -m state --state ESTABLISHED,RELATED -j ACCEPT
	$IPTABLES -A ${LANIF}_IN -p udp --dport 32768: -m state --state ESTABLISHED,RELATED -j ACCEPT

	# ICMP vom $LAN erlauben
	$IPTABLES -A ${LANIF}_IN -p icmp -m limit --limit $PING_FLOOD -j ACCEPT

	# allow possible TCP connections
	if [ "$LAN_ALLOW_HOST_TCP" != "" ] ; then
		for rule in $LAN_ALLOW_HOST_TCP ; do
			echo "$rule" | {
				IFS='#' read host sport dport
				$IPTABLES -A ${LANIF}_IN -p tcp -s $host --sport $sport --dport $dport -j ACCEPT
			}
		done
	fi

	# allow possible UDP connections
	if [ "$LAN_ALLOW_HOST_UDP" != "" ] ; then
		for rule in $LAN_ALLOW_HOST_UDP ; do
			echo "$rule" | {
				IFS='#' read host sport dport
				$IPTABLES -A ${LANIF}_IN -p udp -s $host --sport $sport --dport $dport -j ACCEPT
			}
		done
	fi

	# DROP not matched LANIF-INPUT
	$IPTABLES -A ${LANIF}_IN -j LOGDROP
}



# --------------------------- lanif_output ---------------------------
function lanif_output() {
	# DROP bad packets (NULL,ALL,fragments,invalid)
	$IPTABLES -A ${LANIF}_OUT -p tcp --tcp-flags ALL NONE -j LOGDROP
	$IPTABLES -A ${LANIF}_OUT -p tcp --tcp-flags ALL FIN,URG,PSH -j LOGDROP
	$IPTABLES -A ${LANIF}_OUT -f -j LOGDROP
	$IPTABLES -A ${LANIF}_OUT -m state --state INVALID -j DROP

	# Accept OUTPUT to $LAN
	$IPTABLES -A ${LANIF}_OUT -p tcp -j ACCEPT
	$IPTABLES -A ${LANIF}_OUT -p udp -j ACCEPT

	# Allow ICMP out
	$IPTABLES -A ${LANIF}_OUT -p icmp -j ACCEPT

	# DROP not matched LANIF-OUTPUT
	$IPTABLES -A ${LANIF}_OUT -j LOGDROP
}



# ------------------------------- start ------------------------------
function start() {
	# ---------- create chains ----------

	# delete all rules
	$IPTABLES -F INPUT
	$IPTABLES -F OUTPUT

	# create new chains (after deleting them first)
	for chain in $CHAINS ; do
		$IPTABLES -F $chain 2>/dev/null
		$IPTABLES -X $chain 2>/dev/null
		$IPTABLES -t filter -N $chain
	done

	# ---------- default things ----------

	# default policies
	$IPTABLES -P INPUT DROP
	$IPTABLES -P OUTPUT DROP
	$IPTABLES -P FORWARD DROP

	# allow all traffic on lo
	$IPTABLES -t filter -A INPUT -i lo -j ACCEPT

	# allow all traffic on lo
	$IPTABLES -t filter -A OUTPUT -o lo -j ACCEPT

	# SPECIAL: allow DHCP
	$IPTABLES -t filter -A INPUT  -p udp -i $LANIF --sport 68 --dport 67 -j ACCEPT
	$IPTABLES -t filter -A OUTPUT -p udp -o $LANIF --sport 67 --dport 68 -j ACCEPT

	# redirect $LANIF input to chain ${LANIF}_IN
	$IPTABLES -t filter -A INPUT -p tcp  -i $LANIF -s $LAN -d $LANIP -j ${LANIF}_IN
	$IPTABLES -t filter -A INPUT -p udp  -i $LANIF -s $LAN -d $LANIP -j ${LANIF}_IN
	$IPTABLES -t filter -A INPUT -p icmp -i $LANIF -s $LAN -d $LANIP -j ${LANIF}_IN

	# redirect $LANIF output to chain ${LANIF}_OUT
	$IPTABLES -t filter -A OUTPUT -p tcp  -o $LANIF -s $LANIP -d $LAN -j ${LANIF}_OUT
	$IPTABLES -t filter -A OUTPUT -p udp  -o $LANIF -s $LANIP -d $LAN -j ${LANIF}_OUT
	$IPTABLES -t filter -A OUTPUT -p icmp -o $LANIF -s $LANIP -d $LAN -j ${LANIF}_OUT

	# LOGGING chains
	logdrop

	# ---------- INPUT ----------

	# chain ${LANIF}_IN
	lanif_input

	# DROP Windows Network Broadcast immediately
	#$IPTABLES -A INPUT -p tcp -d 10.1.1.255 --dport 137:138 -j DROP
	#$IPTABLES -A INPUT -p udp -d 10.1.1.255 --dport 137:138 -j DROP

	# DROP IPP immediately (adlib cups server)
	$IPTABLES -A INPUT -p udp -s ${ADLIB} -d 10.1.1.255 --sport 631 --dport 631 -j DROP

	# DROP not matched INPUT
	$IPTABLES -A INPUT -m limit --limit $LOG_FLOOD -j LOG --log-level $crit --log-prefix "UNKNOWN INPUT DROPPED "
	$IPTABLES -A INPUT -j DROP

	# ---------- OUTPUT ----------

	# chain ${LANIF}_OUT
	lanif_output

	# DROP not matched OUTPUT
	$IPTABLES -A OUTPUT -m limit --limit $LOG_FLOOD -j LOG --log-level $crit --log-prefix "UNKNOWN OUTPUT DROPPED "
	$IPTABLES -A OUTPUT -j DROP
}



# ----------------------------- main part ----------------------------
case "$1" in

  start)
	echo "Starting Korseby iptables firewall..."

	# check for iptables
	[ -x $IPTABLES ] || error "iptables ($IPTABLES) not found !"

	# check if interfaces exist
	for iface in $INTERFACES; do
		ifconfig $iface &>/dev/null || error "No Interface $iface found."
	done

	# check for sysctl
	[ -x $SYSCTL ] || error "sysctl ($SYSCTL) not found !"

	# load modules
	modprobe ip_tables
	modprobe iptable_filter
	modprobe ipt_state
	modprobe ip_conntrack
	modprobe ipt_limit
	modprobe ipt_LOG

	# sysctl
	sysctl set

	# start
	start
	;;

  stop)
	echo "Stopping Korseby iptables firewall..."

	# flush built-in chains
	$IPTABLES -F INPUT
	$IPTABLES -F OUTPUT

	# flush,delete chains
	for chain in $CHAINS ; do
		$IPTABLES -F $chain
		$IPTABLES -X $chain
	done

	# sysctl
	sysctl unset

	# unload modules
	rmmod iptable_filter
	rmmod ipt_state
	rmmod ipt_LOG 
	rmmod ipt_limit
	rmmod ip_conntrack
	rmmod ip_tables
	;;

  restart)
	$0 stop
	$0 start
	;;

  status)
	$IPTABLES -L -n -v
	;;

  test)
	$0 start
	$0 stop
	;;

  *)

	echo "Usage: $0 {start|stop|restart|status}"
	exit 1

esac

exit $?

