Die Firewall bauen

Leider können auch andere ungehindert die Firewall attakieren, sobald forwarding und nat wie oben aktiviert wird. Um das nun zu verhindern, bastelt man sich am besten ein firewall Skript, in dem zunächst sämtlicher Verkehr über die firewall ins interne Netz verboten wird. Des Weiteren ist es zunächst auch verboten, Pakete nach draußen ins Internet zu schicken. Schrittweise werden wir dann Dienst für Dienst die Firewall freischalten, um Programme wie KMail und Netscape nutzen zu können. Wir öffnen dementsprechend jene Ports die wir für die alltägliche Arbeit mit dem Internet benötigen. Das firewall Skript, welches wir schreiben werden wir als c-shell-Skript schreiben. Beginnen wir mit den Vereinbarungen:
# ======================================================================
# Teil 1:	Variablendeklaration
# ======================================================================

set IPTABLES = /sbin/iptables

# ----------------------------------------------------------------------
# spezielle Ports

set p_high	= 1024:65535 	# unprivilegierte Ports
set p_ssh	= 500:1023	# gemeinsame SSH Quell Ports

# ----------------------------------------------------------------------
# Interfaces

set EXT	= ppp0			# Hier DSL durch pppd erzeugtes Geraet
set INT	= eth0

set IF 	= ( $EXT $INT )

# ----------------------------------------------------------------------
# IP Hosts

set NS		= ( 192.168.0.1 )

set loghost	= 192.168.0.1

set INTERN	= 192.168.0.0/255.255.255.0	# Internes Netzwerk
set FIREWALL	= 192.168.0.1
Hier passiert zunächst nicht viel. Wir deklarieren hier im Prinzip Variablen, damit wir im Verlauf des Skriptes nicht mit wilden IP-Nummern und Ports hantieren müssen. Ein Wort zu den Variablen p_high und p_ssh. Die unprivilegierten Ports p_high sind Quell, bzw. Source Ports, von denen aus manche Dämonen, oder andere Serverdienste, einen Verbindungsaufbau instantiieren möchten. Beispielsweise eine Nameserveranfrage. Startet man nslookup, versucht der Client auf den Server und dessen Port 53 ein Verbindung anzufordern. Der Client selbst muß dies aber nicht zwingen vom Port 53 tun. Er kann auch als Quellport 32129 (oder einen anderen) verwenden. Um nun solche Anfragen zu erlauben, müssen wir bestimmte Portbereiche, von denen Userspace Programme Anfragen starten, als Quellport erlauben. Dazu gleich ein wenig später. Kommen wir zur Grundabsicherung. Im nächsten Teil des Skriptes folgt:
# ======================================================================
# Teil 2:	Grundkonfiguration absichern
# ======================================================================
# dynamische Kernelparameter setzen

echo "0" > /proc/sys/net/ipv4/ip_forward	# erstmal abschalten
echo "1" > /proc/sys/net/ipv4/tcp_syncookies	
echo "1" > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts
echo "1" > /proc/sys/net/ipv4/icmp_ignore_bogus_error_responses
#echo "5" > /proc/sys/net/ipv4/icmp_destunreach_rate
#echo "5" > /proc/sys/net/ipv4/icmp_echoreply_rate
#echo "5" > /proc/sys/net/ipv4/icmp_paramprob_rate
#echo "10" > /proc/sys/net/ipv4/icmp_timeexceed_rate

foreach if ( $IF )
   echo "1" > /proc/sys/net/ipv4/conf/$if/rp_filter
   echo "0" > /proc/sys/net/ipv4/conf/$if/accept_redirects
   echo "0" > /proc/sys/net/ipv4/conf/$if/accept_source_route
   echo "0" > /proc/sys/net/ipv4/conf/$if/bootp_relay
   echo "1" > /proc/sys/net/ipv4/conf/$if/log_martians
end

# -----------------------------------------------------------------------
# Default Policy und flush

$IPTABLES -P INPUT DROP
$IPTABLES -P FORWARD DROP
$IPTABLES -P OUTPUT DROP

$IPTABLES -F			# flush aller chains (Tabelle filter)
$IPTABLES -t nat -F		# flush aller chains (Tabelle nat)
$IPTABLES -X			# delete all userdefined chains
				# (Tabelle filter)

# ----------------------------------------------------------------------
# lokale Prozesse

