Java APIs in Android: Der Rechtsstreit ist entschieden

Die Auseinandersetzungen zwischen Oracle und Google um Google’s Nutzung von Java APIs in Android sind offenbar vorbei: Das Oberste Gericht der USA hat entschieden, dass diese Nutzung unter den Fair Use fällt. Neben IT und Tech Seiten wie Heise und The Verge haben auch SPIEGEL und FAZ direkt darüber berichtet. Das Urteil ist spannend zu lesen:

Fast 11 Jahre vor Gericht

Zusammenfassungen der verschiedenen Etappen dieser Auseinandersetzung zwischen den beiden IT Giganten lassen sich an verschiedenen Stellen im Netz finden, für mich verbindet sich die Anfangsphase noch mit intensiven Diskussionen darüber beim schon lange eingestellten Google+. Dort haben sich naturgemäß die eher Google-freundlichen Diskutanten eingefunden, aber die Frage ob Google sich unrechtmäßig der kreativen Leistung anderer bedient hat, oder ob Oracle mit seinem Versuch Programmierschnittstellen nachträglich als urheberrechtlich geschützte Werke zu interpretieren die Softwarewelt aus ihren gewohnten Angeln hebt, war heiß umstritten. Ich war da eher auf der Seite, die Oracles Klagen strikt ablehnte. Die folgende Karikatur aus dieser Zeit zu den Organisationsstrukturen verschiedener IT Unternehmen ist mir daher direkt wieder eingefallen:

Humoristische Organisationscharts verschiedenen US IT Unternehmen: Apple, Google, Microsoft, Oracle, Facebook, Amazon
Comic von Manu Cornet

Interessante Auszüge aus dem Urteil

Das Urteil kann man hier als PDF vom Heise Verlag abrufen. Es ist durchaus lesenswert und auch ohne umfangreiche juristische Kenntnis durchaus verständlich. Ein paar Abschnitte habe ich mir rauskopiert:

‚To decide no more than is necessary to resolve this case, the Court assumes for argument’s sake that the copied lines can be copyrighted, and focuses on whether Google’s use of those lines was a “fair use.”

Das oberste Gericht hat also die für die IT Welt – insbesondere auch die Open Source Community – wesentliche Frage ob eine API überhaupt ganz grundsätzlich ein Urheberrecht haben kann, nicht entschieden. Und da hatte Oracle in früheren Instanzen einen Etappensieg errungen. Auch wenn die Oracles Niederlage nun vielleicht einen abschreckenden Effekt hat, grundsätzlich ist damit zumindest in den USA wohl mit weiteren Verfahren zu rechnen, in denen API-Nutzungen Anlass zu Gerichtsverfahren geben. Und nicht jede Beklagte ist so mächtig und wohlhabend wie Google.

‚Computer programs differ to some extent from many other copyrightable works because computer programs always serve a functional purpose.‘

Das Gericht hat sich aber Gedanken dazu gemacht, in wie weit das Urheberrecht eigentlich auf Software angewendet werden kann.

‚As a result, this code is different from many other types of code, such as the code that actually instructs the computer to execute a task. As part of an interface, the copied lines are inherently bound together with uncopyrightable ideas (the overall organization of the API) and the creation of new creative expression (the code independently written by Google). Unlike many other computer programs, the value of the copied lines is in significant part derived from the investment of users (here computer programmers) who have learned the API’s system.

Hier macht das Gericht zum ersten, aber nicht zum letzten Mal, einen interessanten Punkt: Eine API – zumindest die, um die es hier konkret geht – gewinnt dadurch an Wert, dass Programmierer*innen sie erlernen und dafür Zeit investieren. Damit wird die Wertgenerierung nicht mehr allein den Erschaffer*innen zugebilligt, sondern jede Java Entwickler*in, die diese APIs gelernt hat, vergrößert durch ihr Investment den Wert der Sprache.

‚Google copied only what was needed to allow programmers to work in a different computing environment without discarding a portion of a familiar programming language.‘

Auch hier wird wieder der Verweis auf das existierende Knowhow von Entwickler*innen gemacht und das es Googles Vorgehensweise diesen ermöglicht habe ihr Wissen nun in einem weiteren Umfeld zu nutzen. Dieses Wissen also wertvoller wurde.

