#!/bin/bash
# $Id: packetfilter,v 1.46 2003/02/19 22:32:58 aleks Exp $
#
# Name:	       /usr/local/sbin/packetfilter
# purpose:     packetfilter for use@home  
# Author:      Alexander Stielau <aleks@buug.de>
# Codebase:    nightmare.sailtraining.de ipchains for 2.2.x
#              Karl-Heinz Haag <kh@lux.in-berlin.de>
#
# HOOK FOR STARTUP in /etc/ppp/ip-up.d/
# SEE ALSO:    http://www.buug.de/~aleks/iptables
#              /usr/local/bin/restorefilter
#	       /etc/firewall/fw-config
#		
# GET ANNOUNCES FOR NEW FEATURES BY MAIL:
# mail -s "subscribe shrekskripte" shrekskripte-request@buug.de < /dev/null 
#
# cvs-changelog
# $Log: packetfilter,v $
# Revision 1.46  2003/02/19 22:32:58  aleks
# Falsches File eingecheckt...
#
# Revision 1.44  2003/02/19 22:01:41  aleks
# GW_WORKSTATION für Alles -> internes Netz erweitert
#
# Revision 1.43  2003/01/30 20:10:09  aleks
# Colins Patches eingespielt
#
# Revision 1.42  2002/05/23 17:35:28  aleks
# Hinweis von Matthias Hölsche auf einen Fehler in Zeile 753 gefixt
#
# Revision 1.41  2002/03/20 22:50:32  aleks
# Von jjfux gefundene Fiptehler eingearbeitet
#
# Revision 1.40  2002/02/25 23:38:58  aleks
# - Idiot-Mode entfernt - nutzt eh keiner.
# - -m unclean Regel auskommentiert - macht Ärger mit T-Online, zumindest
#   manchmal. Gründe sind noch /etwas/ unklar.
# - einige größere redaktionelle am begleitenden Text
#
# Revision 1.39  2002/02/21 18:20:12  aleks
# - diverse Fiptehler korrigiert
# - eine hyperfluide SSH-Regel entfernt
# - Die Fußschuss-Bremse wird nun wie in der Doku angekündigt durch TESTING=1
#   getriggert und nicht durch DEBUG
#
# Revision 1.38  2002/02/18 23:14:52  aleks
# - ein fi in Zeile 789 entfernt
#
# Revision 1.37  2002/02/17 21:07:04  aleks
# - diverse Bugs zur Vorversion gefixt.
# - all_gate hinter die transproxy-Regeln geschoben
# - Regel für gesperrte Dienste vereinfacht
#
# Revision 1.36  2002/02/16 17:46:36  aleks
# - Reject-Regel für übliche Windows-Protokolle und anderes, was man garantiert
#   nicht haben will, eingefügt (STOP_HERE).
# - Eine sinnvolle Regelung für https gefunden.
# - Dokumentation weiter ergänzt
#
# Revision 1.35  2002/02/10 17:43:17  aleks
# Doku zum SSH-Tunnel durch ein Beispiel ergänzt, Doku zu APT-Feature
# korrigiert.
#
# Revision 1.34  2002/02/09 22:31:47  aleks
# Hinzufügung einer TCPMMS-Regel, um die MSS von TCP-SYN-Paketen auf einen
# ungefährlichen Wert für krank konfigurierte Router bei DSL-Providern zu
# setzen.
# Scheinbar nicht notwendig, wenn man einen Proxy benutzt. Jedenfalls hatte ich
# das Problem noch nie. Danke an Gunnar für den Tipp.
#
# Revision 1.33  2002/02/09 19:37:18  aleks
# FTP/WWW-Freigabe für das Gateway selbst an die Variable APT gekoppelt.
# (apt-get&Co direkt aus dem Netz)
#
# Revision 1.32  2002/02/01 00:49:09  aleks
# Alle eigenen Chains entsprechend HOWTO auf lowercase gesetzt.
# Doppelte Skizze entfernt.
#
# Revision 1.31  2002/01/29 23:27:46  aleks
# Umstellung auf CVS-Log-IDs als Versionshistory. Mal sehen, wie das aussieht.
# :-)
#
#
#
# Old changelog (major changes):
#
# 29.12.01     1.28 Debug-Level eingefügt, Policy-Änderung
# 21.12.01     1.26 Spoofing-rules rewritten.
# 15.12.01     1.18 switching to a separate config-file
# 13.12.01     1.14 script will find device-defs for external and internal
#		    net by itself. 
#		    Also network and netmask for the internal net
# 12.12.01     1.12 fixes in rulebase, local loopback iface was wrong
#		    named, causes in problems with rules which using this
#		    iface.
# 		    Changed default for ALL_GATE, every outgoing traffic 
#		    from gate is accepted now. 
#		    
# 12.12.01     1.5 niceing the comments
# 11.12.01     1.4 New Versions, using CVS now
# 11.12.01     3.2 IDIOT-Mode enabled. For Lusers which cannot read.
#		   SET IDIOT=yes. More documentation
# 10.12.01     3.1 NOW EXTENSIVE COMMENTING FOR UEBERLUSER 
# 17.07.01     3.0 changed back to my handmade rules
# 15.07.01     2.6 switch kernel-console logging to 7
# 06.05.01     2.5 ssh-connection enabled
# 06.05.01     2.4 new rule for spoofing-protection
# 25.03.01     2.3 logging enabled :-)
# 23.03.01     2.2 playing around with policies and logging
# 17.03.01     2.1 logging of dropped packets, check for ppp0-Device
#		   before running rule-creation
# 17.03.01     2.0 complete rewriting for iptables 1.1.1. with Kernel 
#	           2.4.2 and test
# 14.03.01     1.1 minor enhancemends for variables-handling
# 11.03.01     1.0 inital version for test
#
# =====================================================================
#
# Dieses Skript dient eher der Betrachtung der Arbeitsweise von iptables.
# Mehr als nur Logging-Rules ein- oder auszukommentieren erfordert etwas Wissen,
# also behutsam vorgehen, wenn überhaupt.
#
# Die Variablen werden alle in /etc/firewall/fw-config gesetzt.
# Und zwar alle! 
#
# Hier mit wird die Datei eingelesen. Alle Variablen darin sind nun auch in
# diesem File gültig.
#
. /etc/firewall/fw-config
#
# Das externe Interface wird gecheckt und bei Vorhandensein ausgelesen.
# Dies funktioniert, wenn die Verbindung besteht, automatisch. 
# Ansonsten wird die Aktion komplett abgebrochen.
#
# Zur Kontrolle wird die IP ausgegeben.
#
#
CHECK_EXT="`$IFCONFIG $EXT_IFACE 2>/dev/null`"
#
if [ "$CHECK_EXT" != "" -a $? -eq 0 ] ; then
   #
   EXT_IP="`$IFCONFIG $EXT_IFACE | $AWK '/inet addr:/ \
           {print $2}'| $SED 's/addr://'`"
   $ECHO "Die IP-Nummer des externen Interfaces ist ${EXT_IP}"
   #
  else
   # switch off fw and warn.
   #
   $ECHO "W A R I N G   -  EXTERNAL IFACE CHECK FAILED"
   $ECHO "============================================"
   $ECHO "Filter will switched off now"
   /usr/local/sbin/restorefilter
   $ECHO "============================================"
   $ECHO "FILTER IS OFF NOW.  Device ${EXT_IFACE} is unsecure!"
   $ECHO "============================================"
   exit 1
