チャプター ▾ 第2版

7.14 Gitツール - 認証情報の保存

認証情報の保存

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

幸いにも、Gitにはこれを助けることができる認証情報システムがあります。Gitにはいくつかのオプションがデフォルトで提供されています

  • デフォルトでは全くキャッシュされません。すべての接続でユーザー名とパスワードの入力を求められます。

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

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

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

  • Windowsを使用している場合、Git for Windowsのインストール時にGit Credential Manager機能を有効にするか、最新のGCMをスタンドアロンサービスとして個別にインストールできます。これは上記で説明した「osxkeychain」ヘルパーに似ていますが、Windows Credential Storeを使用して機密情報を制御します。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はユーザー名とパスワードをリスト内のすべてのヘルパーに送信し、各ヘルパーはそれらをどうするかを選択できます。以下は、USBメモリに認証情報ファイルがあり、ドライブが接続されていない場合にインメモリキャッシュを使用して入力の手間を省きたい場合の.gitconfigの例です

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

内部の仕組み

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

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

$ 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] 」です。stdin/stdoutプロトコルはgit-credentialと同じですが、若干異なるアクションのセットを使用します

  • getはユーザー名/パスワードのペアを要求します。

  • storeはこのヘルパーのメモリに認証情報のセットを保存する要求です。

  • eraseはこのヘルパーのメモリから指定されたプロパティの認証情報を削除します。

storeeraseアクションの場合、応答は不要です(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にいくつかの認証情報を保存するよう指示します。https://mygithostにアクセスする際に、ユーザー名「bob」とパスワード「s3cre7」が使用されます。

  2. 次に、それらの認証情報を取得します。既に知っている接続の一部(https://mygithost)と空行を提供します。

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

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

https://bob:s3cre7@mygithost

これは一連の行で、それぞれに資格情報が装飾されたURLが含まれています。osxkeychainwincredヘルパーは、そのバッキングストアのネイティブフォーマットを使用しますが、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