‚Google copied approximately 11,500 lines of declaring code from the API, which amounts to virtually all the declaring code needed to call up hundreds of different tasks. Those 11,500 lines, however, are only 0.4 percent of the entire API at issue, which consists of 2.86 million total lines. In considering “the amount and substantiality of the portion used” in this case, the 11,500 lines of code should be viewed as one small part of the considerably greater whole. As part of an interface, the copied lines of code are inextricably bound to other lines of code that are accessed by programmers. Google copied these lines not because of their creativity or beauty but because they would allow programmers to bring their skills to a new smartphone computing environment.‘

Und noch einmal der Verweis auf die Möglichkeit für Entwickler*innen ihr Knowhow in einer neuen Umgebung zu nutzen. Verbunden mit der Entlastung von Google, die eine vergleichsbare API oder gleich eine ganze Programmiersprache auch leicht hätten selbst entwickeln können (was sie in späteren Jahre mit Dart und Go taten).

‚The fourth statutory factor focuses upon the “effect” of the copying in the “market for or value of the copyrighted work.” §107(4). Here the record showed that Google’s new smartphone platform is not a market substitute for Java SE. The record also showed that Java SE’s copyright holder would benefit from the reimplementation of its interface into a different market

Mir persönlich gefällt, dass das Gericht den positiven Einfluss von Android auf das ganze Java Ökosystem hier honoriert. Schon bei den ersten Diskussionen gab es diesen Punkt, wenn es Oracle wirklich darum gegangen wäre Java zu pushen, dann hätten sie direkt mit Google zusammenarbeiten können. Und offenbar ist im Laufe der Jahre das Argument von Oracle verloren gegangen, wonach Google mit Android das ‚florierende‘ Ökosystem von Java ME Handys vernichtet habe.

Die Entwickler*innen als eigene Partei

Abgesehen von der eigentlichen Urheberrechtsfrage, die vermutlich in anderen Gerichtsverfahren – vielleicht nicht nur in den USA – entschieden wird, finde ich den Aspekt, das wir als Entwickler*innen eine wesentliche Rolle in solchen Erwägungen spielen, sehr spannend:

Wenn wir uns auf eine Sprache / API / Technologie einlassen kostet uns dies Zeit und vielleicht auch Geld und eine Technologie kann sich kaum durchsetzen ohne eine entsprechende Menge von Menschen, die sich damit gut auskennen. Und unsere Interessen sind dabei durchaus anders als die der potentiellen Urheberrechtsinhaber*innen: Für uns ist es attraktiv die Wahl zu haben, zwischen Werkzeugen und zwischen Implementierungen einer API, und jedes neue Feld, in dem wir unsere Kompetenz einsetzen können, ist zunächst einmal gut für uns und den Wert unseres Wissens.

Man wird sehen, ob dieser Aspekt auch in zukünftigen Gerichtsverfahren eine Rolle spielen wird.

restart.sh – Ein Bash Script für zuverlässige Server Restarts

Auf Github ist jetzt ein kleines Bash Script zu finden, welches einen einfachen Zweck verfolgt: Als Teil eines Cronjobs soll es einen Serverprozess zuverlässig neu starten können. Anlass war ein Server, der alle paar Tage eine Instabilität entwickelte, die sich nicht ohne weiteres beheben lies. Der regelmäßige Neustart ‚behebt‘ das Problem, auch wenn er natürlich nur eine Krücke ist. Aber manchmal geht es eben nicht besser…

Ziel des Scripts

Der Script solle in der Lage sein diese Funktion auszuüben:

  1. Den relevanten Prozess finden
  2. Wenn der Prozess läuft das reguläre Stopkommando ausführen
  3. Abwarten, bis sich der Prozess normal beendet. Im konkreten Anwendungsfall brauchte der entsprechende Server für einen regulären Shutdown an die 30 Sekunden
  4. Es darf nicht unendlich lange gewartet werden, um auch bei einem dysfunktional gewordenen Prozess einen Neustart ausführen zu können. Auf der anderen Seite soll der Neustart möglichst rasch erfolgen, sobald sich der Prozess normal beendet hat
  5. Neustart des Prozesses

