08
Mar 13

Git mit mehreren SSH-Users auf Windows-Hostsystem und Linux-Guestsystem (VirtualBox)

Das GitHub HowTo “Generating SSH Keys” zeigt den einfachsten und kürzesten Weg, Git mit GitHub zum Laufen zu bekommen, und ist ein hevorragendes QuickStrart-Tutorial für die Einseiger, die zunächst damit zufrieden sind, dass es überhaupt läuft und sie mit den Standard-Einstellungen clonen und pushen können. So war das auch bei mir. Doch die Ansprüche — meine jedenfalls — an die Arbeitsumgebung wachsen und irgendwann ergab sich für mich folgende Fragestallung:

Ich nutze Windows als Host-System, Debian als Gast-System (VirtualBox VM), habe mehrere Accounts auf GitHub und möchte auf sie (lesend und schreibend) über msysGit  und TortoiseGit auf dem Windows Host-System und über das Standard-Terminal (Gnome Terminal) auf dem Debian Gast-System zugreifen.  Im Folgenden wird gezeigt, wie das geht. Ich nehme als Beispiel zwei imaginäre GitHub-Accounts, aber das Ganze gilt natürlich genauso im Falle eines einzigen bzw. von mehr als zwei Accounts.

0. Ausgangssituation

Es wird vorausgesetzt, dass auf dem Linux-Gastsystem Git und auf dem Windows-Hostsystem Git samt msysGit sowie TortoiseGit installiert ist; ein oder mehrere GitHub-Accounts sind angelegt.

1. SSH-Keys erstellen

Da GitHub es nicht zulässt, ein und den selben SSH-Schlüssel für mehrere Accounts zu verwenden muss zuerst ein Schlüssel(-Paar) je Account erstellt werden. In unserem Fall also zwei. (Davor sollten die ggf. vorhandenen Key-Dateien gesichert werden.) Hier so wie im ganzen Artikel wird msysGit als Windows-Terminal verwendet.

hostuser@hostsystem /usr
$ cd ~/.ssh/
 
hostuser@hostsystem ~/.ssh
$ ssh-keygen -t rsa -C "foomail@foo.com"
Generating public/private rsa key pair.
Enter file in which to save the key (/c/Users/hostuser/.ssh/id_rsa): id_rsa_fookey
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in id_rsa_fookey.
Your public key has been saved in id_rsa_fookey.pub.
The key fingerprint is:
65:e7:25:77:4f:00:c3:9b:56:b2:c4:fe:c8:4a:0d:e8 foomail@foo.com
 
hostuser@hostsystem ~/.ssh
$ ssh-keygen -t rsa -C "barmail@bar.com"
Generating public/private rsa key pair.
Enter file in which to save the key (/c/Users/hostuser/.ssh/id_rsa): id_rsa_barkey
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in id_rsa_barkey.
Your public key has been saved in id_rsa_barkey.pub.
The key fingerprint is:
eb:c9:d8:41:9a:d4:f5:53:b6:9d:f2:3b:7b:8e:96:99 barmail@bar.com

2. Keys auf dem Server / zum GitHub-Account hinzufügen.

Das Kopieren der Schlüssel geht am einfachsten über die Konsole:

hostuser@hostsystem ~/.ssh
$ clip < ~/.ssh/id_rsa_fookey.pub
 
hostuser@hostsystem ~/.ssh
$ clip < ~/.ssh/id_rsa_barkey.pub

3. Hosts einstellen

Testet man nun die Verbindung, wie es im Standard-HowTo steht, scheitert sie mit einem Fehler:

hostuser@hostsystem ~/.ssh
$ ssh -T git@github.com
The authenticity of host 'github.com (207.97.227.239)' can't be established.
RSA key fingerprint is 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'github.com,207.97.227.239' (RSA) to the list of known hosts.
Permission denied (publickey).

Zunächst wird github.com zur Liste der bekannten Hosts bzw. zur ~/.ssh/known_hosts hinzugefügt.

Dann wird ein Verbindungsversuch gestartet -- und der scheitert. Ich erkläre mir das wie folgt: Der SSH-Client sucht zuerst nach Standard-Keyfiles wie id_rsa bzw. id_rsa.pub. Als er sie nicht findet, untersucht er die Datei ~/.ssh/config (falls vorhanden), in der die (Pfade zu den) Key-Dateien auf die Hosts gemappt sind. Findet er die Datei nicht bzw. keinen passenden Eintrag in der Datei, "weiß" er nicht, welcher Schlüssel verwendet werden soll, und kann sich nicht auf dem Server autentifizieren.

Ergo brauchen wir eine Konfigurationsdatei config in ~/.ssh/, und zwar mit folgendem Inhalt:

#foo account
Host github.com-foo
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_rsa_fookey
 
#bar account
Host github.com-bar
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_rsa_barkey

Nun testen wir die Verbindung für jeden der Accounts/Schlüssel und stellen fest, dass sie funktioniert:

hostuser@hostsystem ~/.ssh
$ ssh -T git@github.com-foo
Hi foo! You've successfully authenticated, but GitHub does not provide shell access.
 
hostuser@hostsystem ~/.ssh
$ ssh -T git@github.com-bar
Hi bar! You've successfully authenticated, but GitHub does not provide shell access.

