未知の脅威に対抗するメルカリのCI再設計

この記事は、Mercari Advent Calendar 2022 の3日目および Developer Productivity Engineering Campブログシリーズの一環で、メルカリCI/CDチームのMichael Findlater (@michaelfindlater)が執筆したものです。

※本記事は2022年2月3日に公開された記事の翻訳版です。

ここではメルカリにおける次世代Continuous Integration(CI)システムの実装、そしてそれに向けたいくつかの技術的な取り組みについて解説します。またこの施策の動機とも言える、サプライチェーンアタックがどのようにCI/CDエンジニア達にとって今後より重要になってきたのかについて解説します。

背景

これまでも常にCI/CDパイプラインに対する攻撃は存在していたものの、ここ最近のこのエリアに対しての攻撃の急増は脅威となりつつあります。これには言語ごと(Java, JavaScript, Python, .Net等)の開発環境エコシステムのダウンロード数で測った場合、利用率が前年比で73% の指数関数的な成長を見せているオープンソースソフトウェア市場が絡んでいます。この成長市場のサプライチェーンは、同時に攻撃者達にとって魅力的なものとなっているのです。

ここ最近のサプライチェーンアタックはこれまで攻撃者達が利用してきた脆弱性をつくタイプの攻撃とは全く違ったものとなってきています。これまでのように脆弱性が見つかるのを待つのではなく、もはや自ら脆弱性を新たに作る方向にシフトしてきているのです。このような攻撃者達は悪意のあるコードを直接オープンソースプロジェクトのコードに注入し、システムへの侵入やPersonally Identifiable Information (PII)を含む情報の抽出、そして認証情報の奪取による同システム内の他コンポーネントへの侵入などに利用しているのです。

この攻撃者によるより能動的な攻撃手法が、これらのサプライチェーンへの脆弱性注入による攻撃が「次世代型」と呼ばれる所以です。昨年だけでもSolarWinds Orion, Codecov, MicrosoftのWinGet、そして Kaseyaなどの有名な攻撃がありました。もうひとつ有名な攻撃例としては、Linux Hypocrite Commits (2020)と呼ばれる、Linuxカーネルを狙った攻撃もあります。EUのEuropean Union Agency for Cybersecurity (ENISA)によればここ最近のサイバー攻撃の66%がサプライチェーンを狙った攻撃となっており、1年間で400% も急増しているとしています。このような状況はCI/CDの世界にとっても大きな脅威となります。

これらの新しい攻撃手法の台頭により様々な企業がセキュリティ警戒度をあげるなど世界中に大きな影響を与えています。国連も2年をかけて調査・立案したサイバーセキュリティ指針をうちだしてサプライチェーンを守るための勧告を出しています。他に様々な公的機関もソフトウェアサプライチェーンを守るための様々なガイドラインや方策をうちたてています(米国であればC-SCRMSSDF、英国ではThe Supply Chain Security Training Act、EUのENISAからはUnderstanding the increase in Supply Chain Security Attacksなどが出ています)。

悪意のある攻撃者たちが用いる攻撃手法は常に進化し続けているため、我々の防御手段やポリシーも同様に進化しつづけなければなりません。それだけでなく、常により効果的な手法を選択し続けるためにも我々の防御手段はなんらかの形でその効果を計測できるようにする必要があると考えます。

CI再設計の主な目的

昨年、メルカリの開発者たちが使うCIシステムの再設計を行うためのプロジェクトが開始されました。この新しいシステムはCodecovなどを含む、前述した新しい攻撃手法に対する防御力を向上させるのが主な目的です。

また、それだけにとどまらず以下に解説するいくつかの条件も満たす必要がありました。

CI改ざんの抑制

悪意のあるユーザーがなんらかの攻撃手法コードを含んだブランチをCIワークフロー定義に注入することによる、CIワークフローの改ざんを防ぐ必要がありました。このためCI設定の変更を容易にできないようにするか、設定をそもそもユーザーの管理下からはなす仕様が必要でした。

CI job code injection attack
CI改ざんの攻撃例

シークレット管理方法の改善

シークレット(いわゆるトークンなどの秘密情報)の管理方法もより安全な方法を模索する必要がありました。例えば;

  • 環境によってどのシークレットを使えるか限定する
  • CIジョブ内でシークレットをアクセスするには人間によるレビューを必須とする
  • 自動的・強制的なシークレットのローテーションを有効にする

などです。

セキュリティ制御の向上