Implementierung

Das Script restart.sh setzt die Anforderungen mehr oder weniger 1-zu-1 um:

  1. Suchen der PID des Prozesses über ein eindeutiges Muster in der Prozessliste
  2. Wenn die PID gefunden wurde ‚freundliche‘ Beendigung durch das normale Stopkommando (welches vielleicht nur ein „kill $PID“ ist)
  3. Beginn eines Countdowns, bei dem regelmäßig – z. B. jede Sekunde – geprüft wird ob der Prozess noch aktiv ist
  4. Wenn der Countdown eine maximale Laufzeit erreicht wird ein „kill -9 $PID“ abgesetzt und der Prozess damit gewaltsam beendet
  5. Absetzen des Startkommandos und Beendigung des Scripts

Eigentlich eine sehr einfache Funktion, aber ich hatte im Netz nichts gefunden, was mir genau diese Funktion bot.

Inbetriebnahme

Die Implementierung ist unter MacOS erfolgt, auf anderen unixartigen Plattformen sind vielleicht kleine Anpassungen notwendig, insbesondere in Hinblick auf die Verarbeitung der Ausgabe des ps Kommandos. Zusätzlich muss der Prozessname angepasst werden und die Wartezeit, die maximal zulässig sein soll. Siehe dazu die Kommentare in restart.sh.

Um erste Tests ohne Aufwand durchführen zu können wird dummy.sh verwendet. Das ist ein lange laufendes Script, welches sich nicht durch ein einfaches kill beenden lässt. Dazu dummy.sh in einem Terminal starten, restart.sh in einem zweiten und sehen wie sich beide Scripts verhalten.

https://github.com/ihbrune/Restart.sh

Sinn und Unsinn von Stagingkonzepten

Der CommitStrip bringt es auf den Punkt:

I’m not sure wether I should feel proud, or worried..
CommitStrip vom 10.02.2017: ‚Proud or worried?‘

Bis zu welchem Grad ist der Aufbau von Staging Umgebungen nützlich, und ab wann ist die Stelle erreicht, ab der der Betrieb und die Komplexität zu viel kosten? Oder umgekehrt: Wann lohnt es sich eigentlich ein Staging einzuführen, wenn man – wie wir – bisher keines hat?

Warum wir (bisher) kein Staging haben

Tatsächlich haben wir in dem umfangreichen Softwareprojekt, welches ich leite, bisher gar kein echtes Staging, faktisch gibt es nur das produktive System und die jeweiligen Entwicklungssysteme. Warum das bei uns so funktioniert würde ich mit diesen Punkten erklären:

Softwarequalität

Das beste Argument gegen ein Staging ist sicher eine hohe Softwarequalität: Wenn (fast) nie Fehler passieren (oder sie niemand bemerkt, weil sie so schnell behoben werden), dann wird auch niemand nach zusätzlicher Qualitätssicherung rufen. Die Softwareentwicklung wird bei uns von einem kleinem Team geleistet, welches teilweise schon sehr lange im Thema ist und dementsprechend erfahren ist. Häufige Releases mit entsprechend kleinen Entwicklungsschritten vereinfachen die Qualitätssicherung, da sie die Komplexität der einzelnen Schritte deutlich reduzieren. Die häufigen Releasezyklen werden durch eine Aufteilung des Produkts (hier ein Campusmanagementsystem) in viele Teilmodule erreicht, die weitgehend unabhängig voneinander sind (trotz einer in weiten Teilen gemeinsamen Codebasis).

Agilität

Bei uns fallen Softwareentwicklung und Serverbetrieb zusammen. Wenn es tatsächlich Fehler in die Produktion schaffen sind wir handlungsfähig und machen meist gar kein Rollback, sondern erstellen direkt eine neue Version mit entsprechender Fehlerkorrektur. Möglich ist dies durch eine Serverstruktur, die es erlaubt zu jeder Zeit für die Benutzer unmerkliche Updates durchzuführen, die nicht lange geplant oder angekündigt werden müssen.

Freigabeprozesse

