Workstation-Firewall (iptables)

 Zielgruppe: Fortgeschrittene 

Dieses Script ist für den Einsatz auf Arbeitslatzrechnern gedacht. Bei mir laufen diese Filterregeln auf einem Rechner hinter einem Router, der auch als DNS-Server dient. Bei Verbindungen über ISDN/DSL mögen daher vielleicht noch kleine Änderungen nötig sein. Es kommt als Paketfilter iptables zum Einsatz, welches bei den 2.4er Kerneln verwendet wird. Es wird bei Debian mit dem Paket "iptables" zur Verfügung gestellt. Um die Funktion des Scriptes zu gewährleisten, ist das Laden von Kernel-Modulen notwendig, wie sie bei vielen Standard-Kerneln gängiger Distributionen beiliegen. Unter Debian woody kann man die Module dauerhaft mit dem Programm

modconf

ins System einbinden. Sie werden dann bei jedem Systemstart geladen. Dazu gehören die Module

ipt_LOG
ipt_state
iptable_filter
ip_tables
ip_conntrack_ftp
ip_conntrack

die man unter kernel/net/ipv4/netfilter findet. Erst danach stehen entsprechende Funktionen für das Filtern der Pakete zur Verfügung. Darüber hinaus reicht es bei Debian aus, wenn man das nachstehende Script einmal startet und dann die aktuellen Filterregeln durch Eingabe von

/etc/init.d/iptables save active

als root dauerhaft speichern. So gesehen ist das Script nur eine Hilfe bei der Konfiguration und erspart das manuelle Eintippen der Filterregeln. Das Script eignet sich weniger als typisches Start-/Stopp-Script unter /etc/init.d. Zumindest empfehle ich die Verwendung von /etc/init.d/iptables für diesen Zweck. Sollte der obige Speicherbefehl nicht funktionieren, weil zum Beispiel in Debian sarge das init-Script fehlt, der kann mit

iptables-save /var/lib/iptables/filterregeln

seine Filterregeln speichern und durch den Aufruf von

iptables-restore /var/lib/iptables/filterregeln

wieder rauskramen. Das entsprechende Verzeichnis /var/lib/iptables muss dabei ggf. noch angelegt werden. Dies ermöglicht das Ablegen von verschiedenen Firewall-Konfigurationen für unterschiedliche Situationen. Bitte beachte auch, dass bei den NTP-Servern in der Regel nur die Namen angegeben werden. Deshalb ist eine Internet-Verbindung beim Starten des Scriptes nötig, damit automatisch die IP-Adresse geholt werden kann. Wenn du das nicht willst, solltest du die IP-Adressen angeben. Die Filterregeln der Firewall kannst du mit

iptables -n -L

als root anschauen. Um den Rechner dann per default mit dem Paketfilter zu starten, muss man noch sicher stellen, dass entsprechende Links unter /etc/rc*.d auf /etc/init.d/iptables erzeugt wurden. Dies kann man aber leicht mit dem Tool "update-rc.d" nachholen. Dann werden automatisch die unter dem Namen "active" gespeicherten Filterregeln geladen.

#!/bin/sh
# Funktion:  Script zur Konfiguration einer Firewall für Arbeitsplatz-Rechner
# Lizenz:    Frei für nicht-kommerzielle Nutzung. Dieser Kommentarblock
#            muss erhalten bleiben und darf nicht verändert werden.
# Copyright: Jens-D. Neppe 
#            Inspririert von dem DSL-Router Firewall Script von Martin Rasp.
# Scriptname:fwrules.sh
# Aufruf:    fwrules.sh [set|clear]
# Datum:     2002-10-08
# Änderung:  2002-12-19 (Fehlerausgabe an den Anfang gesetzt, Scriptname und
#                        Parameter umbenannt.)
# Haftung:   Keine Haftung bei Schäden, die aus der direkten/indirekten
#            Nutzung dieses Shell-Scripts entstehen können.
##############################################################################

IPTABLES=/sbin/iptables
IFCONFIG=/sbin/ifconfig