fi
#
# Das interne Interface wird gecheckt und bei Vorhandensein ausgelesen.
# Ansonsten wird eine Fehlermeldung abgegeben, aber nicht abgebrochen.
# Das externe Interface funktioniert ja.
#
# Die Bestimmung des internen Netzes funktioniert nur, wenn das externe 
# Interface die Defaultroute hat, und keine weiteren internen Netze 
# existieren, die am gleichen Interface hängen. Grund für diese Einschränkung
# ist die relativ billig Art des Parsens an dieser Stelle.
#
# Zur Kontrolle wird die IP ausgegeben.
#
if [ "$LOC_NET_ENABLE" = "yes" ] ; then
   #
   # Check, ob das Interface vorhanden ist
   CHECK_LOC="`$IFCONFIG $LOC_IFACE 2>/dev/null`"
   #
   if [ "$CHECK_LOC" != "" -a $? -eq 0 ] ; then
        # LOC_IP
        # Nun wird die IP des internen Devices ausgelesen und ausgegeben.
        LOC_IP="`$IFCONFIG $LOC_IFACE | $AWK '/inet addr:/ \
	       {print $2}'| $SED 's/addr://'`"
        $ECHO "Die IP-Nummer des internen Interfaces ist ${LOC_IP}"
        #
        # LOC_NET
        LOC_NET="`$ROUTE -n | $GREP $LOC_IFACE | $GREP -v UG | \
                $AWK '{print $1 "/" $3}'`"
        $ECHO "Das lokale Netz hat das Netz ${LOC_NET}"
       else
        # warning
   	$ECHO "W A R I N G   -  INTERNAL IFACE CHECK FAILED"
	$ECHO "============================================"
   	$ECHO "Filter will not work correctly for local net"
   	$ECHO "============================================"
   fi
   #
  else
   $ECHO "Es ist kein lokales Netz definiert."
fi
#
###########################################################
#
# Erklärungen zu der iptables-Notation erfolgen im laufenden
# Skript. Also weiterlesen.
#
###########################################################
# insert modules
# ==============
# Die folgenden Module werden benötigt, bitte Kernel (2.4.x) entsprechend 
# basteln. Wenn der Kernel monolithisch ist, einfach alles
# auskommentieren. 
#
# Zum Thema Module siehe auch Kernel-HOWTO.
#
$INSMOD ip_tables &> /dev/null
$INSMOD ip_conntrack &> /dev/null
$INSMOD ip_conntrack_ftp &> /dev/null
$INSMOD ipt_state &> /dev/null
$INSMOD iptable_nat &> /dev/null
$INSMOD ipt_REJECT &> /dev/null
$INSMOD ipt_MASQUERADE  &> /dev/null
$INSMOD ipt_tcpmss  &> /dev/null
#
# switching kernel-console logging to 7
# =====================================
# Verhindert wirksam das Vollmüllen der Konsole mit Logging-Kram.
#
$ECHO "4 4 1 7"> /proc/sys/kernel/printk
#
# deactivate forwarding first
# ===========================
# Schaltet erstmal das Routing zwischen den Interfaces ab, bis 
# alle Regeln gesetzt sind. Wird am Ende des Skriptes wieder geändert.
#
# Auf einem Gateway sollte das Forwarding zwischen den Interfaces auf dem
# distributionspezifischen Wege sowieso abgeschaltet sein.
#
$ECHO "0" > /proc/sys/net/ipv4/ip_forward
#
# flushing old rules; setting default-policy 
# ==========================================
# Löschen der eventuell vorhandenen alten Regeln.
#
# Die Bedeutung der Optionen werden im Laufe des Skriptes erklärt.
# Soviel vorab: 
# -F	  flush, löschen der Regeln einer Kette
# -X	  delete, löschen der Kette selbst
#	  das geht nur in dieser Reihenfolge, erst leere Ketten können
#	  gelöscht werden.
#         Eine Kette (chain) ist eine Gruppe von Regeln, die unter einem Namen
#         zusammengefasst sind. Es gibt einige Standardchains - diese sind in
#         diesem Skript GROSS geschrieben, eigene Ketten sind klein
#         geschrieben.
# -t	  table. Tabellen für z.B. filter, nat und mangle
# -P	  Policy. Wenn keine Regel matcht, wird diese Defaultregel
#	  genutzt. 
#
$IPTABLES -F
$IPTABLES -X
$IPTABLES -F -t nat
$IPTABLES -F -t mangle
$IPTABLES -t nat -X
$IPTABLES -t mangle -X
if [ "$TESTING" = "0" ] ; then 
   $IPTABLES -P INPUT DROP
   $IPTABLES -P FORWARD DROP
   $IPTABLES -P OUTPUT DROP
 else
   $IPTABLES -P INPUT ACCEPT
   $IPTABLES -P FORWARD ACCEPT
   $IPTABLES -P OUTPUT ACCEPT
