Gegen die Feuerwand: Mal wieder DRDoS-Attacke auf OpenArena-Server

Seit ein paar Tagen wird mein OpenArena-Server erneut mit einer sogenannten DRDoS-Attacke überzogen. Der oder die Angreifer senden dabei gefälschte IP-Pakete mit Hilfe von Spoofing an den Server, damit dieser wiederum Statusanfragen an Webserver und ähnliche Dienste weiterleitet, deren Kapazitäten irgendwann nicht mehr ausreichen, um die gerechtfertigten Anfragen zu beantworten.

Mit iftop sieht das im Moment so aus:

Ich bin mir nicht sicher, ob das Ganze überhaupt durchdacht worden ist, momentan rauscht einfach alles in die Firewall. Es wird nicht einmal geprüft, ob der Server verwundbar ist, sondern einfach stur immer die gleichen Anfragen geschickt. Dass die Attacke somit sehr ineffizient ist, scheint niemanden zu stören.

Ich bin mit dem Server einen Port weitergezogen, um irgendeine negative Auswirkung auf das Spielerlebnis auszuschließen. Die beim letzten Mal erstellten Firewallregeln und der gepatchte Server haben zuvor ihr Übriges getan.

Da zwar sehr viele Anfragen hereinkommen, jedoch keine beantwortet wird, kann man diesen Umstand wunderbar mit vnstat beobachten. Normalerweise ist der Wert des TX-Graphen (grau) größer als der RX-Graph (grün).

Vnstat-stündlicher-Netzwerkverkehr
Vnstat-täglicher-Netzwerkverkehr

Letztes Mal hat es knapp zwei Wochen gedauert bis der Spuk vorbei war. Mal schaun wie lange es dieses Mal dauert.

Meine Debian-Aktivitäten im Juni 2012

Ein wenig inspiriert von Raphaël Hertzogs regelmäßigen Posts, was er in den Vergangenheit für Debian unternommen oder entwickelt hat, sind hier meine, zugegeben etwas kleineren, Unternehmungen, ein paar Bugs auszumerzen.

Im letzten Jahr beschäftigten mich noch unter anderem Probleme mit PolicyKit und einem scheinbar raren Problem mit libgcrypt11. Letzteres ist weiterhin nicht gelöst, scheint aber bei anderen Benutzern auch nur selten aufzutreten.

Am spannendsten war dieses Jahr bisher eine Schwäche im OpenArena-Server, die es Angreifern erlaubt hat mittels Spoofing gefälschte UDP-Anfragen an den Server zu schicken, der die Ausgabe, Statusvariablen des Servers, dann an Web- oder Mailserver adressierte, um sie durch eine kombinierte DRDoS-Attacke lahm zu legen. Einen Patch gab es zum Glück schon, nur noch der Paketverwalter musste benachrichtigt werden, der das Problem innerhalb weniger Stunden lösen konnte.

