Git
Chapters ▾ 2nd Edition

7.14 Utilitaires Git - Stockage des identifiants

Stockage des identifiants

Si vous utilisez le transport SSH pour vous connecter à vos dépôts distants, il est possible d’avoir une clé sans mot de passe qui permet de transférer des données en sécurité sans devoir entrer un nom d’utilisateur et un mot de passe. Cependant, ce n’est pas possible avec les protocoles HTTP ‑ toute connexion nécessite un nom d’utilisateur et un mot de passe. Cela devient même plus difficile avec des systèmes à authentification à deux facteurs, où le mot de passe utilisé est généré dynamiquement au hasard et devient imprononçable.

Heureusement, Git dispose d’un système de gestion d’identifiants qui peut faciliter cette gestion. Git propose de base quelques options :

  • Par défaut, rien n’est mis en cache. Toutes les connexions vous demanderont votre nom d’utilisateur et votre mot de passe.

  • Le mode « cache » conserve en mémoire les identifiants pendant un certain temps. Aucun mot de passe n’est stocké sur le disque et les identifiants sont oubliés après 15 minutes.

  • Le mode « store » sauvegarde les identifiants dans un fichier texte simple sur le disque, et celui-ci n’expire jamais. Ceci signifie que tant que vous ne changerez pas votre mot de passe sur le serveur Git, vous n’aurez plus à entrer votre mot de passe. Le défaut de cette approche et que vos mots de passe sont stockés en clair dans un fichier texte dans votre répertoire personnel.

  • Si vous utilisez un Mac, Git propose un mode « osxkeychain », qui met en cache les identifiants dans un trousseau sécurisé attaché à votre compte système.

  • Si vous utilisez Windows, vous pouvez installer une application appelée « winstore ». C’est similaire à l’assistant « osxkeychain » décrit ci-dessus, mais utilise le Windows Credential Store pour sauvegarder les informations sensibles. winstore peut être téléchargé à https://gitcredentialstore.codeplex.com.

Vous pouvez choisir une de ces méthodes en paramétrant une valeur de configuration Git :

$ git config --global credential.helper cache

Certains de ces assistants ont des options. L’assistant « store » accepte un argument --file <chemin> qui permet de personnaliser l’endroit où le fichier texte est sauvegardé (par défaut, c’est ~/.git-credentials). L’assistant cache accepte une option --timeout <secondes> qui modifie la période de maintien en mémoire (par défaut, 900, soit 15 minutes). Voici un exemple de configuration de l’option « store » avec un nom de fichier personnalisé :

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

Git vous permet même de configurer plusieurs assistants. Lors de la recherche d’identifiants pour un serveur donné, Git les interrogera dans l’ordre jusqu’à la première réponse. Pour la sauvegarde des identifiants, Git enverra le nom d’utilisateur et le mot de passe à tous les assistants et ceux-ci pourront choisir ce qu’ils en font. Voici à quoi ressemblerait un .gitconfig si vous utilisiez un fichier d’identifiants sur une clé USB mais souhaiteriez utilisez l’option de cache pour éviter des frappes trop fréquentes si la clé n’est pas insérée.

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

Sous le capot

Comment tout ceci fonctionne-t-il ? La commande d’origine de Git pour le système d’assistants d’indentification est git credential, qui accepte une commande comme argument, puis d’autres informations via stdin.

Un exemple peut aider à mieux comprendre cela. Supposons qu’un assistant d’identification a été configuré et que l’assistant a stocké les identifiants pour mygithost. Voici une session qui utilise la commande « fill » qui est invoquée quand Git essaie de trouver les identifiants pour un hôte :

$ 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. C’est la ligne de commande qui démarre l’interaction.

  2. Git-credential attend la saisie d’informations sur stdin. Nous lui fournissons les informations que nous connaissons : le protocole et le nom d’hôte.

  3. Une ligne vide indique que l’entrée est complète et le système d’identification devrait répondre avec les informations qu’il connaît.

  4. Git-credential prend alors la main et écrit sur la sortie standard les informations qu’il a trouvées.

  5. Si aucune information d’identification n’a été trouvée, Git demande le nom d’utilisateur et le mot de passe, et les fournit sur la sortie standard d’origine (ici elles sont rattachées à la même console).

Le système d’aide à l’identification invoque en fait un programme complètement séparé de Git lui-même. Lequel est invoqué et comment il est invoqué dépendent de la valeur de configuration credential.helper. Cette valeur peut prendre plusieurs formes :

Valeur de configuration Comportement

foo

lance git-credential-foo

foo -a --opt=bcd

lance git-credential-foo -a --opt=bcd

/chemin/absolu/foo -xyz

