公開中 · CLI

mask-pipe

コマンドの出力をパイプで通し、含まれるシークレットを伏せ字にするツール

github.com/c12o-dev/mask-pipe
ターミナルで env と docker logs の出力を mask-pipe に通し、AWS キーや GitHub トークン、DB パスワードが伏せ字になっている様子

作った理由

画面共有中に env を実行してシークレットを表示してしまうことや、ログを表示する前に中身を気にしなければならないことを避けたくて作りました。シークレットの漏えいを防ぐツールには、コミット前にチェックするものや、リポジトリを後からスキャンするものがあります。ターミナルに表示する時点で伏せるツールは、自分の環境にはありませんでした。

mask-pipe は標準入力を読み、既知のシークレット形式に一致した部分を置き換えて標準出力に書き出します。置き換えた内容をファイルなどに保存することはありません。

技術的な判断

シェルの全出力を自動でマスクする機能は作らず、利用者がコマンドごとにパイプでつなぐ形にしました。シェルの設定で全出力にフィルタを挟むと、子プロセスの標準出力が端末ではなくなり、isatty() が false を返します。その結果、出力の色が消え、TUI アプリが正しく動かなくなります。これは Unix の仕組みによる制約で、mask-pipe 側の実装では回避できません。パイプで明示的につなぐ形なら、どのコマンドの出力がマスクされているかを利用者が見て確認できます。

検出するパターンは8個に絞っています。シークレットではない出力を誤って伏せるほうが、シークレットを見逃すよりも利用者に嫌われると考えたためです。各パターンは誤検知が出ないように条件を調整しています。たとえば AWS のシークレットキーは、40文字のランダム文字列をすべて伏せるのではなく、aws_secret_access_key= のようなキー名と一緒に出力された場合だけ伏せます。

秘密鍵は行単位の処理では検出できません。PEM ブロックが数十行にわたるためです。入力全体をバッファに溜めると tail -f の出力が止まってしまうので、BEGIN マーカーを見つけたときだけ END マーカーまでバッファに溜める方式にしました。バッファが上限を超えた場合は、マスクせずにそのまま出力します。

言語は Go を選びました。単一バイナリで配布できることと、標準の正規表現エンジンが入力長に対して線形時間で処理することを保証している点が理由です。mask-pipe はパイプの途中に置くため、特定の入力で処理が極端に遅くなると、後ろのコマンドへの出力も遅れます。

技術スタック

言語
Go
検出
Go regexp
設定
TOML
ビルド・配布
GoReleaserHomebrew tap
テスト・品質
Go testinggo vetgofmtGitHub Actions