Neulich war das unvermeidliche mal wieder zu beobachten: Einige meiner Adressen und auch ein paar Kundenwebseiten waren mal wieder in einer Datenbank für Kommentarspammer gelandet und die Spamkommentare häuften sich. Was tun?
Am besten wäre es doch, die Spammer hätten überhaupt keine Möglichkeit mehr, sich mit dem Server zu verbinden – also muss die Firewall herhalten und die Spammer abhalten. Aber nun von Hand alle IP-Adressen auf allen meinen Servern in iptables eintragen würde dann doch einen nicht ganz unerheblichen Aufwand bedeuten. Eine Lösung, die sich über alle meine Server synchronisiert muss also her.
Auf Xela’s Linux Blog las ich dazu einen interessanten Artikel.
Die Spamhausliste dürfte wohl eher Mailspammer enthalten. Aber da Mailspam genau wie Kommentarspam sehr häufig von gehackten Servern seinen Ursprung hat, ist das doch schon mal ein exzellenter Anfang!
Ausgangspunkt für meine Lösung sollten also die beiden Skripte von Xelas Blog werden, das einzige was ich noch ergänzen musste war ein zweiter Aufruf für eine eigene Block-Liste, die ich einfach auf einem meiner Webserver hoste und mit IP-Adressen füttern kann, die in beliebigen meiner Seiten oder meiner Kundenseiten negativ als Spam-Ursprung aufgefallen sind. Die Skripte werden auf allen meinen Servern installiert, egal ob Mailserver oder Webserver und somit werden täglich alle Server mit einer aktuellen Liste gepflegt. Da ich vermutlich nicht täglich neue IP-Adressen einpflegen werde, habe ich es mir einfach gemacht und das Expiredatum meiner eigenen Liste einfach mal auf Ende 2030 gelegt.
So – nun aber mal konkret an die Arbeit:
Als erstes legen wir uns einen Ordner für die gespeicherten ipset Regeln an:
mkdir /var/lib/ipset/
Der zweite Schritt ist eine angepasste Version des droplist Download-Programms.
Dazu legen wir mit dem Editor unserer Wahl folgende Datei an:
/usr/local/bin/update-ipset.sh:
#!/bin/bash
# author: xela
# Addendum by mbitcon# Updates ipset with fresh drop (dont root or peer) list from spamhaus. And fetches central mbitcon droplist
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
# Do the same thing with my own blocklist
mbitconsavefile=’/var/lib/ipset/blacklist-mbitcon.save’
url=’https://www.wp-webseite.de/drop/drop.txt’
tempfile=$(mktemp /tmp/mbitcon.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 5 ]
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 mbitcon seems outdated!”
fi
fi
fi
set_exists=$($ipset_bin -n list | grep -c blacklist-mbitcon)
if [ $set_exists -eq 0 ]
then
$ipset_bin create blacklist-mbitcon hash:net family inet
else
$ipset_bin flush blacklist-mbitcon
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-mbitcon “$line”
done
$ipset_bin save blacklist-mbitcon > $mbitconsavefile
records=$(grep -c ‘^add’ $mbitconsavefile)
expdate=$(grep Expires $tempfile | cut -d ‘:’ -f 2,3,4)
logger -t ‘UpdateIpset’ “Updated blacklist-mbitcon ipset with $records entries, expiredate$expdate”
rm $tempfile
rm $tempfile2
exit 0
Die Datei machen wir noch ausführbar:
chmod 755 /usr/local/bin/update-ipset.sh
Als nächstes bearbeiten wir die Datei /etc/ufw/after.init , oder falls es sie noch nicht gibt, legen wir sie an. Hier ist ein bisschen Fingerspitzengefühl gefragt, denn man sollte schon einen Blick in die Datei werfen, ob hier leere Standarddeklarationen für start, stop, status vorhanden sind, oder ob hier schon mal jemand Arbeit reingesteckt hat und schon “echte” andere Funktionalität vorhanden ist.
Wir nehmen an, dass wie in meinem Fall die Datei keine Zusatzfunktionen enthält und wir den Inhalt einfach überschreiben können.
/etc/ufw/after.init:
#!/bin/bash
# by xela, inspired by https://bugs.launchpad.net/ufw/+bug/1571579
# Addendum by mbitcon
savefile=”/var/lib/ipset/blacklist-drop.save”
if [ ! -f “$savefile” ]; then
echo “Could not find ‘$savefile'” >&2
return
fi
mbitconsavefile=”/var/lib/ipset/blacklist-mbitcon.save”
if [ ! -f “$mbitconsavefile” ]; then
echo “Could not find ‘$mbitconsavefile'” >&2
return
fi
#check whether ipset is installed
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] “
exists=$($IPSET_BIN -n list | grep -c blacklist-mbitcon)
if [ $exists -eq 0 ]; then
# Loading ipset
$IPSET_BIN restore < “$mbitconsavefile”
fi
# Setting firewall rules
iptables -I INPUT -m set –match-set blacklist-mbitcon src -j DROP
iptables -I INPUT -m set –match-set blacklist-mbitcon src -j LOG –log-prefix “[UFW BLOCK bl-mbitcon] “
;;
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
# Unset firewall rules
iptables -D INPUT -m set –match-set blacklist-mbitcon src -j DROP || true
iptables -D INPUT -m set –match-set blacklist-mbitcon src -j LOG –log-prefix “[UFW BLOCK bl-mbitcon] ” || true
# Backup ipset list and destroy ipset
$IPSET_BIN save > “$mbitconsavefile”
$IPSET_BIN destroy blacklist-mbitcon || true
;;
status)
echo “= after.init =”
$IPSET_BIN -t list
echo “”
;;
*)
echo “‘$1’ not supported”
echo “Usage: after.init {start|stop|status}”
;;
esac
Auch diese Datei machen wir mit
chmod 755 /etc/ufw/after.init
ausführbar und nun können wir alles schon mal aktivieren:
/usr/local/bin/update-ipset.sh; /etc/ufw/after.init
Und eine Kontrolle mittels
ipset list
sollte uns nun einen ganzen Schwung geblockte Adressen und Netze auswerfen.
Jetzt bleibt uns noch eines, nämlich für eine regelmäßige Ausführung des Droplist-Downloaders zu sorgen:
Dazu legen wir uns einen cronjob an, indem wir folgende Datei anlegen:
/etc/cron.d/update-ipset
mit folgendem Inhalt:
21 7 * * * root /usr/local/bin/update-ipset.sh
Die Uhrzeit ist natürlich willkürlich, ich habe sie einfach übernommen, man kann hier natürlich eintragen, was man möchte.
Und natürlich nicht vergessen, eine eigene Liste auf dem Webserver anzulegen. Das Format habe ich einfach von der Spamhaus Liste übernommen.