fi 
$IPTABLES -t nat -P PREROUTING ACCEPT
$IPTABLES -t nat -P POSTROUTING ACCEPT
#
#
# Setting new policies
# ====================
# Alles, was nicht explizit erlaubt ist, wird verboten.
# Sinnvoll, nicht nur für Paranoide.
#
# Hier funktioniert übrigens REJECT nicht. Um usenet(dcsf-faq)kompatibel 
# zu sein - also REJECT zu allen falschen Paketen - siehe letzte 
# Regel im Skript.
#
$IPTABLES -P INPUT   DROP
$IPTABLES -P OUTPUT  DROP  
$IPTABLES -P FORWARD DROP
#
#
###########################################################
# HERE WE GO!  
# ===========
#
# Generelles zur Syntax von iptables gibt es unter
# http://www.netfilter.org/documentation/HOWTO/de/NAT-HOWTO.txt
#
# Ansonsten man iptables. Zum Nachsehen, was das alles soll.
# 
# Wichtig zum Vergleich mit ipchains: Der Regellauf ist ein anderer:
# Pakete, die nicht für den Router selbst bestimmt sind, werden nur
# durch die FORWARD-Regel geschoben - den Router selbst betreffende 
# Pakete müssen durch INPUT bzw. OUTPUT. Siehe:
#                                 
# Eingehend                                           Ausgehend
#     ---> --PREROUTING--+--FORWARD--+--POSTROUTING-- --->
#                        |           |
#                      INPUT       OUTPUT
#                        |           |
#                        +-localhost-+
#
# Das ist aus dem o.g. HOWTO, also LESEN, ist auf deutsch und gut 
# übersetzt.
#
# Die Regeln werden nach dem First-Match-Prinzip durchlaufen,
# d. h., wenn eine Regel matcht, werden ab diesen Zeitpunkt nur noch 
# die Regeln durchlaufen, die zum Sprungziel der matchenden Regel 
# gehören.
#
# Es folgen diverse Regeln, um 'malicious' Pakete abzuwehren, wie
# z.B. Scans oder absichtlich kaputte Pakete.
#
# -A  appended eine neue Regel, die mit '-N Kettenname' eingeführt wird.
# 
# Nochmal : Festeingebaute Ketten werden GROSS geschrieben (INPUT, OUTPUT, 
# FORWARD im wesentlichen), eigene Ketten werden klein geschrieben.
#
########## WALLING & BLOCKING #############################
#
# Erstmal alles plattmachen, was generell kaputt ist oder 
# Angriffsversuche darstellen könnte.
#
# kill irregular packets
#
$IPTABLES -N invalid
$IPTABLES -A INPUT   -m state --state INVALID -i ! $LOOP_IFACE -j invalid
if [ "$LOC_NET_ENABLE" = "yes" ] ; then
   $IPTABLES -A FORWARD -m state --state INVALID -j invalid
fi
# unclean bereitet ab und zu im Zusammenhang mit T-Online (nicht nur?)
# Probleme. Also erstmal raus, ist eh ein testing-feature.
#$IPTABLES -A INPUT   -m unclean -i ! $LOOP_IFACE -j invalid
if [ "$DEBUG" = "1" ] ; then 
   $IPTABLES -A invalid   -j LOG --log-prefix "invalid "
 else
   $IPTABLES -A invalid   -m limit -j LOG --log-prefix "invalid "
fi
$IPTABLES -A invalid   -j REJECT 
#
# Allgemein:
# -m state definiert den Status eines Paketes, wie zum Beispiel,
#          ob es zu einer bestehenden Verbindung, zu einer neuen
#          Verbindung oder zu einer anderen Verbindung (z.B. ftp,
#          Datenkanal/Steuerkanal) gehört. Werte: Siehe manpage
# -j       ist ein Sprungziel. Pakete, die die vorhergehenden 
#          Bedingungen erfüllen (ja, alles andere sind Bedingungen, 
#          die geprüft werden, und alle zutreffen müssen), werden damit
#	   an eine andere Kette übergeben und dort weiter verarbeitet.
#	   Das können vordefinierte Ketten (INPUT, OUTPUT), oder auch
#	   selbst definierte (-N) sein.
#           
# LOG      Das Sprungziel LOG verhält sich anders als die anderen
#	   eingebauten Chains - nach dem Loggen (im Syslog
#	   /var/log/messages) mit dem Logeintrag
# 	   --log-prefix "sowieso " wird das Paket wieder in die
# 	   Main-Regel übergeben. Also zum Loggen zwei Regeln, eine, die
#	   loggt, eine die dann was macht. Wenn man sicher ist, das
#	   alles geht, kann man dann das Logging auskommentieren.
#	   Das Bauen von neuen Ketten begünstigt das Logging,
# 	   weil man für jede Kette einzeln loggen kann. 
#
# Also bedeutet die obige Kette 'invalid' das folgende:
# Alles, was in das System reinkommt (INPUT), dabei aber nicht vom Interface
# lo (-i ! $LOOP_IFACE) sondern von allen anderen kommt und den Status
# ungültig (-m state --state INVALID) hat, wird in die Kette 'invalid'
# weitergeleitet.
# Das gleiche wird auch mit Paketen gemacht, die nur geforwardet werden
# sollen, wenn die Firewall mit einem lokalen Netz verbunden ist (if [
# "$LOC_NET_ENABLE" = "yes" ] ; then).
# Je nach Debug-Level wird jedes Paket, daß die Kette invalid durchläuft
# geloggt, oder nur jeweils die ersten drei in einer definierten Zeiteinheit
# (-m limit). Nach dem Loggen werde die Pakete abgelehnt (-J REJECT).
#
#
# scan-packets    log and drop 
# block XMAS packets
#
$IPTABLES -N xmas
$IPTABLES -A INPUT   -p tcp --tcp-flags ALL ALL -j xmas
if [ "$LOC_NET_ENABLE" = "yes" ] ; then
   $IPTABLES -A FORWARD -p tcp --tcp-flags ALL ALL -j xmas