In der selben OpenArena-Version 0.8.5 gibt es noch einen weiteren Bug, der zum Crash des Servers führen kann, wenn Bots und menschliche Spieler auf bestimmten Maps spielen und durch eine scheinbar fehlerhafte Wegepunkt-Logik die Bots den Server zum Absturz bringen. (#664637)
Ich denke in der neuesten Version 0.8.8 taucht er so nicht mehr auf, dafür stürzt der Server nun scheinbar zufällig ab, wenn ausschließlich menschliche Spieler spielen. Das passiert zum Glück recht selten, leider aber immer dann wenn sieben oder sogar 15 Leute gleichzeitig spielen, was natürlich mehr als ein Showstopper ist.

Ich versuche im Moment den Server zu debuggen, wobei mir Simon McVittie die richtige Vorgehensweise gezeigt hat. Man erstellt sich z.B. ein Wrapper-Skript in /usr/local/bin/openarena-server-wrapper mit folgendem Inhalt.

#!/bin/sh
OPENARENA_BACKTRACE=1
export OPENARENA_BACKTRACE
exec > /var/games/openarena-server/.openarena/baseoa/openarena-server.log 2>&1
exec /usr/games/openarena-server "$@"

In /etc/init.d/openarena-server ersetzt man dann den Wert der DAEMON-Variable durch den Pfad zu diesem Wrapper-Skript. Zuvor muss man noch den GNU Debugger (gdb) und die Debug-Symbole für Ioquake3 und für den OpenArena-Server installieren, damit die Ausgabe des Debuggers nützlich und nachvollziehbar ist.

Es scheint so, als ob das Problem nicht debianspezifisch sei und irgendein Fehler in der Spiellogik vorliegt. Wenn etwas Brauchbares dabei herauskommt, frage ich bei Gelegenheit direkt mal im offiziellen OpenArena-Forum um Hilfe nach und komme auf den Fehler zurück.

Durch den Absturz des Servers ist mir dann aufgefallen, dass die PID-Datei des Servers nicht ordnungsgemäß entfernt wird und der Server durch Monit auf Grund einer if-Bedingung im Init-Skript daraufhin den Start verweigert. (#678543) Das Problem hat der Paketverwalter ebenfalls in wenigen Stunden gelöst und schon eine neue Version nach Unstable hochgeladen.

Ansonsten habe ich wie angekündigt einen Wishlist-Bug gegen Qstat (#675838) eingereicht mit der Bitte um ein Paket-Update. Bisher gab es aber noch keine Rückmeldung des Betreuers, weswegen es ein neues Paket nicht mehr nach Wheezy schaffen wird.

Ebenfalls auf der Wunschliste stand die Bitte eine Beispielkonfiguration für das Cube2:Sauerbraten-Paket bereitzustellen (#673808). Ich denke mein Patch fand Anklang, auch wenn sich der Verwalter eine generischere Konfiguration gewünscht hätte. Nachdem ich die nachgeliefert hatte, scheint es aber so, dass er die nächste offizielle Veröffentlichung von Sauerbraten abwarten möchte, weswegen die Lösung dieses Bugs für die Version in Wheezy noch warten muss.

Ansonsten fiel mir noch ein „Serious-Fehler“ (#647613) für Boswars, ein Strategiespiel, auf, der mittlerweile aber nur noch „Important“ ist. Ich konnte den Fehler bestätigen. Das Problem ist bei Upstream bekannt.

Dass es sich hier um Debianspiele handelte, ist natürlich kein Zufall. Durch das vServer-Projekt bin ich automatisch auf Fehler gestoßen und irgendwo gibt es sie eben immer. Wie die Sache mit der DRDoS-Attacke zeigt, sollte man nicht zögern, das sofort zu melden. Das gleiche Problem gab es nämlich auch im Tremulous-Paket, wodurch nicht nur Debian, sondern auch Fedora betroffen war.

Alles in allem kann die Fehlersuche sogar richtig Spaß machen. Nun bin ich gespannt, was das Debuggen des OpenArena-Servers bringt. 🙂

vsftpd und OpenArena: Konfiguration eines sicheren FTP-Servers mit ausschließlich anonymen Zugang

Vor kurzem ergab sich die Gelegenheit einen FTP-Server aufzusetzen. Für ein anderes Vorhaben abseits von linuxiuvat.de brauchte ich zuerst nur einen OpenVPN-Server, da aber noch ausreichend Leistung zur Verfügung stand, installierte ich zusätzlich noch vsftpd.

Der FTP-Server eignet sich ideal, um PAK3-Dateien von meinem OpenArena-Server herunterladen zu können. Falls der Spiel-Client nicht kompatibel sein sollte, wird der Client automatisch beim Verbinden zum FTP-Server umgeleitet und lädt von dort Patches, Karten, Texturen und neue Sounds herunter.

Installation und Konfiguration

Vsftpd ist stark konfigurierbar und beherrscht noch viele weitere Optionen, die mit man vsftpd.conf einsehbar sind. Ein sicherer anonymer Zugang zum Herunterladen ohne Berechtigungen zum Dateiupload, lässt sich mit wenigen Handgriffen so verwirklichen.

aptitude install vsftpd

dpkg-reconfigure vsftpd

Mit Hilfe von DebConf wird der FTP-Benutzer und das FTP-Wurzelverzeichnis festgelegt.

  1. Den Systembenutzer „ftp“ für vsftpd einrichten.
  2. Das FTP-Wurzelverzeichnis definieren, aus dem der anonyme Benutzer nicht wechseln kann.

Die Standardeinstellungen kann man hier problemlos beibehalten.

Die weitere Konfiguration befindet sich in /etc/vsftpd.conf. Im Prinzip halten sich auch hier die Veränderungen in Grenzen. An dieser Stelle dokumentiere ich nur die Veränderungen zur Standardinstallation mit Debian Squeeze.

nopriv_user=ftpsecure

Vsftpd verwendet einen weiteren unpriviliegerten und vollkommen isolierten Systembenutzer. In der Standardeinstellung ist das „nobody“. Da dieser auch für andere Dienste oft verwendet wird, empfiehlt es sich aus Sicherheitsgründen noch einen weiteren, einzigartigen Systembenutzer anzulegen, z.B. ftpsecure.
adduser --system --no-create-home --disabled-login ftpsecure
Der FTP-Server wird zwar mit Root-Rechten gestartet, sobald aber eine Verbindung mit dem Client hergestellt wird, startet der Kindprozess mit den unpriviliegerten Rechten von ftpsecure und ftp.

# Die Dateiberechtigungen sollen für den anonymen Benutzer immer mit ftp:ftp angezeigt werden
hide_ids=YES

# Das FTP-Wurzelverzeichnis lässt sich mit dieser Variable ändern, wenn man
# nicht auf die DebConf-Methode zurückgreifen möchte.
# anon_root=/home/ftpsecure

# Maximale Anzahl an erlaubten gleichzeitigen Verbindungen. Entspricht bei
# mir der maximal möglichen Anzahl von Spielern auf dem OpenArena-Server.
max_clients=16

# Wieviele Verbindungen pro IP sind erlaubt
max_per_ip=4

Die gesamte vsftpd.conf sieht in meinem Fall so aus.

listen=YES
anonymous_enable=YES
dirmessage_enable=YES
use_localtime=YES
xferlog_enable=YES
connect_from_port_20=YES
nopriv_user=ftpsecure
ftpd_banner=Welcome to another FTP service.
secure_chroot_dir=/var/run/vsftpd/empty
pam_service_name=vsftpd
rsa_cert_file=/etc/ssl/private/vsftpd.pem
hide_ids=YES
max_clients=16
max_per_ip=4

Abschließend lässt sich mit mount --bind das Datenverzeichnis von OpenArena noch einmal im FTP-Wurzelverzeichnis im Unterordner /oa/baseoa nur lesbar einhängen. Um die Veränderung dauerhaft zu behalten, kann der Vorgang auch in /etc/fstab festgeschrieben werden.

/usr/share/games/openarena/baseoa       /srv/ftp/oa/baseoa      none     ro,bind        0       0 

Nach diesem Schema lassen sich weitere Verzeichnisse nur lesbar im FTP-Wurzelverzeichnis einhängen.

Einstellungen für OpenArena

In der server.cfg von OpenArena kann man festlegen, ob der Download von Dateien erlaubt ist oder nicht.

sets sv_allowdownload 1

Ist das Herunterladen wie in diesem Fall mit 1 gestattet, wird mit sv_dlURL bestimmt, von welchem Web- oder FTP-Server der Client die Dateien beziehen soll, sofern sich seine Version von der des Servers unterscheidet. Der Vorgang ist auch als Fast Download bekannt und ein Feature, dass nach der Veröffentlichung des Quellcodes von Quake3 hinzugefügt worden ist.

sv_dlURL „ftp://123.123.123.123/oa“

Dabei kann man den letzten Schrägstrich nach dem Verzeichnis „oa“ weglassen, da er automatisch durch den OpenArena-Server gesetzt wird. Im Verzeichnis „oa“ auf dem FTP-Server muss das Unterverzeichnis baseoa erstellt werden, welches wiederum die PAK3-Dateien zum Spielen enthält.

Gibt man keine URL zum Herunterladen an, liefert der OpenArena-Server die Dateien nach der alten Quake3-Methode selbst aus, was sehr lange dauern kann. In neueren Versionen der ioquake3-Engine gibt es die Möglichkeit eine neue Variable zu definieren, welche die Download-Geschwindigkeit erhöht. Folgender Wert setzt diese auf 100 Kb/s pro Client fest.

sv_dlRate „100“

Wichtig

Wer bei Debian und Ubuntu einzelne Custom Maps zum Download anbieten möchte, muss diese bei OpenArena 0.8.8 im versteckten Verzeichnis /var/games/openarena/.openarena/baseoa ablegen, wo sich auch die server.cfg befinden kann, wenn man sich nicht für /etc/openarena-server/ entschieden hat. Auf dem FTP-Server hingegen verändert sich nichts und alle PAK3-Dateien liegen weiterhin im Verzeichnis baseoa.

Pyqscore: Spielstatistiken für OpenArena erstellen

Zurück zu meinem Steckenpferd. Im folgenden geht es darum, wie man aus einer Logdatei für OpenArena eine sinnvolle Spielstatistik erstellen kann. OpenArena ist kurz gesagt ein modifiziertes Quake3 mit Medieninhalten, die vollständig unter einer freien GPL-Lizenz stehen. Für meinen öffentlichen OpenArena-Server habe ich mich für Deathmatch entschieden, da ich das Gefühl hatte, die meisten Spieler bevorzugen diesen Spielmodus.

Ein weiterer Vorteil ist sicherlich, dass „Jeder-gegen-Jeden“ am einfachsten zu überwachen ist, denn im Prinzip geht es nur darum, dass Spieler X Spieler Y fraggt und alles ein Zeitspiel ist.

Es gibt verschiedene Werkzeuge und Programme, die einem beim Erstellen von Statistiken helfen können. Im Grunde läuft es aber immer auf das Parsen der Logdatei heraus. Das OpenArena-Wiki führt hier z.B. VSP, AEstats oder oastats an.

Für mich war nur eine Sache klar. Ich wollte keinen zusätzlichen Datenbank-Server. Irgendwie erschien mir das nicht die richtige Lösung zu sein, um eine einzelne Logdatei zu parsen.

Der Gewinner war schließlich Pyqscore.

Pyqscore ist ein in Python geschriebenes Programm, dass die Datei games.log analysieren kann und zusätzlich noch einen Cache benutzt, damit schon zuvor analysierte Werte nicht noch einmal ausgewertet werden müssen.

Das ursprüngliche Skript pyqscore.py habe ich auf meinem Server in /usr/local/bin abgespeichert. Es wird täglich mit Hilfe von Cron ausgeführt. Erwähnt hatte ich es schon bei meinem Cron- und Logrotate-Beispiel.

Jeden morgen wird deswegen die zuvor mit Logrotate rotierte Datei games.log.1 ausgewertet und als daily.htm abgespeichert. Mit Hilfe von cat addiere ich alle täglichen Logdateien in die Datei monat.log, worüber ich dann ein leicht modifiziertes Pyqscore ausführen lasse, dass die Statistik des Monats wiederum in der Datei monthly.htm abspeichert.

Für die täglichen Statistiken benutze ich dieses Skript

#!/bin/sh
# Tägliche Statistiken mit pyqscore erzeugen, im HTML-Format ausgeben und mit tidy säubern
set -e

Base="/var/games/openarena-server/.openarena/baseoa/"

cd $Base
if [ -f games.l_cache.p ]; then
        rm games.l_cache.p
fi

/usr/local/bin/pyqscore.py games.log.1
/usr/bin/tidy -utf8 -m -q daily.htm || true

Hier wird jeden Tag in das entsprechende Verzeichnis gewechselt und der alte Cache gelöscht. Das ist notwendig, weil Pyqscore bei einer größeren Logdatei den Cache zum schnelleren Analysieren heranzieht, bei kleineren Logdateien aber eine komplett neue Cache-Datei anlegt.

Damit keine Fehler entstehen, lösche ich den Cache bei der täglichen Statistik. Anschließend wird nur noch die games.log.1 ausgewertet und mit Hilfe von Tidy gesäubert.

Ein anderes Skript kopiert dann automatisch die erzeugte daily.htm in das Webserver-Verzeichnis.

Für die monatlichen Statistiken verfahre ich so:

#!/bin/sh
# Monatliche Statistiken mit pyqscore erzeugen und im HTML-Format ausgeben
set -e

Today=`/bin/date +%d`
Month=`/bin/date -d "last month" +%m`
Year=`/bin/date +%y`
Base="/var/games/openarena-server/.openarena/baseoa/"

if [ $Today -eq 02 ]; then
        rm ${Base}monat.log
        cp ${Base}monthly.htm ${Base}${Year}${Month}_stats.htm
fi

cd $Base
cat games.log.1 >> monat.log
/usr/bin/nice -19 /usr/local/bin/pyqscore_mon.py monat.log
/usr/bin/tidy -utf8 -m -q monthly.htm || true

Ok, hier werden folgende Sachen überprüft. Am Morgen jedes zweiten Tages im Monat wird die alte Datei monat.log gelöscht und die Auswertung beginnt von vorne. Ich sichere danach die alte Datei monthly.htm.

Ursprünglich hatte ich daran gedacht, so etwas wie eine „Siegerehrung“ zu machen, also eine Extraseite mit den ersten Plätzen. Sollte tatsächlich jemand das Bedürfnis nach einer solchen Seite haben, kann ich das nun immer noch nachtragen. 😉

Ansonsten wird die tägliche Logdatei an die monatliche angehängt und ausgewertet und das ganze wieder mit Tidy ordentlich aufbereitet. Damit ich nicht permanent E-Mails von Cron mit Fehlermeldungen erhalte, erzwinge ich den Exit-Status von Tidy mit „true“. Ansonsten kann man jeden Tag lesen, dass die ursprüngliche HTML-Datei Fehler enthalten hat.

Das war es im Prinzip auch schon. Der Vorteil von Pyqscore liegt darin, dass man statische HTML-Seiten mit Statistiken erhält ohne zuvor eine Datenbank installiert zu haben. Bei einem Projekt, wo es um jedes MB Arbeitsspeicher geht, sicher ein Vorteil.

Ich habe Pyqscore an meine Bedürfnisse angepasst. Im Laufe der Zeit sind mir bisher ein paar kleinere Schwächen aufgefallen, die ich versucht habe zu beseitigen. Für alle Interessierten biete ich Pyqscore hier zum Download an.

Cron und Logrotate: Ein Beispiel anhand eines Spieleservers

Genauso unabdingbar wie Liebe und guter Wein für Goethe waren, sind Cron und Logrotate das Lebenselixier für jeden Serveradmin. Vielleicht etwas übertrieben formuliert, aber auch nur fast. 🙂 Als „normaler“ Linuxnutzer kommt man nur sehr selten direkt mit den beiden in Berührung, da in der Regel einfach alles läuft.

Nur am Rande bemerkt: Wer an Debians und Ubuntus „Popularity Contest“ teilnimmt und ihn nicht auf einem Server laufen lässt, sollte auf jeden Fall noch anacron installiert haben, da das Senden der Berichte zu wechselnden Tageszeiten erfolgt und man unter Umständen den Rechner dann nicht eingeschaltet hat, weswegen der Cron-Job nicht ausgeführt werden kann.

Im Gegensatz zu Anacron arbeitet Cron präzise zu einem bestimmten Zeitpunkt Aufträge ab und setzt damit voraus, dass der Rechner kontinuierlich verfügbar ist. Im Zusammenspiel mit Logrotate, das Logdateien nach einem vorgegebenen Zeitintervall verschieben und komprimieren kann, ist er äußerst wichtig und nützlich für jeden Server.

Logrotate-Beispiel anhand von OpenArena

Der OpenArena-Server speichert den Verlauf der Spiele in der Logdatei games.log. Hierfür habe ich eine neue Konfigurationsdatei in /etc/logrotate.d/ angelegt, die wie folgt aufgebaut ist.

/home/openarena/.openarena/baseoa/games.log {
        daily
        missingok
        rotate 14
        compress
        delaycompress
        notifempty
        create 644 openarena openarena
        sharedscripts
        postrotate
                if [ -x /usr/sbin/invoke-rc.d ]; then 
                        invoke-rc.d oa_ded restart > /dev/null 2>&1; 
                else 
                        /etc/init.d/oa_ded restart > /dev/null 2>&1; 
                fi; 
        endscript
}

Die Syntax ist sehr logisch.

  • Wann soll rotiert werden? Alternativen sind weekly und monthly
  • daily

  • Sollte die Logdatei nicht vorhanden sein, ignoriere den Fehler und gehe zur nächsten Datei.
  • missingok

  • Wie oft werden Logdateien rotiert? In diesem Fall werden für jeden Tag für insgesamt 14 Tage Logdateien vorgehalten. Am 15. Tag würde also die älteste Logdatei überschrieben werden. Wäre z.B. weekly und rotate 8 eingestellt, würde die Logdatei jede Woche für insgesamt 8 Wochen rotiert.
  • rotate 14

  • Um Platz zu sparen sollten die Dateien mit gzip komprimiert werden.
  • compress

  • Verzögere die Kompression um einen Rotationszyklus, da manche Programme noch in die letzte Logdatei schreiben können. Dadurch ist games.log.1 immer unkomprimiert.
  • delaycompress

  • Wenn die Datei leer ist, soll gar nichts gemacht werden.
  • notifempty

  • Eine neue games.log wird automatisch mit den Dateirechten 644 und Benutzer und Gruppe openarena erzeugt.
  • create 644 openarena openarena

  • Normalerweise wird die Anweisung prerotate und postrotate bei jeder Logdatei ausgeführt, also wenn anstelle von games.log z.B. eine Wildcard wie *.log angegeben worden wäre. In diesem Fall nicht entscheidend, sondern nur zur Sicherheit.
  • sharedscripts

  • Mit dem letzten Kommando postrotate kann eine Aktion nach der durchgeführten Logrotation ausgeführt werden. In meinem Fall starte ich danach den Server immer neu. Das hat den zusätzlichen Vorteil, dass eventuell durch Spieler geänderte Servervariablen wieder auf die Standardwerte zurückgesetzt werden. Für den Teeworlds-Server war ein Neustart sogar notwendig, da er ansonsten nicht in die neue Logdatei geschrieben hat.

Weitere Ideen und Anregungen finden sich im Verzeichnis /etc/logrotate.d/ und natürlich mit man logrotate. Die Konfigurationsdatei für logrotate ist /etc/logrotate.conf.

Cron in Kürze

Cron ist das Herzstück jedes Servers. Er arbeitet zeitgesteuert alle Shellskripte in /etc/cron.hourly, /etc/cron.daily, /etc/cron.weekly und /etc/cron.monthly ab und wertet die Informationen in /etc/crontab und den jeweiligen crontabs aus. Cron muss nicht neugestartet werden, wenn eine Einstellung verändert wird.

In meiner /etc/crontab habe ich z.B so etwas stehen:

 05 7    * * *   openarena       /usr/local/bin/oa_stats_daily.sh > /dev/null 2>&1

Hier wird mit den Rechten des Benutzers openarena ein kleines Skript ausgeführt, welches die täglichen Statistiken um 7 Uhr und fünf Minuten für OpenArena generiert.

Mehrere Beispiele für Cron aus der Wikipedia:

#M    S   T   M  W    Befehl
5     *   *   *  *    /usr/bin/message.sh     #Befehl wird fünf Minuten nach jeder vollen Stunde aufgerufen.
*/5   *   *   *  *    /usr/bin/message.sh     #Befehl wird alle 5 Minuten aufgerufen (die Schrittweite wird durch */Schrittweite angegeben).
59    23  *   *  0    gzip /var/log/messages  #Befehl wird einmal pro Woche sonntags um 23:59 Uhr ausgeführt.
0     0   *   *  *    gzip /var/log/auth.log  #Befehl wird täglich um 00:00 Uhr ausgeführt.
20,30 1   *   *  1-5  /usr/bin/work.sh        #Befehl wird montags bis freitags jeweils um 01:20 und 01:30 ausgeführt.
0     1   1-7 12 1    /usr/bin/work.sh        #Befehl wird am 1. bis 7. Dezember sowie an jedem Montag im Dezember um ein Uhr nachts ausgeführt.

Aus Sicherheitsgründen kann man die Erlaubnis für Cron-Jobs einschränken, indem eine Datei /etc/cron.allow mit den berechtigten Benutzern angelegt wird. (Ein Name pro Zeile). Ansonsten darf jeder Benutzer mit Eingabe von crontab -e seine eigenen Aufträge anlegen.

Cron bietet damit neben seinen typischen Aufräum- und Prüfarbeiten die Möglichkeit eigene Skripte zeitlich gesteuert auszuführen. In Zusammenspiel mit Logrotate lassen sich so ganz leicht Statistiken für den Spieleserver generieren, sofern das Spiel diese überhaupt mitloggt. Zu dem genauen Inhalt des Skripts ein anderes Mal mehr.

Links

http://de.wikipedia.org/wiki/Cron
http://linux.die.net/man/8/cron
http://www.debian-administration.org/articles/56