自己管理ランナーのセキュリティ

GitLab CI/CDパイプラインは、単純または複雑なDevOps自動化タスクに使用されるワークフロー自動化エンジンです。これらのパイプラインはリモートコード実行サービスを可能にするので、セキュリティリスクを減らすために以下のプロセスを実装する必要があります:

  • テクノロジースタック全体のセキュリティを設定する体系的なアプローチ。
  • プラットフォームの設定と使用に関する継続的な厳格なレビュアー。

GitLab CI/CDジョブをセルフマネージドランナーで実行する予定であれば、コンピュートインフラとネットワークにセキュリティリスクが存在します。

RunnerはCI/CDジョブで定義されたコードを実行します。プロジェクトのリポジトリに対して Developer ロールを持つユーザーであれば、意図的かどうかにかかわらず、Runner をホストする環境のセキュリティを侵害する可能性があります。

このリスクは、自己管理ランナーがエフェメラルでなく、複数のプロジェクトに使用されている場合、さらに深刻になります。

  • 悪意のあるコードが埋め込まれたリポジトリからのジョブは、非エフェメラルランナーによってサービスされる他のリポジトリのセキュリティを危険にさらす可能性があります。
  • Executorによっては、ジョブはRunnerがホストされている仮想マシンに悪意のあるコードをインストールすることができます。
  • 侵害された環境で実行されるジョブに公開されるシークレット変数は、CI_JOB_TOKEN を含むがこれに限定されない、盗まれる可能性があります。
  • 開発者ロールを持つユーザーは、サブモジュールのアップストリームプロジェクトにアクセスできなくても、プロジェクトに関連するサブモジュールにアクセスできます。

異なるエクゼキュータのセキュリティリスク

使用するエクゼキュータによって、異なるセキュリティリスクに直面する可能性があります。

Executor の使い方

** shell Executor を使ってビルドを実行すると、Runner ホストとネットワークに高いセキュリティリスクが存在します。** ジョブは GitLab Runner ユーザーの権限で実行され、このサーバーで実行されている他のプロジェクトのコードを盗む可能性があります。信頼できるビルドを実行するためだけに使用してください。

Docker Executorの使い方

Dockerは非特権モードで実行すると安全であると考えられます。 このような設定をより安全にするには、sudo を無効にするか、SETUIDSETGID の機能を落とした Docker コンテナで、非 root ユーザーとしてジョブを実行します。

cap_add/cap_drop の設定により、非特権モードでより詳細な権限を設定することができます。

caution
Dockerの特権コンテナは、ホストVMのすべてのルート機能を持ちます。詳細については、ランタイム特権とLinux機能に関するDocker公式ドキュメントをご覧ください。

コンテナを特権モードで実行することはお勧めしません。

特権モードが有効になっていると、CI/CDジョブを実行しているユーザーがランナーのホストシステムへの完全なrootアクセス権、ボリュームのマウントとアンマウントの権限、ネスト化されたコンテナの実行権限を得る可能性があります。

特権モードを有効にすることで、コンテナのセキュリティメカニズムをすべて事実上無効にし、ホストを特権昇格にさらすことになり、コンテナの脱走につながる可能性があります。

ランナーが複数の組織で共有されている場合は特に危険です。たとえば、GitLab.com のようなインスタンスワイドランナーでは、複数の組織が同時に作業する可能性があります。

Docker Machine Executorを使用する場合は、MaxBuilds = 1 。これは、1つの自動スケールされたVM(特権モードによってセキュリティ上の弱点がもたらされるため、潜在的に危険にさらされる可能性があります)が、1つだけのジョブを処理するために使用されることを保証するものです。

if-not-present プルポリシーによる非公開Dockerイメージの使用

高度な設定:非公開コンテナレジストリの使用で説明したプライベートDockerイメージのサポートを使用する場合は、pull_policy の値としてalways を使用する必要があります。特に、DockerまたはKubernetes Executorで公開共有Runnerをホストしている場合は、always pull policyを使用する必要があります。

プルポリシーがif-not-present に設定されている例を考えてみましょう:

  1. ユーザーAはregistry.example.com/image/name に非公開画像を持っています。
  2. ユーザーAは共有Runner上でビルドを開始します:ビルドはレジストリ認証情報を受け取り、レジストリで作成者が認証された後にイメージを取り出します。
  3. イメージは共有ランナーのホストに保存されます。
  4. ユーザーBはregistry.example.com/image/nameの非公開イメージにアクセスできません。
  5. ユーザーBは、ユーザーAと同じ共有Runner上でこのイメージを使用するビルドを開始します。Runnerはローカルバージョンのイメージを見つけ、認証情報が見つからないためにイメージをプルできなくても、それを使用します。

