Ipsets sind eine ausgesprochen praktische und performante Lösung, um umfangreiche Blacklist mit Iptables zu verwenden.
UFW ist ein Management-Werkzeug für Iptables mit sehr einfachem Kommandozeilen Interface.
An sich unterstützt UFW (Uncomplicated FireWall) keine Ipsets. Trotzdem ist es möglich, beides gemeinsam zu verwenden.
Zwei Bash-Skripte sind dazu nötig.
In diesem Beispiel verwenden wir die DROP von Spamhaus - eine Liste von IP-Ranges, mit denen man am besten keinerlei Kontakt hat.
Zuerst brauchen wir ein Skript und einen Cronjob, um die DROP Liste täglich zu aktualisieren.
Öfter als einmal täglich macht keinen Sinn, weil die Liste sich nicht so oft ändert, seltener macht keinen Sinn, weil man dann Änderungen verpassen könnte.
Das Skript /usr/local/bin/update-ipset.sh:
#!/bin/bash
# author: xela
# Updates ipset with fresh drop (dont root or peer) list from spamhaus.
ipset_bin='/sbin/ipset'
savefile='/var/lib/ipset/blacklist-drop.save'
url='https://www.spamhaus.org/drop/drop.txt'
tempfile=$(mktemp /tmp/spamhaus.drop.XXXXX)
wget -q -O - "$url" > $tempfile
if [ ! -f $tempfile ]
then
logger -t 'UpdateIpset' "Fatal: file $tempfile not found"
exit 1
else
linecnt=$(cat $tempfile | wc -l)
if [ $linecnt -lt 100 ]
then
logger -t 'UpdateIpset' "Fatal: file $tempfile is way too short to contain the actual block list"
exit 1
else
expires=$(grep Expires $tempfile | awk -F ': ' '{print $2}' | date +"%s" -f -)
yesterday=$(date +"%s" -d yesterday)
if [ $expires -lt $yesterday ]
then
logger -t 'UpdateIpset' "Warning: block list fetched from spamhaus seems outdated!"
fi
fi
fi
set_exists=$($ipset_bin -n list | grep -c blacklist-drop)
if [ $set_exists -eq 0 ]
then
$ipset_bin create blacklist-drop hash:net family inet
else
$ipset_bin flush blacklist-drop
fi
tempfile2=$(mktemp /tmp/spamhaus-drop-tmp.XXXXX)
grep -v '^;' $tempfile | cut -d ' ' -f 1 > $tempfile2
cat $tempfile2 | while read line
do
$ipset_bin add blacklist-drop "$line"
done
$ipset_bin save blacklist-drop > $savefile
records=$(grep -c '^add' $savefile)
expdate=$(grep Expires $tempfile | cut -d ':' -f 2,3,4)
logger -t 'UpdateIpset' "Updated blacklist-drop ipset with $records entries, expiredate$expdate"
rm $tempfile
rm $tempfile2
exit 0
Das Skript ausführbar machen (755) und laufen lassen.
Der Cronjob dazu, z.B. /etc/cron.d/update-ipset:
21 7 * * * root /usr/local/bin/update-ipset.sh
Das Skript legt das Ipset auch an, sofern es noch nicht existiert. Die Rules greifen aber noch nicht, weil sie noch nirgends in Iptables/UFW eingebunden sind.
Das passiert mit dem nächsten Schritt.
Jetzt zur Datei /etc/ufw/after.init. Möglicherweise existiert sie schon, ansonsten legen wir sie neu an.
#!/bin/bash
# by xela, inspired by https://bugs.launchpad.net/ufw/+bug/1571579
savefile="/var/lib/ipset/blacklist-drop.save"
if [ ! -f "$savefile" ]; then
echo "Could not find '$savefile'" >&2
return
fi
if [ ! -x "/sbin/ipset" ]; then
echo 'Ipset binary not found'
return
fi
IPSET_BIN="/sbin/ipset"
case "$1" in
start)
exists=$($IPSET_BIN -n list | grep -c blacklist-drop)
if [ $exists -eq 0 ]; then
# Loading ipset
$IPSET_BIN restore < "$savefile"
fi
# Setting firewall rules
iptables -I INPUT -m set --match-set blacklist-drop src -j DROP
iptables -I INPUT -m set --match-set blacklist-drop src -j LOG --log-prefix "[UFW BLOCK bl-drop] "
;;
stop)
# Unset firewall rules
iptables -D INPUT -m set --match-set blacklist-drop src -j DROP || true
iptables -D INPUT -m set --match-set blacklist-drop src -j LOG --log-prefix "[UFW BLOCK bl-drop] " || true
# Backup ipset list and destroy ipset
$IPSET_BIN save > "$savefile"
$IPSET_BIN destroy blacklist-drop || true
;;
status)
echo "= after.init ="
$IPSET_BIN -t list
echo ""
;;
*)
echo "'$1' not supported"
echo "Usage: after.init {start|stop|status}"
;;
esac
Auf diese Art (mit after.init) überlebt das Ipset auch Reboots und sonstige Neustarts der Firewall.
Ausführbar machen (755) nicht vergessen.
Dann starten:
/etc/ufw/after.init start
Wichtig: den Ordner /var/lib/ipset/ unbedingt anlegen - er existiert noch nicht.