fail2ban: punish retries

One problem of fail2ban setups is that many bots seem to have adapted to the ban behavior: they detect being banned, start probing periodically and simply continue their attack after having been unbanned.

A simple solution is to choose a high value for banTime (or infinite), but that has the disadvantage of permanently banning regular users who just forgot their password, which unnecessarily generates support requests.

A different solution without this downside is a second-level approach: why not monitor /var/log/fail2ban.log for banned hosts and permanently ban them after some number of continued attacks?

Of course we do this with fail2ban, here’s the configuration:

# /etc/fail2ban/filter.d/fail2ban.conf
 
[Definition]
 
failregex   = fail2ban.actions: WARNING \[.*\] Ban <HOST>
ignoreregex = fail2ban.actions: WARNING \[fail2ban\] Ban <HOST>
# /etc/fail2ban/action.d/permaban.conf
 
[Definition]
actionstart = 
actionstop = 
actioncheck = 
 
actionban   = iptables -I banned 1 -s <ip> -j DROP
actionunban = /bin/true
 
 
[Init]

(Important here is to choose a different iptables chain than the one used for regular (i.e. temporary) bans!)

# /etc/fail2ban/jail.conf
 
(...)
 
[fail2ban]
enabled  = true
findtime = 86400
filter   = fail2ban
action   = permaban
           mail[logpath=/var/log/fail2ban.log]
logpath  = /var/log/fail2ban.log
maxretry = 2

Still testing it, but looks promising :)

SPAM mit SPF und DKIM

Kurz nach Aufsetzen von DKIM- und SPF-Filter auf meinem Mailserver nun ein enttäuschendes Novum: mich erreicht die erste SPAM-Mail mit SPF-legitimiertem Absender und valider Signatur:

Return-Path: <bounce@live-kameras.in>
X-Original-To: mail@alexanderkoch.net
Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=46.167.40.90; helo=mail89.sysmessage.net; envelope-from=bounce@live-kameras.in; receiver=mail@alexanderkoch.net 
Authentication-Results: vserver3082.vserver-on.de; dkim=pass
	(1024-bit key; insecure key) header.i=@live-kameras.in;
	dkim-adsp=pass
Received: from mail89.sysmessage.net (mail89.sysmessage.net [46.167.40.90])
	by vserver3082.vserver-on.de (Postfix) with ESMTP id 8215A1A21DC
	for <mail@alexanderkoch.net>; Wed, 20 Jun 2012 22:06:01 +0200 (CEST)
Received: from localhost (mail89.sysmessage.net [46.167.40.90])
	by mail89.sysmessage.net (Postfix) with ESMTP id 7B80CED333A6
	for <mail@alexanderkoch.net>; Wed, 20 Jun 2012 21:46:36 +0200 (CEST)
MIME-Version: 1.0
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
From: Carola Gerber <carola@live-kameras.in>
To: mail@alexanderkoch.net
Subject: Kontaktanfrage
Message-ID: <1340221596.312.61884107@live-kameras.in>
Date: Wed, 20 Jun 2012 21:46:36 +0200
DKIM-Signature: v=1; a=rsa-sha1; s=c; d=live-kameras.in; l=180;
	t=1340221596; c=relaxed/relaxed;
	h=mime-version:content-type:content-transfer-encoding:from:to:subject:message-id:date;
	bh=PHke5WSAMx7YYOAPDF0i9PGyalA=;
	b=Scw49u8DJmqQ3FfNmqoukpaAvtg7+nif45MlE1Kqs8LSoMM8K96W5zJYg5TbLOpYBFRtYor8n6i/
	iJvPpU7sY1hwtBIpyeteus44NjV5HMZivINxQjs57hfhouN58zs5n5uN424Mid6qmbhK3cnxvXXL
	YFzc6ciq7OGm5iqipHI=
DomainKey-Signature: a=rsa-sha1; c=nofws; d=live-kameras.in; s=c;
	h=mime-version:content-type:content-transfer-encoding:from:to:subject:message-id:date;
	b=CNcWdpqE/rnJRUra4YbpLf3lh8JsLQ2OcB4h/lc4JvHOrQiDcut8lYwxOpAqLvPcAlmmKPIMLLrv
	3kqHqbcGkO31n77gIhum1j9UQiJdwrKp5X7m5gmm0P8buFIs3Oju1ZzFYEy8rsC+Fv5pJ6eV9+Ui
	jnBNeS5bN44sO7BT9Mg=
 
Hallo,
 
m=F6chtest Du deutschen M=E4dchen live vor der Kamera zuschauen?
Das kannst Du hier: http://www.live-kameras.in/
 
Viel Spa=DF!
 
Liebe Gr=FC=DFe,
Carola / Cam-Team

Tja, auch Spammer rüsten auf ;)

DKIM mit Postfix für mehrere Domains

Gerade noch schnell DKIM für Nerd-grade Kommunikation auf meinen Domains aufgesetzt :)

Dabei war dieses Tutorial als Basis hilfreich, ich habe nur die Pfade der Keyfiles anders strukturiert.