fi
if [ "$DEBUG" = "1" ] ; then 
   $IPTABLES -A xmas    -j LOG --log-level info \
                        --log-prefix "xmas-scan " 
 else 
   $IPTABLES -A xmas    -m limit -j LOG --log-level info \
                        --log-prefix "xmas-scan " 
fi
$IPTABLES -A xmas    -j REJECT 
#
# -p 	beschreibt das Protokoll, das ist entweder tcp, udp oder icmp.
# 	Wenn -p gesetzt ist, kann auch der Port (bei tcp/udp)
# 	definiert werden, und dies als Source-(Quell-) und/oder
# 	Destination-(Ziel-)Port. 
#       Später kommen Beispiele, die Optionen heißen --sport, --dport
# Die tcp-flags sind die das, was in der Literatur unter ACK-Bits 
# läuft, und die eine Verbindung starten, beenden, bestätigen usw.
#
# block NULL packets
#
$IPTABLES -N null_scan
$IPTABLES -A INPUT     -p tcp --tcp-flags ALL NONE -j null_scan
if [ "$LOC_NET_ENABLE" = "yes" ] ; then
   $IPTABLES -A FORWARD   -p tcp --tcp-flags ALL NONE -j null_scan
fi  
if [ "$DEBUG" = "1" ] ; then 
   $IPTABLES -A null_scan -j LOG --log-level info --log-prefix "null-scan " 
 else  
   $IPTABLES -A null_scan -m limit -j LOG --log-level info \
                          --log-prefix "null-scan "
fi   
$IPTABLES -A null_scan -j REJECT 
#
# spoofed packets log and drop 
#
# Das ganze ist komplizierter, wenn es im lokalen Netz mehrere
# unterschiedliche Netzbereiche gibt, die durch die Netzmaske des eigenen
# Subnetzes nicht erfaßt werden. Die Spoofing-Regeln für das lokale Netz
# funktionieren dann sehr kontraproduktiv. Für Home-Einsatz wird dies aber
# nicht der Fall sein.
#
$IPTABLES -N spoofing
if [ "$LOC_NET_ENABLE" = "yes" ] ; then
   $IPTABLES -A INPUT    -i $LOC_IFACE -s ! $LOC_NET -j spoofing
   $IPTABLES -A FORWARD  -i $LOC_IFACE -s ! $LOC_NET -j spoofing
   $IPTABLES -A FORWARD  -i $EXT_IFACE -s 192.168.0.0/16 -j spoofing
   $IPTABLES -A FORWARD  -i $EXT_IFACE -s 172.16.0.0/12 -j spoofing
   $IPTABLES -A FORWARD  -i $EXT_IFACE -s 10.0.0.0/8 -j spoofing
fi
#
# Wenn das externe Interface ein lokales Netz ist (z.B., wenn man seinen 
# Laptop in ein fremdes, privates Netz hängt und diesen davor schützen will,
# braucht man spezielle Spoofing-Regeln. 
# (das ist ziemlich mit der Kneifzange gemacht):
#
SPOOF_CHECK1="`$ECHO $EXT_IP|$CUT -f 1,2 -d.`"
SPOOF_CHECK2="`$ECHO $EXT_IP|$CUT -f 1 -d.`"
#
if [ "$SPOOF_CHECK1" = "192.168" ] ; then 
   $IPTABLES -A INPUT -i $EXT_IFACE -s 172.16.0.0/12 -j spoofing
   $IPTABLES -A INPUT -i $EXT_IFACE -s 10.0.0.0/8 -j spoofing
fi 
if [ "$SPOOF_CHECK1" = "172.16" ] ; then 
   $IPTABLES -A INPUT -i $EXT_IFACE -s 10.0.0.0/8 -j spoofing
   $IPTABLES -A INPUT -i $EXT_IFACE -s 192.168.0.0/16 -j spoofing
fi
if [ "$SPOOF_CHECK2" = "10" ] ; then 
   $IPTABLES -A INPUT -i $EXT_IFACE -s 172.16.0.0/12 -j spoofing
   $IPTABLES -A INPUT -i $EXT_IFACE -s 192.168.0.0/16 -j spoofing
fi
if [ "$DEBUG" = "1" ] ; then 
   $IPTABLES -A spoofing -j LOG --log-level info \
                         --log-prefix "spoofing "
 else
   $IPTABLES -A spoofing -m limit -j LOG --log-level info \
                         --log-prefix "spoofing "
fi
$IPTABLES -A spoofing -j REJECT
#
# -i 	bezeichnet das Interface, über das die Daten in der Regel
# 	reinkommen (incoming). Das Gegenteil wäre -o (outgoing).
#
########## END WALLING & BLOCKING #########################
#
#
########## CONNECTING & ADMITTING #########################
# 
# connect to the loopback
#
$IPTABLES -N lo_accept
$IPTABLES -A INPUT       -i $LOOP_IFACE -m state --state NEW -j lo_accept
$IPTABLES -A OUTPUT      -o $LOOP_IFACE -m state --state NEW -j lo_accept
if [ "$DEBUG" = "1" ] ; then 
   $IPTABLES -A lo_accept   -j LOG --log-prefix "lo_accept "
fi
$IPTABLES -A lo_accept   -j ACCEPT

