PublicなリポジトリでGitHub Actionsを扱う技術
GitHub Actions便利ですよね。
筆者が参加しているFlutterKaigi 2023の公式アプリも、GitHub Actionsでビルドしています。
実装してみて初めて実感したのですが、公開リポジトリにおけるGitHub Actionsの扱いは、非公開のものと比べ少し面倒です。
本記事では、公開リポジトリでGitHub Actionsと機密情報を扱うための技術について紹介します。
概要
本記事は、その性質上、GitHubが公開しているドキュメントやブログを参考にしています。
このため、まず次の記事を読んでいただくことを推奨します。
この上で、本記事では次の内容について扱います。
- GitHub Actionsにおける
pull_request
とpull_request_target
の違い -
pull_request
とworkflow_run
の組み合わせ -
actions/github-script
の使い方
もしもPublicなリポジトリでGitHub Actionsを扱うことになった場合、ぜひ参考にしていただければと。
pull_request
と pull_request_target
の違い
GitHub Actionsにおける PrivateなリポジトリでGitHub Actionsを扱う場合、pull_request
イベントをトリガーにして、PRの内容をビルドすることが大半ではないでしょうか。
しかしPublicなリポジトリでは、この方法では一部の機能が利用できません。この制限については、公式ドキュメントに記載されています。
pull_request#フォークされたリポジトリのワークフロー
GITHUB_TOKEN
の例外を除き、ワークフローがフォークされたリポジトリからトリガーされた場合、シークレットはランナーに渡されません。GITHUB_TOKEN
には、フォークされたリポジトリからの pull request での読み取り専用アクセス許可があります。 詳しくは、「自動トークン認証」を参照してください。
この問題に対して、GitHubはpull_request_target
イベントを利用できます。
ただし、ドキュメントには次のような注意書きがあります。注意しましょう。
このイベントは、
pull_request
イベントのようにマージ コミットのコンテキストではなく、pull request のベースのコンテキストで実行されます。 これで、リポジトリを変更したり、ワークフローで使うシークレットを盗んだりする可能性がある、pull request の head から安全ではないコードが実行されるのが避けられます。 このイベントを使うと、ワークフローでは、pull request に対するラベルやコメントなどの作業をフォークから行うことができます。 pull request からコードをビルドまたは実行する必要がある場合は、このイベントを使わないでください。
ここで「秘密情報」とされているのは、GitHub Actionsでsecrets
として利用できるものです。
FlutterKaigi/conference-app-2023では、Firebase Hostingにpreview環境をデプロイするために、Firebaseのservice accountに関する情報をsecretsとして登録しています。こういった情報を、悪意のあるユーザーに渡してしまうと、Firebaseを不正に利用される可能性があります。
想定される攻撃としては、悪意のあるユーザーがPRを作成し、GitHub Actionsを実行させるケースです。
なおpull_request
が利用される場合、秘密情報にアクセスできないため、このような攻撃を防ぐことができます。
一方、pull_request_target
は、PRのベースブランチのコンテキストで実行されるため、秘密情報にアクセスできます。もちろん、このような攻撃を防ぐために、GitHubは「リポジトリのオーナーによる許可がなければ、GitHub Actionのフローが実行されない」という制約を追加しています。しかし「最初に安全そうなPRを提出し、後ほど悪意のある処理を追加する」ようなケースでは、この制約は有効ではありません。
pull_request
と workflow_run
の組み合わせ
前項の最後で記したとおりpull_request_target
によりGitHub Actionsを外部コントリビューターに開放することは、推奨されていません。
TL;DR: Combining
pull_request_target
workflow trigger with an explicit checkout of an untrusted PR is a dangerous practice that may lead to repository compromise.
代わりに利用できるのが、workflow_run
イベントです。
このイベントは、ワークフローの実行が要求されたか完了したときに発生します。 これで、別のワークフローの実行または完了に基づいてワークフローを実行できます。
workflow_run
は秘密情報へのアクセスが可能になります。このためworkflow_run
をpull_request
イベントの完了後に走るようにすることで、外部コントリビューターによるGitHub Actionsの実行を行うことができるようになります。
セキュリティについては、workflow_run
はdefault branchに存在するyamlファイルの内容が実行されるため、default branchに存在するファイルに悪意あるコードが追加されない限り、安全です。つまり、悪意のあるコードがdefault branchにマージされない限り、安全となります。
pull_request
からworkflow_run
に処理を移行する場合、少しだけ注意が必要です。
というのも「pull_request
はPull Requestの作成に伴って実行される」ため、pull_request
の処理の中でPRに関する情報を取得できます。
対してworkflow_run
は「任意のpull_request
の処理が完了したのちに実行される」ため、そのままではpull_request
に関する情報を取得できません。
この問題に対して、GitHubのブログではactions/upload-artifact
とactions/github-script
を利用する方法が紹介されています。
actions/github-script
の使い方
Pull Requestの番号などと成果物の両方をまとめたzipを作成し、actions/upload-artifact
でアップロードすることで、workflow_run
で情報と成果物を取得できます。
ブログ記事の中ではCommentPR.yml
として処理が紹介されています。
一点注意が必要なのは、このブログが書かれたのが2021年なため、最新のactions/github-script
と処理に際がある点です。
特にv5からv6にかけてAPIに破壊的変更が入っており、ブログのコード(v3)のコードをそのまま利用することはできません。
主にgithub.*
で呼び出されていたメソッドが、github.rest.*
にマッピングされ直しています。利用できるAPIはGitHub APIのドキュメントを読みつつ、呼び出し方はgithub-scriptのworkflowsを参考にすることをお勧めします。
なお、actions/download-artifact
を利用することは、2023年9月現在できません。
下記Issueの解消を待つ必要があります。
まとめ
PublicなリポジトリでGitHub Actionsを扱うための技術について紹介しました。
なかなか参照するケースはないかなと思いますが、もしも必要になった際には、ぜひ参考にしていただければと思います。
Discussion