また管理者がセキュリティポリシーをより細かく調整できたり、CIワークフローにおけるイベントをもっと細かく調査できるようにモニタリングを強化するなどのセキュリティ関連のシステム制御機能の向上を目指しました。これには以下のような機能がふくまれます;

  • ネットワーク上のリソースに対するアクセス制御
  • よりフレキシブルなモニタリング
  • よりよい監査機能の導入
Next-gen supply chain attack
egress制御の欠落によるデータ抽出の次世代サプライチェーン攻撃の例

目指しているビジョン

これらのことを踏まえ我々が目指したのはすでに表面化してきている新しい脅威への対処方法および、今後出現するであろう未知の脅威に対する対策を取れるような柔軟なシステムの構築です。新しいCIシステムではセキュリティはシステム内の支柱の一つにならないといけません。そしてこれらの機能はただ存在するだけでなく、有効性を計測できるような形にしなければいけません。この開発プロセスはCI/CDチームだけでなくセキュリティチームとも綿密に連携をとりつつ進められました。

バランスの難しい点としては、セキュリティの質を落とさず、しかし同時に開発者たちのユーザーとしての体験を損なわないようにしなければいけないことでした。この方面に対してはメルカリのPlatform Developer Experienceチーム(Platform DXチーム)に設計フェーズにおいて協力してもらうことができたため、設計の段階でユーザーへの影響などを考慮しながら進めることができました。

さらに設計段階での大きな悩みはこれらの条件を満たしながら必要以上のシステム詳細をユーザーから隠すことでした。システムの詳細がユーザーから丸見えだとユーザーはそのシステムの状態(バージョンや設定条件等)に依存した行動をしたりコードを書いてしまいがちですが、必要十分な情報だけ開示することによりそれを排除して将来的に内部システムを入れ替える必要が出た際にも問題が起きないようにする必要がありました。この点に関してはPlatform DXチームが提供するmicroservice-starter-kitという仕組みを使って対応しました。

脅威モデルの研究

セキュリティの優先順位が高い今回のプロジェクトの仕様要求を洗い出すためにまず最初に我々が着手したのはCI/CDパイプラインに対する攻撃シナリオの研究でした。これもメルカリのセキュリティチームとの共同作業でした。

スタート地点としてMITRE ATT&CKフレームワークを選択しました。そこからCI/CDパイプラインに特化した脅威モデルを開発しました。このモデルは2021年10月に行われたCode Blueセキュリティカンファレンスで公開されています(slides)。この内容はGithubのrung/threat-matrix-cicdでも触れることができますので、ご興味のある方は是非試してみてください。

脅威モデリングができあがったあとはそれらに対する防御方法について考察を始めました。同時にそれらの防御手法の限界も同時に見極めていきました。

Design feedback loop
本プロセスを補助し、エンドプロダクトが十分なクリティカルレビューを受けたことを保証するためにフィードバックループを導入しました。

仕様の策定

脅威モデリングの解析を通じて、セキュリティ・コンプライアンスの仕様要求を策定しました。これらの仕様要求はさらに実行可能なユーザーストーリーに変換され、レビューのあとデザインドキュメントに落とし込まれました。

以下に我々が策定した仕様要求の一部を紹介します。

ユーザー

  • CIはユーザーの開発の妨げにならぬよう、充分に高速でないといけない
  • ワークフローはケースに応じてカスタマイズ可能で、再利用可能、そしてそれぞれ環境ごとに設定を変更できるべきである
  • ログ出力の閲覧には特別な操作はいらず、ユーザーがそれぞれデバッグすることが可能である
  • 実行環境の制御および保護機能やルール設定機能は必須

セキュリティコンプライアンス

  • CIシステムからのGitHubへのアクセスはIPアドレスで制御可能である
  • CIから外へ向けたアクセスは限定できるようにする
  • IDS/IPS/EDRによるセキュリティ監視機能は必須
  • 信用されていないツールや依存関係を利用不可能にする
  • 各ジョブはまっさらな環境で実行される
  • CIは独立したクラスタで実行され、他システムへの干渉を防げるようにする
  • 一般ユーザーはCIおよび関連システムへの直接アクセスを不可能とする
  • CI設定変更の際はレビューを必須とする
  • シークレット情報を利用するジョブは追加のレビューを必須とする
  • シークレット管理ツールを最初から統合し、ユーザーがローテーション等の管理する必要を無くす
  • 監視ログを必須とする

サプライチェーンセキュリティの計測

サプライチェーンへの攻撃の急増からCI/CDセキュリティの質の向上にも使えるフレームワークの作成にも多くの労力が費やされてきました。

2021年6月にはGoogle’s Security Blog 上でSLSAの発表がされました。これはサプライチェーン上のソフトウェア成果物の整合性を検証するためのend-to-endフレームワークです。このフレームワークはそのルーツを“Binary Authorization for Borg”に持っており、すでに8年に渡りGoogleのシステム上での稼働実績があります。

