Kapitel ▾ 2. Auflage

7.14 Git-Werkzeuge - Anmeldeinformationen speichern

Anmeldeinformationen speichern

Wenn Sie den SSH-Transport für die Verbindung zu Remotes verwenden, ist es möglich, einen Schlüssel ohne Passphrase zu haben, was Ihnen ermöglicht, Daten sicher zu übertragen, ohne Ihren Benutzernamen und Ihr Passwort eingeben zu müssen. Dies ist jedoch mit HTTP-Protokollen nicht möglich – jede Verbindung benötigt einen Benutzernamen und ein Passwort. Dies wird noch schwieriger für Systeme mit Zwei-Faktor-Authentifizierung, bei denen das Token, das Sie als Passwort verwenden, zufällig generiert und unaussprechlich ist.

Glücklicherweise verfügt Git über ein Anmeldeinformationssystem, das dabei helfen kann. Git bietet einige integrierte Optionen

  • Die Standardeinstellung ist, überhaupt nicht zu cachen. Jede Verbindung wird Sie nach Ihrem Benutzernamen und Passwort fragen.

  • Der „cache“-Modus speichert Anmeldeinformationen für eine bestimmte Zeit im Speicher. Keine der Passwörter werden jemals auf der Festplatte gespeichert und sie werden nach 15 Minuten aus dem Cache gelöscht.

  • Der „store“-Modus speichert die Anmeldeinformationen in einer Klartextdatei auf der Festplatte, und sie verfallen niemals. Das bedeutet, dass Sie Ihre Anmeldeinformationen 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 Home-Verzeichnis gespeichert werden.

  • Wenn Sie macOS verwenden, wird Git mit dem Modus „osxkeychain“ geliefert, der Anmeldeinformationen im sicheren Schlüsselbund Ihres Systemkontos speichert. Diese Methode speichert die Anmeldeinformationen auf der Festplatte und sie verfallen niemals, aber sie werden mit demselben System verschlüsselt, das HTTPS-Zertifikate speichert und Safari automatisch ausfüllt.

  • Wenn Sie Windows verwenden, können Sie die Funktion **Git Credential Manager** aktivieren, wenn Sie Git for Windows installieren oder den neuesten GCM separat als eigenständigen Dienst installieren. Dies ähnelt dem oben beschriebenen „osxkeychain“-Helfer, verwendet jedoch den Windows Credential Store zur Verwaltung sensibler Informationen. Es kann auch Anmeldeinformationen für WSL1 oder WSL2 bereitstellen. Weitere Informationen finden Sie in den GCM-Installationsanweisungen.

Sie können eine dieser Methoden wählen, indem Sie einen Git-Konfigurationswert festlegen

$ git config --global credential.helper cache

Einige dieser Helfer haben Optionen. Der „store“-Helfer kann das Argument --file <path> annehmen, das den Speicherort der Klartextdatei anpasst (Standard ist ~/.git-credentials). Der „cache“-Helfer akzeptiert die Option --timeout <seconds>, die die Laufzeit seines Daemons ändert (Standard ist „900“ oder 15 Minuten). Hier ist ein Beispiel, wie Sie den „store“-Helfer mit einem benutzerdefinierten Dateinamen konfigurieren würden

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

Git erlaubt Ihnen sogar, mehrere Helfer zu konfigurieren. Bei der Suche nach Anmeldeinformationen für einen bestimmten Host fragt Git diese in der Reihenfolge ab und stoppt, nachdem die erste Antwort gegeben wurde. Beim Speichern von Anmeldeinformationen sendet Git den Benutzernamen und das Passwort an **alle** Helfer in der Liste, und diese können entscheiden, was sie damit tun. So würde eine .gitconfig aussehen, wenn Sie eine Anmeldeinformationsdatei auf einem USB-Stick hätten, aber den In-Memory-Cache verwenden möchten, um Tipparbeit zu sparen, falls das Laufwerk nicht angeschlossen ist

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

Hinter den Kulissen

Wie funktioniert das alles? Der Git-Root-Befehl für das Anmeldeinformations-Helper-System ist git credential, der einen Befehl als Argument entgegennimmt und dann weitere Eingaben über stdin erhält.

Dies lässt sich vielleicht leichter anhand eines Beispiels verstehen. Angenommen, ein Anmeldeinformations-Helfer wurde konfiguriert und hat Anmeldeinformationen für mygithost gespeichert. Hier ist eine Sitzung, die den „fill“-Befehl verwendet, der aufgerufen wird, wenn Git versucht, Anmeldeinformationen 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. Dies ist der Befehl, der die Interaktion initiiert.

  2. Git-credential wartet dann auf Eingaben auf stdin. Wir geben ihm die Dinge, die wir wissen: das Protokoll und den Hostnamen.

  3. Eine leere Zeile bedeutet, dass die Eingabe abgeschlossen ist und das Anmeldeinformationssystem mit dem antworten soll, was es weiß.

  4. Git-credential übernimmt dann und schreibt auf stdout mit den Informationen, die es gefunden hat.

  5. Wenn Anmeldeinformationen nicht gefunden werden, fragt Git den Benutzer nach dem Benutzernamen und dem Passwort und gibt sie an das aufrufende stdout zurück (hier sind sie an dieselbe Konsole angehängt).

Das Anmeldeinformationssystem ruft tatsächlich ein separates Programm von Git auf; welches und wie, hängt vom Konfigurationswert credential.helper ab. Es gibt mehrere Formen, die es annehmen kann