# icmp handling
#
# In der Literatur wird immer wieder darum gestritten, ob es sinnvoll ist, daß
# ein im Netz hängender Rechner auf ICMP-Anfragen (z.B. ping) antwortet oder 
# nicht. 
#
# Die Leute, die behaupten, daß ein Rechner, der nicht auf ICMP-Requests
# antwortet, wäre sicherer, haben nicht begriffen, wozu ICMP dient:
#
# 1. Kann ein Rechner, der keine Services nach draußen anbietet, beim Einsatz 
#    von aktueller Software nicht durch ICMP-Requests angegriffen werden.
# 2. Wird ICMP genutzt, um Fehlermeldungen zu übertragen - diesen Mechanismus
#    sollte man nicht unterdrücken.
# 3. Ist auch ein Rechner, der nicht auf ICMP-Requests reagiert, im Netz zu
#    orten. So etwas wie stealth gibt es nur in der Vorstellung von fertigen
#    Marketingdroids.
# 
# Dieses Firewallskript antwortet also korrekt auf ICMP-Anfragen - mit einer
# Ausnahme. ICMP type 5 (redirection) wird abgewiesen. In einem privaten
# Dialup-Netz gibt es keine Instanz, die das Recht hätte, das Routing
# verändern zu wollen.
#
$IPTABLES -N icmp_allow
$IPTABLES -N icmp_reject
$IPTABLES -A INPUT -p icmp --icmp-type ! 5 -j icmp_allow
$IPTABLES -A INPUT -i $EXT_IFACE -p icmp --icmp-type 5 -m limit \
		   -j icmp_reject
if [ "$DEBUG" = "1" ] ; then 
   $IPTABLES -A icmp_allow -j LOG --log-prefix "icmp "
fi
$IPTABLES -A icmp_allow -j ACCEPT
if [ "$DEBUG" = "1" ] ; then 
   $IPTABLES -A icmp_reject -j LOG --log-prefix "icmp_rej "
 else  
   $IPTABLES -A icmp_reject -m limit -j LOG --log-prefix "icmp_rej "
fi
$IPTABLES -A icmp_reject -j REJECT --reject-with icmp-host-unreachable
#
######### REGULAR SERVICES ################################
#
# Hier beginnen die regulären Regeln. Als erstes das, was das Gateway
# selbst benötigt: DNS, bei Debian der Kram für APT, ICMP.
# Weil es nur um das Gateway selbst geht, keine Forward-Regeln in diesem
# Abschnitt.
#
######### services for gate ###############################
# allow pings from gate
#
$IPTABLES -N icmp_gate
$IPTABLES -A OUTPUT    -p icmp -j icmp_gate
if [ "$DEBUG" = "1" ] ; then 
   $IPTABLES -A icmp_gate -j LOG --log-prefix "icmp_gate_out "
fi
$IPTABLES -A icmp_gate -j ACCEPT
#
# allow ftp/www outgoing (for apt)
#
# Für Debian-Systeme zum Updaten und Einspielen von neuen Paketen direkt aus
# dem Netz. Dies ist eventuell auch mit YOU (YaST Online Update) so nutzbar.
# Vielleicht schickt mir mal jemand seine Erfahrungen mit YOU.

if [ "$APT" = "yes" ] ; then

   $IPTABLES -N apt_gate
   $IPTABLES -A OUTPUT -p tcp --dport $FTP -s $EXT_IP -m state --state \
                       NEW -o $EXT_IFACE -j apt_gate
   $IPTABLES -A OUTPUT -p tcp --dport $WWW -s $EXT_IP -m state --state \
                       NEW -o $EXT_IFACE -j apt_gate
   if [ "$DEBUG" = "1" ] ; then 
      $IPTABLES -A apt_gate -j LOG --log-prefix "apt_gate "
   fi
   $IPTABLES -A apt_gate -j ACCEPT
fi

# Wie angekündigt, hier nun eine explizite Nennung des Ports, der
# erlaubt ist. Damit ist es möglich, die Regel wesentlich präziser zu
# fassen. 
#
# --dport  Destinationport. Der Port, der dort, wo das Päckchen hin
# 	   soll, angesprochen wird.
# --sport  umgekehrt, also der Port, von dem das Päckchen kommt.
#
#	   Die Ports der üblichen Services sind in /etc/services gelistet.
#
# -o	   beschreibt das Device, auf dem das Paket rausgeht
# -i	   beschreibt das Device, auf dem das Paket reinkommt.
# NEW	   ist ein Status, der Pakete kennzeichnet, die eine
#	   Verbindungsaufnahme wünschen. Die Regel trifft nur für
#	   diese zu, Pakete, die zu einer bereits etablierten oder
#	   zugehörigen Verbindung gehören, werden als generell
#	   ungefährlich eingeschätzt und weiter unten unter
#	   Generalamnestie gestellt. 
#	   Damit hat man beim Logging automagisch nur
#	   Verbindungsanforderungen drin, was die Sache
#	   übersichtlicher macht.
#
# get the time 
#
# Um diese Regel zu nutzen, muß auf dem Gateway ein Programm laufen, daß sich
# die Systemzeit von außen holt. Sehr zu empfehlen ist zu diesem Zweck chrony.
#
$IPTABLES -N ntp_gate
$IPTABLES -A OUTPUT -p udp --dport $NTP -s $EXT_IP -m state --state \
                    NEW -o $EXT_IFACE -j ntp_gate
if [ "$DEBUG" = "1" ] ; then 
   $IPTABLES -A ntp_gate -j LOG --log-prefix "ntp_gate "
fi
$IPTABLES -A ntp_gate -j ACCEPT
#
# Wenn man paranoid ist, kann man natürlich die Timeserver hier genau 
# festnageln, in dem man eine -d(estination) festlegt. Genauso bei den folgenden
# DNS-Regeln. Ist aber wohl für zuhause overkill.
#
# allow local dns-querys
#
$IPTABLES -N dns_gate
$IPTABLES -A OUTPUT -p udp -o $EXT_IFACE --dport $DNS \
                   -m state --state NEW -j dns_gate