したがって、異なるユーザーや異なるプロジェクト(非公開と公開のアクセスレベルが混在している)で使用できるRunnerをホストする場合は、プルポリシーの値としてif-not-present

  • never - あなたが事前にダウンロードした画像のみを使用するようにユーザーを制限したい場合。
  • always - ユーザーがどのレジストリからでも画像をダウンロードできるようにしたい場合。

if-not-present プルポリシーは、信頼されたビルドとユーザーが使用する特定の Runnerにのみ使用してください。

詳しくはプルポリシーのドキュメントを読んでください。

Dockerがインストールされているシステム

note
0.5.0未満のインストール、または新しいバージョンにアップグレードされたインストールに適用されます。

DockerがインストールされたLinuxシステムにGitLab Runnerパッケージをインストールすると、gitlab-runnerDocker デーモンへのアクセス権限を持つユーザーが作成されます。これにより、shell Executorで実行されるジョブはフル権限でdocker にアクセスできるようになり、サーバーへのrootアクセスが可能になる可能性があります。

SSHエクゼキュータの使い方

SSHエクゼキュータは StrictHostKeyChecking オプションがないため、MITM攻撃(man-in-the-middle)を受けやすくなっています。これは将来のリリースで修正される予定です。

Parallels Executor の使用法

Parallels Executorは完全なシステム仮想化を使用し、隔離仮想化で実行するように設定された VM マシンと隔離モードで実行するように設定された VM マシンを使用するため、最も安全なオプションです。すべての周辺機器と共有フォルダへのアクセスをブロックします。

ランナーのクローン作成

ランナーは GitLab Server を識別するためにトークンを使用します。Runnerをクローンした場合、クローンされたRunnerはそのトークンに対して同じジョブを受け取る可能性があります。これはRunnerのジョブを “盗む “可能性のある攻撃手段です。

共有環境でGIT_STRATEGY: fetch を使用する際のセキュリティリスク

GIT_STRATEGYfetch に設定すると、Runner は Git リポジトリのローカル作業コピーを再利用しようとします。

ローカルコピーを使うことで、CI/CD ジョブのパフォーマンスを向上させることができます。しかし、その再利用可能なコピーにアクセスできるユーザーなら誰でも、他のユーザーのパイプラインで実行するコードを追加することができます。

Git は、サブモジュール(別のリポジトリに埋め込まれたリポジトリ)の内容を親リポジトリの Git reflog に保存します。その結果、プロジェクトのサブモジュールが最初にクローンされた後、後続のジョブはスクリプトでgit submodule update を実行することでサブモジュールの内容にアクセスできるようになります。これは、サブモジュールが削除されていてジョブを開始したユーザーがサブモジュールのプロジェクトにアクセスできない場合でも同様です。

共有環境にアクセスできるすべてのユーザーを信頼する場合にのみ、GIT_STRATEGY: fetch を使用してください。

セキュリティ強化オプション

特権コンテナの使用によるセキュリティリスクの低減

Dockerの--privileged フラグを使用する必要があるCI/CDジョブを実行する必要がある場合、セキュリティリスクを低減するために以下の手順を取ることができます:

  • 隔離されたエフェメラルな仮想マシンでのみ--privileged フラグを有効にして Docker コンテナを実行します。
  • Dockerの--privileged フラグを使用する必要があるジョブを実行する専用のRunnerを設定します。そして、保護されたブランチ上でのみジョブを実行するように、これらのRunnerを設定します。

ネットワークのセグメンテーション

GitLab Runnerはユーザーが制御するスクリプトを実行するように設計されています。ジョブが悪意のあるものであった場合の攻撃対象を減らすために、ジョブを独自のネットワークセグメントで実行することを検討することができます。そうすることで、他のインフラやサービスからネットワークを分離することができます。

すべてのニーズはユニークですが、クラウド環境の場合、次のようなことが考えられます:

  • ランナー仮想マシンを独自のネットワークセグメントに設定
  • インターネットからRunner仮想マシンへのSSHアクセスのブロック
  • Runner 仮想マシン間のトラフィックの制限
  • クラウドプロバイダのメタデータエンドポイントへのアクセスのフィルタリング
note
全てのRunnerはGitLab.comやGitLabインスタンスへのアウトバウンドネットワーク接続が必要です。ほとんどのジョブは、依存関係のプルなどのために、インターネットへのアウトバウンドネットワーク接続も必要となります。

ランナーホストのセキュリティ

ベアメタルであれ仮想マシンであれ、ランナーに静的ホストを使用する場合、ホストオペレーティングシステムのセキュリティのベストプラクティスを実装する必要があります。

CI ジョブのコンテキストで実行される悪意のあるコードはホストを危険にさらす可能性があるため、セキュリティプロトコルはその影響を軽減するのに役立ちます。その他の留意点としては、SSH キーのような、攻撃者が環境内の他のエンドポイントにアクセスできるようにする可能性のあるファイルを、ホストシステムからセキュリティで保護したり、削除したりすることが挙げられます。