Git
章 ▾ 第2版

7.14 Gitツール - 認証情報ストレージ

認証情報ストレージ

リモートへの接続にSSHトランスポートを使用している場合、パスフレーズのないキーを使用することができ、ユーザー名とパスワードを入力せずに安全にデータを転送できます。ただし、HTTPプロトコルではこれは不可能です。接続ごとにユーザー名とパスワードが必要です。これは、パスワードに使用するトークンがランダムに生成され、発音できない二要素認証を使用するシステムではさらに困難になります。

幸いなことに、Gitにはこれを支援する認証情報システムがあります。Gitには、すぐに使えるいくつかのオプションがあります。

  • デフォルトでは、まったくキャッシュしません。接続ごとにユーザー名とパスワードの入力を求められます。

  • 「cache」モードでは、認証情報を一定期間メモリに保持します。パスワードはディスクに保存されず、15分後にキャッシュから削除されます。

  • 「store」モードでは、認証情報をディスク上のプレーンテキストファイルに保存し、期限切れになりません。つまり、Gitホストのパスワードを変更するまで、認証情報を再度入力する必要はありません。このアプローチの欠点は、パスワードがホームディレクトリ内のプレーンファイルにクリアテキストで保存されることです。

  • macOSを使用している場合、Gitには「osxkeychain」モードが付属しており、システムアカウントにアタッチされた安全なキーチェーンに認証情報をキャッシュします。この方法は、ディスクに認証情報を保存し、期限切れになりませんが、HTTPS証明書とSafariの自動入力が格納される同じシステムで暗号化されます。

  • Windowsを使用している場合は、Git for WindowsをインストールするときにGit Credential Manager機能を有効にするか、スタンドアロンサービスとして最新のGCMを個別にインストールできます。これは、上記で説明した「osxkeychain」ヘルパーに似ていますが、機密情報を制御するためにWindows資格情報ストアを使用します。また、WSL1またはWSL2に認証情報を提供することもできます。詳細については、GCMインストール手順を参照してください。

Git構成値を設定することで、これらのいずれかの方法を選択できます。

$ git config --global credential.helper cache

これらのヘルパーの一部にはオプションがあります。「store」ヘルパーは、プレーンテキストファイルの保存場所をカスタマイズする--file <path>引数を取ることができます(デフォルトは〜/.git-credentials)。「cache」ヘルパーは、デーモンを実行し続ける時間を変更する--timeout <seconds>オプションを受け入れます(デフォルトは「900」、つまり15分です)。カスタムファイル名で「store」ヘルパーを構成する方法の例を次に示します。

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

Gitでは、複数のヘルパーを構成することもできます。特定のホストの認証情報を検索する場合、Gitはそれらを順番に照会し、最初の回答が提供された後に停止します。認証情報を保存する場合、Gitはリスト内のすべてのヘルパーにユーザー名とパスワードを送信し、それらのヘルパーはそれらをどうするかを選択できます。サムドライブに認証情報ファイルがあるが、ドライブが接続されていない場合にタイプ数を減らすためにインメモリキャッシュを使用したい場合、.gitconfigは次のようになります。

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

内部構造

これはすべてどのように機能するのでしょうか?認証情報ヘルパーシステムのGitのルートコマンドはgit credentialであり、引数としてコマンドを取り、stdinを介してさらに多くの入力を受け取ります。

例を挙げると、これは理解しやすいかもしれません。認証情報ヘルパーが構成されており、ヘルパーがmygithostの認証情報を保存しているとします。Gitがホストの認証情報を検索しようとするときに呼び出される「fill」コマンドを使用するセッションを次に示します。

$ 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. これは、インタラクションを開始するコマンドラインです。

  2. Git-credentialは、stdinでの入力を待っています。プロトコルとホスト名という既知のものを提供します。

  3. 空白行は、入力が完了したことを示しており、認証情報システムは既知の情報で応答する必要があります。

  4. Git-credentialが引き継ぎ、見つかった情報をstdoutに書き込みます。

  5. クレデンシャルが見つからない場合、Gitはユーザーにユーザー名とパスワードを尋ね、それらを発行元のstdoutに返します(ここでは同じコンソールに接続されています)。

クレデンシャルシステムは実際にはGit自体とは別のプログラムを呼び出しています。どのプログラムを使用するか、どのように使用するかは、credential.helperの設定値によって異なります。設定値にはいくつかの形式があります。

設定値 動作