INTERNET=0/0

# Das externe Interface kann auch
# ippp0 sein für ISDN-Verbindungen.
# Habe ich aber nicht getestet.
EXT_IF=eth0
LO_IF=lo

case "$1" in
   set)
      EXT_IP=`$IFCONFIG $EXT_IF | grep inet | cut -d : -f 2 | cut -d " " -f 1`
      EXT_MASK=`$IFCONFIG $EXT_IF | grep Mask | cut -d : -f 4`
      EXT_NET="$EXT_IP/$EXT_MASK"
      
      LO_IP=127.0.0.1
      LO_MASK=255.255.255.255 
      LO_NET="$LO_IP/$LO_MASK"
   ;;
   clear)
      echo "Lösche Filterregeln."
   ;;
   *)
      echo "Usage: $0 {set|clear}"
      exit 1
   ;;
esac

Flush_All()
{ # Paketfilterketten löschen
   $IPTABLES -F INPUT
   $IPTABLES -F FORWARD
   $IPTABLES -F OUTPUT
}

Allow_All()
{ # Neue Policy setzen
   $IPTABLES -P INPUT ACCEPT
   $IPTABLES -P FORWARD ACCEPT
   $IPTABLES -P OUTPUT ACCEPT
}

Deny_All()
{ # Neue Policy setzen
   $IPTABLES -P INPUT DROP
   $IPTABLES -P FORWARD DROP
   $IPTABLES -P OUTPUT DROP
}

Enable_Connection_Tracking()
{  # Variablen für Connection-Tracking befüllen:
   OTRACK="-m state --state NEW,RELATED,ESTABLISHED"
   ITRACK="-m state --state RELATED,ESTABLISHED"
}

Disable_Connection_Tracking()
{  # Variablen für Connection-Tracking entleeren:
   OTRACK=""
   ITRACK=""
}

Enable_Log_Incoming()
{  # Wenn jemand versucht, von aussen eine Verbindung mit dem Rechner
   # aufzubauen, dann wird das in die Log-Datei geschrieben.
   $IPTABLES -A INPUT -d $EXT_IP -m state --state INVALID,NEW -j LOG --log-prefix "BOESES PAKET:" --log-level 7
}

Allow_DNS()
{ DNS1=`cat /etc/resolv.conf | grep ^nameserver | cut -d " " -f 2 | head -n 1`
   DNS2=`cat /etc/resolv.conf | grep ^nameserver | cut -d " " -f 2 | tail -n 1`
   
   # UDP-Abfragen. Hier wird berücksichtigt, dass man u.U. vielleicht nur einen DNS hat.
   if [ -n $DNS1 ] ; then
      $IPTABLES -A OUTPUT -p udp -s $EXT_IP -d $DNS1 --destination-port 53 $OTRACK -j ACCEPT
      $IPTABLES -A INPUT -p udp -s $DNS1 --source-port 53 -d $EXT_IP $ITRACK -j ACCEPT
   fi
   if [ ! $DNS1 = $DNS2 ] && [ -n "$DNS2" ] ; then
      $IPTABLES -A OUTPUT -p udp -s $EXT_IP -d $DNS2 --destination-port 53 $OTRACK -j ACCEPT
      $IPTABLES -A INPUT -p udp -s $DNS2 --source-port 53 -d $EXT_IP $ITRACK -j ACCEPT
   fi
}

Allow_lo_traffic()
{ # Traffic über das lo-Device erlauben
   $IPTABLES -A OUTPUT -s $LO_IP -d $LO_IP -j ACCEPT
   $IPTABLES -A INPUT -s $LO_IP -d $LO_IP -i $LO_IF -j ACCEPT
}

Allow_http()
{ # Port 80
   $IPTABLES -A OUTPUT -p tcp -s $EXT_IP --destination-port 80 $OTRACK -j ACCEPT
   $IPTABLES -A INPUT -p tcp -d $EXT_IP --source-port 80 $ITRACK -j ACCEPT
}