if [ "$DEBUG" = "1" ] ; then 
   $IPTABLES -A dns_gate -j LOG --log-prefix "dns_gate "
fi
$IPTABLES -A dns_gate -j ACCEPT
#
# allow gate ssh connections
#
$IPTABLES -N ssh_gate

if [ "$SSH_FROM_OUTSIDE" = "yes" ] ; then
   $IPTABLES -A INPUT  -p tcp -m state --state NEW -d $EXT_IP \
                       --dport $SSH -j ssh_gate
fi
$IPTABLES -A OUTPUT -p tcp -m state --state NEW \
                    --dport $SSH -j ssh_gate
if [ "$LOC_NET_ENABLE" = "yes" ] ; then
   $IPTABLES -A INPUT  -p tcp -m state --state NEW -s $LOC_NET \
                       --dport $SSH -d $LOC_IP -j ssh_gate
fi		       
if [ "$DEBUG" = "1" ] ; then 
   $IPTABLES -A ssh_gate -j LOG --log-prefix "ssh_gate "
fi
$IPTABLES -A ssh_gate -j ACCEPT

# allow gate smtp connections to outside
#
$IPTABLES -N smtp_gate
$IPTABLES -A OUTPUT -p tcp -o $EXT_IFACE -m state --state NEW \
		    --dport $SMTP -j smtp_gate
if [ "$LOC_NET_ENABLE" = "yes" ] ; then
   $IPTABLES -A OUTPUT -p tcp -o $LOC_IFACE -m state --state NEW \
  		       --dport $SMTP -d $LOC_NET -j smtp_gate
fi
if [ "$DEBUG" = "1" ] ; then 
   $IPTABLES -A smtp_gate -j LOG --log-prefix "smtp_gate "
fi
$IPTABLES -A smtp_gate -j ACCEPT


########## services for gate end #########################
#
########## services to/from localnet #####################
#
# Hier würde man jetzt für ein richtiges Netz festlegen, 
# was alles erlaubt ist. Zuhause sage ich aber, alles, was von innen 
# kommt, ist erlaubt.
#
# Und das lokale Netz ist bei mir mit einer Ausnahme von aussen nicht
# zu erreichen. Wer das will, kann sich ja über die obigen ssh-Regeln
# reintunneln. 
#
# nating/masquerading of outgoing packets only from $LOC_NET
#
if [ "$LOC_NET_ENABLE" = "yes" ] ; then
$IPTABLES -t nat -A POSTROUTING -o $EXT_IFACE -s $LOC_NET -j SNAT \
          --to-source $EXT_IP
fi
#
# Das ist eine sehr wichtige Regel, mit der der gesamte lokale Traffic
# nach außen maskiert wird.
# -t nat  ist eine neue Form, die nun volles NAT macht (im
#	  Gegensatz zu ipchains, das nur MASQ konnte).
#	  Das hier ist allerdings nur MASQ, alles aus dem lokalen
#	  Netzwerk wird auf die IP des exteren Interfaces gemappt.
#	  POSTROUTING/PREROUTING bestimmt den Zeitpunkt, wann genattet
#	  wird, beim MASQ will man das nach dem Routing. Will man die
# 	  Zieladresse ändern, macht man dies vor dem Routing, damit die 
#	  richtige Routing-Entscheidung getroffen werden kann.
# -j SNAT ist also Soure-Network-Address-Translation, die
#	  Quell-IP des Paketes wird umgewandelt. Beim DNAT (also
#	  Destination-NAT) wird entsprechend die Zieladresse manipuliert.
# --to-source gibt die neue Quelladresse an. Beim DNAT kann man
#	  da auch noch den gewünschten Zielport angeben.
#
# MTU-Spielchen
# 
if [ "$LOC_NET_ENABLE" = "yes" ] ; then
$IPTABLES -I FORWARD -p tcp --tcp-flags SYN,RST SYN \
                     -j TCPMSS --clamp-mss-to-pmtu
fi
#
# Mit dieser eher obskuren Option werden die MSS-Werte von TCP-SYN-Paketen auf
# einen für krank konfigurierte Router brauchbaren Wert gesetzt.
#
# Dazu aus der Kernel-Doku:
# TCPMSS target support
# CONFIG_IP_NF_TARGET_TCPMSS
#  This option adds a `TCPMSS' target, which allows you to alter the
#  MSS value of TCP SYN packets, to control the maximum size for that
#  connection (usually limiting it to your outgoing interface's MTU
#  minus 40).
#
#  This is used to overcome criminally braindead ISPs or servers which
#  block ICMP Fragmentation Needed packets.  The symptoms of this
#  problem are that everything works fine from your Linux
#  firewall/router, but machines behind it can never exchange large
#  packets:
#        1) Web browsers connect, then hang with no data received.
#        2) Small mail works fine, but large emails hang.
#        3) ssh works fine, but scp hangs after initial handshaking.
#
#  Workaround: activate this option and add a rule to your firewall
#  configuration like:
#
#        iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN \
#                 -j TCPMSS --clamp-mss-to-pmtu
#
# PORTFORWARDING
# ==============
# Ich erlaube es mir, auf dem Port 21 (eigentlich FTP) meiner Firewall ssh
# nach innen zu tunneln und damit direkt auf meinem Server im internen Netz 
# anzukommen:
#
if [ "$SSH_TUNNEL_ALLOW" = "yes" ] ; then
   $IPTABLES -t nat -A PREROUTING -i $EXT_IFACE -p tcp --dport $FTP \
             -d $EXT_IP -j DNAT --to-destination $SSH_TUNNEL_TO:$SSH
   $IPTABLES -A FORWARD -p tcp --dport $SSH -d $SSH_TUNNEL_TO -i $EXT_IFACE \
      	                -o $LOC_IFACE -j ACCEPT