$IPTABLES -A OUTPUT -o lo -j ACCEPT
$IPTABLES -A INPUT -i lo -j ACCEPT
Am Anfang des Teil 2, wird zunächst ip_forward abgeschaltet, tcp_syncookies, icmp_echo_ignore_broadcasts, icmp_ignore_bogus_error_responses eingeschaltet. Ip_forward schalten wir erst wieder ein, sobald alle wichtigen Regeln gesetzt wurden. Danach wird erst einmal der gesamte Rechner komplett geschützt, indem man alles DROP't (versperrt, abweisst). Schließlich werden auch die bisherigen im Kernel bereits gesetzten Standardregeln sicherheitshalber gelöscht. Damit haben wir zunächst erreicht daß niemand mehr auf diesen Rechner von außen zugreifen kann, auch wir nicht. Um jedoch lokalen Diensten den Zugang zu Server zu erlauben, definieren wir wie unter lokale Prozessezu sehen, die Regeln für die lokalen Dienste. Man erkennt hier auch die Syntax von iptables sehr gut. Allgemein lautet sie:
iptabes -A <chain> -[o,i] <device> -j [ACCEPT,REJECT,DROP]
Da Teil 3 die ganzen Regeln beinhaltet, widmet sich das nächste Kapitel dem vollständigen Skript. Auf eine Regelkette möchte ich besonders eingehen. Nämlich der des Nameservers. Wir betreiben auf diesem Server bind. Um Clients Anfragen zu erlauben und nur diesen Nameserver zu verwenden, müssen wir Regeln folgender Art definieren:
# ----------------------------------------------------------------------
# ausgehende Pakete bei bereits aufgebauter Verbindung
#
# Damit BIND nicht aufzuloesende Pakete weiterleiten kann muss noch ein NEW
# bei der OUTPUT Chain bei state hinzugefuegt werden.

$IPTABLES -A OUTPUT \
	  -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT

$IPTABLES -A FORWARD -i $INT -o $EXT \
	  -m state --state ESTABLISHED,RELATED -j ACCEPT
Durch das Hinzufügen von NEW vor ESTABLISHED, wird erreicht, das wenn BIND eine neue Verbindung zu anderen Nameservern im Internet aufbaut, diese Verbindung von der Firewall nicht verhindert wird. Sonst scheitern sämtliche nicht aufzulösenden Namen an der Firewall.
# ----------------------------------------------------------------------
# Rueckkanal: eingehende Pakete zu einer bestehenden Verbindung
#
# Hinweis: der sichere Eintrag ist:
#
# $IPTABLES -A INPUT -m state --state NEW,INVALID -j my_drop
#
# Da aber DNS-Anfragen von Clients bei state ein NEW benoetigen, muessen
# wir das NEW entfernen! - damit der hier lokal installierte BIND-Server
# funktioniert! Des Weiteren geht auch ssh schneller, da die NEW Anfragen
# nicht mehr gedropt werden

$IPTABLES -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
$IPTABLES -A INPUT -m state --state INVALID -j my_drop
Mit dem Hinweis oben ist folgendes gemeint - Sollte man die auskommentierte Regel setzen, wehrt die Firewall alles ab, was einen Verbindungsaufbau zur Firewall (neue Verbindung NEW) versucht. Von außen, dem Internet ist das auch O.K. - doch leider blockt sie auch alle möglichen neuen Verbindungen von Innen ab. Genau dort ist das Problem. Startet man beispielsweise Netscape und versucht eine Internetadresse in den Browser einzugeben, wird eine Nameserveranfrage gestartet, diese jedoch von unserer Firewall geblockt. Dementsprechend lockern wir hier den Gürtel hier ein wenig und geben mit der nächsten Regel explizit an, das nur die Pakete von außen (deshalb -o ppp0), die eine Neue (NEW) Verbindung aufbauen möchten, geblockt werden.
# Um diese eben erwaehnte Sicherheit wiederherzustellen entweder:
$IPTABLES -A INPUT -i $EXT -m state --state NEW,INVALID -j my_drop

$IPTABLES -A FORWARD -i $EXT -o $INT \
	  -m state --state ESTABLISHED,RELATED -j ACCEPT

$IPTABLES -A FORWARD -i $EXT -o $INT \
	  -m state --state NEW,INVALID -j my_drop
...
# ----------------------------------------------------------------------
# DNS

$IPTABLES -A INPUT -i $INT -s $INTERN \
	  -m state --state NEW \
	  -p UDP --sport $p_high -d $NS --dport domain \
	  -j ACCEPT

$IPTABLES -A INPUT -i $INT -s $INTERN \
	  -m state --state NEW \
	  -p TCP --sport $p_high -d $NS --dport domain \
	  -j ACCEPT

$IPTABLES -A OUTPUT -o $EXT -s $FIREWALL \
	  -m state --state NEW,ESTABLISHED,RELATED \
	  -p UDP --sport domain --dport domain \
	  -j ACCEPT

$IPTABLES -A OUTPUT -o $EXT -s $FIREWALL \
	  -m state --state NEW,ESTABLISHED,RELATED \
	  -p TCP --sport domain --dport domain \
	  -j ACCEPT
Die letzten vier Regeln definieren den Umgang mit Nameserveranfragen. Die beiden INPUT-Chains (einmal UDP und dann TCP) erlauben die Anfragen aus dem internen Netz an unseren Nameserver, die beiden OUTPUT-Chains das unser Nameserver die Anfragen, sofern er sie nicht selbst beantworten kann, weiterleiten darf. Nach einer kleinen Einarbeitung mit IPTABLES dürfte nun das folgende Skript kein Problem mehr sein.

Sven Alisch 2005-08-28