git credentialにpassを使う
2022/02/07現在、passはtree 2.0.0のリリースの影響を受けて
git-credentialと組み合わせて使うと上手く動きません(何も出力してくれない)
この修正については別途記事を書きますが、 passを呼び出している所を
pass 3>&-
と修正してください。ファイルディスクリプタ3番が開いている場合の
仕様変更を修正できます。
gitの認証情報を保存する為に使う git-credential helper
ですが、passを用いたものはデフォルトで存在しないので作ることにします。
ちなみに元はどこかで見たアイデアです。 pythonを用いた実装があります(archlinux wiki -- Pass#Gitの統合を参照) 私はなんとなく自作したかったので作っています。
git credential helperについて
git credentialは、機密を保存する為に外部のプログラムを使用します。 その方法は git-scm.com Git-様々なツール-認証情報の保存 で書かれています。
Git credential helperの役割
git credential helperは、3つの用途で呼ばれます。
store
get
erase
です。
store -- 新しい認証情報を登録する
Storeは、新しい認証情報を登録する時に呼び出されます。 標準入力から情報を受け取り、それを保存します。 標準出力から何かを返しても良いですが、gitはそれを無視するので必要ではないです。
erase -- 既存の認証情報を消去する
Eraseは、認証情報を消去したい時に呼ばれます。 標準入力から情報を受け取り、それに該当するものがあればそれを消去します。 標準出力から何かを返しても良いですが、これについてもgitは無視するので必要ではないです。
get -- 渡された情報を基に認証情報を返す
Getは、渡された情報に合致するエントリがあればその情報を返します。 この場合のみ標準出力は必須で、gitはこの標準出力をそのまま代入文として扱います。
helperの作成
さて、簡単な説明も済んだので実際にスクリプトを組もうと思います。 例示ではRubyが使われていましたが、passコマンドを使用することと、自分がbash に慣れていることから、bashを使おうと思います。
データの格納方法
データはpassに格納しますが、格納する階層構造は以下のようにしようと思います。
Password Store └── git-cli └── <protocol>:<host> └── <username>
トップの git-cli
はgitコマンドで使うものであることを示し、後はそのままです。
URI形式にしようと思いましたが、
//
がパスとして認識されてしまうUsername
はget
の際に与えられない
ことから、
プロトコルとホスト名の間はコロン1つ
ユーザー名は下位エントリ
にしました。
1. storeの作成
storeは、シンプルに与えられた情報からpassに登録をします。 標準入力は
protocol=<protocol> host=<host> username=<username> password=<password>
という4つの入力です。 これを保存します
function createStoreKey {
case $1 in
"store") echo "git-cli/${2}:${3}/${4}";;
"get") echo "git-cli/${2}:${3}";;
*) ;;
esac
}
function store_key() {
declare -A input=()
# バックスラッシュをエスケープ用に使わないために ~-r~ が必要です。
while IFS=\= read -r key value; do
input[$key]=$value
done < <(cat -)
store_key=$(createStoreKey "store" ${input[protocol]} ${input[host]} ${input[username]})
echo "Save credential info into pass."
echo "Key is set to \"${store_key}\""
cat <<EOF | pass insert $store_key
${input[password]}
${input[password]}
EOF
[ $? -eq 0 ] && echo "Credential saved noormally" \
|| echo "Failed to save credential"
}
実例
例として
protocol=https host=myGitHost.com username=me password=somesimplepassword
を登録してみます。
cat - | store_key
2. getの作成
getは、標準入力からホストとプロトコルの情報を読み込み、 標準出力からユーザー名とパスワードを返します。
function createStoreKey {
case $1 in
"store") echo "git-cli/${2}:${3}/${4}";;
"get") echo "git-cli/${2}:${3}";;
*) ;;
esac
}
declare -A input=()
while IFS=\= read -r key value; do
[[ ${#key} -eq 0 ]] && continue
input[$key]=$value
done < <(cat -)
store_key=$(createStoreKey "get" ${input[protocol]} ${input[host]})
username=$(LANG=C pass show "$store_key" | awk -v storeKey="$store_key" -v store_key="$store_key" '$1 == store_key { getline; sub("`-- +", ""); print }')
[[ -z $username ]] && { echo "failed to look up user for ${store_key}. exit" ; exit; }
password=$(LANG=C pass show "${store_key}/$username")
cat <<EOF
username=$username
password=$password
EOF
実例
getに必要なのはホストとプロトコル(とそれに続く空白行)だけなので以下の情報を入れます
protocol=https host=myGitHost.com
3. Eraseの作成(未作成)
Eraseも本来は存在しますが、入力で何を期待すれば良いのかが分からないため今はスルーすることにします。
exit
最終的に出来たもの
Cj-bc/dotfiles -- dotfiles/bin/git-credential-pass にあります。
このポストから org tangle
しようとしたのですが、諸々(関数化したりetc)上手く出来なかったのでtangleではないです
実用的なスクリプトとしてはそちらを参照して下さい