4. Projekt einstellen

Wenn ein Repository schon vor den Spielereien mit mehreren Accounts geclonet wurde, wird die remote.origin.url nun wahrscheinlich micht mehr richtig gesettz sein, nämlich auf die URL:

hostuser@hostsystem ~/Desktop/test/barproject (master)
$ git config --local remote.origin.url
git@github.com:bar/barproject.git

Wie man sieht, fehlt hier hinter URL der mit einem Bindestrich von ihr getrennter Benutzername. Beim Versuch, sich zu autentifizieren, führt es zu einem Fehler:

hostuser@hostsystem ~/Desktop/test/barproject (master)
$ git push
Permission denied (publickey).
fatal: The remote end hung up unexpectedly

Die remote.origin.url kann entweder direkt in der Datei [Projekt-Root]/.git/config oder über git config definiert werden.

Die Struktur der URL zum Repository sieht (im Fall meines Servers) wie folgt aus: git@%Host aus der ~/.ssh/config%:%Projektname%.git. Und für GitHub-Repositories git@%Host aus der ~/.ssh/config%:%GitHub Benutzername%/%Projektname%.git.

hostuser@hostsystem ~/Desktop/test/barproject (master)
$ git config remote.origin.url git@github.com-bar:bar/barproject.git

Nun funktioniert es wieder:

hostuser@hostsystem ~/Desktop/test/barproject (master)
$ git push
Everything up-to-date

5. git clone mit msysGit

hostuser@hostsystem ~/.ssh
$ cd ~/Desktop/
 
hostuser@hostsystem ~/Desktop
$ mkdir test
 
hostuser@hostsystem ~/.ssh
$ cd test/
 
hostuser@hostsystem ~/Desktop/test
$ git clone git@github.com-foo:foo/fooproject.git
Cloning into 'fooproject'...
remote: Counting objects: 572, done.
remote: Compressing objects: 100% (120/120), done.
remote: Total 572 (delta 434), reused 564 (delta 431)
Receiving objects: 100% (572/572), 1.78 MiB | 425 KiB/s, done.
Resolving deltas: 100% (434/434), done.
 
hostuser@hostsystem ~/Desktop/test
$ git clone git@github.com-bar:bar/barproject.git
Cloning into 'barproject'...
remote: Counting objects: 99, done.
remote: Compressing objects: 100% (55/55), done.
remote: Total 99 (delta 31), reused 95 (delta 31)
Receiving objects: 100% (99/99), 284.33 KiB | 281 KiB/s, done.

6. GIT_SSH

Falls dieser Fehler auftritt:

hostuser@hostsystem ~/Desktop/test
$ git clone git@github.com-foo:foo/fooproject.git
Cloning into 'fooproject'...
fatal: The remote end hung up unexpectedly
 
hostuser@hostsystem ~/Desktop/test
$ git clone git@github.com-bar:bar/barproject.git
Cloning into 'barproject'...
fatal: The remote end hung up unexpectedly

liegt es möglicherweise an der Umgebungsvariablen GIT_SSH (die wahrscheinlich auf C:\Program Files\TortoiseGit\bin\TortoisePlink.exe gesetzt sein wird). Diese muss entfernt werden.

7. TortoiseGit

Dazu ist nicht viel zu sagen. Funktioniert.

8. Virtuelle Maschine einstellen

Die auf dem Host-System unter ~/.ssh/ befindlichen Dateien (Private und Public Key Files sowie config und known_hosts) müssen nach ~/.ssh/ des Hosts kopiert werden.

Sollte dieser Fehler auftreten:

root@devvm:~/Desktop/test# git clone git@github.com-bar:bar/barproject.git
Cloning into barproject...
Bad owner or permissions on /root/.ssh/config
fatal: The remote end hung up unexpectedly

müssen eine oder mehrere Eigenschaften der [chmod 700 config] korrigiert werden: Benutzer muss auf root, Benutzergruppe ebenfalls auf root und das Zugriff-Level auf 700 gesetzt werden.

root@devvm:~/.ssh# chown root:ax config
root@devvm:~/.ssh# chown root:root config

9. ~/.ssh/ als Shared Folder

Um sich das Leben einfacher zu machen und nicht an die Keys denken zu müssen, wenn man seine VM jemandem zur Verfügung stellt, kann man das ~/.ssh/ Verzeichnis komplett ins Host-System "auslagern", indem man es als Shared Folder verwendet. Wie Shared Folders im Allgemeinen angelegt werden, beschreibe ich im Artikel "Shared Folders für die VirtualBox-VM". Was speziell den Fall mit dem .ssh-Verzeichnis angeht, muss beim Mounten -- um den "Bad owner or permissions"-Fehler zu vermeiden -- auf den Benutzernamen, die Benutzergruppe und die eingeschränkten Zugriffsrechte geachtet werden. Für den root User (ob man im Kontext einer Entwicklungs-VM als root arbeiten darf, sei dahingestellt) mit uid 0 der Gruppe mit gid 0 wird der Mount-Befehl etwa so aussehen:

mount -t vboxsf -o uid=0,gid=0,umask=077 sshkeys /root/.ssh

That's it! :)