lance /chemin/absolu/foo -xyz

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

Le code après ! est évalué dans un shell

Donc les assistants décrits ci-dessus sont en fait appelés git-credential-cache, git-credential-store, et ainsi de suite et nous pouvons les configurer pour accepter des arguments en ligne de commande.

La forme générale pour ceci est git-credential-foo [args] <action>. Le protocole stdin/stdout est le même que pour git-credential, mais en utilisant un ensemble d’actions légèrement différent :

  • get est une requête pour une paire nom d’utilisateur/mot de passe.

  • store est une requête pour sauvegarder des identifiants dans la mémoire de l’assistant.

  • erase purge de la mémoire de l’assistant les identifiants répondants aux critères.

Pour les actions store et erase, aucune réponse n’est exigée (Git les ignore de toute façon). Pour l’action get cependant, Git est très intéressé par ce que l’assistant peut en dire. Si l’assistant n’a rien à en dire d’utile, il peut simplement sortir sans rien produire, mais s’il sait quelque chose, il devrait augmenter l’information fournie avec celle qu’il a stockée. La sortie est traitée comme une série de déclarations d’affectation ; tout ce qui est fourni remplacera ce que Git connaît déjà.

Voici le même exemple que ci-dessus, mais en sautant git-credential et en s’attaquant directement à 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. Ici nous indiquons à git-credential-store de sauvegarder des identifiants : le nom d’utilisateur (username) « bob » et le mot de passe (password) « s3cre7 » doivent être utilisés quand https://mygithost est accédé.

  2. Maintenant, nous allons récupérer ces identifiants. Nous fournissons les parties de l’information de connexion que nous connaissons (https://mygithost), suivi d’une ligne vide.

  3. git-credential-store répond avec le nom d’utilisateur et le mot de passe que nous avons précédemment stockés.

Voici à quoi ressemble le fichier ~/git.store :

https://bob:s3cre7@mygithost

C’est juste une série de lignes, chacune contenant des URLs contenant les informations d’identification. Les assistants osxkeychain et winstore utilisent le format natif de leurs banques de stockage, tandis que cache utilise son propre format en mémoire (qu’aucun autre processus ne peut lire).

Un cache d’identifiants personnalisé

Étant donné que git-credential-store et consort sont des programmes séparés de Git, il y a peu à penser que n’importe quel programme peut être un assistant d’identification Git. Les assistants fournis par Git gèrent de nombreux cas d’utilisation habituels, mais pas tous. Par exemple, supposons que votre équipe dispose de certains identifiants qui sont partagés par tous, pour le déploiement. Ils sont stockés dans un répertoire partagé, mais vous ne les copiez pas dans votre propre magasin d’identifiants parce qu’ils changent souvent. Aucun assistant existant ne gère ce cas ; voyons ce qu’il faudrait pour écrire le nôtre. Ce programme doit présenter certaines fonctionnalités clé :

  1. La seule action à laquelle nous devons répondre est get ; store et erase sont des opérations d’écriture, donc nous sortirons directement et proprement dans ces cas.

  2. Le format du fichier d’identifiants partagés est identique à celui utilisé par git-credential-store.

  3. L’emplacement de ce fichier est assez standard, mais nous devrions pouvoir laisser l’utilisateur spécifier une chemin en cas de besoin.

Une fois de plus, nous écrirons cette extension en Ruby, mais n’importe quel langage fonctionnera, tant que Git peut lancer un exécutable à la fin. Voici le code source complet de ce nouvel assistant d’identification :

#!/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'] then
        puts "protocol=#{prot}"
        puts "host=#{host}"
        puts "username=#{user}"
        puts "password=#{pass}"
        exit(0)
    end
end
  1. Ici, nous analysons les options de la ligne de commande, pour permettre à l’utilisateur de spécifier un fichier. Par défaut, c’est ~/.git-credentials.

  2. Ce programme ne répondra que si l’action est get et si le fichier magasin existe.

  3. Cette boucle lit depuis stdin jusqu’à la première ligne vide. Les entrées sont stockées dans le hash known pour référence ultérieure.

  4. Cette boucle lit le contenu du fichier magasin, et recherche les correspondances. Si le protocole et l’hôte depuis known correspondent à la ligne, le programme imprime les résultats sur stdout et sort.

Nous allons sauvegarder notre assistant comme git-credential-read-only, le placer quelque part dans notre PATH et le marquer exécutable. Voici à quoi ressemble une session interactive :

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

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

Puisque son nom commence par git-, nous pouvons utiliser une syntaxe simple pour la valeur de configuration :

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

Comme vous pouvez le voir, étendre ce système est plutôt direct et peut résoudre des problèmes communs pour vous et votre équipe.