secretlint を使って機密情報を git commit できない環境を作る

AWS アクセストークンなど、機密情報の git commit を防ぐ secretlint の紹介記事です
2021.04.25

tl;dr

開発をはじめる前に、次の手順を実行しよう

シェルスクリプトの保存

次のシェルスクリプトを ~/.git-template/hooks/pre-commit として保存しよう。

#!/bin/sh

FILES=$(git diff --cached --name-only --diff-filter=ACMR | sed 's| |\\ |g')
[ -z "$FILES" ] && exit 0

echo "$FILES" | xargs docker run \
  -v "$(pwd):$(pwd)" \
  -w "$(pwd)" \
  --rm \
  secretlint/secretlint secretlint

RET=$?
if [ $RET -eq 0 ] ;then
    exit 0
else
    exit 1
fi

コマンド実行

次のふたつのコマンドを叩こう。

chmod +x ~/.git-template/hooks/*
git config --global init.templatedir ~/.git-template

Docker のインストール

Docker をインストールしよう。

前置き

AWS のアクセスキーをはじめ、他者に知られると被害が発生するものは取り扱いに注意する必要があります。この記事では、こういった情報を機密情報と呼びます。

そもそもアクセスキーを発行しないようにすべきですが、ソフトウェア開発をする場合、そうも言っていられません。 CI/CD や、定期実行するソフトウェアに対して、権限を付与しなければならない場面も多いためです。

開発時に気をつけるべきケースは機密情報を git commit してしまったことに気づかず git push してしまうことです。これを機会的に防ぐ方法をまとめます。

やりたいこと

git commit しようとするときに、 stage に上がっているファイル内に機密情報があると自動的にコミットを失敗させ、機密情報の位置を表示させます。

secretlint とは

secretlint はその名の通り、機密情報が含まれていないかをチェックし、もし機密情報があればエラーとともにその場所を表示してくれるツールです。

git-secret に比べると、単純なコマンドラインということで様々な環境や Git リポジトリーに適用しやすいものとなっています。また、 Docker イメージも配布されているので、セットアップが簡単です。

サポートしている機密情報も多彩です。 2021 年 4 月時点で次をサポートしています。

  • AWS
  • GCP
  • Slack
  • ベーシック認証
  • npm
  • 秘密鍵
  • SendGrid
  • Kubernetes

Git pre-commit フック

git commit 前に何かさせたい場合は pre-commit フックを使います。

幸い、 secretlint 公式が pre-commit 用のスクリプトを公開してくれていますので、これを使いましょう。 Docker を使うバージョンに書き換えたものを掲載します。

#!/bin/sh

FILES=$(git diff --cached --name-only --diff-filter=ACMR | sed 's| |\\ |g')
[ -z "$FILES" ] && exit 0

echo "$FILES" | xargs docker run \
  -v "$(pwd):$(pwd)" \
  -w "$(pwd)" \
  --rm \
  secretlint/secretlint secretlint

RET=$?
if [ $RET -eq 0 ] ;then
    exit 0
else
    exit 1
fi

ダブルクォーテーションで変数をくくっているなど、細部が異なっていますが読みやすさと可搬性を考慮した変更です。

git diff --cached --name-only --diff-filter=ACMR というコマンドで stage に上がっており、追加、コピー、変更、名前変更されたファイル一覧を取得します。これらに対して secretlint でチェックをかける、というシェルスクリプトです。

Docker イメージの secretlint/secretlint でチェックしてくれる機密情報の種類はここで定義されています。 AWS や GCP 、 Slack など、開発者が普段使うものは定義されています。

最後の if 節ですが、 secretlint では失敗時に 1 以外のコードを返す場合があるようです。この場合を考慮して失敗はすべて 1 として返却し、 Git にコミットを続行させないようにするテクニックが使われています。

さて、このスクリプトを適当な Git リポジトリーの .git/hooks/pre-commit という名前で保存し、実行権限をつけてください。すると、機密情報を stage して git commit を打った際に次のようなメッセージが表示され、コミットできなくなります。

takagi.kensuke$ git commit

/Users/takagi.kensuke/secretlint-sample/index.js
  1:23  error  [AWSAccessKeyID] found AWS Access Key ID: AKIASUZWCFAMGDUVMVRE  @secretlint/secretlint-rule-preset-recommend > @secretlint/secretlint-rule-aws

✖ 1 problem (1 error, 0 warnings)

ここに表示している AWS アクセスキーはポリシーをまったく適用していない IAM ユーザーのものです。このように他者に知られても問題ない状態で実験してください。

Git リポジトリーのテンプレートディレクトリー指定

やりたいことはできましたが、 Git リポジトリーごとにファイルをコピーして実行権限をつけて、という操作は開発者のやることではありません。これらの手順を自動的にやってくれるよう設定しましょう。

Git ではリポジトリーのテンプレートディレクトリーを指定可能です。 git initgit clone する際に、指定したディレクトリーの内容をリポジトリーへコピーしてくれます。

ここでは ~/.git-template というディレクトリーを作成し、これをテンプレートディレクトリーとして Git に設定しましょう。

mkdir -p ~/.git-template/hooks
git config --global init.templatedir ~/.git-template

最後に、前節で紹介した pre-commit スクリプトを ~/.git-template/hooks/pre-commit として保存すれば完了です。

他の環境でもこの設定を使いたい、という場合は次のふたつをコピーすることで可能です。 GitHub などに push しておくと共有が楽なのでオススメです。

  • ~/.gitconfig
  • ~/.git-template

まとめ

人間はミスする生き物ですが、この記事ではミスしても事故を防ぐことが可能な方法を紹介しました。

ただ、今回紹介した方法は、各開発者が事前に設定して意味があるものです。何らかの理由でこういった前提が崩れてしまう場合、公式ドキュメントに書かれている方法でプロジェクトごとに設定しましょう。