foo

git-credential-fooを実行します。

foo -a --opt=bcd

git-credential-foo -a --opt=bcdを実行します。

/absolute/path/foo -xyz

/absolute/path/foo -xyzを実行します。

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

!の後のコードがシェルで評価されます。

上記で説明したヘルパーは実際にはgit-credential-cachegit-credential-storeなどの名前で呼ばれており、コマンドライン引数を受け取るように設定できます。この一般的な形式は「git-credential-foo [args] <action>」です。stdin/stdoutプロトコルはgit-credentialと同じですが、使用するアクションのセットが若干異なります。

  • getは、ユーザー名/パスワードのペアのリクエストです。

  • storeは、このヘルパーのメモリにクレデンシャルのセットを保存するリクエストです。

  • eraseは、このヘルパーのメモリから、指定されたプロパティのクレデンシャルを消去します。

storeおよびeraseアクションの場合、応答は不要です(Gitはとにかく無視します)。ただし、getアクションの場合、Gitはヘルパーが何を言っているかに非常に興味があります。ヘルパーが役立つ情報を何も知らない場合は、何も出力せずに終了できますが、知っている場合は、保存されている情報で提供された情報を拡張する必要があります。出力は一連の代入文のように扱われます。提供されたものはすべて、Gitが既に知っているものを置き換えます。

上記の例と同じですが、git-credentialをスキップして、直接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. ここでは、git-credential-storeにいくつかのクレデンシャルを保存するように指示しています。ユーザー名「bob」とパスワード「s3cre7」は、https://mygithostにアクセスするときに使用されます。

  2. 次に、これらのクレデンシャルを取得します。既に知っている接続の一部(https://mygithost)と空の行を提供します。

  3. git-credential-storeは、上記で保存したユーザー名とパスワードで応答します。

~/git.storeファイルは次のようになります。

https://bob:s3cre7@mygithost

これは、クレデンシャルで装飾されたURLを含む一連の行です。osxkeychainおよびwincredヘルパーは、バックアップストレージのネイティブ形式を使用しますが、cacheは独自のインメモリ形式を使用します(他のプロセスは読み取れません)。

カスタムクレデンシャルキャッシュ

git-credential-storeなどがGitとは別のプログラムであることを考えると、どのプログラムでもGitクレデンシャルヘルパーにできることは容易に想像できます。Gitが提供するヘルパーは、多くの一般的なユースケースに対応していますが、すべてではありません。たとえば、チームがチーム全体で共有するクレデンシャルを持っているとします。これはおそらくデプロイメント用です。これらは共有ディレクトリに保存されていますが、頻繁に変更されるため、独自のクレデンシャルストアにコピーしたくありません。既存のヘルパーはどれもこのケースをカバーしていません。独自のヘルパーを作成するにはどうすればよいかを見てみましょう。このプログラムに必要な重要な機能がいくつかあります。

  1. 注意する必要のあるアクションはgetだけです。storeeraseは書き込み操作であるため、それらを受信したときに正常に終了するだけです。

  2. 共有クレデンシャルファイルのファイル形式は、git-credential-storeで使用される形式と同じです。

  3. ファイルの場所はかなり標準的ですが、念のため、ユーザーがカスタムパスを渡せるようにする必要があります。

今回も、この拡張機能をRubyで記述しますが、Gitが完成品を実行できる限り、どの言語でも機能します。これが新しいクレデンシャルヘルパーの完全なソースコードです。

#!/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. ここでは、コマンドラインオプションを解析し、ユーザーが入力ファイルを指定できるようにします。デフォルトは~/.git-credentialsです。

  2. このプログラムは、アクションがgetで、バッキングストアファイルが存在する場合にのみ応答します。

  3. このループは、最初の空白行に達するまでstdinから読み取ります。入力は、後で参照するためにknownハッシュに保存されます。

  4. このループは、ストレージファイルの内容を読み取り、一致するものを探します。knownのプロトコル、ホスト、ユーザー名がこの行と一致する場合、プログラムは結果をstdoutに出力して終了します。

ヘルパーをgit-credential-read-onlyとして保存し、PATHのどこかに配置して、実行可能にします。インタラクティブセッションは次のようになります。

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

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

名前が「git-」で始まるため、設定値には単純な構文を使用できます。

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

ご覧のとおり、このシステムの拡張は非常に簡単で、あなたとあなたのチームの一般的な問題を解決できます。

scroll-to-top