Posts mit dem Label IT werden angezeigt. Alle Posts anzeigen
Posts mit dem Label IT werden angezeigt. Alle Posts anzeigen

Freitag, 13. Juli 2012

Sicheres Passwort Hashing

Nachdem in letzter Zeit immer und immer wieder Webseiten vor allem dadurch auffallen, dass sie Passwörter im Klartext speichern, hier zwei Funktionen aus der User-Klasse eines meiner letzten Projekte...

Übrigens: Wenn Ihr bei Klick auf 'Passwort vergessen' von einer Webseite das Passwort im Klartext per Email zugesendet bekommt, macht man dort genau das falsch. Schaut dann mal in deren AGB oder Datenschutzbestimmung, ob sie dort behaupten die Passwörter zu hashen. Oft sind die nur Copy&Paste. Und bittet sie das Verfahren umzustellen. Oder schreibt Heise.

Der Code

Für die meisten Probleme gibt es Standards, so auch für das Passwort-Hashing. Es gibt also keinen Grund sich selbst mit Funktionen wie (der nicht mehr zu benutzenden) md5() und String-Operationen seine eigene crypt-Funktion zu schreiben.

Hier nun also eine Lösung mit php, weil die grade hier rumlag.

    /* Code Licence: Public Domain
     *
     * $this->pwhash wird in der DB gesichert bzw. daraus gelesen.
     * die Konfiguration in $_CONFIG sieht z.B. so aus:
     *
     * // Hier ist ein zufälliger Wert pro Webseite einzutragen:
     * $_CONFIG["pw_sitekey"]="897a-d21lk.a8q";
     * // Und hier der Hash-Also. sha1, sha512, NICHT md5!
     * $_CONFIG["pw_hashalgo"]="sha512";
     *
     */

    public function setPassword($password){
        global $_CONFIG;
        $site_key=$_CONFIG["pw_sitekey"];
        $algo=$_CONFIG["pw_hashalgo"];
        $nonce=uniqid();
        $hash = hash_hmac($algo, $password . $nonce, $site_key);
        $this->pwhash = $nonce.':'.$hash;
    }

    public function verifyPassword($password){
        global $_CONFIG;
        $site_key=$_CONFIG["pw_sitekey"];
        $algo=$_CONFIG["pw_hashalgo"];
        $nonce= strstr($this->pwhash, ':',TRUE);
        $hash = strstr($this->pwhash, ':',FALSE);
        $chash= ":".hash_hmac($algo, $password . $nonce, $site_key);
        return ($hash == $chash);
    }

Demo

Was kommt da nun also heraus? Dazu eine kleine Demo
        //Passwort
        $password="very secret";

        //Konfiguration:
        $site_key="12345";
        $algo="sha512";

        $nonce=uniqid();
        $hash = hash_hmac($algo, $password . $nonce, $site_key);
        $pwhash = $nonce.':'.$hash;

        //ausgabe zur Kontrolle
        echo "pwhash= $pwhash\n";

        //zu pruefendes Passwort:
        $check_password="very secret";

        $vnonce= strstr($pwhash, ':',TRUE);
        $vhash = strstr($pwhash, ':',FALSE);
        $chash= ":".hash_hmac($algo, $check_password . $vnonce, $site_key);

        if ($vhash == $chash){
            echo "passwords match!\n";
        }

Die Ausgabe bei übereinstimmenden Passwörtern:
pwhash= 50000b502b234:b038dfb61ba609604e7be78f3aa2cd84fb03970031f5e8d6f2eacc10542741dda5b7f41a716f3f78c305f6898d9b82838ce6b9df15dc94d00c37af393cc51de1
passwords match!

Passwort vergessen!

Wie setzt man das nun organisatorisch mit dem 'Passwort vergessen' Link zusamen? Ganz einfach. Wenn die Nutzer ihr Passwort vergessen können sie sich an ihre Emailadresse einen Link zusenden, mit dessen Hilfe sie auf der Webseite ihr Passwort selbst zurücksetzen können. Der Link enthält dabei ein zufällig erzeugtes Token und verliert nach Benutzung (erfolgreiches Ändern des Passworts) seine Gültigkeit.

Nachtrag: Und natürlich sollte der Link wenn er nicht benutzt wurde nach X Tagen ebenfalls invalidiert werden.

Sonntag, 31. Juli 2011

postfix: IP-Adresse des Absenders verbergen

Nach langer Zeit gibt es heute mal wieder einen kleinen Post aus der Sysadmin-Ecke.

Die Ausgangslage

Ein Benutzer liefert seine Emails mit Authentifizierung bei einem durch mich betriebenen Mailserver ein, der für die entsprechende Domain zuständig ist.

In den Headern dieser Mail wird nun die IP-Adresse auftauchen, von der aus die Email eingeliefert wurde. Dies kann die IP zu Hause sein, die von einem Bekannten oder die von einer Institution. Postet derjenige häufiger auf Mailinglisten kann man so schon ein Profil erstellen und bekommt auf jeden Fall heraus, dass er z.B. Emails von der Uni aus sendet, etc.

Dies möchte man nun verhindern - und das funktioniert mit postfix Bordmitteln sogar relativ einfach.

Mehr Information ist am Ende doch weniger

Zunächt müssen wir die entsprechenden Header erkennen können. Da bei allen nicht-authentifizierten Emails die IP-Adresse des einliefernden MTA im Header unverändert erhalten bleiben soll, schon um Spammer identifizieren zu können ohne extra in die Logs schauen zu müssen, brauchen wir einen Weg, die passenden Received:-Header zu erkennen. Dies erreichen wir, indem wir dem postfix sagen, dass er den Benutzernamen des Absenders auf dem Mailserver in den Header einfügen soll. Dies geschieht über die Anweisung smtpd_sasl_authenticated_header = yes in der Datei main.cf des postfix.

Unser header sieht nun ungefähr so aus:

Received: from [192.168.101.150] (guest123.guest.example.org [192.0.43.10])
  (using TLSv1 with cipher DHE-RSA-CAMELLIA256-SHA (256/256 bits))
 (No client certificate requested)
 (Authenticated sender: username)
 by mail.somedomain.tld (Postfix) with ESMTPSA id 32A59B1C032
 for ; Thu, 30 Jun 2011 12:36:07 +0200 (CEST)

Die hervorgehobene Zeile ist neu hinzugekommen und ermöglicht es uns im zweiten Schritt nach genau diesen Headern zu suchen.

pcre ftw!

postfix bietet dafür die Möglichkeit der header_checks. Damit kann man mittels (perl) regular expressions auf den Headern der Email arbeiten und Zeilen einfügen, Zeilen ändern und eigentlich vor allem Aktionen auslösen wie das Aufhalten einer Email, Abweisen, Weiterleiten, etc.

Wir benutzen diese Möglichkeiten, um auf der oben gezeigten Received:-Zeile ein paar Ersetzungen vorzunehmen.

Dazu stellen wir zunächst sicher, dass die main.cf des Postfix auch header checks aktiviert hat:

 smtpd_sasl_authenticated_header = yes
 ...
 header_checks      =  pcre:/etc/postfix/header_checks
 mime_header_checks =  pcre:/etc/postfix/header_checks

Nun koennen wir in der angegebenen Datei header_checks mit folgender Zeile das Umschreiben aktivieren:

/^Received:\ from (.*)\(.*\)(.*)\(Authenticated\ sender:\ .*\)(.*by mail\.somedomain\.tld.*)$/U  REPLACE Received: from $1 (Remote IP hidden) $2(Sender was authenticated on somedomain.tld) $3

Hinweis: Die Zeile ist relativ lang, beim Kopieren aufpassen sie komplett zu erwischen. Wichtig ist die Option U an der Regex, diese schaltet sie auf ungreedy. Siehe man pcre_table.

Der Name des Mailservers taucht hier nochmal auf, damit ich die Original-IP bei extern eingelieferten Emails nicht verwerfe - das kann zum Identifizieren von Spammern helfen, auch wenn die Header oft gefälscht sind.

Im Ergebnis sieht der Header der Email nun aus wie folgt:

Received: from [192.168.101.150]  (Remote IP hidden) 
 (using TLSv1 with cipher DHE-RSA-CAMELLIA256-SHA (256/256 bits))
 (No client certificate requested)
 (Sender was authenticated at somedomain.tld) 
 by mail.somedomain.tld (Postfix) with ESMTPSA id A36241184685
 for ; Fri, 29 Jul 2011 20:23:31 +0200 (CEST)

Wenn man die private IP auch noch entfernen möchte, kann man sich jetzt natürlich einen weiteren Header-check überlegen, der auf '\[.*\].*Remote IP hidden' triggert und diese ersetzt. Das kann im Einzelfall notwendig werden, wenn kaputte Antispam-Maßnahmen anderer Empfänger solche Emails ablehnen.

Wenn man misstrauisch ist fügt man ausserdem noch eine Zeile hinzu, die jedes Vorkommen von '(Authenticated sender: ...)' mit etwas belanglosem ersetzt, falls man unter allen Umständen die internen Nutzernamen geheim halten will.

Dienstag, 2. November 2010

RANT: Wer darf heutzutage eigentlich Mailserver betreiben?

Mir platzt hier gleich die Hutschnur. Man ist mal eine Woche zum Entspannen (und wegen einer Tagung)) aufs Land gefahren, und dann ist man andauernd am Server fixen. Arrgh.

Ein Drama in drei Akten

Dass einer meiner Server am Sonntag (natürlich während des gemütlichen Abendessens bei der Familie) wegen zu wenig RAM ausstieg ist ja meine Schuld, aber was dann gestern und heute sonst noch passierte muss ich einfach mal loswerden.

1. Akt: Die Uni

Gestern Abend hatte die Uni Probleme. Ausnahmslos alle Emails wurden mit einem permanenten Fehler abgewiesen:

 ...
 host localhost [127.0.0.1]: 
   550-Mailbox unknown. Either there is no mailbox associated with this
   550-name or you do not have authorization to see it.
   550 5.1.1 User unknown

Das ist natürlich genial für Mailinglisten - von denen fliegt man dann in der Regel runter - aber auch, wenn man wie ich bei seinem Hoster diese Adresse angegeben hat (weil unabhängig von den Systemen die man dort hosted) und dann die Rechnung oder Trafficabrechnung einen User unknown rauswirft.

Natürlich war gestern der Monatserste und natürlich hat mein Hoster mir versucht an diesem Tag die Rechnung und die Trafficabrechnung zu schicken. Die Trafficabrechnung ist angekommen. Die Rechnung (noch?) nicht.

2. Akt: Der Ticketversender

Ich bin diese Woche weggefahren, weil ich am Donnerstag zu einer Tagung will. Dazu braucht man natürlich ein Ticket. Und wie bekommt man das? Richtig, über einen Dienstleister. Dazu meldet man sich dort an, bezahlt das Ticket und wartet auf das pdf mit dem Ticket.

Und wartet. Und wartet. Und schreibt eine Email an den Support.

Denn: zum Passwort ändern auf der Plattform muss man eine Email bestätigen, aber die kommt ebenfalls nicht an.

Nun bekam ich dann heute eine html-only Email, dass das Ticket nochmal versendet wurde. Aber es kam nicht an, dafür füllte sich mein Maillog:

postfix/smtp[18334]: connect to mail2.amiando.com[213.183.6.102]:25: Connection refused
postfix/smtpd[18137]: NOQUEUE: reject: RCPT from mail.amiando.com[195.71.125.161]: 
  450 4.1.7 <no-reply@mail.amiando.com>: Sender address rejected: unverified address:
      Address verification in progress; from=<no-reply@mail.amiando.com>
      to=<xxxxx@XXXXXXXXX> proto=ESMTP helo=<lnxp-1930.srv.mediaways.net>