Außerdem würde ich noch folgenden DNS-Record setzten:

_adsp._domainkey.DOMAIN     IN    TXT    "dkim=all"

Damit funktioniert dann auch ADSP.

Confixx Setup nachbessern

An einem frisch per Virtualisierungs-Container installierten Confixx schraubt man üblicherweise an ein paar Stellen herum, um das Panel abzusichern. Bei einem kürzlich von mir durchgeführten Server-(Provider-)Wechsel war diesmal jedoch so viel Handarbeit nötig, dass ich diese hier mal kurz festhalten möchte.

Ohne manche der mit [Fix] markierten Schritte hätten einige Dienste übrigens garnicht oder mit unschönen Nebeneffekten funktioniert, weshalb ich vom Installations-Image meines Providers auch ziemlich enttäuscht war – da hätte ich es besser selbst aufgesetzt :)  Als Distribution kam bei mir Debian 6.0.4 Squeeze zum Einsatz.

Hinweis: Irren ist menschlich, alle hier vorgestellten Schritte erfolgen auf eigene Gefahr! Backups sind Gold wert!

Upgrade

Bei mir war ein Upgrade auf die aktuelle Version 3.3.9 erforderlich, wer einen Guide sucht kann hier fündig werden.

[Security] MySQL-Accounts ändern

Das Wichtigste zuerst: unbedingt alle mit Confixx in Verbindung stehenden Passwörter überprüfen. Bei meinem Setup war doch tatsächlich als Passwort für den MySQL-root ‘T’ (ja, der Buchstabe ‘T’) gesetzt! Und das bei einem standardmäßig über PHPMyAdmin von außen erreichbaren Dienst!

Ich habe einen separaten MySQL-Benutzer für Confixx erstellt und ihm entsprechende Rechte gegeben. Anzufassen sind folgende Configs:

  • <confixx-install-dir>/confixx_main.conf: ab Zeile 547
  • /var/www/confixx/settings.inc.php: Werte aus der confixx_main.conf übertragen
  • /etc/spamassassin/local.cf (wird aber sowieso neu generiert)

[Fix] Mail-Aliase behalten

Diverse Pakete legen eigene Benutzer an und tragen diese in die /etc/aliases ein, z.B. logcheck. Dumm nur, dass Confixx die /etc/aliases periodisch (bzw. bei jeder Änderung an Postfächern) neu schreibt, und zwar “dumm” nach einem eigenen Template, was die neuen Benutzer nicht reflektiert.

Hier ist also dauerhaft Handarbeit angesagt: neue System-Aliase oder eigens angelegte müssen in die <confixx-install-dir>/safe/aliases_header aufgenommen werden, um nicht verloren zu gehen.

[Fix] Spamassassin

Ohne Nachbessern war bei mir Spamassassin nicht funktionstüchtig. Ich habe einen separaten Benutzer für den spamd angelegt:

$ groupadd -g 5001 spamd
$ useradd -u 5001 -g spamd -s /usr/sbin/nologin -d /var/lib/spamassassin spamd
$ mkdir /var/lib/spamassassin
$ chown spamd:spamd /var/lib/spamassassin

Dann die Startoptionen für den Daemon korrigieren:

# /etc/default/spamassassin
(...)
OPTIONS="--create-prefs --nouser-config --max-children 5 -H /var/lib/spamassassin -u spamd -q"
(...)

[Fix] AWStats

AWStats muss scheinbar das 10-Minuten-Update als root durchführen, daher unter /etc/cron.d/awstats beim oberen Eintrag den Benutzer auf root ändern. Das habe ich allerdings nicht näher untersucht, also besser selbst nochmal checken.

[Fix] Apache “other vhosts” log

Da Confixx für jeden Vhost nur ein Custom-Log über eine Pipe setzt (/etc/apache2/confixx_vhost.conf), bewirkt die Direktive in /etc/apache2/conf.d/other-vhosts-access-log, dass jeder (!!) Zugriff auf irgendeinen Vhost unter /var/log/apache2/other_vhosts_access.log vermerkt wird. Das lässt das File je nach Auslastung des Servers extrem schnell wachsen, man sollte die Option also der Performance und des Plattenplatzes zuliebe besser auskommentieren.

[Fix] Postfix SASL authentication

Abgesehen von einer komplett überarbeiteten main.cf musste ich für Postfix noch die SASL-Authentifizierung fixen, was praktischerweise in der /etc/default/saslauthd schon vorgesehen ist:

# Example for postfix users: "-c -m /var/spool/postfix/var/run/saslauthd"

Details siehe /usr/share/doc/sasl2-bin/README.Debian.gz.

 

Das wars erstmal mit den wichtigsten Nachbesserungen. Confixx in eine eigene Subdomain zu sperren und das Vhost-Schema dauerhaft zu ändern war nochmal eine ganz eigene Angelegenheit – vielleicht dazu demnächst ein HowTo.

iptables init-script für Arch

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 :)

Plesk, Qmail, xinetd und SSL – die Zweite

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… ;)

Trau’ nicht dem LUKS!

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!

iptables Setup Script

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

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.