SLSAの非常に優れている点は、なんといっても中間的なマイルストーンを設定しつつ、段階的なセキュリティの適用・向上を計測しながら行えることです。

我々の設計の段階では何度もSLSAを参照し、目標としてSLSAのLevel 4相当のCIシステムの構築を目指しています。

プラットフォームの選定

このようにして仕様要求を決定したのち、このシステムを構築するプラットフォームの選定に入りました。今回のシステムに最も適したプラットフォームのために各選択肢ごとにその機能と我々の要求を対応させたマトリックスを作り検討しました。

今回は諸条件が最も近かったため、Proof of Concept (PoC)としてGitHub Actionsを選択しました。最終的にはこのPoCは予想通り動きました。以下ではこのGitHub Actionsの実装について一部の詳細を解説します

GKE上のGitHub Actions

GitHubではActionsのジョブ実行ランナーをユーザーの提供する環境上で実行するオプションがあります。これを利用することで、IPアドレスの制限や外向きトラフィックの制限なども我々の側で調整をすることができます。このようなSelf Hostedランナーを使うとはジョブ実行環境が取り扱うメモリなどのリソース類の制御も可能となるためメルカリで取り扱うワークロードにあわせた環境構築も可能となりました。

今回はactions-runner-controllerというツールを利用することによりGKE上でself hostedなランナーを準備して、GKE上、つまりKubernetes上でランナーを実行する方向で開発を行いました。

この方式で特に良い点は各ランナーを必要に応じて、全く必要とされていないときは0個から、大量に必要な時はそれに準じた数だけ効率よく実行できることです。また、actions-runner-controllerの機能によりこれらのランナーは永続的な領域を持つことなく、ジョブ実行ごとに初期化された状態を提供することでセキュリティ上の仕様も満たすことができます。ジョブが完了するごとにPodは破棄され、毎回新しいまっさらな環境でジョブが実行できるのです。

GitHub Actions on GKE

GKE上でランナーを実行する上でまた他にもセキュリティ上の利点があります。たとえばGKE Sandbox (gVisor)による追加セキュリティ機能だったり、Network PolicyやWorkload Identity の利用がそれです。

GKE SandboxはgVisorを利用するための諸々の面倒を見てくれるマネージドサービスです。今回我々の利用方法では、gVisorとそれが提供する各コンテナ実行環境の隔離を含む追加セキュリティ機能はとても重要なコンポーネントでした。

Dataplane V2を使ったKubernetes Network Policyは各ランナーごとの内向き・外向きのネットワークの制御に大変便利でした。

そしてWorkload Identityを扱うことによって各ランナーに固有の識別子を割り振り、GCPコンポーネント間でのアクセスにサービスキーを使う必要がなくなりました。

アーキテクチャ

Self-hosted GitHub Actions runners on GKE

上記がGKE上でセルフホストされたGithub Actionランナーのアーキテクチャ全体図です。以下でこのアーキテクチャの詳細を解説していきます。

クラスタへのアクセス

今回提供した複数の環境ではそれぞれが専用のGKEクラスタを付与されました。ユーザーはこれらのクラスタに直接アクセスすることはできません。実はCI/CDチームメンバーもこのアクセスは必要ありません。なぜなら全てのコードのデプロイはmicroservice-starter-kitのterraformを利用したInfrastructure-as-codeを通して行われるため、直接操作をする必要が一切ないのです。

スケーラビリティ

それぞれのランナーはKubernetesで0個から任意の数まで自動的にスケールされます。この方式の利点は、例えば要求するリソースが異なるいくつかの種類のランナー(例: 大き目のCPU版、多めのメモリ版、など)をデプロイした際、必要に応じて効率よくリソースを分配してくれることです。これで開発者の要求にフレキシブルに応えることができます。

ランナーのスケーリングはactions-runner-controllerのHorizontalRunnerAutoscalerを使って行われます。このスケーリングは GitHubからのworkflow_runイベントを監視することによりほぼリアルタイムで自律的に行われます。

なおスケーリングに関しては今後はKubernetesのVertical Pod Autoscalingが使えないかに注目しています。

プライベートアセット

各ランナーにはソフトウェア成果物を格納するため専用のストレージが付与されます。またCI上で作成されたコンテナイメージを格納するためのArtifact Registryも自動的に作成・付与されます。さらに各ランナーごとに専用のシークレット格納用の領域が割り振られ、Secret Managerを通して利用できるようになっています。

