この記事の背景

New RelicをTerraformで導入する検証のため、AWS CodeCommit(以下CodeCommit)に新しくリポジトリをたてて作成したtfファイルをpushしようとした際に403エラーがでました。

前日まで同じ手順でリポジトリを立てて、pushする際には403エラーが出ることはなかったのですが、今回403エラーが出てしまったというのが背景です。

エラーの切り分けのために何をしたのか

私がまず確認したのは以下の3点です。

・AWS環境のクレデンシャル情報を正しく登録できているか

・リポジトリの認証情報ヘルパーの設定

・CloudTrailのイベント履歴

一つ一つ詳細に深掘りしていきます。

AWS環境のクレデンシャル情報

実行コマンド

aws sts get-caller-identity

このコマンドを実行することで今自分がどの権限でどのAWS環境を操作しているかを確認することができます。

アカウント情報やロールの情報などを確認するコマンドなので、実行結果をこの記事に記載することはできないのですが、認証情報が期限切れになっていると以下のエラー文が表示されます。

An error occurred (ExpiredToken) when calling the GetCallerIdentity operation: The security token included in the request is expired

今回確認をした際にはこのエラー文が表示されなかったかつ、検証用のadmin権限のロールが確認できたので、この確認事項については問題ないと判断できます。

リポジトリの認証情報ヘルパーの設定

リポジトリの認証情報ヘルパーの設定とは、GitがCodeCommitと通信する際の認証を自動化するための設定です。

git pushやgit pullをするたびにユーザー名やパスワードを入力する代わりにAWS CLIに一時的なパスワードを自動生成させてGitに渡すように指示することができます。

この設定には、以下の二つのコマンドを実行する必要があります。

credential.helper=!aws codecommit credential-helper $@

credential.usehttppath=true

一つ目のcredential.helper=!aws codecommit credential-helper $@コマンドを実行することで、認証情報が必要になった際にaws codecommit credential-helperコマンドをシェルコマンドで実行させることができます。

二つ目のcredential.usehttppath=trueコマンドを実行することで、ホスト名が同じでも、リポジトリのパスが異なったら別の認証情報として扱うように認識させることができます。

これらの設定が正しくできているかを確認する方法として、git config –local -lというコマンドがあります。

このコマンドを実行することで、リポジトリ限定の設定内容を全て一覧表示することができます。

このコマンドを実行した際に、一覧の中に下の2行が表示されれば、問題なく設定できているということになります。

credential.helper=!aws codecommit credential-helper $@

credential.usehttppath=true

確認コマンドの実行結果

core.repositoryformatversion=0

core.filemode=true

core.bare=false

core.logallrefupdates=true

core.ignorecase=true

core.precomposeunicode=true

credential.helper=!aws codecommit credential-helper $@

credential.usehttppath=true

remote.origin.url=https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/k-nakatani-training-dev-terraform

remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*

この一覧の中に上記の2行が含まれているので、この確認事項については問題ないと判断できます。

CloudTrailのイベント履歴

CloudTrailのイベント履歴を確認することで、これまでCodeCommitへ送信されたGitコマンドのイベント情報を確認することができます。

このスクリーンショットから確認できることとして、403エラーが起きたのは10月31日なのに対して、最後に確認されたイベント情報は10月28日です。

このことからローカルから実行したgit pushコマンドがAWS環境のCodeCommitへリクエストが渡されていない(AWS到達する前にローカルで失敗している)と判断しました。

今回参考になった公式ドキュメント

https://docs.aws.amazon.com/ja_jp/codecommit/latest/userguide/troubleshooting-ch.html

調査の結果上記の公式ドキュメントを発見し、内容を確認することで今回の403エラーの対処ができました。

ドキュメントの中に記載されていたgit config -l –show-origin | grep credentialコマンドを実行することで、自身のPCに設定されているGitの全設定を、どのファイルから読み込んだかを一覧表示をしその中から認証情報に関する設定だけを絞り込むことができます。

このコマンドの実行結果が以下の内容になります。

file:/Library/Developer/CommandLineTools/usr/share/git-core/gitconfig credential.helper=osxkeychain

file:/Users/k-nakatani/.gitconfig credential.helper=!aws codecommit credential-helper $@

file:/Users/k-nakatani/.gitconfig credential.usehttppath=true

