CI/CD
GitHub Actionsキャッシュ:仕組みと、見えなくなるもの
GitHub Actionsキャッシュは、ワークフローの実行間でファイルを保存し、ジョブがすでに行ったダウンロードを省けるようにします。ビルドは速くなります。その一方で、パッケージが取得されることなくビルドに入ってくることになり、ほとんどのビルドの記録はそれを捉えられません。
更新日
キャッシュの仕組み
actions/cacheアクションは、ジョブの終了時に一連のパスをキーのもとに保存し、後のジョブの開始時に復元します。一般的なnpmの構成では、パッケージマネージャーのダウンロードキャッシュをキャッシュし、ロックファイルをキーにします。
- uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
restore-keys: |
npm-${{ runner.os }}-
- run: npm ci
復元時、アクションはまずkeyに完全一致するものを探し、次に部分一致を探し、続いてrestore-keysの各プレフィックスを順に探します。プレフィックスに一致するキャッシュが複数ある場合は、最も新しく作成されたものを復元します。このフォールバックによって、ロックファイルが変わった後もビルドは速く保たれますが、ジョブが古いロックファイル向けに作られたキャッシュから始まる可能性もあります。
多くの場合、このアクション自体は不要です。setup-node、setup-python、setup-java、setup-go、setup-ruby、setup-dotnetにはキャッシュ機能が組み込まれています。Node.jsの場合、setup-nodeはnpm、Yarn、pnpmのグローバルなパッケージデータをキャッシュし、node_modulesはキャッシュしません。また、package.jsonがpackageManagerとしてnpmを宣言している場合は、npmのキャッシュを自動的に有効にします。
スコープと制限
| ルール | GitHubの動作 |
|---|---|
| 実行が復元できるキャッシュ | 自身のブランチとデフォルトブランチ。プルリクエストはベースブランチも |
| プルリクエストが書き込めるもの | マージrefをスコープとするキャッシュのみ。デフォルトブランチには書き込めない |
| ストレージ | デフォルトでリポジトリあたり10 GB。組織とエンタープライズのオーナーは引き上げ可能 |
| 削除 | 7日以上アクセスされていないエントリと、ストレージが満杯のときは最も長く使われていないもの |
| シークレット | キャッシュしないこと。プルリクエストのアクセス権を持つ人は誰でもキャッシュの内容を読み取れる |
キャッシュがSBOMから隠すもの
キャッシュは、パッケージレジストリを迂回する近道です。それこそがキャッシュの目的であり、ビルドが何を使ったかのあらゆる記録を混乱させる理由でもあります。
- ダウンロードがなければ痕跡もない。キャッシュから復元されたパッケージは、レジストリへのリクエストなしにインストールされます。ビルドのネットワーク通信から作られた記録は、キャッシュを追跡しない限り、それらを見落とします。
-
新しいキーのもとに古い内容。
restore-keysの部分一致では、以前のロックファイル向けに作られたキャッシュが復元されます。その後のインストールで変更分が追加・置換されますが、ジョブの出発点は現在のロックファイルが記述する内容とは異なります。 - 依存関係以外のもの。キャッシュにはビルドツール、コンパイラー、生成ファイルも含まれ、それらはどのアプリケーションのマニフェストにも記載されていません。
- スキャンでは判別できない。ロックファイルに基づくSBOMは、ジョブがキャッシュからまさにそのとおりのものをインストールしたかどうかにかかわらず、ロックファイルの記載内容を記述します。
これがより大きな問題とどう関係するかについては、ロックファイルが出荷したものの記録にならない理由と推移的依存関係をご覧ください。
キャッシュポイズニング
キャッシュはリポジトリ内のワークフロー間で共有されるため、ワークフロー同士をつなぐ経路になります。2024年5月、セキュリティ研究者のAdnan Khanは、脆弱なpull_request_targetワークフローや侵害された依存関係を通じてデフォルトブランチのコンテキストでコードを実行できる者は誰でも、キャッシュを復元するワークフロー内のファイルを上書きできることを示しました。実行中のジョブからキャッシュトークンを入手した攻撃者は、リポジトリのキャッシュを埋め尽くしてGitHubに正規のエントリを削除させ、同じキーのもとに汚染されたエントリを書き込めます。それらを復元したリリースワークフローは、自身のシークレットを使って攻撃者のファイルを実行します。
リスクを軽減するには:
- リリースはクリーンな状態から、またはリリースワークフローだけが書き込むキャッシュからビルドする。
- 信頼できないプルリクエストのコードを、デフォルトブランチのコンテキストで決して実行しない。
- シークレットとトークンをキャッシュ対象のパスに含めない。
- キャッシュから復元されたものを、ダウンロードと同じ精査が必要なビルドへの入力として扱う。
CRACIによるキャッシュの扱い
CRACIはGitHub Actionsランナーなので、actions/cacheとsetup-*のキャッシュは、コンテナレイヤーのキャッシュとあわせて、現在と同じように動作します。キャッシュは、より高速なハードウェアと短い待ち時間とともに、CRACIがGitHubホステッドランナーの約2倍の速さでビルドを実行できる理由の一つでもあります。
変わるのは記録です。CRACIのパッケージを認識するプロキシはジョブが取得するパッケージを記録し、エビデンスはキャッシュと一緒に引き継がれます。ジョブがキャッシュを復元すると、CRACIは把握している依存関係をそのジョブのSBOMに引き継ぎます。
- キャッシュごとの完全性。すべてのジョブと同様に、すべてのキャッシュに完全性の状態があります。不完全なキャッシュを復元したジョブのSBOMは不完全になり、そのジョブが生成するキャッシュもその状態を引き継ぐため、欠落が知らないうちに完全な記録に変わることはありません。
- アーカイブから推測しない。CRACIはキャッシュアーカイブ内のパッケージマニフェストを読み取りません。キャッシュを生成したジョブを記録しているため、キャッシュの中身を把握しています。
-
生成ジョブごとに1つのキャッシュとし、生成ジョブのエグレスポリシーを
default: denyに設定することで、そのエビデンスの信頼性が保たれます。詳しくはキャッシュとSBOMの完全性に関するガイドをご覧ください。
記録の仕組みについてはビルド時SBOMをご覧いただくか、デモを予約して、キャッシュを使うワークフローの1つをCRACIで実行してみてください。
GitHub Actionsキャッシュに関するよくあるご質問
GitHubはActionsキャッシュをどのくらいの期間保持しますか?
GitHubは、7日以上アクセスされていないキャッシュエントリを削除します。リポジトリがストレージの上限(デフォルトで10 GB)を超えた場合も、最終アクセスが古いものから順にキャッシュを削除します。
setup-nodeはnode_modulesをキャッシュしますか?
いいえ。actions/setup-nodeのcache入力がキャッシュするのは、npm、Yarn、pnpmのパッケージマネージャーのグローバルなパッケージデータであり、node_modulesではありません。インストールステップは引き続き実行されますが、パッケージをダウンロードする代わりに復元されたキャッシュから読み取ります。
プルリクエストはmainブランチのキャッシュを読み取れますか?
はい。ワークフローの実行は、自身のブランチとデフォルトブランチのキャッシュを復元でき、プルリクエストはベースブランチのキャッシュも復元できます。プルリクエストで作成されたキャッシュはそのマージrefをスコープとするため、デフォルトブランチのスコープに書き込むことはできません。
リリースワークフローでActionsキャッシュを使っても安全ですか?
トレードオフがあります。2024年に公開された研究では、デフォルトブランチのワークフローでコードを実行できる者は誰でも、リリースワークフローを含む他のワークフローが復元するキャッシュを汚染できることが示されました。リリースを公開するワークフローやデプロイ用のシークレットを持つワークフローは、クリーンな状態から、または自身だけが書き込むキャッシュからビルドするほうが安全です。
復元されたキャッシュによってSBOMは変わりますか?
変わる可能性があります。キャッシュから復元されたパッケージはダウンロードされずにインストールされるため、ネットワーク通信から作られた記録は、キャッシュを追跡しない限りそれらを見落とします。また、ロックファイルのスキャンでは、キャッシュにまさにそのバージョンが含まれていたかどうかは分かりません。
キャッシュを活かしながら完全なSBOMを
ワークフローをCRACIで実行してください。キャッシュは引き続きビルドを高速化し、キャッシュが運ぶ依存関係は各ジョブの記録されたSBOMに残ります。
デモを予約