Die Zuständigkeiten für das Produkt und die inhaltlichen wie technischen Kompetenzen für die Weiterentwicklung sind bisher nahezu komplett im Team gebündelt. Bei neuen Releases müssen daher nur selten Freigaben durch Key User eingeholt werden. Und falls doch, so wird dies auf den Entwicklungssystemen in direktem Kontakt gemacht. Da wir ein hausinterner Dienstleister sind und sich unsere Ansprechpartner in räumlicher Nähe befinden können wir so vorgehen.

Java

Das ‚Java Versprechen‘, wonach eine auf einer Plattform entwickelte Software auch problemlos auf einer anderen laufen wird, hat sich für uns weitgehend erfüllt: Heute findet die Entwicklung unter Windows statt und verwendet eine PostgreSQL Datenbank, während das produktive System unter Solaris und einer anderen relationalen Datenbank betrieben wird. Insgesamt gab es in der langen bisherigen Projektlaufzeit nur extrem selten Probleme, die sich auf den Entwicklungssystemen nicht nachstellen ließen. Diese wenigen Probleme waren so komplex, dass eine Nachstellung in einer Staging Umgebung mit einem enormen Aufwand verbunden gewesen wäre.

Backup

Um das Risiko von fatalen Fehlern, die es in die Produktion schaffen sollten, zu minimieren, ist ein leistungsfähiges und zugreifbares (!) Datenbankbackup essentiell. Mit fatalen Fehlern meine ich dabei solche, die Datenbankinhalte vernichten oder unmerklich korrumpieren. Ein solches Backup muss bei einer komplexen Anwendung in der Lage sein ohne großen Aufwand jederzeit Teildaten in verschiedenen Versionsständen zu liefern.

Unittests

Man kann nie genug automatisierte Tests haben, aber an bestimmten entscheidenden Stellen haben wir sie und können damit auch nach Systemänderungen sicher sein, dass diese Programmierungen noch korrekt funktionieren. 

Was einem das Staging verleiden kann

Es gibt viele Gründe Staging Konzepte einzusetzen, einen guten Teil nennt der Comic Strip bereits: Entwicklungssysteme, Testsysteme mit verschiedenen Zielsetzungen, Demosysteme, Freigabesysteme, etc. Und in den meisten Konstellationen wird man auch nicht ohne Staging auskommen. Was einem an der ganzen Sache den Spaß verderben (oder die Aufwandskalkulation für die entsprechenden IT Abteilungen nach oben treiben) kann, sind dann u. a. diese Punkte:

Wer kümmert sich um die Server?

Auch wenn es ‘nur’ ein interner Testserver ist: Irgendjemand muss ihn warten. Wird er nicht gewartet, so ist er ein zukünftiges Einfallstor für Angreifer, selbst wenn er sich tief in internen Netzen befindet. Mit jeder Stage wächst die Zahl der Server, um die sich irgendjemand kümmern muss. Falls man den Versuch unternimmt Stages aufzubauen, die der produktiven Umgebung sehr nahe sind, kann die Zahl der zusätzlichen Server enorm werden, vor allem wenn für verbundene Systeme wie Datenbanken, Identity Provider etc. ebenfalls entsprechende Staging Umgebung aufzubauen sind. Gut, wenn man hier schon eine Automatisierungslösung im Einsatz hat, die dies vereinfacht.

Echtdaten für Stages

Schnell wird man bei dem Wunsch landen in nicht-produktiven Stages die Daten des Produktivsystems zur Verfügung zu haben. Solche Daten(und ggf. Konfigurations-)kopien anzulegen erzeugt mindestens initial Aufwände für die Inbetriebnahme.

Wenn es sich um personenbezogene Daten handelt wird man sich zusätzlich den Fragen der Datenschutzbeauftragten stellen müssen. Und landet schnell bei einem Schutzbedarf der nicht-produktiven Stages, der dem Schutzbedarf der produktiven Stages in nichts nachsteht, denn auch von solchen Stages können sensible Unternehmensdaten abhanden können. Alternativ müssen Pseudonymisierungsverfahren entwickelt werden, damit die Echtdaten so unkritisch werden, dass sie auch in weniger geschützten Umgebungen genutzt werden können.

