Secure Shell ssh Möglichkeiten und Gefahren

Der beliebte Zugang zu Remote-Hosts hat auch gewaltige Fallstricke

Dank Corona und seinen Lockdowns durfte bzw. musste ich mich in die Tiefen der SecureShell openssh einarbeiten. Ich nutze diese Möglichkeit des entfernten Zugriffs auf ein System schon fast so lange, wie ich Linux nutze… Bald werden das 2 Jahrzehnte.

Jetzt jedoch durfte ich lernen, dass die ssh auch gewaltige Fallstricke beinhaltet. Bisher nutzte ich die ssh nur um von einem Laptop im LAN auf einen anderen zuzugreifen, oder einen Server im Firmen-LAN zu erreichen. Mein letzter Job brachte es mit sich, dass ich auch mal auf Kundensysteme “draußen” musste… Da hab ich mir noch nicht so viele Gedanken über die Sicherheit gemacht, da “ssh ja sowieso sicher ist. Und jedenfalls VIEL besser als telnet”… dachte ich. “ForwardAgent” stand immer auf “yes”, weil das klingt gut… ohne zu wissen, was ich damit eigentlich mache.

Ich lernte zwar, dass Username/Passwort-Authentifizierung nicht gut ist, aber praktisch. PubkeyAuthentication war zwar meistens aktiviert, aber genutzt hab ich es nicht immer.

Dann lernte ich, dass ein Unternehmen zwar ISO 9001 zertifiziert sein kann, es voll wichtig ist, dass man Windows einsetzt, damit der Bildschirm sicher und vom Domain-Admin kontrolliert nach 10Minuten gesperrt wird… es aber gleichzeitig egal ist, dass das VPN zum einen Kunden nicht vom VPN zu anderen Kunden getrennt ist (und somit Kunde A zugriff auf das interne Netz von Kunde B bekommt…). Seit ich das mitgekriegt habe, halte ich von dieser Zertifizierung noch weniger, als ich es vorher schon tat.

Nun ja, ich bin nun bei Kunde C angelangt. Hier werden sensible Daten verarbeitet. (Eh wie bei jedem Kunden). Dieser Kunde legt aber großen Wert auf Security. Und das ist gut. Denn so durfte ich - wie schon geschrieben - in die Tiefen von openssh eintauchen, und wie man dies sicher nutzt (machen ist das falsche Wort… den openssh ist sicher, es bietet jedoch viele Möglichkeiten, es unsicher zu nutzen und damit viel Schaden anzurichten).

PubkeyAuthentication yes

