PHPGangsta - Der praktische PHP Blog

PHP Blog von PHPGangsta


Dieser Schlüssel zerstört sich in 30 Sekunden selbst

with 11 comments

Gastartikel von Oliver Sperke.

Ich bin 34 Jahre alt und seit 10 Jahren selbständiger Webentwickler. Mein Fokus liegt dabei auf der Erstellung, Beratung und Optimierung in den Bereichen High Performance, Usability und Sicherheit in den gängigsten Internetsprachen: PHP, HTML, Javascript und CSS.

Nachdem ich ja in den letzten Wochen über den sicheren Umgang mit Passwörtern in Webprojekten erzählt habe, soll es heute mal um etwas anderes gehen (aber trotzdem um das Thema Sicherheit). Wenn man oft auf fremden oder eigenen Servern arbeitet, kommt man an OpenSSH eigentlich nicht vorbei. Und wenn man dazu noch so faul ist wie ich (und vermutlich die meisten von Euch auch), hat man keine dutzenden Dateien mit Passwörtern, sondern wickelt die gesamten Loginvorgänge mit Hilfe von öffentlichen und privaten Schlüsseln ab. Dieses Verfahren ist nicht nur bequem, es bietet auch zusätzliche Sicherheit, da man nicht in Versuchung kommt, einfache Passwörter zu verwenden. Ausserdem ist die eigene Passwortliste schon von Haus aus verschlüsselt.

Das Verfahren hat aber für einen Serverbetreiber Nachteile. Der öffentliche Schlüssel muss auf dem Server hinterlegt werden. Ausserdem muss man sobald der Zugang nicht mehr benötigt wird, nachsehen, ob ein Benutzer sich vielleicht eigene Schlüssel hinterlegt hat und natürlich muss man ihm den Zugang manuell sperren.

Alle diese Vorgänge sind lästig und zeitaufwendig und bergen Gefahren, vor allem dann, wenn man die Zugänge von vielen Benutzern verwalten muss. Schnell hat man eine Zeile in der „authorized_keys“ Datei für einen Benutzer übersehen und handelt sich damit dauerhaft ungebetene Gäste ein. Seit OpenSSH 5.6 gibt es nicht nur die Möglichkeit, öffentliche und private Schlüssel zu hinterlegen, sondern den öffentlichen Schlüssel zu signieren. Dieses Verfahren der Signierung oder Zertifizierung bietet uns gewaltige Vorteile.

Der Server kann bei einer Signierung erkennen, ob ein Schlüssel nur für einen gewissen Zeitraum gültig sein soll oder schon abgelaufen ist und ob gewisse Rechte, z. B. zum X11 Forwarding oder Port Weiterleitung erteilt wurden oder nicht. Ausserdem müssen wir die Public Keys der Nutzer nicht mehr hinterlegen und unser Privatekey ist quasi unangreifbar. Dieses Verfahren benötigt fertig eingerichtet nicht mal mehr einen Login auf dem Server zur Erstellung neuer Zugänge.

Wichtiger Hinweis und Ausgangssituation

Lest bitte erst den ganzen Text, damit Ihr nicht in der Mitte stecken bleibt. Wenn Ihr nicht regelmäßig auf Linuxsystemen arbeitet, dann richtet euch eine virtuelle Maschine ein, die Ihr nach Lust und Laune kaputt konfigurieren könnt. Das Verfahren nutze ich selbst auf meinen Servern, wenn Ihr Euch trotzdem aussperrt, dann ist das nicht meine Schuld, bei mir geht es ja. Man kann das Verfahren auch auf einem Rechner testen.

Falls Euer Zielrechner nicht greifbar für Euch ist, also irgendwo im Rechenzentrum steht, lasst die Passwortauthentifizierung an und loggt Euch zusätzlich auf einer anderen Konsole ein. Lasst das Fenster offen. Sollte etwas schief gehen, könnt Ihr es in diesem Fenster immer noch reparieren. Diese Anleitung richtet sich eindeutig an erfahrende Nutzer und ist nichts für Hobbyadmins und „Menschen mit schwachem Herz“.

