ブログ

ミュータントタグの攻撃! または、タグの可変性が真のセキュリティ脅威である理由

Google Cloudとコンテナの継続的なセキュリティ

本文の内容は、2020年6月23日にÁlvaro Iradierが投稿したブログ(https://sysdig.com/blog/toctou-tag-mutability/)を元に日本語に翻訳・再構成した内容となっております。

タグの可変性により、複数の機能およびセキュリティの問題が発生する可能性があります。コンテナランドでは、タグは特定の時点で作成されたイメージバージョンにおける変化する事を前提としたリファレンスです。タグは予期せず、いつでも変更される可能性があります。この記事では、それらを防ぐ方法を学びます。

20200624-title.jpg

タグの可変性により、とりわけ、チェック時間と使用時間(TOCTOU)の問題が発生します。CI/CDパイプラインまたはKubernetesのアドミッションフェーズで検証されるイメージは、クラスターにデプロイされているイメージとは異なり、イメージスキャンのセキュリティチェックをバイパスします。

たとえば、レジストリに直接アクセスできる開発者は、この事実を悪用して、イメージのスキャン後にタグを変更し、プロダクションクラスター内で悪意のあるバージョンのマイクロサービスを実行し、機密情報を漏らしたり、バックドアを仕掛ける可能性があります。

20200624-01.png

不変タグを使用すると、これらの問題を回避できますが、タグの可変性は多くのシナリオで非常に便利であり、不変タグはレジストリで広くサポートされていません。

この記事では、タグの変更可能性の影響、利点と欠点、および予期しない問題を防ぐためのいくつかの実践と手法について説明します。また、Sysdigイメージスキャナーとアドミッションコントローラーツールを使用してKubernetesデプロイメントを保護する方法についても説明します。

タグの可変性の探索

レジストリ、イメージ、タグの基本

タグの可変性とは何かを説明する前に、いくつかの重要な概念を定義しましょう。

20200624-02.png
  • レジストリは、コンテナイメージの保存と取得(プッシュ/プル)に使用するサービスです。docker.io、quay.io、GCR、ECRなどのパブリックSaaSサービス、またはOn-Premでホストされるプライベートレジストリを使用できます。
  • レジストリ内のイメージはリポジトリにグループ化され、リポジトリは組織、プロジェクトなどの追加のサブグループを持つことができます。
  • リポジトリは複数のタグを含みます。
  • コンテナを作成するときは、レジストリからイメージをプルし、リポジトリから特定のタグを選択します。このタグはイメージに対応し、そのイメージで定義されたファイルシステム内のプロセスを分離して実行します。

したがって、次のようなコマンドで特定のイメージを使用してコンテナを実行します。

docker run my-registry.com/some-organization/a-repository:tag1

しかし、正確には何がイメージのフードの下にあるのでしょうか?イメージは、以下に説明するイメージマニフェストと呼ばれる単なるJSONドキュメントです。

  • イメージの設定(起動、環境、その他のパラメーター)
  • このイメージを使用してコンテナのルートファイルシステムを構成する一連のファイルシステムレイヤー
20200624-03.png

マニフェスト、イメージレイヤー、およびイメージ設定は、レジストリ内にバイナリBLOBとして保存されます。それらに対処するために、「20feefdbe22e555a5922072161f0bf42fdee25f38c026997c0f4c025e3edbe00」のように、SHA256ハッシュを使用しますが、この記事では、明確にするために最初の桁のみを使用します:20feefdb...

これらのハッシュダイジェストは便利ではないので、それらを参照するタグを定義できます(少なくともマニフェストに)。上記のシナリオでは、tag1は1ab50232のダイジェストを含むマニフェストファイルへのリンクです。たとえば、そのイメージをダウンロードするためにそのマニフェストを参照する場合は、 myregistry/myimage:tag1を使用できます。

マニフェストには、イメージ設定への参照と、イメージファイルシステムを構成するレイヤーのダイジェストが含まれています(e74c2290 ...、71bb4d21 ...、等)。

タグの可変性とは

ダイジェストはオブジェクトのコンテンツに基づいて作成されるため、特定のダイジェストは常に同じオブジェクトを識別します。それらは不変です。

これはコンテンツの重複を避けるのに最適です。たとえば、2つの異なるイメージで同じコンテンツを持つ2つのレイヤーは、同じダイジェストを持ちます。そのレイヤーを再利用して、スペースと帯域幅を節約できます。

ダイジェストの制限を回避するため、マニフェストファイルへの単なるポインタであるタグを使用します。そして、ポインターとして、それらは時間とともに変化する可能性があります。

ミュータントタグ(またはミュータブルタグ)は、マニフェストを介して特定のイメージを参照するタグですが、別の時点で別のイメージを参照することもできます。

不変タグは特定のイメージに関連付けられたタグであり、後で変更することはできません。

ミュータントタグの作成

ミュータントタグの作成は、かなり一般的で簡単なプロセスです。

20200624-04.png

新しいイメージ(つまり、myimage:tag1)をビルド、タグ付け、プッシュします。

20200624-05.png

イメージをレジストリにプッシュすると、クライアントはすべてのレイヤーをアップロードします。

20200624-06.png

次に、マニフェストファイル(この例では1ab50232...)と、リポジトリmyimageでそのマニフェストを参照するタグtag1を作成します。

20200624-07.png

別のイメージを作成し(Dockerfileコマンドまたはイメージのコンテンツを変更)、同じタグ名tag1を使用してタグを付け、レジストリにプッシュします。イメージのコンテンツが変更されたため、イメージレイヤーが異なり、新しいマニフェストがダイジェスト17b345ca...で作成されます。

20200624-08.png

プッシュが終了すると、tag1は別のマニフェストを参照するようになります。タグ tag1を変更しました。

20200624-09.png

ビルド/プッシュプロセスを繰り返して、タグを変更することができます!

古いマニフェストは引き続きレジストリで使用できるため、イメージタグの代わりにマニフェストダイジェストを使用してそれらを取得できます。以下を使用できます。

  • myimage@sha256:1ab50232...最初のイメージを取得します。
  • myimage@sha256:17b345ca... 2番目のものをプルします。
  • そして、myimage:tag1またはmyimage@sha256:612bc919...のいずれかで、3番目のものをプルします。
20200624-10.png

また、複数のタグが同じマニフェストを指すことができます。この例では、myimage:tag1とmyimage:othertagがこの時点で同じイメージを参照していることがわかりますが、変更される可能性があります。

タグの可変性の使用例

明らかに、複数のユースケースがあるので、ミュータブル/ミュータントタグは便利です。例えば:

"latest"(指定されていない場合はデフォルトのタグ)を使用して、常に最新バージョンのイメージを指すか、"alpine"や "slim"などのバリアントの最新バージョンを常に指すイメージバリアントを指します。

また、ミュータントタグを使用して、"dev", "prod", "qa"などのさまざまな環境でバージョンを追跡することも一般的です。これらのタグは、新しいバージョンがこれらの環境のいずれかにデプロイされると更新されます。

バージョン管理を簡単にするために、:1または:2.0のようなタグは、マイナーバージョンまたはパッチバージョンのエイリアスにすることができます。これらのタグを使用すると、ユーザーは予期しない動作や構成の変更なしで、最新のメジャーバージョンまたはイメージをプルできますが、バグとセキュリティ修正が含まれた最新のマイナーバージョンを取得していることを確認できます。

タグの可変性に関するデプロイメントの問題

開発者や運用チームが直面するいくつかの典型的な問題は、タグの変更とコンテナのデプロイに関連しています。タグが期待されるイメージに対応している保証がないため、イメージタグを使用してコンテナをデプロイする場所はすべて、非決定的なデプロイメントを実行しています。これは、単純なdocker run、またはdocker-compose、Kubernetesなどのオーケストレーションツールに適用されます。

例を見てみましょう:

  • 開発者のPeterは、myimage:latestを使用してラップトップでコンテナを実行します。彼はアプリケーションのバージョン1.1をプルして実行します。
  • 数時間後、ローラは自分のコンピュータで同じmyimage:latestをプルして実行します。しかし、それは同じイメージですか? いいえ、イメージのメンテナーはバージョン1.2を公開しており、最新版は現在バージョン1.2を参照しています。
  • 来週、アプリケーションを本番クラスターにデプロイしますが、myimage:latestはバージョン2.0を指しているため、設定ファイルフォーマットに重大な変更が加えられています。

その結果、本番環境でアプリケーションがクラッシュします。

Kubernetesの場合、ポッドのスケジュールとボラティリティ、またはホストキャッシュのステータスに応じて、同一のコンテナに対して異なるイメージを実行する可能性があります。

以下のスクリーンキャストは、ノードの1つでポッドのスケジュールを遅らせることで問題を再現しています。cordonノードの1つでスケジューリングを無効にし、タグを変更してからノードを変更するために使用しuncordonますが、ノードが後でクラスターに追加された場合、またはポッドが追放されて別のノードで再スケジュールされた場合も同じ問題が発生します。

スクリーンキャストは、英語版のブログページにてご覧いただけます。

繰り返し可能なデプロイメント

確定的で反復可能なデプロイメントを実現するために、ピーターとローラはタグの代わりにダイジェストを使用できます。myimage:tagの代わりにmyimage@sha256:<digestValue>を使用することで、コンピューターと本番環境に同じマニフェストを確実にデプロイし、この種の予期しないバージョンの変更を回避できます。

たとえば、myimage@sha256:latestの代わりに myimage@sha256:1ab50232...を使用します。

Kubernetesの動作に関する1つの注意:ポッドがスケジュールされ、イメージがpullされた後、 Kubernetesはタグをイメージダイジェストに解決します。ポッドリソースを作成する前に解決されていれば、いくつかの問題から救われたでしょう。また、KubernetesにはさまざまなImagePullポリシーがあるため、Alwaysキャッシュされたイメージを使用する代わりに、常にレジストリからダウンロードします。

TOCTOU問題

これまでのところ、これらの問題はタイミングの悪さ、予期しない更新などに関連していますが、悪意のあるアクティビティやセキュリティの脅威には関連していません。TOCTOUはTime-of-check Time-of-useの略であり、チェックが使用とは異なる瞬間に行われるとTOCTOUバグが発生し、システムのセキュリティが低下する可能性があります。

クラスターにデプロイする前にすべてのイメージをスキャンするKubernetes Admission Controllerを使用している場合でも、攻撃者がTOCTOUバグを悪用してクラスターで悪意のあるソフトウェアを実行する可能性があるケースを分析してみましょう。アドミッションコントローラは、コンテナ内の不正行為や脆弱性を検出する必要があるため、セキュリティポリシーに準拠していないイメージを禁止できます。

これは、このようなバグを悪用する攻撃者のワークフローの例です。

20200624-11.png
  1. ポッドの作成/更新リクエストがKubernetes APIに送信されます。
  2. アドミッションコントローラはリクエストをインターセプトし、イメージスキャンをトリガーします。
  3. リクエストにはイメージ名としてmyimage:latestが含まれているため、イメージスキャナーはレジストリにそのイメージをリクエストします...
  4. ...そして、マニフェストと対応するレイヤーを取得し、イメージを構成してスキャンします。
  5. イメージスキャナーはイメージを処理し、レポートを生成して、アドミッションコントローラーに送り返します。脆弱性がないため、アドミッションコントローラーはスキャンレポートを評価し、ポッドを許可します。
  6. この時点で、スキャンが終了した後、攻撃者は同じmyimage:latestタグを使用して悪意のあるイメージをレジストリにプッシュします。
  7. Kubernetesがポッドをスケジュールし、コンテナランタイムがノードのレジストリからイメージをpullします。
  8. プルされたイメージはスキャンされたイメージとは異なり、潜在的に悪意のある(または少なくともスキャンされていない)イメージがクラスターにデプロイされます。

スキャンとポッドスケジュールの間で悪意のあるイメージをプッシュするのはどれほど簡単ですか?

イメージのスキャンには、イメージのサイズによって数秒から数分かかる場合があり、スケジューリングにも時間がかかる場合があるため、その間にプッシュを実行するのに十分な時間があります。

悪意のあるイメージをビルドしてリポジトリにプッシュし、ローカルでmyimage:latestにタグを付け直すことで、悪意のあるイメージを事前に準備できます。

$ docker build -t myregistry.com/maliciousimage .
Uploading context 18.829 MB
Step 1/2 : FROM busybox
 ---> 769b9341d937
...
Successfully built 99cc1ad10469
$ docker push myregistry.com/maliciousimage
The push refers to repository [myregistry.com/maliciousimage]
e9f56d359a24: Pushed
...
latest: digest: sha256:8360f9950adc9... size: 735
$ docker tag myregistry.com/maliciousimage myimage:latest

そして、スキャンがステップ4でプルをトリガーした直後に、タグ付けされた悪意のあるイメージをプッシュします。

$ docker push myimage:latest
e9f56d359a24: : Mounted from maliciousimage
...
latest: digest: sha256:8360f9950adc9... size: 735

これにより、以前にプッシュされたのすべてのレイヤーが再利用maliciousimageされるため、それらは再度アップロードされません。

DockerレジストリAPIで説明されているように、イメージをプッシュするために必要なすべての手順を実行し、最終的なマニフェストアップロードは保持する ように、Containerdなどのリポジトリクライアントを変更することで攻撃を自動化することもできます。タグの変更を引き起こすこの最後のステップは、イメージスキャナーからのWebhook、Kubernetes監査ログからのイベントなどによってトリガーされます。

タグの可変性の問題の軽減

タグの変異に関連する問題は非常に一般的で再現が簡単です。予期しないイメージの更新に遭遇したときの不運のためか、意図的に引き起こされたためです。

デジタル署名されたイメージの使用、パブリックリポジトリまたは信頼されていないリポジトリの回避、イメージの取得元の厳密な制御の実装などの一般的なセキュリティプラクティス以外に、いくつかのトラブルを回避するためのいくつかの優れたプラクティスについて話しましょう。

非常に不安定/変更可能なタグ(latest、...)の使用を避けます

本番環境、重要な環境、または簡単なテストではないものへの導入では、 "latest" または同様のタグのようなタグを避けてください。特定のバージョンのタグなど、より安定したタグを使用してください。ただし、これらのタグも変更できないという保証はありません。

さらに、 "latest" は単なる慣習です。存在すること、または最新の既存バージョンを指すことが保証されていません。3年前のバージョンを指す可能性があります。

ミュータントタグを作成しない

イメージのタグ付けプロセスを制御している場合は、ミュータントタグを作成しないように、既存のタグをプッシュしないでください。一部のレジストリには不変のタグを作成するオプションがあり、既存のタグをプッシュしようとすると失敗します。

タグの不変性のオプションがない場合は、Docker APIを使用してタグでマニフェストを取得し、その存在を確認してプッシュを中止するなど、開発プロセスまたはCI/CDパイプラインに追加のメジャーを追加できます。

タグの代わりにダイジェスト

8360f9950adc9...名前付きタグの代わりに、@sha256:のようなイメージダイジェストを使用します。これは、使用するイメージが常に同じであることを保証します。

を使用docker images --digestsしてイメージダイジェストを表示したり、でデプロイされたポッドを検査したりできますkubectl inspect pod。

私たちの変化するアドミッションコントローラー

Sysdig Admission ControllerはSysdig Secureをイメージスキャナーとして使用してポッドを検証しますが、スペックを変更してイメージタグの代わりにダイジェストを使用します。これにより、スケジューラは以前にスキャンされたものとまったく同じイメージを確実にプルします。心配しないでください。元のイメージとタグは参照用にアノテーションに保持されるため、失われることはありません。

ビルド後のスキャンや、イメージがレジストリにプッシュされたときなど、他の変更不可のアドミッションコントローラーや他のアプローチを使用すると、クラスターがTOCTOUの問題にさらされる可能性があります。

不変のタグ

ガベージコレクションが期待どおりに機能しないなど、ミュータントタグには他にも問題があります。または、Registry APIがダイジェストによってイメージを削除するが、タグによってリストされたイメージがいくつかの混乱バグを引き起こす可能性があるという事実があります。

1.10以降のHarbor2019年7月以降のECR などの一部のレジストリでは、タグを不変にする構成設定を提供しています。Harborでは、リポジトリごとに、またはワイルドカード式を使用してルールを定義できます。不変性を有効にすると、レジストリは既存のタグのプッシュを許可しません。

まとめ

可変または不変のタグ?

可変性は便利ですが、気づいていない場合や管理する準備ができていない場合も危険です。

詳細については、Virtual Rejekts 2020での私のプレゼンテーションSysdig Admission Controllerに関するブログ投稿スライドを確認できます。

したがって、「大きな力には大きな責任が伴います」、「知識は力です」。これらの2つの原則に従って、ミュータントタグの力を解き放ち、Sysdig SecureイメージスキャンSysdig Admission Controllerなどの知識、優れた実践、ツールを適用して、タグを制御し、予期しない問題を防ぎます。

Sysdigに関するお問い合わせはこちらから

最近の投稿

カテゴリー

アーカイブ

ご質問・お問い合わせはこちら

top