Als Angestellter in der IT muss ich häufig auf entfernte Rechner zugreifen. Meistens funktioniert das mit Hilfe von SSH und Public-Key-Kryptographie. Obwohl ich Mails und Daten bereits mit meinem OpenPGP-Schlüsselpaar verschlüssele, brauche ich für den SSH-Zugang noch ein weiteres Paar. Das kann ganz schön nerven und ist unpraktisch. In diesem Artikel zeige ich, wie man seinen PGP-Schlüssel auch mit SSH nutzen kann und damit das SSH-Schlüsselpaar überflüssig macht.
Dieser Artikel ist Teil der Serie "OpenPGP im Berufsalltag". Du möchtest auch die anderen Artikel lesen? Hier geht es zur Übersicht.
Die sogenannte Secure Shell ist in der IT weit verbreitet. Sie bietet einen verschlüsselten Zugang zu einem entfernten Rechner. Meistens öffnet sich eine Kommandozeile, man kann aber auch einfach "nur" Dienste, die im eigenen Netzwerk nicht vorhanden sind, tunneln, oder Daten kopieren.
Zur Authentifizierung eines Nutzers wird heutzutage statt einer Nutzer- / Passwort-Kombination ein Public-Key-Kryptoverfahren angewandt, das ähnlich zu denen in OpenPGP verwendeten ist – beispielsweise können sowohl ein SSH-Zugang, als auch ein OpenPGP-Schlüsselpaar mit RSA umgesetzt werden.
Obwohl beide Technologien im Prinzip gleich funktionieren, sind die verwendeten Schlüsselpaare in der Praxis nicht kompatibel, da sie jeweils in unterschiedlichen Formaten gespeichert werden. SSH kann also OpenPGP-Schlüssel nicht lesen, obwohl das unterliegende Kryptosystem mit diesen ebenfalls funktionieren würde.
Aus diesem Grund ist man meist gezwungen, neben seinem OpenPGP-Schlüsselpaar noch mindestens ein zusätzliches SSH-Schlüsselpaar vorzuhalten. Dieses liegt in einem eigenen Schlüsselring und unterliegt den SSH-Sicherheitsfunktionen und -beschränkungen.
Man muss also nicht nur zwei Systeme lernen, man hat auch mit der Schlüsselverwaltung doppelte Arbeit. Insbesondere muss man auch SSH-Schlüssel für die Nutzung auf anderen Geräten mitführen bzw. kopieren.
Meine SSH-Schlüssel sind oft projektbezogen und über die Jahre tendieren sie bei mir dazu, auf Tauchstation zu gehen, d. h. ich verliere sie hier und da. In diesem Fall muss ich neue Schlüssel generieren und dem entfernten Rechner bekannt machen. Weil solche Probleme natürlich genau dann auftreten, wenn man keine Zeit dafür hat, ist das besonders nervig. Ich habe auch bemerkt, dass ich beim Generieren der Schlüssel und bei der Auswahl der Passphrase nicht soviel Sorgfalt walten lasse, wie bei meinen OpenPGP-Schlüsseln – d. h. durch diesen projektbezogenen "Mal eben"-Charakter kann unter Umständen auch die Sicherheit gefährdet sein.
Schön wäre es also, wenn man die so sorgfältig gepflegten PGP-Schlüssel auch für den SSH-Zugang nutzen könnte. Damit entfiele der doppelte Arbeitsaufwand. Als Lösung kommt einem sofort eine Umwandlung des PGP-Schlüsselformats in das SSH-Format in den Sinn.
Wie es der Zufall so will, kann gpg
das bereits seit Version 2.2, d. h. alle aktuellen Versionen (einschließlich gpg4win) sollten diese Funktion beinhalten.
Doch Moment: Einfach eine SSH-Variante des PGP-Schlüsselpaars vorhalten, löst das Problem nicht zufriedenstellend. Immerhin lebt der geheime Schlüssel seit Teil 7 entsprechend sicher auf einer OpenPGP-Card und man hätte ja trotzdem ein zweites Schlüsselpaar, das nicht ohne Aufwand auf anderen Geräten genutzt werden kann.
Die Lösung ist das sogenannte "On the fly"-Übersetzen des geheimen Schlüssels. gpg
kann so konfiguriert werden, dass bei einer SSH-Anfrage die geheimen PGP-Schlüssel temporär übersetzt und an ssh
geliefert werden. Wie man das mit Windows einrichtet, werde ich im Folgenden erklären.
Es gibt verschiedene Wege, das Ziel zu erreichen. Ich werde hier einen zeigen, der auf dem Einsatz von gpg4win und der Git-bash bzw. des dort eingebauten SSH-Clients auf Basis von MSYS fußt. Das hat den Vorteil, dass man damit in der gleichen Shell ssh, sowie
git
(mit Commit-Signing) einsetzen kann. Der unter Windows ebenfalls beliebte Client PuTTY ist mir persönlich zu umständlich. Außerdem lassen sich so ssh
-Kommandos zwischen Windows und Unix austauschen.
Außerdem benutze ich Schlüssel auf Basis des RSA-Verfahrens. Inwiefern es beispielsweise auch mit ECC-Schlüsseln funktioniert, kann ich im Moment nicht sagen.
Man benötigt also:
Wir werden den in der git-Bash-Installation enthaltenen SSH-Client benutzen. Optional ist außerdem eine Installation von winpty (in unserem Fall die MSYS-Variante) vorteilhaft.
Denn: Beim Umgang mit der git-Bash sind zwei Dinge zu beachten:
gpg
-Client oder auch telnet
) können in der git-Bash Probleme bei Ausgabe und Eingabe von Zeichen haben. In diesen Fällen empfiehlt sich der Einsatz von winpty, das ein passendes Terminal simuliert. Der Aufruf ist denkbar einfach: winpty <kommando>
, z. B.: winpty telnet
gpg
-Client mit. Diesen möchten wir nicht benutzen, denn wir haben ja gpg4win. Zusätzlich haben einige gpg4win-Befehle Probleme mit der Zeichendarstellung in der git-Bash. Um beide Welten unter einen Hut zu bekommen, kann man ein sogenanntes Alias in der Datei C:\Users\<USER>\.bashrc
eintragen. Bei mir sieht deren Inhalt so aus:export LC_ALL="de_DE.UTF-8" alias gp='git fetch -p && git pull' alias gpgw='winpty /c/devtools/gnupg/bin/gpg' #Absoluter Pfad zur ausführbaren Datei von gpg4win alias gpg2='gpgw'
gpgw
kann man also das "richtige" gpg
inklusive Terminalemulation starten.
Als Erstes müssen wir unseren öffentlichen Schlüssel dem entfernten Server bekannt machen, damit dieser die Public-Key-Authentifizierung durchführen kann. Dazu muss unser öffentlicher PGP-Key im SSH-Format exportiert werden. Das geht so:
# The key ID of my public key is 0x37f0780907abef78. gpg --export-ssh-key 0x37f0780907abef78 > 37f0780907abef78.pub.ssh
Der Inhalt dieser Datei muss dann auf dem Server entsprechend in das SSH-Setup eingetragen werden. Für gewöhnlich muss man dafür den Schlüssel in die Datei ~/.ssh/authorized_keys
eintragen. Wenn man keinen entsprechenden Zugriff auf den Server hat, muss man dafür eine Administratorin fragen.
Für die "On-the-fly"-Fähigkeit des OpenPGP-Setups, muss man den gpg-agent
umkonfigurieren. Dazu fügt man in die Datei <GNUPGHOME>\gpg-agent.conf
die Zeile enable-putty-support
ein.
Obwohl PuTTY nicht zum Einsatz kommen wird, konfigurieren wir den Agent so um, dass er mit anderen Programmen über das sogenannte PuTTY-Protokoll sprechen kann. Im nächsten Schritt, werden wir SSH beibringen, das gleiche Protokoll zu sprechen und stellen mit diesem Trick eine Verbindung von der SSH-Welt in die OpenPGP-Welt her.
Zunächst müssen wir nach der Änderung den gpg-agent-Daemon neu starten, damit er die Änderung registriert. Auf der Windows-Konsole sieht das dann so aus:
C:\>gpg-connect-agent killagent /bye OK closing connection C:\>gpg-connect-agent /bye gpg-connect-agent: no running gpg-agent - starting 'C:\devtools\gnupg\bin\gpg-agent.exe' gpg-connect-agent: waiting for the agent to come up ... (5s) gpg-connect-agent: connection to agent established
Der ssh
-Client muss jetzt beim gpg-agent
über das PuTTY-Protokoll nach Schlüsseln fragen. Das wird mit Hilfe des Tools ssh-pageant
bewerkstelligt. Dieses Tool wird mit der git-Bash geliefert und ersetzt den normalen ssh-agent
. Die originäre Aufgabe des ssh-agent
ist es, dem ssh
-Client sicheren und dennoch bequemen Zugriff auf das Schlüsselmaterial zu gewährleisten, ohne dass dieser die technischen Details kennen muss. Der ssh-pageant
bietet ssh
die gleichen Funktionen an, beherrscht aber im Gegensatz zum ssh-agent
das PuTTY-Protokoll. ssh
kann damit also über den ssh-pageant
mit dem gpg-agent sprechen, für den wir ja bereits das PuTTY-Protokoll aktiviert haben.
Damit dies funktioniert, muss man ssh-pageant
starten und einige Umgebungsvariablen setzen. Beides zusammen kann man mit folgendem Kommando bewerkstelligen:
$ eval $(/usr/bin/ssh-pageant -r -a "/tmp/ssh-pageant-$USERNAME") ssh-pageant pid 16804
eval
ist ein bash-Kommando, das alle Argumente als Shell-Kommando ausführt.$( ... )
das Kommando innerhalb der Klammern wird noch vor der Ausführung von eval
ausgeführt. Sämtliche Ausgaben werden zu Argumenten von eval
./usr/bin/ssh-pageant -r -a <FILE> –
Benutze die Datei FILE
zum Austausch mit gpg
. Lege sie an, wenn sie nicht existiert./tmp
wird von git-Bash automatisch nach C:\Users\<USERNAME>\AppData\Local\Temp
aufgelöst, was das temporäre Standardverzeichnis für einen lokalen Windows-Account ist.ssh-pageant-$USERNAME –
Über diese Datei werden Daten ausgetauscht. $USERNAME
wird noch vor Ausführung des ssh-pageant
-Befehls in den Namen des aktuell angemeldeten Nutzers aufgelöst, bei mir also in ssh-pageant-mosig_user
.ssh-pageant pid 16804
ist die Ausgabe des eval
-Befehls. Der ssh-pageant
ist jetzt ein Hintergrunddienst und hat die Prozess-ID 16804 (kann auf anderen Systemen anders sein).ssh-pageant
ohne eval
startet, wird es einige Kommandos zum Setzen von Umgebungsvariablen ausgeben:$ /usr/bin/ssh-pageant -r -a "/tmp/.ssh-pageant-$USERNAME" SSH_AUTH_SOCK='/tmp/.ssh-pageant-mosig_user'; export SSH_AUTH_SOCK; SSH_PAGEANT_PID=16804; export SSH_PAGEANT_PID; echo ssh-pageant pid 16804;
eval
ausgeführt werden. Damit werden die genannten Umgebungsvariablen gesetzt.
Damit man den eval
-Befehl nicht immer manuell suchen und ausführen muss, kann man ihn in die C:\Users\<USER>\.bashrc
eintragen. Er wird dann beim Start von git-bash immer ausgeführt. Gibt es bereits eine laufende Instanz, sorgt ssh-pageant
automatisch dafür, dass keine neue gestartet wird. Meine .bashrc
sieht jetzt so aus:
export LC_ALL="de_DE.UTF-8" alias gp='git fetch -p && git pull' alias gpgw='winpty /c/devtools/gnupg/bin/gpg' alias gpg2='gpgw' eval $(/usr/bin/ssh-pageant -r -a "/tmp/.ssh-pageant-$USERNAME") export SSH_PAGEANT_PID=$(ps | grep $(which ssh-pageant) | head -n 1 | awk '{print $1}')
Die letzte Zeile ist ein Kniff, um ssh-pageant
jederzeit bei Bedarf wieder beenden zu können. Das kann man mit ssh-pageant -k
machen. Allerdings benötigt man dafür die Prozessnummer (PID) der aktuell laufenden Instanz, ansonsten passiert Folgendes:
$ ssh-pageant -k ssh-pageant: SSH_PAGEANT_PID not set, cannot kill agent
Diese muss in der Umgebungsvariable SSH_PAGEANT_PID
gespeichert sein. Der Befehl in der letzten Zeile der .bashrc
sorgt dafür. Unser eval
-Befehl wird diese Variable nur setzen, wenn ssh-pageant
noch nicht läuft.
# No instance running yet. $ /usr/bin/ssh-pageant -r -a "/tmp/.ssh-pageant-mosig_user" SSH_AUTH_SOCK='/tmp/.ssh-pageant-mosig_user'; export SSH_AUTH_SOCK; SSH_PAGEANT_PID=4772; export SSH_PAGEANT_PID; echo ssh-pageant pid 4772; # One instance running. SSH_PAGEANT_PID is not set. $ /usr/bin/ssh-pageant -r -a "/tmp/.ssh-pageant-mosig_user" SSH_AUTH_SOCK='/tmp/.ssh-pageant-mosig_user'; export SSH_AUTH_SOCK;
Es braucht also noch den zusätzlichen export
, damit die Variable immer gesetzt ist. Wie man die PID eines laufenden Prozesses mit git-Bash herausfindet, habe ich mir bei superuser.com abgeschaut.
PuTTY bringt ebenfalls einen Agent namens pageant.exe
mit. Es kann jeweils entweder ssh-pageant
, oder pageant.exe
aktiv sein. Beide zusammen scheinen nicht zu funktionieren.
Damit ist unser Setup komplett. Zeit, das Ganze auszuprobieren.
Die eigentliche Arbeit mit ssh
bleibt gleich. Durch die Entkopplung mit Hilfe des ssh-pageant
bekommt ssh
selbst gar nichts von unserem Setup mit. Aus ssh
-Sicht ist alles wie immer. Das bedeutet auch, dass es für ssh
unerheblich ist, ob die OpenPGP-Schlüssel lokal auf dem Rechner oder auf einer OpenPGP-Card, beispielsweise einem YubiKey, liegen. Sobald gpg
Zugriff auf die Schlüssel hat, hat auch ssh
Zugriff. Das ist das Schöne an diesem Konzept.
Man loggt sich also wie üblich ein. Im Beispiel habe ich mal mittels -v
den Debug-Modus aktiviert.
$ ssh mosig@server_url -v
führt jetzt einen Handshake mit dem Server durch und wird dann nach dem passenden Schlüssel für den vom Server geforderten "suchen". In meinem Fall liegt dieser auf meinem YubiKey. Das ist der Grund, warum beim Login dessen PIN abgefragt wird. Dieser Vorgang wird komplett von gpg4win gesteuert. Nach erfolgreicher Eingabe wird die Verbindung dann schließlich hergestellt.
ssh
Wer alle Teile der Serie bis hierhin nachvollzogen hat, verfügt jetzt über ein ausgereiftes OpenPGP-Setup, das auch für die Nutzung von SSH taugt. Damit fallen viele Stolpersteine in der täglichen Arbeit weg.
Wie es mit der Merhfachnutzung des gleichen Schlüssels aber so ist, besteht natürlich immer die Gefahr, dass mit einem kompromittierten Schlüssel mehrere Ressourcen kompromittiert werden können, beispielsweise E-Mail-Verschlüsselung und SSH-Zugänge. Hier braucht es wie immer eine Abwägung zwischen Benutzbarkeit und Sicherheit. Denkbar wäre auch die Generierung und Ablage weiterer Schlüssel auf anderen YubiKeys, die dann für den Zugang zu besonders kritischen Ressourcen genutzt werden können. Damit hätte man im wahrsten Sinne des Wortes einen Schlüsselbund für seine Zugänge.