Wenn man es irgendwie vermeiden kann (Unittests, Generierung realistischer Testdaten) sollte man die Echtdatenübernahme in andere Stages vermeiden.

Enttäuschte Erwartungen I: Key User

Bei einer starken Aufgabenteilung zwischen IT Betrieb und Key Usern entsteht gelegentlich die Erwartung der IT, dass die Key User neue Versionen einer Software über eine für sie bereitgestellte Stage ausgiebig testen und damit in gewisser Weise die ‘Verantwortung’ übernehmen, dass die Software korrekt funktioniert. Die Frage ist allerdings inwieweit diese Erwartung realistisch sein kann? Key User, die per Definition ExpertInnen in dem fraglichen Themenfeld sein müssen, sind erfahrungsgemäß sowieso in ihren Abteilungen stark belastet und werden Besseres zu tun haben als neue Releases systematisch zu testen. Funktionieren kann so ein Konzept höchstens wenn es darum geht Weiterentwicklungen im Einzelfall abzunehmen. 

Enttäuschte Erwartungen II: Lasttests

Ein weitere Enttäuschung droht im Bereich von Lasttests. Gerade bei neuen, unbekannten Systemen kann der Wunsch entstehen die notwendige Dimensionierung der Infrastruktur vorher durch synthetische Belastungstests ermitteln zu wollen. Hier gibt es zwei grundlegende Schwierigkeiten:

  1. Wie erzeuge ich realistische Lasten? Bei einem schon laufenden System kann ich versuchen in den vorhandenen Protokollierungen Nutzungsmuster zu finden, die mir ein Testdesign ermöglichen. Bei Systemen mit vielen Nutzern kann es trotzdem schwierig sein die gefundenen Nutzungsmuster in realistischer Weise in einem Test auszuführen (wie simuliere ich Zugriffe von tausenden von Clients aus unterschiedlichen Netzen?).
  2. Wie erzeuge ich eine realistische Staging Umgebung? Bei einem System, welches sich im Aufbau befindet, kann ich vor der Inbetriebnahme die produktive Stage für entsprechende Tests verwenden. Nach der Inbetriebnahme ist das natürlich kaum noch möglich. Eine Stage für realistische Lasttests aufzubauen kann eine Duplizierung weiter Teile der unterstützenden IT Infrastruktur erfordern und lässt einen schnell bei der Forderung nach Echtdaten landen.

Im Endeffekt hat man ein hohes Risiko kein angemessenes Ergebnis für den erbrachten, erheblichen Aufwand zu erhalten. In vielen Fällen wird man vermutlich besser fahren, wenn man seine Systemarchitektur so aufstellt, dass auf unerwartete Lasten rasch reagiert werden kann. Dies ist mit heutigen Virtualisierungskonzepten auch normalerweise problemlos möglich.

Warum wir vermutlich doch (irgendwann) ein Staging haben werden

Aber auch für uns wird sich das Thema ‘Staging’ in Zukunft neu stellen. Ein wichtiger Treiber ist dabei die Idee Deployments unseres Produktes in Zukunft automatisieren zu können (CI/CD).

Everyone has a test environment. some are lucky enough to have a production environment too
@stahnma auf Twitter am 22.08.2015

Ein erster Schritt in diese Richtung wäre daher eine ‘Demo- oder Reviewstage’, die automatisiert die jeweils aktuell in der Versionskontrolle befindlichen Versionen unserer Anwendungsmodule verwendet, so dass sie sich schnell vorführen und prüfen lassen. Ggf. könnte man hier auch gleich weiter denken und diese Stage für verschiedene Branches verfügbar machen.

Ein anderer Treiber ist die durch Organisationsveränderungen fortschreitende funktionale Differenzierung im IT Betrieb, die die Serveradministration in andere Hände legen wird. Auch hier ist möglicherweise ein Staging ein Mittel um sowohl die Produktqualität, die Handlungsfähigkeit und die Releasegeschwindigkeit hoch halten zu können.

Anti-DoS Tomcat Valve auf Github veröffentlicht

