Gitリポジトリに誤ってAWSアクセスキーなどをコミットしないようにgit-secretsを導入

2023-12-29Git

はじめに

サービスの事項環境として AWS を利用していた場合に特に気をつけなければ行けないのが、AWS のアクセスキーのような「API キー」の類をコミットしてしまうことです。

コードに記述しないように気をつけていたとしても、ひょんな事でコミットしてしまうことがあります。

このようなミスに気付けるように git-secrets を導入してみましょう。

検証環境

$ uname -moi
arm64 unknown Darwin

$ bash -version | head -n 1
GNU bash, バージョン 5.2.15(1)-release (aarch64-apple-darwin22.1.0)

git-secrets とは

git-secrets は、「API キー」だけでなく、パスワードやその他のセンシティブ情報のコミットを防ぐためのツールです。

Git リポジトリに秘匿情報が含まれないように、次のものを監視します。

  • コミット
  • コミットメッセージ
  • --no-ff マージ

上記のいずれかに秘匿情報が含まれていた場合には、 コミットが拒否されるようにできます。

git commit のタイミングでチェックが実行されるということです

インストール

PATH 環境変数に設定されているいずれかのディレクトリ ( echo "$PATH" | sed 's/:/\n/g' で確認できる ) に配置しておきましょう。

Mac OS 環境で Homebrew が使える場合はかんたんにインストールできます。

$ brew install git-secrets

Linux 環境では make を利用します。

$ git clone https://github.com/awslabs/git-secrets.git
Cloning into 'git-secrets'...
fatal: unable to access 'https://github.com/awslabs/git-secrets.git/': server certificate verification failed. CAfile: none CRLfile: none

$ cd git-secrets/

$ make install

それ以外の環境へのインストール方法もドキュメントに掲載してくれています。

使い方

※インストールしたばかりですが、ここから注意が必要です

※インストールしたばかりですが git-secrets はリポジトリのコミットを監視してくれません

すでに git clone 済みのローカルリポジトリに対しては、次のコマンドを実行し git-secrets を有効にする必要があります。

$ cd /path/to/my/repo

$ git secrets --install
✓ Installed commit-msg hook to .git/hooks/commit-msg
✓ Installed pre-commit hook to .git/hooks/pre-commit
✓ Installed prepare-commit-msg hook to .git/hooks/prepare-commit-msg

$ git secrets --register-aws
OK

git secrets --install

git secrets --install を実行すると、 .git/hooks ディレクトリ配下に 3 つのスクリプトが作成されます。

中身を見るとわかりますが、 git secrets コマンドを実行しています。

$ cat .git/hooks/commit-msg
#!/usr/bin/env bash
git secrets --commit_msg_hook -- "$@"

git secrets --register-aws

git secrets --register-aws を実行すると、 .git/config に設定が追加されます。

