GitHub Actions でセキュアにコードを修正する
GitHub Actions でセキュアにコードを修正するための OSS を作ったので紹介します。
本記事では概要を紹介します。
公式ドキュメントを結構しっかり書いたつもりなので詳細はそちらに譲ります。
実現すること
主に業務で扱うプライベートリポジトリでチームで開発することを想定しています。
Public リポジトリでは autofix.ci を推奨します。
- GitHub Actions で Pull Request のコードをセキュアに修正する
- 例: コードのフォーマット、コードからドキュメントの自動生成
- コードを修正する GitHub Actions に修正に使われる GitHub App の Private Key を渡さない
- コードの修正が可能な GitHub App の悪用を防ぐ
- GitHub Actions で完結し、サーバーの運用などを必要としない
- 専用の action を実行するだけで簡単に修正できる
導入
Pull Request の CI でコードを自動で修正できると便利です。
- コードのフォーマット
- コードからドキュメントの生成
- etc
GitHub Actions で 修正を commit, push することが出来ます。
GitHub App と commit-action を使うと署名付きの commit でコードを簡単に修正できます。
しかし、 GitHub Actions でコードを修正するには contents:write
権限を持った GitHub App が必要です。
GitHub Actions からこの GitHub App を自由に使えるようになると、この App が悪用されるリスクがあります。
例えば、自分以外が作った PR にこの App を使ってコミットして approve してマージすることで、他人のレビューなしで変更をマージすることが出来ます。
詳細はこちらの記事でも説明しているので参照してください。
今回の記事で扱うのは、上の記事で対策するのが難しいとしたパターンに対する一つの解決策です。
悪用を防ぐため、 GitHub Actions に GitHub App の Private Key を渡したくありません。
autofix.ci ではこの問題をうまく解決しています。
autofix.ci は GitHub Actions ではなく GitHub App でコミットを生成することにより、 GitHub Actions にコミットする権限を付与せずにセキュアなコード修正を実現しています。
autofix.ci の挙動を簡単に説明すると以下のような感じになります。
- コードの修正内容を GitHub Actions Artifact に upload
- GitHub Actions からサーバにリクエストを送信
- サーバが GitHub Actions Artifact から修正内容をダウンロード
- サーバが PR に修正をコミット
autofix.ci は OSS プロジェクトでは無料で使えますし、様々な OSS で使われています。
プライベートリポジトリでも利用できますが、業務で使うには幾つかハードルがあります。
- 有償である
- App がソースコードにアクセスするのを許容する必要がある (セキュリティ的に許容できない場合がある)
- CI でコードをコミットするだけなら autofix.ci を使わなくてもできるので、 autofix.ci を使う理由が弱い
ただ、 autofix.ci の workflow に secret を渡さずにセキュアな環境で処理を実行する
というアーキテクチャは非常に参考になります。
同様の仕組みを作るとしたら上述したように何らかの形でサーバーを実装する必要があります。
パッと思い浮かぶのは Lambda や ECS, k8s などでサーバーをホストすることですが、個人的には Lambda や k8s に依存したくありませんし、運用をしたくありません。
そこで GitHub Actions で完結する仕組みを考案・実装しました。
仕組み
サーバーの代わりに専用のリポジトリと workflow を作成します。
そしてそのリポジトリに label を作成(作成後に即削除)し、 label の created event によって workflow を実行し、コードを修正するというアプローチです。
これであればサーバーが不要になり GitHub Actions で完結します。
GitHub Actions Workflow を API で呼び出したい場合、 repository_dispatch
や workflow_dispatch
event を使うのが一般的でしょう。
しかし、これらを実行するには actions:write
権限が必要になります。
この権限を client 側に渡すのは少々不安です。
そこでより安全な event はないか模索したところ、 label event に目をつけました。
label event であれば issues:write
権限があれば良いですし、悪用されてもリスクが低いです。
Server GitHub App と workflow の保護
contents:write
権限を持った server 側の GitHub App と Workflow を強く保護する必要があります。
これらが悪用されては意味がありません。
まず Server Repository の default branch は Branch Rulesets で保護します。
- Require a pull request before merging
- Require review from Code Owners
- Require approval of the most recent reviewable push
- Require status checks to pass
そのうえで Private Key の保存場所としては以下が考えられます。
- GitHub Environment Secret
- default branch からのみ利用を許可
- AWS Secrets Manager などの secret manager
- アクセスできる人を厳しく制限
- OIDC の claim で CI からのアクセスを制限 (repository, branch, event, workflow name, etc)
また、 Server Repository の write 権限を持つメンバーを制限するのもありでしょう。
なぜ安全なのか
- Client 側に公開している GitHub App は
issues:write
という悪用するのが難しい弱い権限しか持たないため - 強い権限を持つ GitHub App の Private Key の公開範囲を Server 側だけに制限でき、 Private Key が悪用されるリスクが低いため
- 実行可能な API とパラメータを厳しく制限できるため
- 決まった API (commit の生成) しか呼べない
- client 側と同じ repository と branch にしか commit を生成できない
悪用できないか
今回の仕組みが本当にセキュアと言えるのか、悪用できないか考察しました。
今回防ぎたいのは、下記の記事で言及している、 自分以外が作成した PR に Machine User や bot で commit を追加して自分で approve
するパターンです。
攻撃者がその PR にコミットしているのであれば上記の記事で紹介している仕組みで self approve を防げます。
というより、攻撃者が PR にコミットするのであれば態々 App にコミットさせる意味がありません。
つまり、攻撃者がその PR にはコミットせずに GitHub App を使って任意の commit を生成できるかが焦点になります。
今回の仕組みで使用する App とは別の App を悪用して commit できる場合、攻撃者はその App を悪用して commit すればいいだけなので、それはできない前提で考えます。
攻撃者は任意の name や description で label を生成できます。
つまり、任意の repository と workflow run id を指定できます。
ただし、それでも今回の仕組みが生成するコミットの内容は、commitを生成する branch に紐づく workflow run から upload された artifact になります。
つまり、攻撃者はなんとかして対象の PR の head branch に紐づく workflow run で artifact を upload する必要がありますが、攻撃者自身がその PR の branch にコミット出来ない以上、それは困難です。
攻撃者は適当な workflow を作成し、 GitHub Actions Artifact に upload される修正されるファイル名のリスト
や PR 番号
を改竄して commit 生成の workflow を呼び出すことが出来ます。
しかし PR 番号は commit 生成の失敗時の通知に使われているだけなので、改竄されたとしても特に問題はありません。
ファイル名のリストを改竄しても攻撃には繋がらないでしょう。
よって、悪用は困難だと思います。
さいごに
以上、 GitHub Actions でセキュアにコードを修正するための OSS を紹介しました。
詳細は公式ドキュメントに書いてあるのでそちらをご参照ください。
GitHub App や Machine User を CI で利用する場合、それらの Private Key や Access Token を如何に保護するかにはずっと頭を悩ませてきました。
これだけで解決するわけではなく self approve を防ぐ仕組みなどとの併用が必要ですし、既存のコードをこの仕組みに置き換えていくのは中々地道な作業になるかと思います。
それでも、今回の Action とそのアーキテクチャはこの問題を解決するための大きな一歩です。
これから改良・拡張していくつもりですので、また更新があればお伝えできればと思います。
Discussion