Die HTML-Mail löste bei mir leichte Übelkeitsgefühle aus, aber als ich den Eintrag im maillog auswertete wurde mir irgendwie schlecht.

Diese Leute versenden Emails und wundern sich, dass sie als SPAM erkannt wird?

Fassen wir mal zusammen:

Die Email ist HTML-Only, die Absenderadresse existiert nicht, so dass sie bei Fehlzustellungen nichts davon bemerken - deren System wird es also immer wieder versuchen, an eine nicht-funktionierende Adresse von einer ebenfalls nicht-funktionierenden Adresse aus mails zu versenden. Damit macht man sich bei den postmastern, die diese Bounces bekommen, so richtig beliebt.

Dann meldet sich der Mailserver mit dem Namen lnxp-1930.srv.mediaways.net obwohl er laut DNS eigentlich mail.amiando.com heissen sollte. Noch ein SPAM-Kriterium.

Und zur Kröhnung schreiben sie einen Mailserver in ihre Zone, dessen SMTP-Port Verbindungen ablehnt.

3. Akt: Services die verbinden...

Mit dem Spruch wirbt eine andere Firma für ihre Dienste. Leider scheint es damit nich so weit her zu sein. Ich bekomme hier jedenfalls Emails von der Adresse www-data@int3.web.int.fra.privatnetz.org

Mein Mailserver scheitert dann laut syslog aber daran, wenn er versucht mails an die IP 10.1.1.100 zuzustellen.

Die haben doch allerernstes sowohl hostnamen mit privaten IPs in ihrem DNS, als auch MX-Records die auf private IPs zeigen! Stümper! Das ist 'pollution of the public namespace' was den DNS angeht und auch sonst einfach nur kaputt.

Und dann wundern sich Leute, warum ich NAT und Masquerading hasse!

Das Ergebnis ist dasselbe wie oben: Sie werden niemals Fehlermeldungen erhalten und vielfach als SPAMMER erkannt werden.

Fazit

Ich frage mich langsam echt, ob wir einen Internet-Führerschein brauchen - für Diensteanbieter!

Montag, 9. November 2009

Things you don't want to see when creating a filesystem...

