🤖

PublicなリポジトリでGitHub Actionsを扱う技術

2023/09/10に公開

GitHub Actions便利ですよね。
筆者が参加しているFlutterKaigi 2023の公式アプリも、GitHub Actionsでビルドしています。

実装してみて初めて実感したのですが、公開リポジトリにおけるGitHub Actionsの扱いは、非公開のものと比べ少し面倒です。
本記事では、公開リポジトリでGitHub Actionsと機密情報を扱うための技術について紹介します。

概要

本記事は、その性質上、GitHubが公開しているドキュメントやブログを参考にしています。
このため、まず次の記事を読んでいただくことを推奨します。

https://securitylab.github.com/research/github-actions-preventing-pwn-requests/

https://securitylab.github.com/research/github-actions-untrusted-input/

https://securitylab.github.com/research/github-actions-building-blocks/

この上で、本記事では次の内容について扱います。

  • GitHub Actionsにおける pull_requestpull_request_target の違い
  • pull_requestworkflow_run の組み合わせ
  • actions/github-script の使い方

もしもPublicなリポジトリでGitHub Actionsを扱うことになった場合、ぜひ参考にしていただければと。

GitHub Actionsにおける pull_requestpull_request_target の違い

PrivateなリポジトリでGitHub Actionsを扱う場合、pull_request イベントをトリガーにして、PRの内容をビルドすることが大半ではないでしょうか。
しかしPublicなリポジトリでは、この方法では一部の機能が利用できません。この制限については、公式ドキュメントに記載されています。

pull_request#フォークされたリポジトリのワークフロー

GITHUB_TOKEN の例外を除き、ワークフローがフォークされたリポジトリからトリガーされた場合、シークレットはランナーに渡されません。 GITHUB_TOKEN には、フォークされたリポジトリからの pull request での読み取り専用アクセス許可があります。 詳しくは、「自動トークン認証」を参照してください。

この問題に対して、GitHubはpull_request_targetイベントを利用できます。
ただし、ドキュメントには次のような注意書きがあります。注意しましょう。

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_requestworkflow_run の組み合わせ

前項の最後で記したとおりpull_request_targetによりGitHub Actionsを外部コントリビューターに開放することは、推奨されていません。

https://securitylab.github.com/research/github-actions-preventing-pwn-requests/

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は秘密情報へのアクセスが可能になります。このためworkflow_runpull_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-artifactactions/github-scriptを利用する方法が紹介されています。

actions/github-script の使い方

Pull Requestの番号などと成果物の両方をまとめたzipを作成し、actions/upload-artifactでアップロードすることで、workflow_runで情報と成果物を取得できます。
ブログ記事の中ではCommentPR.ymlとして処理が紹介されています。

https://securitylab.github.com/research/github-actions-preventing-pwn-requests/

一点注意が必要なのは、このブログが書かれたのが2021年なため、最新のactions/github-scriptと処理に際がある点です。

https://github.com/actions/github-script

特にv5からv6にかけてAPIに破壊的変更が入っており、ブログのコード(v3)のコードをそのまま利用することはできません。
主にgithub.*で呼び出されていたメソッドが、github.rest.*にマッピングされ直しています。利用できるAPIはGitHub APIのドキュメントを読みつつ、呼び出し方はgithub-scriptのworkflowsを参考にすることをお勧めします。

https://docs.github.com/en/rest?apiVersion=2022-11-28
https://github.com/actions/github-script/tree/main/.github/workflows


なお、actions/download-artifactを利用することは、2023年9月現在できません。
下記Issueの解消を待つ必要があります。

https://github.com/actions/download-artifact/issues/130

まとめ

PublicなリポジトリでGitHub Actionsを扱うための技術について紹介しました。
なかなか参照するケースはないかなと思いますが、もしも必要になった際には、ぜひ参考にしていただければと思います。

GitHubで編集を提案

Discussion