Allow_http_proxy()
{ # Port 8000
   $IPTABLES -A OUTPUT -p tcp -s $EXT_IP --destination-port 8000 $OTRACK -j ACCEPT
   $IPTABLES -A INPUT -p tcp -d $EXT_IP --source-port 8000 $ITRACK -j ACCEPT
}

Allow_cvs()
{ # Port 2401
   $IPTABLES -A OUTPUT -p tcp -s $EXT_IP --destination-port 2401 $OTRACK -j ACCEPT
   $IPTABLES -A INPUT -p tcp -d $EXT_IP --source-port 2401 $ITRACK -j ACCEPT
}

Allow_nntp()
{ # Port 119
   $IPTABLES -A OUTPUT -p tcp -s $EXT_IP --destination-port 119 $OTRACK -j ACCEPT
   $IPTABLES -A INPUT -p tcp -d $EXT_IP --source-port 119 $ITRACK -j ACCEPT
}

Allow_ntp()
{ NTP1=`cat /etc/ntp.conf | grep ^server | cut -d " " -f 2 | head -n 1`
   NTP2=`cat /etc/ntp.conf | grep ^server | cut -d " " -f 2 | tail -n 1`
   
   for ntpserver in $NTP1 $NTP2 ; do
      for pakettype in udp tcp ; do
         $IPTABLES -A OUTPUT -p $pakettype -s $EXT_IP -d $ntpserver --destination-port 123 $OTRACK -j ACCEPT
         $IPTABLES -A INPUT -p $pakettype -d $EXT_IP -s $ntpserver --source-port 123 $ITRACK -j ACCEPT
      done
   done
}

Allow_https()
{ # Port 443
   $IPTABLES -A OUTPUT -p tcp -s $EXT_IP --destination-port 443 $OTRACK -j ACCEPT
   $IPTABLES -A INPUT -p tcp -d $EXT_IP --source-port 443 $ITRACK -j ACCEPT
}

Allow_POP3()
{ # Port 110
   $IPTABLES -A OUTPUT -p tcp -s $EXT_IP --destination-port 110 $OTRACK -j ACCEPT
   $IPTABLES -A INPUT -p tcp -d $EXT_IP --source-port 110 $ITRACK -j ACCEPT
}

Allow_SMTP()
{ # Port 25
   $IPTABLES -A OUTPUT -p tcp -s $EXT_IP --destination-port 25 $OTRACK -j ACCEPT
   $IPTABLES -A INPUT -p tcp -d $EXT_IP --source-port 25 $ITRACK -j ACCEPT
}

Allow_passive_ftp()
{ # FTP, Port 21-Kontolle, 1024:65535-Daten
   
   $IPTABLES -A INPUT -p tcp -d $EXT_IP --sport 21 -m state --state ESTABLISHED -j ACCEPT
   $IPTABLES -A OUTPUT -p tcp -s $EXT_IP --dport 21 -m state --state NEW,ESTABLISHED -j ACCEPT
   
   # passiv - Daten - keine neue Verbindungen
   $IPTABLES -A INPUT -d $EXT_IP -p tcp --sport 1024:65535 --dport 1024:65535 -m state --state ESTABLISHED -j ACCEPT
   $IPTABLES -A OUTPUT -s $EXT_IP -p tcp --sport 1024:65535 --dport 1024:65535 -m state --state ESTABLISHED,RELATED -j ACCEPT
}


# Die nachfolgen Funktionsaufrufe können
# den eigenen Bedürfnissen entsprechend
# mit einem "#" auskommentiert werden.
case "$1" in
   set)
      Deny_All
      Flush_All
      Enable_Connection_Tracking
      Enable_Log_Incoming
      Allow_DNS
      Allow_lo_traffic
      Allow_http
      Allow_https
      Allow_http_proxy
      Allow_POP3
      Allow_SMTP
      Allow_cvs
      Allow_nntp
      Allow_ntp
      Allow_passive_ftp
   ;;
   clear)
      Deny_All
      Flush_All
   ;;
esac

exit 0