Der wohl wesentlichste Punkt bei der Nutzung von ssh ist die ausschließliche Nutzung von Public/Private-Key Authentifizierung. Man konfiguriert den openssh-Server so, dass ein User (und sei es root) ausschließlich mittels Pubkey reinkommt. Wie man ein Public-Private-Keypärchen erstellt, will ich jetzt nicht erläutern. Dazu gibts genügend Anleitungen im Netz. Z.B. [url=https://www.heise.de/tipps-tricks/SSH-Key-erstellen-so-geht-s-4400280.html]hier[/url] und [url=https://git-scm.com/book/de/v2/Git-auf-dem-Server-Erstellung-eines-SSH-Public-Keys]hier[/url] und [url=https://www.uni-bremen.de/zfn/weitere-it-dienste/serverhousing-webhosting/virtuelle-server/ssh-key-erzeugen]hier[/url]. Ich persönlich präferiere ecdsa bzw. ed25519-Keys gegenüber RSA-Keys. Zu RSA-Keys sei gesagt, dass ein Key mit 4096 Bit nicht doppelt so sicher wie einer mit 2048Bit ist, sondern nur eine um ~5% höhere Entropie hat. Der größere Key verbrät also deutlich mehr Rechenleistung beim ver/entschlüsseln (was auch Zeit kostet), bei einem Sicherheitsgewinn von lediglich 5%. Wenn man es also “sicherer” haben will, steigt man auf elliptische Kurven um.

Hardware-Token statt Softwarekeys

Hat man seinen Private-Key am Rechner liegen (also so erzeugt, wie im Schritt davor genannt), dann kann das unter umständen problematisch sein. Das Publickey-Authentifikations-Verfahren ist prinzipiell sicher (solange es noch keine brauch- und leistbaren Quantencomputer gibt. Das muss ehrlicherweise hinzugefügt werden!). Aber wenn dein Rechner kompromittiert wurde, dann weißt du das in der Regel nicht sofort… aber der Angreifer kann und wird sich Zugang (also root…) zu deinem ~/.ssh Verzeichnis verschaffen und alle deine Private-Keys stehlen. Hat jemand deinen Private-Key (und auch noch deine ~/.ssh/config), kommt er als “du” auf alle deine Systeme, kann dort Unfug oder Unheil stiften, und in den Logfiles wird stehen, dass DU es gewesen bist. Also gilt es, seinen Private-Key besonders zu schützen. Ich verwende dazu einen Harware-Token. Ob es ein eToken, ein Yubikey, ein Nitrokey oder sonst irgend einer ist, bleibt dir überlassen. eToken benötigen zwar ein proprietäres Modul und eine gesonderte Konfiguration für opensc (muss von dort ausgenommen werden, sonst wirst du nicht mehr glücklich beim Browsen im Netz), aber prinzipiell funktioniert eToken super. Den Nitrokey bekomme ich erst geliefert (Nitrokey 3 ist auch von der Microchip-Krise betroffen… bestellt im Februar 2021 mit Lieferung 2 Quartal… Jetzt ist das 3. Quartal vorbei, und die Microchips sind noch nicht einmal bei Nitrokey eingetroffen… Bin schon gespannt, wie sich das noch weiterentwickelt), und dann nutze ich Nitrokeys.

Im prinzip ist so ein Harware-Token nix anderes als ein deutlich sichererer Aufbewahrungsort für den Private-Key eines ssh-Public-Private-Keypärchens, als deine Festplatte. Den Key hängst du dir an deinen Schlüsselbund und gehst genauso sorgsam um damit, wie mit deinem Haustürschlüssel oder dem der Türschlüssel des Serverraums deiner Firma.

Aus so einem Token ist der Privatekey nicht extrahierbar. Mit anderen Worten, du kriegst ihn nicht raus von dort. Weder mit Software, noch wenn du den Schlüssel in Säue oder Base einlegst, so die Kunststoffbestandteile wegätzen versuchst um ihn direkt aus der Hardware auszulesen… (es gab von Yubikey, wenn ich mich nicht irre einmal einen, bei dem gelang der Säureangriff… aber auch das ist mittlerweile gefixt). Auf deine Server kommst du NUR, wenn dein Key im Rechner eingesteckt ist. Fertig.

An deiner SSH-Config ändert sich so gut wie nichts wesentliches. Statt des Private-Keys als “IdentityFile” gibts du den Pubkey des Hardware-tokens an. Fertig. Du kannst weiter auf deine Server zugreifen, genauso wie wenn du den Software-Key nutzt. Nur wirst du nach dem PIN (so nennt sich das Passwort in der Regel bei den Hardware-Tokens) des Tokens gefragt, statt dem Passwort für den Private-Key. Du hast doch deinen Private-Key mit einem Passwort erzeugt? Oder?

Ja, und weil du bei JEDER Verbindung nach dem Passwort gefragt wirst, setzen es viele (mich früher mit eingeschlossen) auf “”. Also kein Passwort. Keine Gute Idee beim Token. Gut bei einem Token muss immerhin der Token im Rechner stecken… Aber beim Softwarekey kann wiederum JEDER Angreifer deine Identität nutzen um als DU auf die Systeme zu gelangen.

Beim Software-Key hast du als 2. Faktor “Wissen”. Du musst das Passwort wissen. Beim Token kannst du 3 Faktoren haben. Der 1. ist (wie beim Softwarekey) der Key selbst. Der zweite Faktor ist “Haben”. Du musst den Hardware-Token besitzen und im Gerät haben, damit der 1. Faktor überhaupt zugänglich ist, und der 3. Faktor ist “Wissen”… nämlich den PIN/Passwort.

Da muss man dir schon deinen Schlüsselbund fladern, und dir irgendwie das Passwort für den Token entlocken… Wenn du Geheimagent bist, kann dich das schon mal das Leben kosten… Aber als “normaler” Sysadmin ist dieser Angriffsvektor eher unwahrscheinlich…

Du bist mit PubkeyAuthentication = yes und Private-Key am Hardware-Token also schon mal gut auf der sicheren Seite. Selbst ein Trojaner oder Virus auf deinem Rechner kann dir den Private-Key nicht entlocken. Und das ist gut.

Selbstverständlich benötigst du einen 2. Hardware-Token als Fallback/Backup in deinem Safe oder abgeschlossenen Schublade… Ein Token ist auch nur “Zeugs”. Und “Zeugs” geht nun einmal kaputt und verloren… und dann hast du die lange Nase, wenn du kein Hardware-Backup hast. Du hast ja 2 Token bestellt, und nicht nur einen. Oder?

ssh-agent

Der ssh-agent ist ein wenig bekanntes Helferlein. Wenn dich das andauernde Eintippen des Passwortes für deinen Private-Key nervt, dann deaktiviere es nicht, sondern setze dich mit dem ssh-agent auseinander. Der ssh-agent ist ein Programm, welches normalerweise schon mit deiner Desktop-Umgebung gestartet wird. Genauergesagt, der ssh-agent startet deine Desktop-Umgebung. Damit ist ein ssh-Key in jedem von der Desktop-Umgebung gestarteten Programm verfügbar. Du musst bloß deinen Key mit

ssh-add /path/to/privkey

bzw. konkret

ssh-add ~/.ssh/id_ed25519

zum Agenten hinzufügen. Verwendest du einen HW-Token, dann legst du den Public-Key des Private-Keys in dein ~/.ssh/-Verzeichnis und fügst den Key aus dem HW-Token mit

ssh-add -s ~/.ssh/id_token.pub

hinzu.

Damit bleibt dein Private-Key auf immer und ewig (zumindest bis zum nächsten Logout oder Reboot) in deinem ssh-agent. Was praktisch ist, denn du gibst einmal das Passwort ein, und kannst den PublickKey bei deinen ssh-Verbindungsversuchen verwenden, als ob der zugehörige Privatekey kein Passwort hätte… Und dann loggst du dich einmal ein, und erst nach 3 Monaten wieder einmal, weil du nach dem Update den Rechner neu booten musstest… Du vergisst das Passwort, und bist ausgesperrt… oder jemand hat deine Sitzung übernommen (weil du den Bildschirm nicht gesperrt hast), und hat Unfug oder Unheil auf einem Zielhost von dir angerichtet… und DU bist schuld… Also musst du dem ssh-agent irgendwie mitteilen, dass er den Key nach einer bestimmten Zeit raushaut. Je sensibler die Zielsysteme sind, desto kürzer wähle die Zeit. Bei den oben genannten Befehlen fügst du dann eine Zeitangabe (in Sekunden) hinzu.

ssh-add -t 7200 ~/.ssh/id_ed25519

Ein zusätzliches “-c” verlangt bei jeder Nutzung des Keys die Eingabe des Passwortes. Du kannst jeden Key mit anderen solcher Optionen laden.

Wichtig für den ssh-agent ist zu wissen, dass IMMER der ssh-agent für die Verbindung genommen wird, dessen Pfad zum Agent-Socket im Environment der Sitzung in der Variable $SSH_AUTH_SOCK gespeichert ist.

Da z.B. die Gnome-Session durch den ssh-agent gestartet wird, ist im Environment der Gnomesession diese Variable auf /run/user//keyring/ssh für deinen User gespeichert. Machst du ein Terminal auf, erbt dieses diese Variable von der Gnome-Session, und du kannst den Default-Agent nutzen. Probiere es aus, wie die Variable bei dir gesetzt ist

echo $SSH_AUTH_SOCK

Und dann sieh nach, welche Keys darin schon geladen sind

ssh-add -L
# bzw.
ssh-add -l

dann lösche die Umgebungsvariable mit

unset SSH_AUTH_SOCK

und sieh erneut nach, welche Keys darin geladen sind. Du wirst eine Fehlermeldung bekommen

$ ssh-add -l
Could not open a connection to your authentication agent.

weil kein Agent verfügbar ist.

Ist dein Key im agent geladen, und ist der Agent über das Environment für ssh verfügbar in der Shell, nutzt ssh bei jedem Verbindungsversuch den Agent. Du kannst in deiner ~/.ssh/config im letzten Block

Host *
    IdentityFile ~/.ssh/id_ed25519

oder

Host *
    IdentityFile ~/.ssh/id_ed25519.pub

eintragen. Dann sieht ssh bei jedem Verbindungsversuch im Agent nach, ob es einen Privatekey geladen gibt oder such zum angegebenen Publickey ob der private-Key im Agenten vorhanden ist. Ersteres passt bei Softwarekeys gut, zweiteres verwendest du bei HW-Token.

Du musst, solande der Key im Agent geladen ist, bei keinem Verbindungsversuch mehr das Passwort für den Private-Key bzw. den PIN für den Token angeben. Und dieser bleibt trotzdem passwortgeschützt. Cool. Oder?

Aber was macht jetzt ForwardAgent?

ForwardAgent yes

Dieser ssh-agent ist ein Schlüsselbund. Den kannst du daheim liegen lassen, oder mitnehmen. Mit “ForwardAgent no” in ~/.ssh/config lässt du ihn daheim, mit “yes” nimmst du ihn mit auf deinen Zielhost.

Trägst du es in die letzte Section mit “Host *” ein, gilt es für ALLE Hosts, außer es wurde bei einem Host zuvor anders definiert. ssh parst die Config-Datei(en) immer von oben nach unten. Alles was schon gesetzt wurde, wird NICHT überschrieben. Daher ist es sinnvoll, am Ende der Config-Datei einen Block mit

Host *

einzufügen, wo du dann die Default-Werte für deine Bedürfnisse anpasst, und weiter oben in der Config für einzelne Hosts von diesen Defaults abweichende Werte einsetzt.

Für ForwardAgent z.B. definierst du zum Schluss

Host *
   ForwardAgent no

Willst du auf HostA aber den Agent mitnehmen dann sieht die Config so aus:

Host hosta
    Hostname hosta.domain.tld
    ForwardAgent yes

Host hostb
    Hostname hostb.domain.tld

Host *
    ForwardAgent no

Auf hostb gilt, dass der agent nicht mitgenommen wird (und für alle anderen host ebenfalls). Nur auf Host A (hosta) wird der Schlüsselbund ssh-agent mitgenommen.

Damit kannst du jetzt folgendes anstellen. Deine Firma gewährt dir von daheim aus Zugriff auf einen Jumphost. Und sonst nirgends hin. Vom Jumphost kannst du dann weiter auf alle deine Zielsysteme.

Der Jumphost erlaubt aber kein Login, sondern bloß Authentifizierung.

Daher benötigst du am Jumphost keinen ssh-agent. Aber auf deinem Desktop in der Firma schon. Du loggst dich also mit

ssh firmenpc

ein.

Die Config sieht so aus:

Host jumphost
    Hostname jump.meinefirma.tld
    PubkeyAuthentication yes
    ForwardAgent no
    User <username am jumphost>

Host firmenpc
    Hostname <Interne IP des Firmenpc>
    ProxyJump jumphost
    ForwardAgent yes
    User <username am firmenpc>

ssh steuert nun zuerst den Jumphost an, authentifiziert sich dort. Ist das erfolgreich, hüpft es auf deinen Desktop weiter und authentifiziert sich dort mit den dort passenden Credentials. Du kannst für jeden Jumphost ein eigenes IdentityFile verwenden, oder auch - wenn so konfiguriert - mit Passwort-Authentifikation arbeiten, und beliebig viele Jumphost in einer Kette angeben. Aber den ssh-agent aus deiner Sitzung nimmst du nur auf deinen Desktop in der Firma mit. Der bekommt dort ein eigenes Socket-File in einem temporären Verzeichnis, auf das nur du und root Zugriff hat.

Und damit sind wir beim nächsten Problem. Über den Socket (den ssh forwarded) kannst du vom Zielsystem aus auf den ssh-agent direkt zugreifen, der auf deinem Laptop vor dir läuft. Und du bekommst damit auf alle Private-Keys zugriff, die auf deinem Laptop vor dir im Agenten geladen sind. Und das kann JEDER, der auf den Socket am Zielrechner Zugriff hat. Also root. Oder andere, wenn du die Dateirechte mutwillig oder unabsichtlich veränderst.

Auf deinem Firmenpc wirst du wahrscheinlich der einzige User sein, der darauf physischen oder remote-Zugriff hat. Verbindest du dich vom Firmenpc aber auf einen Server und nimmst mittels “ForwardAgent yes” deinen ssh-agent dorthin mit, wirst du nicht mehr der einzige User mit root-Rechten dort sein. Und ein Kollege der Unfug oder dir Unheil stiften möchte, sieht welchen Socket du geforwardet hast, kann sich mittels Root Zugriff darauf verschaffen, und sich so mit deiner Identität auf Systeme Zugriff verschaffen, auf die er nicht, du aber schon darfst.

Wie Eingangs erwähnt gibt es Umgebungen, wo du mehrere Kunden und deren Systeme betreuen darfst. Verwendest du auf allen Systemen den selben Key, ist die Konfig natürlich einfach. Ein Pubkey/Privkey-Pärchen, und du bist auf allen Systeme. Du musst nicht lange nachdenken, wo du welchen Public-Key deployst.

Beim Kunden, der Interesse an den Banksystemen hat, auf die du auch zugreifst, ist der $BÖSE_ADMIN dann in der Lage, dir deinen Private-Key auf SEINEM System über den ssh-agent zu entreissen, um damit auf die Banksysteme die du auch betreust, zu gelangen. Weil er als root Zugriff auf deinen ssh-agent bekommt. Weil du ihn über den Jumphost auf deinen Firmenpc und von dort auf den Server X mitgenommen hast, obwohl du von Server X gar nicht mehr weiter hoppen müsstest. Deshalb auch als Default “ForwardAgent no” und nur dort aktivieren, wo du es wirklich benötigst.

Hast du jetzt klugerweise unterschiedliche Keys für unterschiedliche Kunden/Systeme, und lädst sie trotzdem alle in den selben ssh-agent… geht das Spiel genauso weiter… Du hast keinen Sicherheitsgewinn. Also lass deinen ssh-agent daheim, wenn er nicht unbedingt erforderlich ist, oder lade nur jene Keys in den ssh-agent, die du am Zielsystem benötigst.

Wie du automatisiert für jede Verbindung(sgruppe) einen eigenen Agent erzeugen/starten kannst, und diesen Agent nur für die entsprechenden Verbindungen nutzt, schreibe ich in einem der nächsten Beiträge in diesem Blog auf. Das Stichwort ist “IdentityAgent” in der ssh-config in einer “Match-Section” Ich habe mir einen Satz an Scriptchens geschrieben, die mit wohldefinierte ssh-Agents erzeugen, die dazugehörigen Identities hinein laden und dann nur für diese Verbindung verwenden. So kann ich gleichzeitig zu verschiedenen Zielsystemen eine ssh-Verbindung aufbauen, und bei jeder Verbindung (wenn es sein muss) einen eigenen ssh-agent mit eigenen Keys darin geladen verwenden. So stelle ich sicher, dass nie der falsche Key irgendwo landet, wo er nicht hin soll.

Und in einem weiteren Beitrag beschäftige ich mich mit der Zertifizierung von Public-Keys. Sowohl für den User, als auch den Host.