Server und Client sind bei mir beide Linuxrechner – für Windows bin ich kein Fachmann. Da aber OpenSSH auch unter Windows arbeitet, dürfte die Konfiguration letzendlich in etwa die gleiche sein.

Alle Einstellungen sind lediglich Vorschläge und nicht verbindlich. Verbesserungen sind natürlich willkommen, eigenes Nachdenken erwünscht. Als Testsystem habe ich Debian Squeeze auf dem Server und bei mir zu Hause ArchLinux. Bei Debian muss der SSH Server aus dem „unstable“ Zweig installiert werden (libssl, openssh-server, openssh-client), da es dort noch kein aktuelles Paket in testing oder stable gibt. Als Mindestversion empfehle ich OpenSSH 5.8, Version 5.6 sollte aber theoretisch auch funktionieren.

Ich arbeite zunächst als root während der Einrichtung bis ich etwas anderes sage. Wer das nicht möchte, muss überall sudo vor die Befehle schreiben. Sollte noch kein SSH Server und/oder Client installiert sein, fangen wir damit an.

Auf dem Server (root)

apt-get install openssh-server openssh-client libssl

Auf dem Client (root)

apt-get install openssh-client

Nach kurzer Wartezeit ist unser SSH Server fertig eingerichtet und gestartet.

Was werden wir tun?

  1. Grundlegende (sichere) Konfiguration des SSH Dienstes
  2. Sperren des SSH Login für root
  3. Anlegen und Einrichten eines neuen Benutzers zum Login
  4. Zugang für diesen Benutzer nur mit gültigem signiertem Schlüssel

Die Konfiguration

Zunächst sichern wir die aktuelle Konfiguration. Dann editieren wir die sshd_config mit nano (joe, vi usw. gehen natürlich auch).

Auf dem Server (root)

cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
nano /etc/ssh/sshd_config

Innerhalb der Datei ändere ich die Einstellungen unter dem Punkt Authentification auf die angegebenen Werte. die LoginGraceTime kann auf 10 Sekunden herunter gesetzt werden, da ein Zugriff mit Passwörtern nicht genutzt wird und meine Schlüssel vom ssh-agent verwaltet werden. Den root Login verbiete ich, denn ich will es „Brute Forcern“ etwas schwerer machen. Die Einstellungen für StrictModes, RSAAuthentication, PubkeyAuthentication bleiben gleich. PasswordAuthentication setze ich hier schon auf no (siehe Hinweise). Wenn Ihr wollt, könnt Ihr auch mit AllowUsers user1 user2 usw. eine „Whitelist“ für erlaubte Loginnamen fest legen. Das bietet aber auch nicht mehr Sicherheit – nur die Fehlermeldung ändert sich.

Die Werte für AuthorizedKeysFile, AuthorizedPrincipalsFile und TrustedUserCAKeys fügen wir hinzu. In der AuthorizedKeysFile stehen normalerweise die Publickeys der Benutzer. Da wir die aber hier nicht brauchen, ändern wir den Wert auf /dev/null. So kann sich kein Benutzer eigene Zugänge hinterlegen. TrustedUserCAKeys ist der öffentliche Teil unseres Zertifikats. In der AuthorizedPrincipalsFile stehen alle Benutzer, die in den Zertifikaten hinterlegt werden. Dazu später noch mehr.

# Authentication:
LoginGraceTime 10
PermitRootLogin no
StrictModes yes
RSAAuthentication yes
PubkeyAuthentication yes

AuthorizedKeysFile /dev/null
TrustedUserCAKeys /etc/ssh/trusted_ca_keys
AuthorizedPrincipalsFile %h/.ssh/authorized_ca_user

PasswordAuthentication no

Die Einstellungen übernehmen wir mit einem SSH Neustart. Wenn Ihr Euch nicht sicher seid, wartet noch. Die Änderungen könnt Ihr jederzeit rückgängig machen, indem ihr die .bak Datei zurück kopiert und dann ssh neu startet.

Auf dem Server (root)