Wo wir schon beim Thema sind. Habe eben ein altes kernel-log auf meinem nas gefunden:
BUG: unable to handle kernel NULL pointer dereference at 00000004
IP: [] mempool_free+0xe/0x97
*pde = 00000000 
Oops: 0000 [#1] PREEMPT 
Modules linked in: xfs raid456 async_xor async_memcpy async_tx xor md_mod dock container video output wmi ohci_hcd nfsd auth_rpcgss exportfs ac battery ipv6 nfs lockd nfs_acl sunrpc loop parport_pc parport floppy pcspkr rtc i2c_i801 i2c_core rng_core button intel_agp agpgart evdev ext3 jbd mbcache cbc dm_crypt crypto_blkcipher dm_mirror dm_snapshot dm_mod ide_generic ide_cd_mod cdrom ide_disk sd_mod usb_storage dc395x 3c59x mii scsi_mod piix ide_core ehci_hcd uhci_hcd usbcore thermal processor fan

Pid: 27646, comm: md0_raid5 Not tainted (2.6.25.1 #3)
EIP: 0060:[] EFLAGS: 00010286 CPU: 0
EIP is at mempool_free+0xe/0x97
EAX: cf5f15a0 EBX: 00000000 ECX: c15f7990 EDX: 00000000
ESI: 00000000 EDI: cf5f15a0 EBP: 00000000 ESP: cf77be20
 DS: 007b ES: 007b FS: 0000 GS: 0000 SS: 0068
Process md0_raid5 (pid: 27646, ti=cf77a000 task=cf484b00 task.ti=cf77a000)
Stack: ...
Call Trace:
 [] return_io+0x2b/0x33 [raid456]
 [] handle_stripe5+0x488/0xdf9 [raid456]
[...]

Der Bug war übrigens reproduzierbar, wenn man versuchte auf einem gerade im Aufbau befindlichen Raid5 einen LUKS-Container anzulegen und darin dann das Filesystem zu erzeugen.

Things you don't want to see when checking a filesystem...

Out of memory: kill process 3641 (xfs_check) score 20034 or a child
Killed process 3642 (xfs_db)
# xfs_check /dev/sda1
Segmentation fault.
*aargh* 1 GB RAM (davon >700MB frei) sollten meiner Meinung nach für ein fsck auf xfs reichen. Und egal wie kaputt das Dateisystem ist, von einem Superblock mit invaliden Daten sollte es keinen Segfault geben.

Sonntag, 30. August 2009

Webseiten automatisch vor Überlast schützen am Beispiel einer MediaWiki-Seite

Das Szenario: Man ist stolzer Betreiber eines eigenen Servers auf dem ein Wiki läuft. Plötzlich berichtet Heise über das eigene Projekt oder man wird von einer anderen der großen Nachrichten-Portale verlinkt und plötzlich geht auf dem Server gar nichts mehr. Selbst der Zugriff per SSH um zu schauen was los ist funktioniert nicht mehr richtig und alle Nagios-Instanzen schlagen Alarm.

Damit es zu diesem Szenario nicht kommt, gibt es natürlich verschiedene Ansätze. Von redundant vorgehaltenen Servern, die per DNS Round-Robin angesprochen werden, über Loadbalancer auf Routingbasis, etc.

Viele dieser Maßnahmen sind aber entweder recht kostspielig oder haben den Nachteil nicht so gut mit soetwas wie einem Wiki zusammenzuarbeiten.

Es gibt aber eine weitere Möglichkeit, die den Dienst http://coralcdn.org benutzt.

Coral Content Distribution Network

Das Coral Content Distribution Network funktioniert transparent durch das Anhängen einer Domain des Dienstes an eine beliebige Webadresse. So wird aus www.google.de ganz schnell die gecachte Version, wenn man statt dessen www.google.de.nyud.net aufruft.

An dieser Stelle vielen Dank an rbu für den Hinweis auf Coral!

Doch wie kann man diesen Dienst benutzen, um ein Wiki bei Überlast des Servers noch einigermassen erreichbar zu halten?

Ein eigener Host für Coral

Zunächst habe ich zum Testen einen weiteren Virtual Host für das Wiki aufgesetzt, so dass ich dort Einstellungen machen kann, ohne das Wiki unbrauchbar zu machen.

Ausserdem hat das den Vorteil, dass ich https-Zugriffe verhindern bzw. umlenken kann, die über Coral natürlich nicht richtig funktionieren.

Und, noch etwas, dieser Virtual Host soll später vielleicht mal auf einen weiteren Rechner umziehen und kann so noch mehr Last abfedern, indem er statische Dateien wie Bilder direkt aus seinem Cache ausliefert.

Dieser VHost hört auf den originellen Namen cache.meinedomain.tld.

Folgende Parameter wurden (zusätzlich) gesetzt:


      #Dies soll ein Proxy werden, also Zugriff auf
      #folgende Seiten per proxy erlauben:

      <Proxy proxy:http://wiki.meinedomain.tld/ >
            Order deny,allow
            Allow from all
      </Proxy>

      ProxyVia On
      ProxyRequests Off

      #Diese Seite liefern wir auf / aus:
      ProxyPass / http://wiki.meinedomain.tld/
      ProxyPassReverse / http://wiki.meinedomain.tld/

      #Ausserdem Cachen wir auf der Festplatte
      CacheRoot /var/cache/apache2
      CacheDirLevels 2
      CacheDirLength 10
      CacheMinFileSize 10
      CacheMaxFileSize 2000000
      CacheEnable disk /
      CacheEnable disk http://wiki.meinedomain.tld/
      CacheEnable disk proxy:http://wiki.meinedomain.tld/

      #Die Cookies wollen wir nicht mit speichern...
      CacheDefaultExpire 3600
      CacheIgnoreHeaders Set-Cookie

      # Bis hier war das nur die (optionale) proxy-Konfiguration
      # kommen wir nun zum LoadBalancer

      RewriteEngine on
      RewriteCond %{HTTP_USER_AGENT} !^CoralWebPrx
      RewriteCond %{QUERY_STRING} !(^|&)coral-no-serve$
      RewriteMap    lb      prg:/var/www/loadbalance.sh
      RewriteCond   ${lb:%{REQUEST_URI}|NULL} !^NULL
      RewriteRule   ^(.*)$ ${lb:$1|%1}           [P,L]

Der zweite Block definiert ein shellscript als Entscheidungshilfe für das Ziel eines Redirects, wenn der Request nicht von dem coral-webproxy kam. Diese Ausnahme müssen wir konfigurieren, da wir sonst den Coral-Server auf sich selbst umlenken würden.

Und dieses Script ist auch schon das Geheimnis.

Das Redirect-Script

Das folgende Script wird vom apache sofort beim Start geforkt und erhält auf stdin fortan die angefragte URL und kann auf stdout dann ausgeben, wohin denn der Request umgelenkt werden soll.

Damit kann man nun nach eigenem Ermessen Paramter suchen, die für einen Redirect sorgen sollen.

Ich nehme hier das maximum von zweien der Load-Werte, so dass bei zuviel Ansturm die Besucher umgelenkt werden und mit Abflauen des Ansturms (die Besucher bleiben dann ja auf dem Proxy) auch wieder neue Besucherrequests angenommen werden.

Ein wenig zusammengehackt und sicherlich nicht die schnellste Implementierung, aber es erfüllt erstmal seinen Zweck, sollte aber durch ein C-Programm ersetzt werden, welches ein _wenig_ effizienter implementiert ist.

#!/bin/bash

loadbalancer="cache.meinedomain.tld.nyud.net";

while read LINE; do
 load="$(awk '{gsub("\\.",""); if ($1>$2){ print $1} else { print $2 }  }'< /proc/loadavg)"
 if [ "$load" -gt "$(cat /var/www/maxload.conf)" ]; then
   logger -t "loadbalance-cache" "redirecting due to load=$load. request: $LINE"
   printf "http://%s/%s\n" "${loadbalancer}" "${LINE}"
 else
   printf "NULL\n"
 fi
done

In der Datei /var/www/maxload.conf habe ich testweise einfach die Zahl 550 eingetragen, so dass ab einer Load größer 5.5 ein Redirect erfolgt.

Konfiguration des Wikis

Bei Zugriffen über den Proxy muss das Wiki read-only arbeiten. Dafür gibt es mehrere Gründe:

Zum Einen sollen nicht die privaten Previews eines Benutzers gecached werden, zum anderen redirecten wir ja wegen hoher Last - und ein Edit eines Artikels wird sicherlich nicht zum Senken der Last beitragen.

Dazu gibt es einen Codeblock in der LocalSettings.php des Wikis:

if ( preg_match( "/^CoralWebPrx/", $_SERVER['HTTP_USER_AGENT']) > 0
    || preg_match( "/coral-no-serve$/", $_SERVER['QUERY_STRING']) > 0) {

 $wgReadOnly="<span style=\"background-color:yellow;\">Das Wiki ist aus Lastgründen read-only. Du greifst daher gerade über ein Cache auf eine gespeicherte Version zu.</span>";
}

Dadurch verlieren wir zwar auch die PageHits-Zählung im Wiki, aber über einen Proxy werden diese ohnhin nicht mehr korrekt gezählt.

Konfiguration des Wiki-Hosts

Nachdem alles getestet ist und funktioniert, können wir bei Überlast alle Requests auf den Wikiserver direkt auf die cache-Variante von Coral lenken. Dies machen wir mit einigen Zeilen in der Definition des Virtual Hosts:

RewriteEngine on
RewriteMap    lbwiki      prg:/var/www/wiki.meinedomain.tld/loadbalance.sh
Folgende Regeln fügen wir nun an die .htaccess des Wikis an. Das muss geschehen, da wir dort noch andere Rewrite-Regeln definiert haben.
# Alle requests des coral-proxies auf den cache lenken,
# requests von unserem Cache nicht auf unser Cache umlenken.
RewriteCond %{REMOTE_ADDR} !^127.0.0.1$
RewriteCond %{REMOTE_ADDR} !^IP.DES.EIGENEN.PROXIES$
RewriteCond %{HTTP_USER_AGENT} ^CoralWebPrx
RewriteRule   ^(.*)$ http://cache.meinedomain.tld/$1           [R,L]

RewriteCond %{REMOTE_ADDR} !^127.0.0.1$
RewriteCond %{REMOTE_ADDR} !^IP_DES_CACHES$
RewriteCond %{QUERY_STRING} (^|&)coral-no-serve$
RewriteRule   ^(.*)$ http://cache.meinedomain.tld/$1           [R,L]


#Folgender Block prueft ob die load zu hoch ist und
#redirected dann auf den gecachten cache...
#RewriteMaps muessen global in der vhost konfig gemacht werden.
RewriteCond %{REMOTE_ADDR} !^127.0.0.1$
RewriteCond %{REMOTE_ADDR} !^IP_DES_CACHES$
RewriteCond %{HTTP_USER_AGENT} !^CoralWebPrx
RewriteCond %{QUERY_STRING} !(^|&)coral-no-serve$
RewriteCond   ${lbwiki:%{REQUEST_URI}|NULL} !^NULL
RewriteRule   ^(.*)$ ${lbwiki:$1|%1}           [R,L]


Wichtig ist hier, dem LoadBalancer einen anderen Namen zu geben.

Wir benutzen für diesen Artikel wieder dasselbe script, so dass direkt auf den Host cache.meinedomain.tld.nyud.net verwiesen wird.

Der erste Block ist eher 'Kosmetik' und verhindert, dass man auf wiki.meinedomain.tld über Coral zugreifen kann.

Samstag, 29. August 2009

easyjet: was kann man alles falsch machen?

Eine Bekannte schrieb mir, beim Buchen von Flügen per easyJet kämen Zertifikatsfehler. Was das denn zu bedeuten habe?

Das Zertifikat für https://www.easyjet.de ist nur für www.easyjet.com gültig. Nungut, kann passieren. Versuchen wir denen das mal zu sagen... Pustekuchen. Ein Kontaktformular haben die auf ihrer Seite gar nicht. Najaah, schauen wir mal im whois, da wird doch - nein, Fehlanzeige, keine Telefonnummern oder Emailadressen im whois. Nur eine Postadresse in London. Hach, raten wir ne mailadresse per telnet auf port 25:
$ dig mx easyjet.de +short
10 mxhost-2.vi.net.
5 mxhost-1.vi.net.
los gehts:
$ nc mxhost-1.vi.net  25
220 mxhost-1.vi.net ESMTP Postfix
HELO mail.xxxxxxxxxxx.net
250 mxhost-1.vi.net
MAIL FROM: <postmaster@xxxxxxxxxxx.net>
250 2.1.0 Ok
RCPT TO: <webmaster@easyjet.de>
554 5.7.1 <webmaster@easyjet.de>: Relay access denied
wat?! Der für die Domain zuständige MTA weigert sich dorthin zu relayen?

Dann kann und will ich ihnen auch nicht mehr helfen...

Die betreiben da unter easyjet.de Flugbuchungen, aber weder ihr SSL-Zertifikat funktioniert, noch ihr Mailsystem?! Super!

Aber das passt zu den Buchungsmails die sie verschicken. Multipart mit text und html-Teil. Aber: Der Text-Teil ist komplett leer, im html-Teil stehen dann die Buchungsdaten.

Mittwoch, 20. Mai 2009

Nur ein kleiner Umbau...

Warum muss Murphy eigentlich so grausam sein?

Ich habe ein externes AIT1 Laufwerk ausgegraben sowie ein AIT2-Laufwerk. Nun dachte ich mir, die Laufwerke sind ja beide gehäusetechnisch kompatibel, sogar die Dipswitches sind an derselben Stelle, also baue ich das AIT2-Laufwerk in das externe Gehäuse ein und hänge es an meinen Fileserver. Weil: Backup ist wichtig!¹

Nachdem der Umbau beendet war wollte ich die Frontblende des Gehäuses wieder einsetzen. Tja, passt nicht, denn bei dem AIT2 Laufwerk handelte es sich wohl um eines, dass in einer Lib zum Einsatz gekommen war, jedenfalls hatte es erstens keine Klappe vor dem Slot und war zweitens etwa 4 mm zu breit. Egal, zum Testen reichts erstmal - jedenfalls wenn man das richtige SCSI-Kabel findet...

Okay, nachdem das Kabel gefunden war (ein internes zwar und bestimmt zu lang, aber egal, denn zum Testen reichts erstmal...) konnte das Laufwerk in Betrieb genommen werden. Ergebnis: läuft

Nächster Schritt: Einbauen

Also heute frisch ans Werk, das Laufwerk gehört in den Server, dann ist es nicht mehr so laut, weil kein eigener Lüfter, und ausserdem ist dann weniger Geraffel und Kabelsalat im Schrank - und das Gehäuse konnte ich ja eh nicht zumachen.

Bei der Gelegenheit wollte ich auch mal schauen welcher RAM im Server verbaut ist - weil das ist definitiv zu wenig im Moment.

Also flux alle Kabel getrennt, Server aus dem Rack gezogen, *uffissderschwer*, aufgemacht, Ram ausgebaut, Riegel verglichen. Natürlich hat man keinen Kompatiblen Riegel hier und es steht auch nicht drauf was für welcher das ist, also nachher mal Google bemühen - Ramriegel neben das Notebook.

Kommen wir zum Laufwerk. Das Gehäuse wird flink zerlegt, Laufwerk raus und zum Server getragen. Nun, das Laufwerk hat zwar die Höhe eines 5 1/4 Laufwerkes, aber nicht die Breite. Gut, Einbauschienen hängen hier auch noch an einer alten SCSI Platte im Schrank, das passt schon - leider erst beim dritten Versuch.

So langsam werde ich leicht genervt, denn jetzt geht das Suchen nach SCSI Kabeln und Adaptern los - intern ist nur noch ein 50 pol. Flachbandstecker am SCSI Controller frei, das Laufwerk hätt aber gerne ein 68 pol. Anschluss. Nach einigem Suchen und dem Auffinden von 10 Stück IDE-Kabeln findet sich schliesslich alles ein.

Also Laufwerk ist eingebaut, Deckel drauf, Klappe zu - Klappe zu?! *arrgh*

Das Laufwerk ist 0,5 cm zu lang, bzw, schaut vorne aus dem Gehäuse raus.. *heul*

Egal zum testen.... also die linke Frontklappe abgebaut, Server ins Rack, und los.

Jetzt noch die Kabel anschliessen, vor allem die USB *AUTSCH*!

Meister, warum is da Saft drauf?

Hmm. Warum bekomme ich am USB-Kabel der externen Festplatte nen Stromschlag?

Mal verifizieren. *AUTSCH* - okay, da ist Strom drauf... Messgerät!

Oh, interessant: 219 Volt, 50 Herz. 20mA gegen Erde, keine Phasenverschiebung. Nur woher kommt das? Die USV ist aus, primär ist ein Schalter in der Steckdosenleiste, der ist aus, dahinter ein Verteiler, ebenfalls mit Schalter, dann die Festplatte. Alles aus. Eigentlich.

Kaltgerätekabel von der USV abgezogen: keine Spannung. Aha, daher weht der Wind?

Also mal das Voltmeter in richtung USV verschoben. An beiden äußeren Pins des Kaltgeräteausgangs liegen 219 Volt gegen Erde. Yay!

Ich hab also mal wieder den Jackpot geknackt: Der Schalter in der Steckdose schaltet also ganz offensichtlich nur einpolig ab - und natürlich, bei einer Chance von 50:50, schaltet der NICHT die Phase ab. Warum auch. Und dann sind da wohl noch irgendwelche Y-Kondensatoren in dem Schaltznetzteil der Festplatte, die sie, mangels Erde weil Eurostecker, wohl gegen die USB-Masse gesetzt haben. *seufz*

Gut, also Stecker um 90 Grad drehen und weitermachen, Zweipoliger Trenner kommt dann morgen.

Also Kabel ran und los.

pieeeeeep

Hmm. Warum piept der jetzt?

Moment, da war doch noch was - aaah, der RAM liegt noch auf dem Schreibtisch.

Ich brauch jetzt erstmal nen Kaffee!

Nach dem Kaffee werde ich wohl das Laufwerk wieder ausbauen, dafür den Ram einbauen, das AIT1 Gehäuse mit einer kleinen Trennscheibe bearbeiten und das Laufwerk da wieder einbauen. *nerv*


¹ Ein Raid ist nett, aber kein Backup, ein Fehler beim mdadm und alles ist weg - wenn man nicht wie ich Glück hat und nur 20 GB verliert, weil man den Rebuild abgebrochen hat. Details vielleicht in einem späteren Post ;)

Samstag, 9. Mai 2009

gpg: SHA1 durch SHA256 ersetzen

Es ist wieder einmal Zeit, einem alten Freund Lebewohl zu sagen.

Wir trauern um
SHA1
*1995       †2009

Viele Jahre hat SHA1 uns stets dabei unterstützt, unsere Nachrichten unverändert ans Ziel zu bringen. Als Nachfolger von SHA0, der viel zu früh von uns gegangen ist, gab uns SHA1 wieder Zuversicht. Doch letzlich mussten wir lernen, dass nichts von Dauer ist.

SHA1 - Du wirst stets unvergessen bleiben.

Die Beisetzung findet im engsten Kreise statt. Wir bitten von Beileidsbekundungen am Grab Abstand zu nehmen. Statt Blumen bitten wir um die Umstellung Ihrer Applikationen auf stärkere Hashverfahren.

In tiefer Trauer,
die Internetgemeinde.

Mehr Informationen gibts hier.

Aus diesem Artikel habe ich auch den Hinweis entnommen, wie man in GnuPG die Hash-Algorithmen 'umstellt':

Zunächst sollte man dafür sorgen, dass alle Nachrichten, die man selbst signiert, nicht mehr mir SHA1 signiert werden. Dazu fügt man an die Datei gpg.conf im .gnupg Verzeichnis zwei drei Zeilen an:

cat >>~/.gnupg/gpg.conf <<EOF
personal-digest-preferences SHA256
cert-digest-algo SHA256
default-preference-list SHA512 SHA384 SHA256 SHA224 AES256 AES192 AES CAST5 ZLIB BZIP2 ZIP Uncompressed
EOF

Danach teilt man noch den Gesprächspartnern mit, dass man gerne mehr Sicherheit hätte, indem man die Vorgaben seines Schlüssels ändert:

$ gpg --edit-key $KEYID
[...]
> setpref SHA512 SHA384 SHA256 RIPEMD160 SHA224 AES256 AES192 AES CAST5 ZLIB BZIP2 ZIP Uncompressed
> save
und den geänderten Schlüssel wieder veröffentlicht:
$ gpg --keyserver keys.gnupg.net --send-key $KEYID

PS: Die 'Vorschau' von Blogger saugt :( PPS: SHA-1 wird immer ale eine der letzten enthalten sein, weil der RFC das verlangt, aber wichtig ist die Reihenfolge - es ist dann nur noch Fallback. Update: Wie das Update des Originalartikels habe ich "default-preference-list" hinzugefügt, damit neue keys gleich entsprechend erstellt werden.

Freitag, 8. Mai 2009

obfuscated bash contest...

Guess what this (bash) one-liner does:

bc <<< `tar -c /tmp/a /tmp/b  2>/dev/null | xxd | \
awk '{print $10}' | tr -d '\n' | \
sed -n 's|^.*[0-9]\{7\}\.[0-9]\{7\}\.[0-9]\{7\}\.000000\
00000\.\(.*\)\.[0-9]\{6\}.*[0-9]\{7\}\.[0-9]\{7\}\.[0-9]\{7\}\.000000\
00000\.\([0-9]\{11\}\)\.[0-9]\{6\}.*$|\1 - \2|p' `
In my case it just printed -5... ;o)

Montag, 2. März 2009

CeBIT - ich komme...

So. Nun fahre ich also auch mal zur CeBIT. Der größten Computermesse überhaupt. Da arbeiten bestimmt nur Profis. Die besten der Besten und so...

Nur leider schaffen es diese Experten nicht, eine RFC-konforme Email zu bauen, das regt mich ja schon wieder auf:

Im Subject haben sie es ja noch hinbekommen, mit dem Encoding. Nur leider im Body nicht.

Es könnte natürlich an der Frickelsoftware liegen, die sie da benutzen ;)