Konfigurationswert Verhalten

foo

Führt git-credential-foo aus

foo -a --opt=bcd

Führt git-credential-foo -a --opt=bcd aus

/absolute/path/foo -xyz

Führt /absolute/path/foo -xyz aus

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

Code nach ! wird in der Shell ausgewertet

Die oben beschriebenen Helfer heißen also tatsächlich git-credential-cache, git-credential-store usw., und wir können sie so konfigurieren, dass sie Kommandozeilenargumente entgegennehmen. Die allgemeine Form dafür ist „git-credential-foo [args] <action>.“. Das stdin/stdout-Protokoll ist dasselbe wie bei git-credential, aber sie verwenden etwas andere Aktionen

  • get ist eine Anfrage nach einem Benutzername/Passwort-Paar.

  • store ist eine Anfrage, eine Reihe von Anmeldeinformationen in diesem Helfer-Speicher zu sichern.

  • erase löscht die Anmeldeinformationen für die gegebenen Eigenschaften aus diesem Helfer-Speicher.

Für die Aktionen store und erase ist keine Antwort erforderlich (Git ignoriert sie sowieso). Für die Aktion get ist Git jedoch sehr daran interessiert, was der Helfer zu sagen hat. Wenn der Helfer nichts Nützliches weiß, kann er einfach ohne Ausgabe beendet werden, aber wenn er etwas weiß, sollte er die bereitgestellten Informationen mit den gespeicherten Informationen ergänzen. Die Ausgabe wird wie eine Reihe von Zuweisungen behandelt; alles, was bereitgestellt wird, ersetzt, was Git bereits weiß.

Hier ist dasselbe Beispiel wie oben, aber wir überspringen git-credential und gehen 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 Anmeldeinformationen zu speichern: Der Benutzername „bob“ und das Passwort „s3cre7“ sollen verwendet werden, wenn auf https://mygithost zugegriffen wird.

  2. Nun rufen wir diese Anmeldeinformationen ab. Wir stellen die Teile der Verbindung bereit, die wir bereits kennen (https://mygithost), und eine leere Zeile.

  3. git-credential-store antwortet mit dem oben gespeicherten Benutzernamen und Passwort.

So sieht die Datei ~/git.store aus

https://bob:s3cre7@mygithost

Es ist nur eine Reihe von Zeilen, die jeweils eine mit Anmeldeinformationen verschlüsselte URL enthalten. Die Helfer osxkeychain und wincred verwenden das native Format ihrer Backing Stores, während cache sein eigenes In-Memory-Format verwendet (das kein anderer Prozess lesen kann).

Ein benutzerdefinierter Anmeldeinformations-Cache

Da git-credential-store und seine Kollegen separate Programme von Git sind, ist es kein großer Sprung zu erkennen, dass *jedes* Programm ein Git-Anmeldeinformations-Helfer sein kann. Die von Git bereitgestellten Helfer decken viele gängige Anwendungsfälle ab, aber nicht alle. Nehmen wir zum Beispiel an, Ihr Team hat einige Anmeldeinformationen, die mit dem gesamten Team geteilt werden, vielleicht für die Bereitstellung. Diese werden in einem gemeinsamen Verzeichnis gespeichert, aber Sie möchten sie nicht in Ihren eigenen Anmeldeinformationsspeicher kopieren, da sie sich häufig ändern. Keiner der vorhandenen Helfer deckt diesen Fall ab. Sehen wir uns an, was erforderlich wäre, um unseren eigenen zu schreiben. Dieses Programm muss mehrere Schlüsselfunktionen haben

  1. Die einzige Aktion, auf die wir achten müssen, ist get; store und erase sind Schreibvorgänge, also werden wir einfach sauber beenden, wenn sie empfangen werden.

  2. Das Dateiformat der Shared-Credential-Datei ist dasselbe wie das von git-credential-store verwendete.

  3. Der Speicherort dieser Datei ist ziemlich Standard, aber wir sollten dem Benutzer erlauben, einen benutzerdefinierten Pfad anzugeben, nur für den Fall.

Auch hier werden wir diese Erweiterung in Ruby schreiben, aber jede Sprache funktioniert, solange Git das fertige Produkt ausführen kann. Hier ist der vollständige Quellcode unseres neuen Anmeldeinformations-Helpers

#!/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.exist? 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 Kommandozeilenoptionen und erlauben dem Benutzer, die Eingabedatei anzugeben. Der Standard ist ~/.git-credentials.

  2. Dieses Programm reagiert nur, wenn die Aktion get ist und die zugrunde liegende Speicherdatei existiert.

  3. Diese Schleife liest von stdin, bis die erste leere Zeile erreicht ist. Die Eingaben werden im known-Hash zur späteren Referenz gespeichert.

  4. Diese Schleife liest den Inhalt der Speicherdatei und sucht nach Übereinstimmungen. Wenn Protokoll, Host und Benutzername aus known mit dieser Zeile übereinstimmen, gibt das Programm die Ergebnisse auf stdout aus und wird beendet.

Wir speichern unseren Helfer als git-credential-read-only, legen ihn irgendwo in unseren PATH und markieren ihn als ausführbar. So sieht eine interaktive Sitzung aus

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

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

Da sein 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 können, ist die Erweiterung dieses Systems ziemlich unkompliziert und kann einige häufige Probleme für Sie und Ihr Team lösen.