/etc/init.d/ssh restart

Der authorisierte Benutzer

Als nächstes legen wir einen neuen Benutzer an. Den nenne ich jetzt einfach mal login, weil er nur dafür da ist, sich auf dem Server anzumelden. Bevor Ihr versucht anonsphere.com zu brute forcen – da hab ich nen anderen Benutzer – ausserdem werde Ihr nach 3 Versuchen gebannt. 🙂 Die zweite Zeile ist optional. Ihr könnt natürlich auch ein Passwort für den Benutzer hinterlegen, aber ich habe keine Lust, dass jedesmal einzugeben. Wer sich als dieser Benutzer einloggen kann, könnte eh unbemerkt keylogger o. ä. installieren. Mir persönlich reicht der abgesicherte Zugang. Wenn der Benutzer eingeschränkt handeln soll, also nicht zum root werden darf, ändert den Befehl von -G users,admin auf -g users

Auf dem Server (root)

useradd -c "Login" -d /home/login -G users,admin -u 1003 -m -s /bin/bash login
passwd --delete login

Der normale Schlüssel

Auf Eurem Clientsystem erzeugen wir für unseren Benutzer einen Schlüssel, falls Ihr schon einen habt, springt zum nächsten Kapitel.

Auf dem Client (Benutzer)

ssh-keygen -t rsa -b 4096 -f id_rsa

Verschiebt den private Schlüssel in den Ordner ~/.ssh/id_rsa. Der Publickey wird nicht auf dem Server hinterlegt!

Auf dem Client (Benutzer)

mkdir ~/.ssh/
mv id_rsa ~/.ssh/id_rsa

Der signierte Schlüssel

Zum Erstellen der Zertifikate brauchen wir noch ein Schlüsselpaar. Den Ablauf kennen wir ja schon. Den Inhalt des öffentlichen Schlüssel hinterlegen wir auf dem Server in der Datei trusted_ca_keys. Dann wechseln wir auf unseren Benutzer und hinterlegen die ID des Zertifikats („principal“), die erlaubt sein sollen – dieser muss nicht mit dem Benutzernamen übereinstimmen. Auf dem Server muss nur der Publickey des Zertifikats liegen.

Auf dem Client oder Server (Benutzer)

ssh-keygen -t rsa -b 4096 -f zertifikat_rsa

Auf dem Server (root)

cat zertifikat_rsa.pub >> /etc/ssh/trusted_ca_keys
su login
mkdir ~/.ssh/
echo "oliver" >> ~/.ssh/authorized_ca_user

Die einzige wichtige Datei ist die zertifikat_rsa. Diese muss gesichert werden (auch mehrfach) und möglichst weit weg von direktem Zugriff (Container, USB Stick, etc.). Alle anderen Dateien sind uninteressant! Wir haben also jetzt unsere Vorbereitungen getroffen. Als letztes muss noch Euer Publickey (aus „Der normale Schlüssel“) signiert werden. Das macht ihr mit folgender Zeile. I ist der Name der Zertifizierung und steht später im auth.log. n ist der principal, also der Benutzername, für den das Zertifikat ausgestellt ist und der in der authorized_ca_user hinterlegt wurde.

ssh-keygen -s zertifikat_rsa -I MeinLogin -n oliver .ssh/id_rsa.pub
ssh-add .ssh/id_rsa

Falls Ihr den SSH Agent benutzt, fügt danach den Schlüssel hinzu mit ssh-add .ssh/id_rsa.

Aufräumen

Jetzt könnte es passiert sein, dass die Rechte nicht korrekt gesetzt wurden bei unseren Aktionen. Dieses Problem erledigen wir mit der beliebten „Holzhammermethode“, damit unser Setup sicher ist. Wir gehen zurück auf den root Account auf dem Server und setzen dann unsere Rechte.

Auf dem Server (root)

