Ja, das vom Paket iptables mitgebrachte init-script leistet wunderbare Dienste. Nein, es besteht kein akuter Grund, eine Alternative zu schreiben…
Einen Nachteil hat es schon: die Konfiguration wird in einem nicht kommentierbaren Format hinterlegt, was bei mir in letzter Zeit ständig zur Frage “WTF hat doch gleich dieser Port da verloren? Was lässt die rule da nochmal durch?” geführt hat.
<bash-addicted>Yep, eindeutige Notwenigkeit für ein selbst gehämmertes, kommentierbares Script!</bash-addicted> Wie in guten alten Debian-Zeiten übernimmt jetzt also bei mir ein einfaches, aber für den Job völlig ausreichendes init-script den Job. Wer es verwenden mag:
#!/bin/bash
#
# easy-to-edit iptables init script for Arch
# 2010 by Alexander Koch
#
# TCP services: ssh
SERVICES_TCP=( 22 )
# UDP services:
SERVICES_UDP=( )
. /etc/rc.conf
. /etc/rc.d/functions
if [ -e "/etc/conf.d/iptables" ]; then
. /etc/conf.d/iptables
fi
if [ -z "$IPTABLES" ]; then
IPTABLES="/usr/sbin/iptables"
fi
if ! [ -x "$IPTABLES" ]; then
echo "unable to execute iptables binary: $IPTABLES"
exit 1
fi
function reset_tables() {
ERR=0
$IPTABLES -F || ERR=1
$IPTABLES -X || ERR=1
$IPTABLES -P INPUT ACCEPT || ERR=1
$IPTABLES -P OUTPUT ACCEPT || ERR=1
if [ $IPTABLES_FORWARD -eq 1 ]; then
$IPTABLES -P FORWARD ACCEPT || ERR=1
echo 1 >/proc/sys/net/ipv4/ip_forward || ERR=1
else
$IPTABLES -P FORWARD DROP || ERR=1
echo 0 >/proc/sys/net/ipv4/ip_forward || ERR=1
fi
return $ERR
}
function setup_tables() {
ERR=0
# setup prevention chain against common attacks
$IPTABLES -N preventions || ERR=1
$IPTABLES -A preventions -f -j DROP || ERR=1 # frags
$IPTABLES -A preventions -p tcp --tcp-flags ALL ALL -j DROP || ERR=1 # XMAS
$IPTABLES -A preventions -p tcp --tcp-flags ALL NONE -j DROP || ERR=1 # null
# setup services chain
$IPTABLES -N services || ERR=1
for PORT in ${SERVICES_TCP[@]}; do
$IPTABLES -A services -p tcp --dport $PORT -j ACCEPT || ERR=1
done
for PORT in ${SERVICES_UDP[@]}; do
$IPTABLES -A services -p udp --dport $PORT -j ACCEPT || ERR=1
done
# allow incoming ping requests
iptables -A services -p icmp --icmp-type echo-request -j ACCEPT || ERR=1
# setup main chains
$IPTABLES -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT || ERR=1
$IPTABLES -A INPUT -i lo -j ACCEPT || ERR=1
$IPTABLES -A INPUT -j preventions || ERR=1
$IPTABLES -A INPUT -m state --state NEW -j services || ERR=1
$IPTABLES -A INPUT -p tcp -j REJECT --reject-with tcp-reset || ERR=1
$IPTABLES -A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable || ERR=1
$IPTABLES -P INPUT DROP || ERR=1
return $ERR
}
case "$1" in
start)
stat_busy "Loading IP Tables"
reset_tables
if ! setup_tables; then
stat_fail
exit 1
else
add_daemon miptables
stat_done
fi
;;
stop)
stat_busy "Flushing IP Tables"
if reset_tables; then
rm_daemon miptables
stat_done
else
stat_fail
exit 1
fi
;;
restart)
if ! ck_daemon miptables; then
rm_daemon miptables
fi
$0 start
exit $?
;;
*)
echo "usage: $0 {start|stop|restart}"
;;
esac
exit 0
Gefiltert werden nur eingehende Pakete; related und established Pakete werden ebenfalls ohne weitere Beachtung durchgelassen, ebenso alles über loopback. Für neue Pakete werden zuerst ein paar Vulnerability-Checks durchgeführt. Nach Außen wird ein Verhalten wie ohne iptables emuliert (z.B. REJECTs mit tcp-reset).
Bugreports wie immer willkommen, und Benutzung auf eigene Gefahr!
Update 31.07.10: Bugfixed
Im Februar hatte ich jede Menge Stress durch die Umstellung eines Mailservers auf xinetd, siehe Artikel. Mitte September lief dann das SSL-Zertifikat ab und ich musste ein neues generieren, was ich nach diesem Blogeintrag tat (Vielen Dank nochmal an dieser Stelle, hat mir viel Arbeit erspart!).
Leider genügt das für ein Setup mit SMTP über stunnel nicht, was ich dummerweise erst gestern beim erfolglosen Versuch des Versendens einer Mail über die Kiste bemerkte. Im stunnel-Log sah das so aus:
2009.11.08 19:47:53 LOG3[15442:3083773632]: SSL_CTX_use_certificate_chain_file: error:0906D06C:PEM routines:PEM_read_bio:no start line
Die Meldung weist auf ein fehlerhaft strukturiertes Zertifikat hin, und ein Vergleich mit dem (glücklicherweise gesicherten) alten zeigte die im neuen fehlenden DH-Parameter.
Diese stammten im alten Zertifikat von /var/qmail/control/dhparam512.pem, von wo aus ich sie dann einfach ans Ende des neuen Zertifikats anfügte – passt.
Bin mir noch nicht sicher, ob das nur ein Dirty Hack war oder sogar sauber…
Man sollte meinen, die Fehlermeldung “No key available with this passphrase” für eine mit LUKS verschlüsselte Partition wäre eindeutig und man hat eine falsche Passphrase eingegeben oder das falsche keyfile benutzt. Dem ist aber nicht so, wie ich kürzlich schmerzhaft lernen musste: diese Meldung wird nämlich auch ausgegeben, wenn schlichtweg das Kernelmodul für den Algorithmus fehlt! In meinem Fall hatte ich einfach vergessen, aes-i586 zu laden…
Tja, leider wie gesagt erst nach dem sinnlosen Formatieren (und damit Wegwerfen) einer 500GB-Partition mit nicht ganz unwichtigen Daten bemerkt, nämlich als beim Bootup nach der Neuformatierung das gleiche Problem auftrat und ich langsam misstrauisch wurde. Ein Vergleich der Vorgehensweisen beim Verschlüsseln und Einhängen brachte dann den entscheidenden Unterschied ans Tageslicht – das fehlende Modul…
Die Moral von der Geschicht’: traue Fehlermeldungen nicht!
Natürlich kann man sowas auch manuell immer wieder neu machen, aber bei vier aufeinanderfolgenden Installationen (allesamt auf Arch) ohne direkte Verbindung hat sich dann doch ein kleines (bash)Script angeboten.
Vielleicht kann es ja noch jemandem praktische Dienste leisten
BENUTZUNG AUF EIGENE GEFAHR!
#!/bin/sh
# check requirements
if [ -x /usr/sbin/iptables ]; then
echo "iptables binary not found."
exit 1
fi
# clear chains
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
iptables -F
iptables -X
# create new chains and link them
iptables -N open # services will be opened here
iptables -N interfaces # completely open interfaces here
iptables -A INPUT -j open
iptables -A INPUT -j interfaces
# deny forwarding
iptables -P FORWARD DROP
# allow all outgoing traffic
iptables -P OUTPUT ACCEPT
# allow all traffic on loopback
iptables -A interfaces -i lo -j ACCEPT
# allow related connections (-> stateful)
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
# allowed services
iptables -A INPUT -p icmp -j ACCEPT # ping
iptables -A open -p tcp --dport 22 -j ACCEPT # ssh
# deny all other incoming traffic
iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset # imitate default linux behaviour
iptables -A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable # "
iptables -P INPUT DROP
# protection against common attacks
iptables -A INPUT -p tcp ! --syn -m state --state NEW -j DROP # SYN for new packets
iptables -A INPUT -f -j DROP # fragmentation check
iptables -A INPUT -p tcp --tcp-flags ALL ALL -j DROP # XMAS-packets
iptables -A INPUT -p tcp --tcp-flags ALL NONE -j DROP # null packets
iptables -I INPUT -p icmp --icmp-type redirect -j DROP # ICMP type match blocking
iptables -I INPUT -p icmp --icmp-type router-advertisement -j DROP # "
iptables -I INPUT -p icmp --icmp-type router-solicitation -j DROP # "
iptables -I INPUT -p icmp --icmp-type address-mask-request -j DROP # "
iptables -I INPUT -p icmp --icmp-type address-mask-reply -j DROP # "
echo "net.ipv4.conf.all.rp_filter = 1" >> /etc/sysctl.conf # enable source address verification
echo "net.ipv4.tcp_timestamps = 0" >> /etc/sysctl.conf # nmap uptime detection
echo "net.ipv4.conf.all.accept_source_route=0" >> /etc/sysctl.conf # we are not a router
echo "net.ipv4.icmp_echo_ignore_broadcasts=1" >> /etc/sysctl.conf # "
echo "net.ipv4.icmp_ignore_bogus_error_messages=1" >> /etc/sysctl.conf # "
# disable ip forwarding
if [ -e /etc/conf.d/iptables ]; then
cat /etc/conf.d/iptables | egrep -v '^IPTABLES_FORWARD' > /tmp/iptables.conf
echo "IPTABLES_FORWARD=0" >> /tmp/iptables.conf
cat /tmp/iptables.conf > /etc/conf.d/iptables
rm /tmp/iptables.conf
fi
# and finally save our work
/etc/rc.d/iptables save
(zum Ausklappen Pfeil benutzen)
Das Script erstellt ein Basis-Regelwerk und lässt nur Ping (ICMP) und SSH (auf allen interfaces) eingehend zu. Alle anderen eingehenden Verbindungen werden geblockt; ausgehende Verbindungen vollständig erlaubt. Desweiteren implementiert es Schutzmaßnahmen gegen die üblichsten Attacken.