fi
#
# Der passende ssh-Befehl ist in der Datei fw-config zu finden.
#
# TRANSPROXY
# ==========
# Wer einen transparenten Proxy für das interne Netz einsetzen will,
# fügt noch die folgende Regel ein, dann müssen alle internen User, die http
# nutzen, über den Squid auf dem Gateway und brauchen das auch nicht einzustellen. 
# Die Konfiguration des Proxies kann hier nachgelesen werden.
# http://www.linuxdoc.org/HOWTO/mini/TransparentProxy-4.html
# 
# Nur einkommentieren, wenn man einen Squid entsprechend konfiguriert
# hat. Sonst ist das Web am Arsch!
#
# Auf Grund der verwendeten PREROUTING-Regel funktioniert das nur für 
# Rechner aus dem lokalen Netz, nicht aber für den Gatewayrechner selbst. 
# Wie das auch noch geht, ist im obigen HOWTO (oder aber in der 
# Squid-FAQ, ich habs vergessen) beschrieben.
#
# Ebenso funktionieren die untigen Regeln nicht, wenn der Squid auf einer
# anderen Maschine als dem Gateway liegt. 
#
# ACHTUNG: Bei Galeon reicht dies nicht aus, da er die DNS-Auflösung 
# selbst machen will. Kaputtes Teil. Workaround gibts später mal.
#
# https über einen transparenten Proxy ist nicht möglich, weil der 
# Proxy dann eine man-in-the-middle Instanz wäre - das Protokoll ist dafür aus 
# Sicherheitsgründen nicht geeignet.
# SSL-Verbindungen werden einfach geforwardet.
#
if [ "$USE_TRANSPROXY" = "yes" ] ; then
   $IPTABLES -t nat -A PREROUTING -i $LOC_IFACE -j DNAT -p tcp \
             --dport $WWW --to-destination $LOC_IP:$SQUID
   $IPTABLES -N transproxy
   $IPTABLES -A INPUT -i $LOC_IFACE -s $LOC_NET -p tcp --dport $WWWS \
                      -m state --state NEW -j transproxy
   if [ "$APT" = "no" ] ; then 
      $IPTABLES -A OUTPUT -o $EXT_IFACE -p tcp --dport $WWWS \
                          -m state --state NEW -j transproxy
   fi
   $IPTABLES -A FORWARD -i $LOC_IFACE -s $LOC_NET -p tcp --dport $WWWS \
                        -o $EXT_IFACE -m state --state NEW -j transproxy
   if [ "$APT" = "no" ] ; then
      $IPTABLES -A OUTPUT -o $EXT_IFACE -p tcp --dport $WWW \
                          -m state --state NEW -j transproxy
   fi
   if [ "$DEBUG" = "1" ] ; then
      $IPTABLES -A transproxy -j LOG --log-prefix "TRANSP "
   fi
   $IPTABLES -A transproxy -j ACCEPT
fi
#
# allow gate everything to the outside 
# (courtesy to Ueberluser) 
#
# Okay, wer auf dem Gateway direkt arbeiten möchte, will auch
# von dort ins Netz und nicht nur die obigen mikrigen Dienste 
# nutzen. Hier mit werden alle Tore (bis auf $STOP_HERE) vom Filtersystem
# in das interne Netz und nach außen auf dem Gateway geöffnet.
# Wer das nicht tun möchte, weil er sowieso nicht auf dem Gateway selbst
# arbeitet, setzt in /etc/firewall/fw-config $GW_WORKSTAT auf "no", damit
# wird aller Traffic vom Gateway nach außen, der nicht oben explizit 
# erlaubt ist, verboten und geloggt.
#
# all_gate ist nun nach der Transproxy-Chain angesiedelt, weil sonst bei
# $GW_WORKSTAT=no die Transproxy-Chain nicht mehr zum Zuge kommen würde.
#
$IPTABLES -N all_gate
$IPTABLES -A OUTPUT -o $EXT_IFACE -m state --state NEW \
                    -j all_gate
if [ "$GW_WORKSTAT" = "yes" ] ; then
   if [ "$LOC_NET_ENABLE" = "yes" ] ; then
   for SERVICE in $STOP_HERE
    do
      for DIREC in $SD_PORT
       do 
        $IPTABLES -A OUTPUT   -p tcp $DIREC $SERVICE \
		-m state --state NEW -j REJECT --reject-with tcp-reset
        $IPTABLES -A OUTPUT   -p udp $DIREC $SERVICE \
		-m state --state NEW -j REJECT 
       done
    done
    $IPTABLES -A OUTPUT -o $LOC_IFACE -m state --state NEW \
                        -j all_gate
   fi 

   if [ "$DEBUG" = "1" ] ; then 
      $IPTABLES -A all_gate -j LOG --log-prefix "all_gate "
   fi
   $IPTABLES -A all_gate -j ACCEPT
 else
   if [ "$DEBUG" = "1" ] ; then 
      $IPTABLES -A all_gate -j LOG --log-prefix "all_gate_rej "
    else
      $IPTABLES -A all_gate -m limit -j LOG --log-prefix "all_gate_rej "
   fi
   $IPTABLES -A all_gate -j REJECT 
fi 
#
# Allow all outbound traffic from local net.
#
# alles andere aus dem lokalen Netz wird einfach mal so erlaubt.
# Wenn nicht, muß jeder einzelne Dienst freigegeben werden.
# Zuhause wäre dies imho Quark und kostet viel Zeit.
# 
# Falls es Windowskisten im Netz gibt, werden die üblichen Verdächtigen von 
# vornherein rejected, erstens, um die Daten zu schützen, und zweitens, um das
# Logfille zu schützen. Ebenso mit portmapper, bootp, telnet usw.
# Weitere Dienste, die gesperrt werden sollen, können in fw-config unter
# $STOP_HERE eingetragen werden.
#
#
if [ "$LOC_NET_ENABLE" = "yes" ] ; then
   for SERVICE in $STOP_HERE
    do
       for DIREC in $SD_PORT
        do 
         $IPTABLES -A INPUT   -p tcp $DIREC $SERVICE -j REJECT --reject-with tcp-reset
         $IPTABLES -A FORWARD -p tcp $DIREC $SERVICE -j REJECT --reject-with tcp-reset
         $IPTABLES -A INPUT   -p udp $DIREC $SERVICE -j REJECT 
         $IPTABLES -A FORWARD -p udp $DIREC $SERVICE -j REJECT 
      done
    done