Auf Github findet sich jetzt unter dem Namen Anti-DoS Valve ein Werkzeug, welches beim Betrieb von Tomcat Servern nützlich sein kann um diese vor Überlastungen durch böswillige Akteure im Netz zu schützen. Dazu implementiert das Valve eine dynamische Zugriffsratenlimitierung, die sich umfangreich konfigurieren lässt. Zur Simulation der Auswirkungen unterschiedlicher Konfigurationen habe ich ein Google Drive Sheet veröffentlicht. Der Code steht unter der Apache Lizenz, kann also frei verwendet werden.

Woher kommt die Idee

Das Valve ist eine verallgemeinerte Version einer kleinen Programmierung, die ich im vergangenen Jahr kurzfristig erstellen musste, nachdem unsere Server immer mal wieder unter Überlastungen litten, die offenbar durch auf gekaperten Rechnern laufende Webcrawler verursacht wurden. Diese Zugriffe waren teilweise so aggressiv, dass sie Auswirkungen auf unsere regulären Nutzer hatten.

Zur Begrenzung dieser unwillkommenen Zugriffe standen zunächst unterschiedliche Optionen im Raum, aber die Option ein Tomcat Valve zu implementieren setzte sich am Ende aus diesen Gründen durch:

  • Ein Valve ist durch seine Verortung im Tomcat Server nahe an der Geschäftslogik bzw. an den Leuten, die wissen was auf dem Applikationsserver läuft. Eine Verortung etwa auf Netzwerkebene hätte einen nicht unerheblichen Wissenstransfer zwischen unterschiedlichen IT Bereichen erfordert
  • Da es sich bei den Angriffen – auch böswillige Webcrawler kann man dabei als Angreifer sehen – nicht um umfangreiche (D)DoS Angriffe handelte war eine Gegenmaßnahme auf Ebene der Applikationsserver ausreichend
  • Der Aufwand für die Programmierung des Valves war auf Grund der vorhandenen Erfahrungen vergleichsweise gering
  • Der Einsatz eines Filters mit identischer Funktion, der innerhalb der Webapplikationen eingesetzt würde, erzeugt im Vergleich zu einem Valve einen höheren Aufwand in der Konfiguration

Was macht das Valve

Ziel ist es unwillkommene Belastungen der eigenen Server so weit begrenzen zu können, dass sie nicht mehr in der Lage sind negative Auswirkungen auf die regulären Nutzer zu entfalten. Dabei soll die Schutzfunktion dynamisch sein, sich also selbstständig auf neue Angreifer einstellen und diese blockieren können. Und schließlich soll der Overhead dieser zusätzlichen Filterung, die zunächst für alle im Tomcat Server eingetreffenden Requests anspringt, möglichst klein sein. In der aktuellen Version arbeitet das Valve dabei auf der Ebene einzelner IP Adressen, für die Limitierungen durchgesetzt werden können.

Für die konkrete Implementierung wurden dann Erfahrungen genutzt, die ich früher bei anderen Programmierungen zu Ratenlimitierungen gemacht hatte, insbesondere beim Aufbau unserer Identity Provider Infrastruktur und der dort durchgesetzten Begrenzung der Anzahl von möglichen Versuchen zum Erraten eines Passworts.

Um die Implementierung ressourcenschonend zu gestalten – insbesondere in einer DoS Situation, wo schnell abertausende von Requests in kurzer Zeit eintreffen können – wird für die Ermittlung der aktuellen Zugriffsrate einer IP Adresse keine gleitende Auswertung gemacht, sondern es wird nur über die Anzahl der Zugriffe innerhalb eines bestimmten Zeitintervalls (in der Programmierung Slot genannt) Buch geführt. Wird innerhalb eines solchen Slots die Anzahl der erlaubten Zugriffe überschritten, so werden alle weiteren Zugriffe bis zum Ende des Slots abgelehnt. Damit nicht mit jedem neuen Slot das Spiel von vorn beginnt kann ein Anteil der in früheren Slots gezählten Zugriffe übernommen werden. Auf diese Weise sind sowohl die Datenhaltung im Valve wie auch Rechenaufwand gering und nehmen auch in einer Hochlastphase nicht wesentlich zu.