X-Mailer: Microsoft CDO for Exchange 2000
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.3790.4133

Der Body ist dann jedenfalls latin1, 8bit, ohne Encoding-Angabe:

Content-Type: text/plain
Content-Transfer-Encoding: 8bit

Naja, wenigstens kein html...

Sonntag, 15. Februar 2009

ddos - Mittäter und Opfer (Update)

Tja, so kanns gehen. Eben noch rege ich mich darüber auf, dass mein Hoster manitu von einer ddos heimgesucht wird, schon darf ich feststellen, dass einer meiner Nameserver an einer anderen Distributed Denial of Service Attacke munter mitgewirkt hat. grmpf.

Aufgefallen ist mir das ganze per munin:

Okay, zwei/drei Anfragen pro Sekunde sind nicht viel, nur sieht man leider dadurch im Graphen den Rest nicht mehr. Ausserdem könnte der Angreifer ja auf die Idee kommen, auch mal mehr Requests zu schicken. Aber was will der eigentlich von mir - und wie ist er auf meinen Server gekommen?! Und zwei Anfragen - wo soll hier der DOS sein? Und warum fragt der ständig nach den Nameservern der '.'-Zone?

Was soll also der 'Mist'?

Des Rätsels Lösung: Natürlich bin ich nicht das Opfer, das war mir eigentlich klar, aber es ist fast noch schlimmer: Ich Mein Nameserver ist Mittäter und die scheinbaren Angreifer sind die tatsächlichen Opfer.