$ cat .git/config
[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
[secrets]
        providers = git secrets --aws-provider
        patterns = (A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}
        patterns = (\"|')?(AWS|aws|Aws)?_?(SECRET|secret|Secret)?_?(ACCESS|access|Access)?_?(KEY|key|Key)(\"|')?\\s*(:|=>|=)\\s*(\"|')?[A-Za-z0-9/\\+=]{40}(\"|')?
        patterns = (\"|')?(AWS|aws|Aws)?_?(ACCOUNT|account|Account)_?(ID|id|Id)?(\"|')?\\s*(:|=>|=)\\s*(\"|')?[0-9]{4}\\-?[0-9]{4}\\-?[0-9]{4}(\"|')?
        allowed = AKIAIOSFODNN7EXAMPLE
        allowed = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

secrets セクションが追加された設定になります。

git-secretspatterns として設定された正規表現にマッチしたものをセキュア情報として認識し、警告します。

git secrets --register-awspatterns として AWS に関するアクセスキーやアカウント ID などを検出する正規表現を追加します。

ただし、 alloed に設定された文字列は例外としてチェック対象外となります

動作確認

動作を確認してみます。

セキュアな情報を含んだファイルをコミットしてみます。

# `patterns` に引っかかりそうな文字列を記述したファイルを作成
$ echo "access_key = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" > memo.txt

# 追加
$ git add .

# コミット
$ git commit -m "first commit."
memo.txt:1:access_key = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

[ERROR] Matched one or more prohibited patterns

Possible mitigations:
- Mark false positives as allowed using: git config --add secrets.allowed ...
- Mark false positives as allowed by adding regular expressions to .gitallowed at repository's root directory
- List your configured patterns: git config --get-all secrets.patterns
- List your configured allowed patterns: git config --get-all secrets.allowed
- List your configured allowed patterns in .gitallowed at repository's root directory
- Use --no-verify if this is a one-time false positive

エラーが発生し、  git commit が失敗しました。

チェック対象外のパターンを追加する

ケースは少ないです、どうしてもチェックの対象から除外したい文字列があった場合には、2 つの方法で実現できます。

1. .git/configallowed を追加する

.git/configallowed を追記します。

# コマンドで追記していますが、エディタで編集しても構わない
$ echo -e '\tallowed = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' >> .git/config

# 末尾に追加されていることを確認
$ tail -n 5 .git/config
        patterns = (\"|')?(AWS|aws|Aws)?_?(SECRET|secret|Secret)?_?(ACCESS|access|Access)?_?(KEY|key|Key)(\"|')?\\s*(:|=>|=)\\s*(\"|')?[A-Za-z0-9/\\+=]{40}(\"|')?
        patterns = (\"|')?(AWS|aws|Aws)?_?(ACCOUNT|account|Account)_?(ID|id|Id)?(\"|')?\\s*(:|=>|=)\\s*(\"|')?[0-9]{4}\\-?[0-9]{4}\\-?[0-9]{4}(\"|')?
        allowed = AKIAIOSFODNN7EXAMPLE
        allowed = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
        allowed = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

先程コミット失敗したファイルをコミットできるようになります。

$ echo "access_key = yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy" > memo.txt

$ git add .

$ git commit -m "first commit."
[master (root-commit) 6d8e2ca] first commit.
 l files changed, 1 insertions(+)
 create mode 100644 memo.txt

正規表現が利用できるため、より簡潔に記述できます。
今度は git-secrets を使って .git/config に対象外パターンを追加してみます。

# y が 40 文字である場合には許可する
$ git secrets --add --allowed 'y{40}'

# 末尾に追加されていることを確認
$ tail -n 5 .git/config
        patterns = (\"|')?(AWS|aws|Aws)?_?(ACCOUNT|account|Account)_?(ID|id|Id)?(\"|')?\\s*(:|=>|=)\\s*(\"|')?[0-9]{4}\\-?[0-9]{4}\\-?[0-9]{4}(\"|')?
        allowed = AKIAIOSFODNN7EXAMPLE
        allowed = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
        allowed = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
        allowed = y{40}

# memo.txt を編集
$ echo "access_key = yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy" > memo.txt

$ git add .

$ git commit -m "first commit."
[master c673b23] first commit.
 1 file changed, 1 insertion(+), 1 deletion(-)

2. .gitallowedallowed を追加する

チームメンバーで共有する場合は、 .gitallowed というファイルを作成し Git リポジトリ上で管理すると良いでしょう。

# y が 40 文字である場合には許可する
$ echo 'z{40}' >> .gitallowed

# 末尾に追加されていることを確認
$ tail .gitallowed
z{40}

# memo.txt を編集
$ echo "access_key = zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" > memo.txt

$ git add .

$ git commit -m "first commit."
[master 7891bf2] first commit.
 1 file changed, 1 insertion(+), 1 deletion(-)

過去のコミット履歴を検査する

git-secrets 導入前のコミットに対してもスキャンを実行したい場合、 git secrets --scan-history コマンドを使うことができます。

# 先程の対象外リストを削除
$ rm .gitallowed

$ git secrets --scan-history
7891bf20b27a4a6a14d68ffcd221293f2161c572:memo.txt:1:access_key = zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz

[ERROR] Matched one or more prohibited patterns

Possible mitigations:
- Mark false positives as allowed using: git config --add secrets.allowed ...
- Mark false positives as allowed by adding regular expressions to .gitallowed at repository's root directory
- List your configured patterns: git config --get-all secrets.patterns
- List your configured allowed patterns: git config --get-all secrets.allowed
- List your configured allowed patterns in .gitallowed at repository's root directory
- Use --no-verify if this is a one-time false positive

git secrets --register-aws を PC 内の全 Git リポジトリに適用

git secrets --register-aws を各 Git リポジトリで実行するのは面倒です。

git secrets --register-aws コマンドは .git/config ファイルに [secrets] セクションを追加します。
.git/config ではなく ~/.gitconfig に設定できれば PC 内の全 Git リポジトリに対する patterns 設定を適用できます。

エディタで編集しても良いですが、 git secrets --register-aws --global コマンドを実行することで ~/.gitconfig に設定が追加されます。

git secrets --installgit init 実行時に適用

ローカルの Git リポジトリ内で git secrets --install コマンドを実行することで、 .git/hooks にスクリプトが作成されました。

すでに存在しているローカル Git リポジトリに対しては面倒ですが git secrets --install する必要があります。

けれども今後 git clone または git init した Git リポジトリに自動的に .git/hooks が作成されるようにしておくことができます。

$ git secrets --install ~/.git-templates/git-secrets
$ git config --global init.templatedir '~/.git-templates/git-secrets'

( おまけ ) git-secrets が正しく動作しない場合

僕の PC 環境では、以前は問題なく利用できていたのですが、 2023-01-31 現在正しく動作していませんでした。

今回のエントリをまとめているときに正しく動作していないことに気が付きました

$ which git
/opt/homebrew/bin/git

$ git --version
git version 2.43.0

$ grep --version | head -n 3
grep (GNU grep) 3.11
Packaged by Homebrew
Copyright (C) 2023 Free Software Foundation, Inc.

git-secrets 実行スクリプトのコードを解析してみました。

$ which git-secrets
/opt/homebrew/bin/git-secrets

# ファイルを開いて内容を確認
$ vi $(which git-secrets)

内部で git grep コマンドを実行していましたが、 -E オプションを指定しています。
-E オプション付きで grep コマンドを実行した場合、 正規表現内でスペース全般(タブ、空白、改行)を表す \s が使えません。
かわりに [[:space::]] という表現が使えます。
加えて、 \\- の表現に問題があるようで、 \\ を除去し - とします。

そこで git secrets --register-aws で生成された .git/config ( --global オプション付きの場合は ~/.gitconfig ) ファイルの内容を書き換えます。

# はじめは以下のようになっている
$ cat .git/config
[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
        ignorecase = true
[secrets]
        patterns = (\"|')?(AWS|aws|Aws)?_?(SECRET|secret|Secret)?_?(ACCESS|access|Access)?_?(KEY|key|Key)(\"|')?\\s*(:|=>|=)\\s*(\"|')?[A-Za-z0-9/\\+=]{40}(\"|')?
        patterns = (\"|')?(AWS|aws|Aws)?_?(ACCOUNT|account|Account)_?(ID|id|Id)?(\"|')?\\s*(:|=>|=)\\s*(\"|')?[0-9]{4}\\-?[0-9]{4}\\-?[0-9]{4}(\"|')?

# \\\\s を [[:space:]] に変換します
$ sed -i '/patterns/s/\\\\s/[[:space:]]/g' .git/config
$ sed -i '/patterns/s/\\\\-/-/g' .git/config

# Global な設定を変更する場合はこちら
$ sed -i '/patterns/s/\\\\s/[[:space:]]/g' ~/.gitconfig
$ sed -i '/patterns/s/\\\\-/-/g' ~/.gitconfig

$ cat .git/config
[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
        ignorecase = true
[secrets]
        patterns = (\"|')?(AWS|aws|Aws)?_?(SECRET|secret|Secret)?_?(ACCESS|access|Access)?_?(KEY|key|Key)(\"|')?[[:space:]]*(:|=>|=)[[:space:]]*(\"|')?[A-Za-z0-9/\\+=]{40}(\"|')?
        patterns = (\"|')?(AWS|aws|Aws)?_?(ACCOUNT|account|Account)_?(ID|id|Id)?(\"|')?[[:space:]]*(:|=>|=)[[:space:]]*(\"|')?[0-9]{4}-?[0-9]{4}-?[0-9]{4}(\"|')?

ひとこと

以前は正常に動いていた気がしていたのですが、勘違いでしょうか?

2023-12-29Git