file:.git/config credential.helper=!aws codecommit credential-helper $@

file:.git/config credential.usehttppath=true

Gitが設定を読み込むための3つの異なる階層があり、ローカル設定・Global設定・System設定(優先順位順)があります。

ローカル設定:設定範囲は今いるリポジトリだけに適用。

ファイルの見分け方:/.git/config

Global設定:設定範囲はPCにログインしているユーザーが使う全てのリポジトリに適用。

ファイルの見分け方:~/.gitconfig

System設定:設定範囲はそのPCを使う全てのユーザー、全てのリポジトリに適用。

ファイルの見分け方:/Library/Developer/…/gitconfigなどのGitをインストールした場所

上記を踏まえてコマンドの実行結果を整理してみると以下になります。

System設定

file:/Library/Developer/CommandLineTools/usr/share/git-core/gitconfig

credential.helper=osxkeychain

上記の内容からGitの認証情報はmacOSのosxkeychainを使って保存・取得されるということがわかりました。

Global設定

file:/Users/k-nakatani/.gitconfig credential.helper=!aws codecommit credential-helper $@

file:/Users/k-nakatani/.gitconfig credential.usehttppath=true

上記の内容から公式ドキュメントの内容と照らし合わせてCodeCommit用の設定が正しいことがわかりました。

ローカル設定

file:.git/config credential.helper=!aws codecommit credential-helper $@

file:.git/config credential.usehttppath=true

上記の内容から公式ドキュメントの内容と照らし合わせてCodeCommit用の設定が正しいことがわかりました。

キーチェーン情報の確認

これらの内容から次に確認した内容がPCのキーチェーンの情報です。

スクリーンショットからキーチェーンに保存されている認証情報が古くなっていることがわかります。

本来Global設定はSystem設定よりも優先順位が高いので問題なく実行できると思っていましたが、credential.helper の設定は特殊で、単に上書きするのではなく「両方」が動こうとして妨害し合っていました。

対処法

①設定の競合を解消

原因がわかったのでまずは.gitconfigファイルを開いてGlobal設定を修正しました。

修正前の.gitconfigファイル(該当の箇所を抜粋)

[credential]

helper = !aws codecommit credential-helper $@

UseHttpPath = true

修正後の.gitconfigファイル(該当の箇所を抜粋)

[credential]

helper =        #この行を追加

helper = !aws codecommit credential-helper $@

UseHttpPath = true

上記の1行を追加することで、Systemから引き継いだosxkeychainの設定をリセットします。これにより、次の行の正しいヘルパーだけが確実に実行されるようになりました。

②キーチェーンに登録されている古くなっていた認証情報を削除する

スクリーンショットにある認証情報をすべて削除しました。

結果

対処法の実施後、再度git pushを実行しました。

実行コマンド

git push origin HEAD                         

実行結果

Enumerating objects: 12, done.

Counting objects: 100% (12/12), done.

Delta compression using up to 8 threads

Compressing objects: 100% (11/11), done.

Writing objects: 100% (12/12), 4.17 KiB | 4.17 MiB/s, done.

Total 12 (delta 0), reused 0 (delta 0), pack-reused 0

remote: Validating objects: 100%

To https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/k-nakatani-training-dev-terraform

* [new branch]      HEAD -> main

403エラーから解放されました!

原因と今後の対策

原因

macOSのSystem設定に存在する osxkeychain と、CodeCommit用の !aws codecommit… ヘルパーが競合していた。

その結果、osxkeychain が15分で期限切れになるCodeCommitの一時パスワードをキーチェーンに保存し続け、Gitがその古い認証情報を使って接続しようとしたため、AWSから403(認証拒否)エラーが返された。

対策

・新しくリポジトリを立てて、ヘルパーを設定する際にgit config -l –show-origin | grep credentialコマンドを実行し、競合がないかを確認する。

・~/.gitconfigファイルに下記の1行を記載しておく

helper =

まとめ

ここまで読んでいただきありがとうございました。

403エラーに半日悩まされましたが、そのおかげでAWSとGitの認証の仕組み、そしてmacOS環境特有の設定の「クセ」について、ドキュメントを読むだけでは決して得られない深い理解と、粘り強く原因を切り分けることの重要性を学ぶことができました。

この経験を今後の対応に活かしていきます。