Jemand im Internet schickt gerade an massiv vielen Nameserver diese Anfragen mit gefälschtem Absender. Das Ergebnis: Nameserver die nicht ganz korrekt konfiguriert sind antworten dann freigiebig mit der Liste aller Root-Nameserver.

Bei wirklich großen Nameservern fallen diese Anfragen vermutlich nicht mal auf, weil sie im Hintergrundrauschen untergehen, so dass nur wenige Admins den Missbrauch ihrer Systeme überhaupt bemerken werden.

(Die Liste der Root-Nameserver gibt es hier oder beim Nameserver Eures Vertrauens per $ dig . NS @nameserver. )

Warum ist das nun ein Problem? Ganz einfach: der Angreifer schickt ein paket mit 40 bytes los, das Opfer erhält aber eine Antwort, die um ein vielfaches größer ist. Die Nameserver fungieren also als Verstärker des Angriffs. Schade eigentlich.

Selbst ein korrekt konfigurierter Nameserver, der die Anfrage ablehnt, antwortet noch mit der Ablehung, trägt also immernoch ein bischen zum DOS bei.

Gegenmassnahmen?

Es sind mehrere denkbar:

Einerseits haben die Anfragen alle denselben Inhalt und sind daher alle 40 byte groß. In 40 byte kann man auch sonst kaum eine sinnvolle Anfrage unterbringen, also kann man alles was per udp auf port 53 ankommt und weniger als 45 byte Größe hat blocken. Damit antwortet man sogar nicht mal mehr mit der Ablehnung und die Anfragen sind aus den logs. Man sollte nur seinen Provider und primäre/sekundäre Nameserver der eigenen Domains whitelisten, für den Fall, dass der Angreifer mal diese als neues Ziel definiert.