Über die Konfigurationsoptionen können dabei nahezu alle Eigenschaften gesteuert werden, neben dem Grenzwert der Zugriffe pro Slot auch die Slotlänge und der Umfang der aus früheren Slots übernommenen Requestanzahlen. Mit diesem einfachen Mittel lassen sich durchaus komplexe Filterungen realisieren.

Es gibt im Valve noch weitere Optionen, über die sich steuern lässt auf welchen Adressen die Ratenlimitierung überhaupt wirksam werden soll (hier kann man z. B. Requests auf statische Inhalte wie JS oder CSS Dateien ausschließen um schärfere Einstellungen zu realisieren) und ob bestimmte IP Adressen oder Adressbereiche generell von einer Filterung ausgenommen werden sollen (z. B. Adressen aus dem Intranet, um interne Nutzer nie zu beeinträchtigen).

Wie legt man los

Während die grundsätzliche Inbetriebnahme des Valves sehr einfach ist (Clone von Github, Ausführen von Maven Install, JAR in Tomcat/lib Verzeichnis kopieren, Valve Konfiguration in server.xml ergänzen), liegt die eigentliche Herausforderung darin die für den eigenen Anwendungsfall passenden Konfigurationsparameter zu finden. Hier gilt es die richtige Balance zu finden zwischen zu engen Einstellungen, die auch die regulären Benutzer beeinträchtigen können, und zu offenen Parametern, die die Angreifer nicht richtig im Zaum halten können und damit auch wieder die regulären Benutzer beeinträchtigen.

In der README findet sich ein eigener Abschnitt dazu, wie man bei der Inbetriebnahme vorgehen kann. Der Kernpunkt ist es hier ein ausreichendes Wissen über die reguläre Nutzung der eigenen Dienste zu haben, also welche Zugriffszahlen OK sind und welche ein böswilliges bzw. störendes Level erreichen. Die Auswertung der Tomcat Access Logfiles ist dabei der Schlüssel. Hat man diese Werte kann man in dem Simulator Sheet vergleichen welche Auswirkungen eine bestimmte Konfiguration hat und damit prüfen, ob sie in der Lage ist die Spreu vom Weizen zu trennen. Diese Knackpunkte gibt es dabei der Erfahrung nach:

  • Wie häufig greifen Suchmaschinen zu? Gutartige Suchmaschinen zeichnen sich meist durch gleichmäßige Zugriffe aus und haben keine Zugriffsspitzen. Je nach Valve Konfiguration kann es aber vorkommen, dass auch solche Zugriffsformen gesperrt werden, die dauerhaft unter den pro Slot erlaubten Zugriffsraten bleiben.
  • Dienen die eigenen Webapplikationen als Service Endpunkt, der von anderen Applikationsservern in großem Umfang genutzt wird? Wenn es sich dabei um gewollte Fälle handelt, so sollte man die entsprechenden Adressen bzw. Adressbereiche grundsätzlich freischalten, damit die Valve Konfiguration nicht auf Grund solcher Einzelfälle zu offen gestaltet sein muss.
  • Wie viele Request löst ein Benutzer beim ersten Besuch (also mit leerem Cache) der eigenen Seiten aus? Hier erreichen moderne Webseiten auf Grund der vielen Komponenten häufig erstaunliche Zahlen, wobei die Masse der Zugriffe auf statische Inhalte entfällt. Um auch hier die Valve Konfiguration nicht zu offen gestalten zu müssen ist es meist sehr sinnvoll die Zugriffe auf statische Inhalte nicht mit in die Zugriffszählungen eingehen zu lassen. Diese Zugriffe sollten sowieso nicht die Hauptlastverursacher in einer dynamischen Webapplikation sein und die böswillige Akteure werden sie sowieso nicht abrufen.
  • Kommen reguläre Benutzer in großer Zahl über Proxies auf den eigenen Server, so dass sich hinter einzelnen IP Adressen faktisch viele Benutzer verbergen?

Auch bei sehr sorgfältiger Vorbereitung ist es aber notwendig zumindest in der ersten Zeit nach der Aktivierung des Valves die Logging Informationen im Auge zu behalten und zu beobachten ob Adressen gesperrt werden, die man eigentlich zulassen möchte.

