章 ▾ 第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の認証情報を保存しているとします。以下は、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はこのヘルパーのメモリから指定されたプロパティの認証情報をクリアします。

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