Dann könnte man ein rate-limit per IP einführen, so dass bei mehr als 20 Anfragen in 20 Sekunden der port 53 für diesen Host für einige Zeit gesperrt wird. Auch hier: Whitelisten nicht vergessen.

Oder man kann regelmässig seine Logs durchgehen, wer denn nun schon wieder soviele Anfragen produziert, und dann diese IP's für port 53 sperren.

Achja, und ganz wichtig: Das Secure BIND Template umsetzen und die default policy der BIND-Installation unter debian ändern, damit man auf die Anfragen nicht die Liste liefert² ;)

In dem Template werden übrigens Views für verschiedene Hostgruppen definiert. Sehr schick - und ich habe auch endlich welche in meinen Configs definiert. Wollte ich schon lange machen.

Damit kann man unterschiedlichen Hostgruppen per acl unterschiedlich antworten. Also z.B. allen hosts aus dem Firmennetz die privaten Adressen und Namen auflösen und mit demselben Nameserver von aussen diese Anfragen nicht erlauben oder eine komplett andere Antwort geben.

Damit könnte man auch mit einem einzigen Nameserver ein Wlan derart gestalten, dass alle noch nicht authentifizierten Benutzer auf die Anmeldeseite gelangen, wo sie einen vpn-Client herunterladen können und später - ohne Ändern der resolv.conf¹ - mit der IP die sie innerhalb des VPN's haben korrekte Namensauflösung vom Nameserver erhalten. (Aber das ist ein anderes Thema...)


