Ink

Contents related to tech, hobby, etc

git credentialにpassを使う

|

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形式にしようと思いましたが、

  • // がパスとして認識されてしまう

  • Usernameget の際に与えられない

ことから、

  • プロトコルとホスト名の間はコロン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
/tmp/babel-EN1Wbc/sh-script-qhWEEa: line 3: store_key: command not found

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

username=me password=some_simple_password

3. Eraseの作成(未作成)

Eraseも本来は存在しますが、入力で何を期待すれば良いのかが分からないため今はスルーすることにします。

exit

最終的に出来たもの

Cj-bc/dotfiles -- dotfiles/bin/git-credential-pass にあります。

このポストから org tangle しようとしたのですが、諸々(関数化したりetc)上手く出来なかったのでtangleではないです 実用的なスクリプトとしてはそちらを参照して下さい