# Die folgende Konstruktion ergibt in diesem Zustand noch nicht besonders 
# viel Sinn - mal abgesehen davon, daß dadurch der Traffic aus dem lokalen
# Netz nach draußen und auf das Gateway extra geloggt wird.
   $IPTABLES -N locnet_out
   $IPTABLES -A INPUT   -s $LOC_NET -i $LOC_IFACE -m state \
                        --state NEW -j locnet_out
   $IPTABLES -A FORWARD -s $LOC_NET -i $LOC_IFACE -o $EXT_IFACE \
                        -m state --state NEW -j locnet_out
   if [ "$DEBUG" = "1" ] ; then 
      $IPTABLES -A locnet_out -j LOG --log-prefix "locnet_o "
   fi   
   $IPTABLES -A locnet_out -j ACCEPT
fi
#
##########################################################
# allow all for established connections
#
# Wie schon oben beschrieben - alles, was zu einer bereits bestehenden
# Verbindung gehört, wird erlaubt.
#
$IPTABLES -A INPUT   -m state --state ESTABLISHED -j ACCEPT
$IPTABLES -A INPUT   -m state --state RELATED -j ACCEPT
$IPTABLES -A OUTPUT  -m state --state ESTABLISHED -j ACCEPT
$IPTABLES -A OUTPUT  -m state --state RELATED -j ACCEPT
if [ "$LOC_NET_ENABLE" = "yes" ] ; then
   $IPTABLES -A FORWARD -m state --state ESTABLISHED -j ACCEPT
   $IPTABLES -A FORWARD -m state --state RELATED -j ACCEPT
fi
#
######### END CONNECTING & ADMITTING #####################
#
# Hier folgen die letzten Regeln.
# Alles, was noch nicht gematcht hat, wird hier geloggt, um entweder 
# zu sterben[TM], oder um das Skript zu verbessern, bis es tierisch 
# den Arsch wegregelt.
#
# The final rules (logging for debugging)
#
if [ "$DEBUG" = "1" ] ; then 
   $IPTABLES -A INPUT   -j LOG --log-prefix "FINAL IN "  
   $IPTABLES -A OUTPUT  -j LOG --log-prefix "FINAL OUT "  
   $IPTABLES -A FORWARD -j LOG --log-prefix "FINAL FOR "  
 else
   $IPTABLES -A INPUT   -m limit -j LOG --log-prefix "FINAL IN "  
   $IPTABLES -A OUTPUT  -m limit -j LOG --log-prefix "FINAL OUT "  
   $IPTABLES -A FORWARD -m limit -j LOG --log-prefix "FINAL FOR "  
fi
$IPTABLES -A INPUT   -p tcp -j REJECT --reject-with tcp-reset
$IPTABLES -A OUTPUT  -p tcp -j REJECT --reject-with tcp-reset
$IPTABLES -A FORWARD -p tcp -j REJECT --reject-with tcp-reset
$IPTABLES -A INPUT   -j REJECT 
$IPTABLES -A OUTPUT  -j REJECT 
$IPTABLES -A FORWARD -j REJECT 
#
###########################################################
#
# Das Forwarding und damit das Routing zwischen den Devices wird 
# eingeschaltet, nachdem alle Regeln gesetzt sind.
#
if [ "$LOC_NET_ENABLE" = "yes" ] ; then
   $ECHO "1" > /proc/sys/net/ipv4/ip_forward
fi
$ECHO "1" > /proc/sys/net/ipv4/conf/all/proxy_arp
#
# Spoof protection by JVT
if [ -e /proc/sys/net/ipv4/conf/all/rp_filter ] ; then
    $ECHO -n "Setting up IP spoofing protection..."
    for f in /proc/sys/net/ipv4/conf/*/rp_filter; do
      $ECHO 1 > $f
    done
    $ECHO "done."
else
    $ECHO "PROBLEMS SETTING UP IP SPOOFING PROTECTION.  BE WORRIED."
fi
#
# Es folgt die Auswertung gegen den Remote-Knieschuss.
#
if [ "$TESTING" = "1" ] ; then 
   if [ "$BACKSECONDS" != "0" ] ; then
      # switch off rules
      $ECHO "W A R N I N G  - FILTER ONLY IN TESTMODE !!!"
      $ECHO "============================================"
      $ECHO "Filter will be switched of again in ${BACKSECONDS}s"
      sleep $BACKSECONDS
      /usr/local/sbin/restorefilter
      $ECHO "==========================================="
      $ECHO "FILTER IS OFF NOW. Device ${EXT_IFACE} is unsecure!"
      $ECHO "==========================================="
   fi
fi
# for debugging purposes: Print all rules after setting.
#
# Es scrollen alle Regeln durch. Sieht nett aus und dient dem
# Kapieren von dem, was man da gerade gemacht hat.
#
if [ "$DEBUG" = "1" ] ; then
   $IPTABLES -L -n -x -v
   $IPTABLES -L -t nat -n
fi 

# mail me the ip
#
# Sehr witzige Sache für Flaträter. So weiß man immer die IP, wenn
# sich die Kiste zuhause automagisch einwählt. 
# Schöner ist es, wenn man eine entsprechende Webseite irgendwo hoch kopiert,
# mit scp oder so. Wenn das jemand realisiert, bitte an mich.
#
if [ "$MAIL_IP" = "yes" ] ; then
    #Mailen der IP an eine definierte Adresse für remote-login
    /usr/bin/mail -s "home_ip $EXT_IP" "$MAIL_TO" < /dev/null
fi

exit 0
