Git
Chapters ▾ 2nd Edition

7.14 Git Tools - Anmeldeinformationen speichern

Anmeldeinformationen speichern

Wenn Sie den SSH-Transport für die Verbindung zu Remotes verwenden, ist es möglich, dass Sie einen Schlüssel ohne Passwort verwenden. Damit können Sie Daten sicher übertragen, ohne Ihren Benutzernamen und ein Passwort einzugeben. Mit den HTTP-Protokollen ist das aber nicht möglich – jede Verbindung benötigt einen Benutzernamen und ein Passwort. Noch schwieriger wird das bei Systemen mit Zwei-Faktor-Authentifizierung, bei denen das Token, das Sie für ein Kennwort verwenden, zufällig generiert wird und nicht ausgeprochen werden kann.

Glücklicherweise hat Git ein Anmeldesystem, das hier weiter helfen kann. Git hat ein paar Optionen im Angebot:

  • Standardmäßig wird überhaupt nicht zwischengespeichert. Bei jeder Verbindung wird nach Ihrem Benutzernamen und Passwort gefragt.

  • Der „Cache“-Modus hält die Anmeldedaten für eine gewisse Zeitspanne im Zwischenspeicher. Keines der Passwörter wird jemals auf der Festplatte abgelegt und nach 15 Minuten werden sie aus dem Cache gelöscht.

  • Der „Speicher“-Modus speichert die Zugangsdaten in einer Klartextdatei auf der Festplatte, und sie verfallen nie. Das bedeutet, dass Sie Ihre Anmeldedaten nie wieder eingeben müssen, bis Sie Ihr Passwort für den Git-Host ändern. Der Nachteil dieses Ansatzes ist, dass Ihre Passwörter im Klartext in einer einfachen Datei in Ihrem Homeverzeichnis gespeichert werden.

  • Wenn Sie einen Mac verwenden, verfügt Git über einen „osxkeychain“-Modus, der die Anmeldeinformationen im sicheren Schlüsselbund, der an Ihr Systemkonto angehängt ist, zwischenspeichert. Diese Methode speichert die Zugangsdaten, ohne Laufzeitbegrenzung auf der Festplatte. Sie werden mit dem gleichen Verfahren verschlüsselt, das auch HTTPS-Zertifikate und Safari-Automatiken verwaltet.

  • Wenn Sie Windows verwenden, können Sie das Hilfsprogramm „Git Credential Manager for Windows“ installieren. Dieses Programm ähnelt dem oben beschriebenen "osxkeychain"-Modus, verwendet jedoch den „Windows Credential Store“ zur Kontrolle sensibler Informationen. Es ist unter https://github.com/Microsoft/Git-Credential-Manager-for-Windows zu finden.

Sie können eine dieser Methoden durch Setzen eines Git-Konfigurationswertes wählen:

$ git config --global credential.helper cache

Einige dieser Hilfsmittel haben Optionen. Der „store“-Assistent kann das Argument --file <path> benutzen, das den Speicherort der Klartextdatei anpasst (der Standard ist ~/.git-credentials). Der „cache“-Assistent akzeptiert die Option --timeout <seconds>, welche die Zeitspanne ändert, in der der Dämon läuft (die Vorgabe ist „900“, oder 15 Minuten). Hier folgt ein Beispiel, wie Sie der „store“-Assistent mit einem benutzerdefinierten Dateinamen konfigurieren könnten:

$ git config --global credential.helper 'store --file ~/.my-credentials'

Mit Git können Sie auch mehrere Assistenten konfigurieren. Wenn Sie nach Anmeldeinformationen für einen bestimmten Host suchen, fragt Git diese der Reihe nach ab und stoppt nach der ersten, erhaltenen Antwort. Beim Speichern der Zugangsdaten sendet Git den Benutzernamen und das Passwort an alle Assistenten der Liste, diese entscheiden, was damit zu machen ist. So würde eine .gitconfig aussehen, wenn Sie eine Datei mit Zugangsdaten auf einem USB-Stick hätten, aber den Zwischenspeicher nutzen wollten, um sich die Eingabe zu sparen, wenn das Laufwerk nicht angeschlossen ist:

[credential]
    helper = store --file /mnt/thumbdrive/.git-credentials
    helper = cache --timeout 30000

Unter der (Motor-)Haube

Wie funktioniert das alles? Der Hauptbefehl für das Anmelde-System ist git credential, der einen Befehl als Argument und dann weitere Eingaben über stdin (Standardeingabe=Tastatur) entgegennimmt.