レビューを通したあとでならGitHub上の環境変数シークレットを扱うこともできます。現在このSecret Managerを通した方法とGitHub環境変数によるシークレットの両方をうまく共存できるようにする解決策を策定中です。これについては近いうちにまたお知らせすることができるかもしれません。

Egress制限

外向きのトラフィックは共通のホワイトリストによって管理されています。今後、各ランナーごとにこのホワイトリストを一時的に操作して項目を追加・削除できるようにするかもしれません。これらの制限を付与する方法に関しては我々のチームがかなり長い時間をかけて様々な試行錯誤をしました。例えばistioのサイドカーを使ったり、スタンドアロンなプロキシを使ったりするなどの方法も試しました。これについては記事がもう一本書けるくらい様々な要素が絡んできますので、そちらも楽しみにしてしていてください。

Microservice-starter-kit

メルカリでは常に新しいマイクロサービスが作成されています。これを効率よく管理するためにPlatform DXチームはTerraformモジュールの形でmicroservice-starter-kitというツールを提供しています。このモジュールは新しいマイクロサービスを作成するために必要な設定の全てをブートストラップしてくれるのです。

このツールを通して、特定のオプションを有効にすることによって我々のGitHub Actions環境を利用できるようにしました。オプションが選択されていることを関知すると、ツール側がランナーを実行するための様々なリソースを準備してくれるのです。

それには以下のようなものが含まれます

  • サービスアカウント
  • Workload Identity設定 
  • CI クラスタ上のネームスペース設定
  • Artifact Registryへのアクセス権限
  • Runnerリソースの確保

我々の提供するActionsランナーを有効にするには以下のような設定をmicroservice-starter-kitの設定に書くだけです

module “service-name” {
  ...
  enable_github_actions = true
  ...
}

これに伴ってセルフホストされたランナーを使うために、ワークフローを以下の用に修正します:

...
  runs-on: ['repository-name', 'prod', 'high-cpu']
...

上記のコンフィギュレーションは、計算集約型のタスク用に最適化されたproduction環境のランナーで実行されます。

microservices-starter-kitを利用することで、開発者にどこのサーバーでランナーが走るというような、一般ユーザーに必要のない詳細を見せる必要がなくなりました。これは同時に将来我々が細部を変更したいと思った際にユーザーに影響を与えずにそれを行えるということでもあります。

このようにしてセキュリティ面をより強化しつつGitHubが提供する Actions環境と遜色ない実行環境を提供することを目標としています。

より安全なサプライチェーンセキュリティを目指して

今回紹介した新しいCIシステムは我々が目指すセキュリティが大幅に強化された環境にたどり着くための出発地点にすぎません。現時点でも多くのサプライチェーンセキュリティ対策を行っていますが、脅威のない世界にたどり着くにはまだまだ先が長いです(そんな世界があるのなら、ですが)。

SLSAの基準という意味では現時点で実装されている内容ですでにかなりのレベルに到達していますし、複数人オーナーによる相互レビューやビルドの来歴管理のような、以前からすでに導入されていた施策もたくさんあります。

今後は以下のような分野においてさらにセキュリティを強化していき、強固な基盤を作っていく予定です:

  • 揮発性のビルド環境
  • さらに強力な外向きトラフィック制御
  • 隔離されたビルド環境

まとめ

CI/CDの世界においては今も、そして今後もサプライチェーンに関するセキュリティは重要な関心事となるでしょう。本記事がそれらに関する多少の警鐘になったり、どのようにそれらの脅威に対処していくかのヒントとなれば幸いです。

例えば、定期的にCI/CDシステムに対する定期的な脅威リスクの判定などは行われてしかるべきでしょう。常に高いレベルでのセキュリティを提供するために定期的に再評価をし、継続的に監視・開発していくことが重要です。

皆さまの仕事で関わっているプロダクトのスケールや、サプライチェーンセキュリティの程度がどのようなものであっても、SLSAのようなフレームワークを利用して段階的にセキュリティを向上していくことは可能です。

皆さまのCI/CDシステムのセキュリティ対策はすでにそこにある、そして未知の脅威に対して万全の体制でしょうか?もしまだそうでないなら、明日と言わず、是非今日今すぐにでも行動に移してください。

明日はEngineering Officeの@joshです。引き続きお楽しみください。

メルカリでもCI/CD環境などのプラットフォーム改善に情熱を持ち、開発者の環境をよりよくしていきたい仲間を探しています。私たちについてもっと知りたいと思われた方は、下記キャリアサイトから詳細をご覧ください。

  • X
  • Facebook
  • linkedin
  • このエントリーをはてなブックマークに追加