¹ Es ist sehr eklig, denn der DHCP-Client im Wlan andere Nameserver einträgt, als der VPN-Tunnel braucht, dann kloppen sich DHCP-Client und VPN-Client nämlich schön darum, wem die resolv.conf gehört. Läuft die DHCP-Lease ab, trägt der DHCP-Client 'seine' DNS-Server ein und man landet wieder nur auf der Startseite für das WLAN mit dem Hinweis, man solle sein VPN aktivieren. Arrrgh.

² Einer meiner anderen Nameserver, der auf ein minimales gentoo aufsetzt, hat interessanterweise diese Anfragen von Anfang an refused, obwohl die Konfiguration nicht wesentlich von der auf dem debian abwich. Grummel.


Nachtrag: Wirksame Gegenmassnahme

Tja, auf die Einfachsten Dinge kommt man zum Schluss: Offenbar sehen die Anfragen ja immer identisch aus, also kann man einfach die Anfrage als solche blocken:

 iptables -I INPUT -i eth0 -p udp  --destination-port 53 \
       -m string --algo kmp --from 30 --hex-string "|010000010000000000000000020001|" -j DROP
und fertig ist die Laube... Ab Position 30 fängt der eigentliche Query an.
0100 - standard Query mit Recursion (1)
0001 - Eine einzelne Frage ist enthalten
Gegen Ende dann:
0002 - NS wird angefragt
0001 - Class IN

Dienstag, 20. Januar 2009

nur ein Monat...

19.12.2008: Bundesrat nickt BKA-Gesetz endgültig ab
20.01.2009: BKA-Chef will Bundestrojaner auch gegen organisierte Kriminalität einsetzen

Im Februar dann gegen alle per EDV begangene Taten, wie üble Nachrede in einem Forum?

Freitag, 9. Januar 2009

große pdf-Dateien mittels gimp und LaTeX verkleinern

Alles fing damit an, dass ich 10 Seiten Unterlagen von einer Bekannten kopiert haben wollte. Sie wohnt nicht in Berlin, also auf den Kopierer werfen ging nicht so einfach.

Sie hat aber einen Scanner und eine Software, die aus den Scans offenbar zusammenhängende pdf-Dateien erzeugen kann. Soweit so gut.

Als ich das pdf dann von ihrem Webspace gezogen habe, wurde ich ob der Größe der Datei schon einmal stutzig - war die Datei doch 38 Megabyte groß.

Ausserdem hatten die Scans schwarze Ränder - sowas mag ich beim Ausdruck nicht haben.

Erster Versuch

Man ist ja erfinderisch, also was tut man, richtig, man importiert das pdf erstmal in gimp und sagt, dass man keine einzelnen Ebenen pro Seite sondern eigene Bilder pro Seite haben will.

Mein Notebook knallte daraufhin seine 2GB Ram und 1GB SWAP zu und die CPU (Dualcore, 2,4 GHz) war auch beschäftigt. Gut, also war das keine gute Idee, alles auf einmal zu öffnen...

# killall gimp

Zweiter Versuch

Gut, versuchen wir es nur mit Seite 1 und 2. Klappt, wunderbar. Mist, 100 dpi sind zu wenig, da kann man nichts mehr lesen. Also nochmal mit 600 dpi.

krrrzzzsss Ah - er swappt. grummel...

Auch wenn wieder 1,2GB meines Arbeitsspeichers von gimp belegt waren, konnte ich die Bilder erfolgreich im Kontrast anpassen, gerade rotieren, schwarze Ränder abschneiden und die Palette von RGB auf schwarz-weiss ändern.

Das ganze als png gespeichert und aus gimp ausgedruckt. Die Druckjobs hatten alle unter 500kb.

Mein HP Laserjet 2200 (Postscriptfähig) brauchte dazu dann aber doch pro Seite 13(!) Minuten. *argh*

Das habe ich auch durchgezogen und 130 Minuten lang Seiten gewendet, weil doppelseitig drucken geht bei Einzelseiten nicht sooo gut.

Dritter Versuch

Heute morgen hatte ich dann eine hervorragende Idee: Latex kann doch Grafiken einbinden und ein pdf erzeugen!

Eine kurze Nachfrage im IRC channel #latex auf freenode ergab den Tipp, die png-Dateien nach pdf umzuwandeln und dann als einzelne Seiten einzubinden. Klappte via 'pdfpages' super!

Die Umwandlung habe ich dann sogar per Webinterface gemacht, es waren keine kritischen Unterlagen. Danke nochmal tlhiv ;) (URL: http://www.tlhiv.org/rast2vec/)

Die resultierende pdf-Datei hatte dann noch 2,2MB und war innerhalb von 3 Minuten gedruckt. Komplett, alle 10 Seiten. Und dafür hab ich die Nacht durchgemacht (unter Anderem) - aargh

Für alle, die es interessiert, hier ein Beispiel LaTeX-Schnipsel dazu:

\documentclass[a4paper,10pt]{report}
\usepackage[utf8]{inputenc}
    \usepackage[pdftex]{color}
    \usepackage[pdftex]{graphicx}
\pdfcompresslevel9
\newcommand{\checkpage}
{
\thispagestyle{empty}
} 
\usepackage[T1]{fontenc}
\usepackage{pdfpages}
\pagestyle{empty}
\begin{document}
\setlength{\topmargin}{-0.5cm}
\includepdf[pagecommand={\thispagestyle{empty}}]{p1.pdf}
\includepdf[pagecommand={\thispagestyle{empty}}]{p2.pdf}
\end{document}