Mit einem Beispiel ist das vielleicht leichter verständlich. Nehmen wir an, dass ein Assistent für die Anmeldung konfiguriert wurde und er Anmeldedaten für mygithost gespeichert hat. Die folgende Sitzung verwendet den Befehl „fill“, der aufgerufen wird, wenn Git versucht, Zugangsdaten für einen Host zu finden:

$ git credential fill (1)
protocol=https (2)
host=mygithost
(3)
protocol=https (4)
host=mygithost
username=bob
password=s3cre7
$ git credential fill (5)
protocol=https
host=unknownhost

Username for 'https://unknownhost': bob
Password for 'https://bob@unknownhost':
protocol=https
host=unknownhost
username=bob
password=s3cre7
  1. Diese Befehlszeile leitet die eigentliche Interaktion ein.

  2. Das Anmeldesystem wartet auf die Eingabe von stdin. Wir geben das „Protokoll“ und den „Hostnamen“ ein.

  3. Eine leere Zeile signalisiert, dass die Eingabe vollständig ist und das Anmeldesystem sollte mit dem antworten, was ihm bekannt ist.

  4. Dann übernimmt das Programm Git-Credential und gibt auf stdout die gefundenen Informationen aus.

  5. Falls keine Anmeldeinformationen gefunden werden, fragt Git den Benutzer nach dem Benutzernamen und dem Kennwort und gibt sie an den aufrufenden stdout zurück (hier sind sie an dieselbe Konsole angeschlossen).

Das Anmeldesystem ruft in Wirklichkeit ein Programm auf, das von Git selbst unabhängig ist und bei dem der Konfigurationswert credential.helper bestimmt, was und auf welche Weise es aufgerufen wird. Es kann unterschiedliche Varianten anbieten:

Konfiguration-Wert Verhalten

foo

läuft als git-credential-foo

foo -a --opt=bcd

läuft als git-credential-foo -a --opt=bcd

/absolute/path/foo -xyz

läuft als /absolute/path/foo -xyz

!f() { echo "password=s3cre7"; }; f

Code nach ! wird in der Shell ausgewertet

Die oben beschriebenen Hilfsprogramme heißen also eigentlich git-credential-cache, git-credential-store usw. und wir können sie so konfigurieren, dass sie Befehlszeilenargumente übernehmen. Die allgemeine Form dafür ist „git-credential-foo [Argument] <Aktion>“. Das stdin/stdout-Protokoll ist das gleiche wie beim Anmeldesystem, aber sie verwenden einen etwas anderen Befehls-Satz:

  • get ist die Abfrage nach einem Benutzernamen/Passwort-Paar.

  • store ist die Aufforderung, einen Satz von Anmeldeinformationen im Speicher dieses Assistenten zu hinterlegen.

  • erase löscht die Anmeldeinformationen für die angegebenen Einstellungen aus dem Speicher dieses Assistenten.

Für die store und erase Aktionen ist keine Reaktion erforderlich (Git ignoriert sie ohnehin). Für die Aktion get ist Git allerdings stark darauf bedacht, was der Assistent sagt. Wenn das Hilfsprogramm nichts Sinnvolles kennt, kann es einfach ohne Ausgabe abbrechen. Weiß es aber etwas, sollte es die bereitgestellten Informationen mit den gespeicherten Informationen ergänzen. Die Ausgabe wird wie eine Reihe von Assignment-Statements behandelt. Alles, was zur Verfügung gestellt wird, ersetzt das, was Git bereits kennt.

Hier ist das gleiche Beispiel von oben, aber ohne das Git-Anmeldesystem und direkt zu git-credential-store:

$ git credential-store --file ~/git.store store (1)
protocol=https
host=mygithost
username=bob
password=s3cre7
$ git credential-store --file ~/git.store get (2)
protocol=https
host=mygithost