chown -R root:root /etc/ssh/*
chmod 600 /etc/ssh/*

Auf dem Client (Benutzer)

chmod 700 ~/.ssh
chmod 600 ~/.ssh/*

Der Login sollte nun funktionieren. Wenn nicht, gibt die Datei /var/log/auth.log (Client und Server) weitere Informationen. Falls da nichts brauchbares steht, erhöht in der ssh_config/sshd_config den LogLevel auf DEBUG3. Fast immer sind irgendwo die Rechte falsch. Wenn bis hier hin alles funktioniert, können wir jetzt ein zeitlich eingeschränktes Zertifikat erzeugen. Dafür benutzen wir diese Zeile.

Auf dem Client (Benutzer)

ssh-keygen -s zertifikat_rsa -I MeinLogin -n oliver -V +5m .ssh/id_rsa.pub

Der Zugang wäre damit fünf Minuten gültig. Probiert es aus! Meldet Euch auf dem Server an, meldet Euch ab und schaut, ob Ihr Euch nach 5 Minuten noch anmelden könnt. Wenn Ihr mit dem Prinzip warm geworden seid, könnt Ihr natürlich auch fremde Publickeys signieren. Der Befehl sieht dann z. B. so aus:

ssh-keygen -s zertifikat_rsa -I Zeitarbeiter -n zeitarbeiter -V +1d fremderPubkey.pub

Neben der Zeit könnt Ihr auch seine Rechte, wie Port- oder X11-Forwarding einschränken. Mehr Infos bekommt Ihr mit

man ssh-keygen

Auf dem Server müsst Ihr dann in der Datei /home/login/authorized_ca_user auf dem Server den Benutzer zeitarbeiter in einer neuen Zeile hinterlegen. Ihr könnt jede ID mehrfach verwenden. So könntet Ihr natürlich auch Signaturen vorher entwerten, indem Ihr einen verwendeten Priincipal löscht. Um einem neuem Benutzer Zugang zu erteilen, benötigt Ihr seinen Publickey. Diesen signiert Ihr mit der zertifikat_rsa und schickt ihm die *-cert.pub Datei zurück. Kein Login nötig!

Was geht? Was geht nicht?

Mit Hilfe der Signaturen ist es nun endlich möglich, Zugänge zu Servern zu erteilen, die nach einer gewissen Zeit selbst verfallen, ab einem gewissem Zeitpunkt gültig sind oder Aktionen verbieten, ohne in der sshd_config Änderungen vorzunehmen. Das Verfahren eignet sich u. a. für die Vergabe von Serverzugängen für Wartungsarbeiten an der Datenbank oder zur Einrichtung von Software, die vom Benutzer genutzt wird. Ausserdem natürlich auch für die sichere Vergabe von SFTP oder FISH Zugänge mit denen der Benutzer seine Dateien ändern kann. Wenn jemand für ein Jahr bezahlt und nicht verlängert, haben wir keine Arbeit mehr damit. Verlängert er den Zugang, können wir ihm einfach lokal ein neues Zertifikat generieren.

Der größte Vorteil ist aber, dass Euer Privatekey quasi nicht mehr kompromitiert werden kann. Ich benutze den RSA Schlüssel nur für meine Serverzugänge. Sollte er mir aus irgendwelchen Gründen gestohlen werden und das Passwort gleich dazu, benenne ich einfach den principal um und erzeuge mir ein neues Zertifikat. Meinen Privatekey kann sich derjenige dann ausdrucken und an die Wand hängen, weil der ist für den Zugang völlig unerheblich. Wichtig ist nur die zertifikat_rsa, die muss geschützt werden und diese liegt natürlich sicher in einer verschlüsselten Datei.

Als Einschränkung müssen wir allerdings hin nehmen, dass das Verfahren bisher nur in aktuellen OpenSSH Versionen integriert ist, was evtl. den Zugang durch Programme wie PuTTY oder WinSCP erschwert. Für Windows habe ich generell noch nach keiner Lösung gesucht. Es kann also sein, dass es schon funktioniert, muss aber nicht – Infos bitte in die Kommentare. Unter Linux funktioniert das Verfahren mit allen Programmen, die auf der Basis von OpenSSH arbeiten, also eigentlich mit allen.

Einen Hinweis noch
Falls Ihr auf eure Server fail2ban einsetzt, müsst Ihr aus der Filterdatei für ssh die Zeile mit „Invalid Publickey“ entfernen oder auskommentieren, denn der ist durch den Verweis auf /dev/null natürlich immer falsch, auch wenn die Authentifizierung letzendlich erfolgreich ist (und ja, ich hatte beim ersten Mal auch nicht dran gedacht). In diesem Fall solltet Ihr auch eine Whitelist setzen, denn ansonsten werden Benutzer mit falschem Namen nicht immer gebannt.

Written by Oliver

August 1st, 2011 at 9:34 am

11 Responses to 'Dieser Schlüssel zerstört sich in 30 Sekunden selbst'

Subscribe to comments with RSS or TrackBack to 'Dieser Schlüssel zerstört sich in 30 Sekunden selbst'.

  1. Thx, bin noch am testen, aber fühlt sich gut an. Tolle Idee!

    serijoscha

    1 Aug 11 at 11:30

  2. Man sollte nur etwas bedenken:

    Wenn ich dieses Setup jetzt auf meinen zig Servern verteile und dann irgendeinen Key signiere, dann greift dieser Login leider erst einmal auf allen meinen Servern, obwohl ich doch eigentlich nur für einen bestimmten Server den Zugriff gewähren wollte…

    Igor

    1 Aug 11 at 17:24

  3. @Igor:
    Ja und Nein! Du kannst das ja mit den principals und/oder Benutzernamen „fein justieren“.

    Oliver

    1 Aug 11 at 17:27

  4. „Auf dem Client (Benutzer)
    ssh-keygen -t rsa -b 4096 -f id_rsa

    Verschiebt den private Schlüssel in den Ordner ~/.ssh/id_rsa. Der Publickey wird nicht auf dem Server hinterlegt!“

    -> Im letzten Satz sollte es da nicht „Privatkey“ heißen?

    Tobias

    5 Aug 11 at 12:24

  5. Auf dem Server liegt immer nur der Publickey, damit man sich dann mit seinem Privatekey und Passwort anmelden kann. Seinen Privatekey woanders als zu Hause in .ssh zu lagern ist eine verdammt schlechte Idee. 🙂

    Oliver

    5 Aug 11 at 12:27

  6. @Oliver: Deswegen sage ich ja das der letzte Satz hier falsch sein muss:
    „Der Publickey wird nicht auf dem Server hinterlegt!”

    Wahrscheinlich nur ein kleiner Typo …

    Tobias

    5 Aug 11 at 12:57

  7. Ja, natürlich hast Du Recht – aber nicht ganz. Das Besondere an dem Verfahren hier ist ja, dass man auch den Publickey des Benutzer nicht auf den Server packt. Dass der Privatekey da auch nicht hingehört, davon bin ich jetzt einfach mal ausgegangen, dass das jeder weiß.

    Richtig wäre, weder Public- noch Privatekey gehören auf den Server.

    Oliver

    5 Aug 11 at 13:03

  8. […] danach starten.sudo /etc/init.d/openbsd-initd startSolltet Ihr für gewöhnlich Publickeys oder Zertifikate benutzen, solltet Ihr einem priviligiertem User oder root ein Passwort verpassen, damit Ihr Euch […]

  9. […] von Anderen angewiesen. Diese sind oftmals, und das sage ich aus eigener Erfahrung, nicht so gesichert, wie man es eigentlich erwarten kann. Das ist aber auch nicht weiter verwunderlich, denn wenn die […]

  10. […] von Anderen angewiesen. Diese sind oftmals, und das sage ich aus eigener Erfahrung, nicht so gesichert, wie man es eigentlich erwarten kann. Das ist aber auch nicht weiter verwunderlich, denn wenn die […]

  11. Coole Sache. Gibt es eine elegante Lösung (Deployment) für große Netzwerke, wo hin und wieder neue Zertifikate für alle Nodes aber für versch. Personen signiert werden müssen?

    Fabian

    19 Sep 12 at 09:39

Leave a Reply

You can add images to your comment by clicking here.