Ist das Valve richtig konfiguriert kann man es in gewisser Weise ‚vergessen‘, da es danach Angriffe automatisch im Zaum halten kann. Änderungsbedarfe ergeben sich dann nur noch bei Modifikationen an den im Server installierten Anwendungen, die das Lastverhalten beeinflussen. Trotzdem sollte ein Monitoring fortgesetzt werden, schon allein um zu sehen ob Angriffe erfolgen und in welchem Umfang.

Was sind die Grenzen

Das Anti-DoS Valve kann seinem Namen nur in einem Teilaspekt des weiteren Themas (D)DoS gerecht werden. Es wird z. B. nicht bei riesigen Überflutungen helfen, die schon die Netzwerkebene überlasten. Es hat aber seine Daseinsberechtigung in dem Bereich, der unterhalb des Angriffslevels liegt, der sich auf Netzwerkebene einfach erkennen lässt:

Im Fall unserer Server werden heute pro Tag normalerweise mehr als eine Million Requests verarbeitet. Starke Belastungen können aber schon entstehen, ohne das sich diese Zahl insgesamt vervielfacht, sofern die Zugriffe auf die entsprechenden Adressen gebündelt werden. An genau dieser Stelle kann das Valve eingesetzt werden, in dem es insbesondere die Zugriffe auf die Adressen reguliert, die geeignet sind hohe Belastungen zu erzeugen. Dafür ist eine Kenntnis der entsprechenden Applikationen und ihrer sensiblen Punkte notwendig.

Die ursprüngliche Version des Valves hat dabei den angestrebten Zweck bisher erfüllt und sich als sehr stabil erwiesen.

Monitoring per JMX

Das Valve kann sehr weitgehend per JMX administriert und beobachtet werden. In der JConsole präsentiert es sich dabei so:

Auf diese Weise können Konfigurationsänderungen auch am laufenden Server durchgeführt werden und gleichzeitig der aktuelle Status z. B. von blockierten IP Adressen beobachtet werden.

Ideen für die Weiterentwicklung

Die bisherige Implementierung hatte das Ziel möglicht einfach gehalten zu sein und ist auf den bisherigen Einsatzzweck zugeschnitten. Für eine Verallgemeinerung der Funktion bieten sich aber eine Reihe von Erweiterungen an:

  • Testmodus: Ein Option das Valve im Server so zu betreiben, dass nur Loggings über Blockaden erfolgen, aber keine echten Blockaden. Auf diese Weise könnten Konfigurationen zunächst risikofrei getestet werden.
  • Zähler auf Ebene von Subnetzen: Nicht nur echte DDoS Attacken verteilen ihre Zugriffe auf viele Ausgangsrechner, auch bei Webscrapern / böswilligen Crawlern kann dies beobachtet werden. Hier wäre eine Verallgemeinerung der Programmierung interessant, die in der Lage ist Zähler nicht nur auf Ebene von einzelnen IP Adressen zu führen. Die Implementierung des Monitors, der die Zugriffszählung durchführt, ist bereits entsprechend verallgemeinert worden.
  • Möglichkeit mehrere Instanzen des Valves im Server nutzen zu können: Zwar kann man heute das Valve mehrfach in einem Tomcat konfigurieren, aber die Monitor Instanz ist nicht getrennt und per JMX wird nur eine Instanz des Valves gezeigt. Die Nutzung unterschiedlicher Valve-Instanzen in einem Server kann nützlich sei falls unterschiedliche Konfigurationen für verschiedene Webapplikationen gefahren werden sollen.
  • Verbesserungen für den Einsatz in Serverfarmen: Heute müssen Konfigurationsänderungen an allen Servern separat erfolgen, auch gibt es keine Möglichkeit wie sich Instanzen des Anti-DoS Valves auf unterschiedlichen Servern verständigen können. In unserer Serverfarm beeinträchtigt uns das heute nicht, aber bei großen Farmen könnte so etwas nützlich sein.

Falls jemand Lust hat an dem Projekt mitzumachen: Einfach melden 🙂