username=bob (3)
password=s3cre7
  1. Hier weisen wir git-credential-store an, einige Anmeldedaten zu speichern: der Benutzername „bob“ und das Passwort „s3cre7“ sollen verwendet werden, wenn auf https://mygithost zugegriffen wird.

  2. Jetzt rufen wir diese Anmeldedaten ab. Wir geben die bereits bekannten Teile der Internetadresse (https://mygithost) und eine leere Zeile ein.

  3. git-credential-store antwortet mit dem Benutzernamen und dem Passwort, die wir beide oben gespeichert haben.

So sieht die Datei ~/git.store jetzt aus:

https://bob:s3cre7@mygithost

Sie besteht nur aus einer Reihe von Zeilen, von denen jede eine mit einem Anmeldeinformationen versehene URL enthält. Die Assistenten osxkeychain und wincred verwenden das native Format ihrer Zwischenspeicher, während cache ein eigenes Speicherformat verwendet (das kein anderer Prozess lesen kann).

Benutzerdefinierter Cache für Anmeldeinformationen.

Angenommen, dass git-credential-store und seine Verwandten von Git getrennte Programme sind, dann ist es kein großer Schritt, zu erkennen, dass jedes Programm ein Hilfsprogramm für die Git-Anmeldung sein kann. Die von Git bereitgestellten Assistenten decken viele gewöhnliche Anwendungsfälle ab, aber nicht alle. Nehmen wir zum Beispiel an, Ihr Team hat einige Anmeldedaten, die dem gesamten Team zur Verfügung gestellt werden sollen, eventuell für die Entwicklung. Diese werden in einem freigegebenen Verzeichnis gespeichert, aber Sie möchten sie nicht in Ihr eigenes Anmeldesystem kopieren, da sie sich häufig ändern. Keines der vorhandenen Hilfsprogramme ist auf diesen Fall anwendbar; schauen wir mal, was nötig wäre, um unser Eigenes zu schreiben. Dieses Programm muss mehrere Schlüsselfunktionen haben:

  1. Die einzige Aktion, auf die wir achten müssen, ist get. Die Aktionen store und erase sind Schreiboperationen, also werden wir sie einfach sauber beenden, sobald sie auftauchen.

  2. Das Dateiformat der Datei für die gemeinsamen Anmeldedaten ist das gleiche wie es von git-credential-store verwendet wird.

  3. Der Speicherort dieser Datei ist eigentlich standardisiert, aber wir sollten es dem Benutzer erlauben, einen benutzerdefinierten Pfad anzugeben, nur für den Fall, dass er es wünscht.

Noch einmal zur Erinnerung: Wir werden diese Erweiterung in Ruby schreiben, aber jede Programmiersprache wird funktionieren, solange Git das fertige Produkt ausführen kann. Hier ist der vollständige Quellcode unseres neuen Anmeldehelfers:

#!/usr/bin/env ruby

require 'optparse'

path = File.expand_path '~/.git-credentials' # (1)
OptionParser.new do |opts|
    opts.banner = 'USAGE: git-credential-read-only [options] <action>'
    opts.on('-f', '--file PATH', 'Specify path for backing store') do |argpath|
        path = File.expand_path argpath
    end
end.parse!

exit(0) unless ARGV[0].downcase == 'get' # (2)
exit(0) unless File.exists? path

known = {} # (3)
while line = STDIN.gets
    break if line.strip == ''
    k,v = line.strip.split '=', 2
    known[k] = v
end

File.readlines(path).each do |fileline| # (4)
    prot,user,pass,host = fileline.scan(/^(.*?):\/\/(.*?):(.*?)@(.*)$/).first
    if prot == known['protocol'] and host == known['host'] and user == known['username'] then
        puts "protocol=#{prot}"
        puts "host=#{host}"
        puts "username=#{user}"
        puts "password=#{pass}"
        exit(0)
    end
end
  1. Hier parsen wir die Befehlszeilenoptionen und erlauben dem Benutzer, die Eingabedatei zu spezifizieren. Die Vorgabe ist ~/.git-credentials.

  2. Dieses Programm antwortet nur, wenn die Aktion get lautet und die Backup-Speicherdatei existiert.

  3. Die Schleife hier, liest von stdin, bis die erste leere Zeile erkannt wird. Die Eingaben werden im Hash known zur späteren Referenz gespeichert.

  4. Die Schleife hier, liest den Inhalt der Speicherdatei und sucht nach Übereinstimmungen. Wenn die Protokolldaten und der Host mit known in dieser Zeile übereinstimmen, gibt das Programm die Ergebnisse auf stdout aus und beendet sich.

Wir speichern unser Hilfsprogramm als git-credential-read-only, legen es irgendwo in unserem PATH ab und markieren es als ausführbar. So sieht dann eine interaktive Sitzung aus:

$ git credential-read-only --file=/mnt/shared/creds get
protocol=https
host=mygithost

protocol=https
host=mygithost
username=bob
password=s3cre7

Da der Name mit „git-“ beginnt, können wir die einfache Syntax für den Konfigurationswert verwenden:

$ git config --global credential.helper 'read-only --file /mnt/shared/creds'

Wie Sie sehen, ist die Erweiterung dieses Systems ziemlich unkompliziert und kann einige typische Probleme für Sie und Ihr Team lösen.