12 コンテナイメージのスキャンに関するベストプラクティス
チームが直面する主な課題の 1 つは、アプリケーションのデリバリーを遅らせることなく、コンテナのセキュリティリスクをどのように管理するかです。この課題に早期に対処する方法は、セキュア DevOps ワークフローを採用することです。DevSecOps としても知られるセキュア DevOps は、開発から本番環境まで、アプリケーションのライフサイクル全体にセキュリティと監視をもたらします。これにより、セキュリティと安定性、そして高性能を備えたアプリケーションをデリバリーすることが可能になります。このワークフローは、既存のツールチェーンに組み込むことができ、DevOps チーム、開発チーム、セキュリティチームに単一の信頼できる情報源を提供して、効率を最大化します。イメージスキャンは、セキュアな DevOps ワークフローに組み込むべき重要な機能です。最初の防衛線の一つとして、脆弱性が悪用される前にそれを検出してブロックするのにも役立ちます。幸い、イメージスキャンは実装と自動化が簡単です。この記事では、効果的なコンテナイメージスキャン戦略の採用に役立つ、イメージスキャンのベストプラクティスとヒントを数多く紹介します。
コンテナイメージのスキャンとは?
イメージスキャンとは、セキュリティの問題、脆弱性、または不適切な慣行を検出するために、コンテナイメージの内容とビルドプロセスを分析するプロセスを指します。
通常、ツールは複数のフィード(NVD、Alpine、Canonical など)から共通脆弱性情報(CVE)情報を収集し、イメージに脆弱性がないかどうかを確認します。また、最も一般的なセキュリティの問題や不適切な慣行を探すための、すぐに使えるスキャンルールを提供しているものもあります。
イメージスキャンは、セキュアな DevOps ワークフローのいくつかのステップに簡単に統合できます。たとえば、CI/CD パイプラインに統合して、脆弱性がレジストリに到達するのを阻止したり、レジストリに統合してサードパーティのイメージの脆弱性から保護したり、ランタイムに統合して新たに発見された CVE から保護したりすることができます。
イメージスキャンを自動化し、ベストプラクティスに従うことで、チームのアプリケーションのデプロイの速度が低下することを防げます。
今すぐ実装できる 12 のイメージスキャンベストプラクティスについて詳しく見ていきましょう。
1: CI/CD パイプラインにイメージスキャンを組み込む
コンテナイメージを構築する際には、公開前に細心の注意を払ってスキャンを行う必要があります。
DevOps ワークフローのためにすでに構築している CI/CD パイプラインを活用し、イメージスキャンを行うステップを 1 つ追加することができます。

コードのテストとビルドが完了したら、イメージを本番リポジトリにプッシュする代わりに、ステージングリポジトリにプushすることができます。その後、イメージスキャンツールを実行します。これらのツールは通常、検出されたさまざまな問題を、それぞれ異なる重大度で一覧表示したレポートを返します。CI/CD パイプラインでは、これらのイメージスキャン結果を確認し、重大な問題がある場合はビルドを失敗させることができます。
自動化が鍵であることを忘れないでください。これはDevOpsの核心的な概念ですよね?同じことがDevOpsのセキュリティにも適用されます。
CI/CDパイプラインにセキュリティを自動化することで、脆弱性がレジストリに侵入する前に検出でき、ユーザーがこれらの問題の影響を受ける機会や、問題が本番環境に到達するのを防ぐことができます。
2:インラインスキャンを採用してプライバシーを管理してください
前のステップでは、CI/CD パイプラインでのイメージスキャンに、従来はステージングレジストリがどのように使用されていたかを見ました。しかし、イメージに誤って認証情報が含まれている場合はどうなるでしょうか?それらは悪用され、漏洩してしまう可能性があります。
さらに一歩進んで、インラインイメージスキャンを実装し、ステージングリポジトリを使用せずに CI/CD パイプラインから直接イメージをスキャンすることができます。
インラインイメージスキャンでは、スキャンメタデータのみがスキャンツールに送信されるため、プライバシーを管理しやすくなります。

Gitlab、Github Actions、AWS Codepipeline、Azure Pipelines、CircleCI、Jenkins、Atlassian Bamboo、Tekton などの一般的な CI/CD ツールでインラインイメージスキャンを実装する方法について、ガイドをご用意しました。
3:レジストリでイメージのスキャンを実行します。
イメージスキャンを実装し始める際には、その実装をレジストリに最初のステップの一つとして含める必要があります。

展開するすべてのイメージは、レジストリから取得されます。そこでイメージをスキャンすることで、少なくとも実行前にスキャンが行われていることがわかります。
4: Kubernetes アクセス制御を有効にする
CI/CD パイプラインで脆弱なイメージをブロックしても、本番環境にデプロイされるのを阻止することはできません。また、レジストリでスキャンされたとしても、外部開発者のイメージをブロックするのは誰でしょうか?
理想的には、Kubernetes がイメージをスケジュールする前にチェックし、スキャンされていないイメージや脆弱なイメージがクラスタにデプロイされないようにブロックしたいところです。
このポリシーを実装するには、アドミッションコントローラーを活用できます。
Kubernetes アドミッションコントローラーは、クラスタで実行を許可するものを定義およびカスタマイズするのに役立つ、Kubernetes ネイティブの強力な機能です。アドミッションコントローラーは、リクエストが認証および承認された後、オブジェクトが永続化される前に、Kubernetes API へのリクエストをインターセプトして処理します。

スキャンツールは通常、オンデマンドでイメージのスキャンをトリガーし、検証結果を返す検証用 Webhook を提供しています。
アドミッションコントローラーは、イメージのスケジューリング前にこの Webhook を呼び出すことができます。Webhook から返されたセキュリティ検証結果は API サーバーに伝播され、API サーバーは元のリクエスト送信者に応答し、イメージがチェックに合格した場合にのみ、オブジェクトを etcd データベースに保持します。
ただし、この決定は、クラスタ内で何が起こっているかについてのコンテキストを一切持たないイメージスキャナによって行われます。OPA を使用することで、このソリューションを改善することができます。
Open Policy Agent (OPA) は、rego という高レベルの宣言型言語を使用する、オープンソースの汎用ポリシーエンジンです。OPA の背後にある重要な考え方の 1 つは、意思決定とポリシーの実施を分離することです。
OPA を使用すると、イメージスキャナではなく、Kubernetes クラスタ内で承認の決定を行うことができます。これにより、ネームスペース、ポッドメタデータなどのクラスタ情報を意思決定に使用することができます。例えば、「dev」ネームスペースにはより寛容なルールを設定し、「production」には非常に制限の厳しいポリシーを設定することができます。
5: イメージのバージョンを固定します
スキャンしたイメージが、Kubernetes クラスタにデプロイしたイメージと異なる場合があります。これは、「latest」や「staging」などの変更可能なタグを使用した場合に発生します。このようなタグは、新しいバージョンで常に更新されるため、最新のスキャン結果がまだ有効であるかどうかを判断することが困難になります。

変更可能なタグを使用すると、同じイメージから異なるバージョンのコンテナがデプロイされる可能性があります。スキャン結果によるセキュリティ上の懸念に加え、これはデバッグが困難な問題の発生につながる可能性があります。
たとえば、ubuntu:focal
を使用する代わりに、可能な限りubuntu:focal-20200423
のような変更不可能なタグの使用を強制する必要があります。
バージョンタグ (一部のイメージ) は、マイナーな、互換性のある変更で更新される傾向があることにご注意ください。そのため、少し冗長に見えますが、再現性を確保する唯一の方法は、実際のイメージ ID を使用することです。
ubuntu:@sha256:d5a6519d9f048100123c568eb83f7ef5bfcad69b01424f420f17c932b00dea76
これは、イメージスキャンのベストプラクティスを超えた問題だと思われるかもしれません。その通りです。一連の Dockerfile のベストプラクティスが必要です。これは、Dockerfile の FROM
コマンドだけでなく、Kubernetes 展開ファイル、およびイメージ名を指定するほぼすべての場所に影響します。
イメージスキャンの観点から、何ができるでしょうか?
このポリシーは、Kubernetes アドミッションコントローラー、イメージスキャナー、および前のポイントで説明したシステムである OPA Engine を組み合わせて実施できます。
6: OS の脆弱性をスキャンします
一般的なイメージスキャンのベストプラクティスとして、「イメージは軽いほど良い」という原則を念頭に置いてください。イメージが軽いほど、ビルドとスキャンが高速になり、潜在的な脆弱性を持つ関連コンポーネントも減ります。
新しい Docker イメージは通常、既存のベースイメージから構築されるか、その上にレイヤーが追加されます。このベースイメージは、イメージの Dockerfile 内の FROM
ステートメントで定義されます。その結果、レイヤードアーキテクチャ設計となり、最も一般的なタスクの時間を大幅に節約できます。たとえば、イメージスキャンでは、ベースイメージを 1 回スキャンするだけで済みます。親イメージに脆弱性がある場合、その上に構築された他のイメージも脆弱になります。

イメージに新しい脆弱性を導入していなくても、ベースイメージの脆弱性の影響を受けます。
そのため、スキャンツールは、既知の脆弱性のあるイメージの脆弱性フィードを積極的に追跡し、そのうちの 1 つを使用している場合は通知する必要があります。
7: distroless イメージを活用する
distroless イメージは、パッケージマネージャー、シェル、および標準的な Linux ディストリビューションに通常含まれるその他のプログラムを含まないベースイメージです。distroless イメージを使用すると、アプリケーションとその関連コンポーネントのみを軽量なコンテナイメージにパッケージ化できます。
実行時コンテナ内に必要なものだけを厳密に制限することで、攻撃対象領域を最小限に抑えることができます。また、スキャナー(例:CVE)の信号対雑音比が向上し、必要なものだけにプロバンスの確立の負担を軽減できます。
以下の例は、Ubuntu とディストリレス上で実行される Go の「Hello world」アプリケーションの Dockerfile を示しています。
FROM ubuntu:focal COPY main / ENTRYPOINT ["/main"]

スキャンした結果、24 件の OS 脆弱性が発見され、そのうち 2 件は中程度の深刻度でした。また、このようなシンプルなアプリとしては、イメージサイズが 77.98MB とかなり大きくなっています。
次に、distroless イメージをベースにした同じアプリを見てみましょう。
FROM gcr.io/distroless/static-debian10 COPY main / ENTRYPOINT ["/main"]

スキャン結果では、24 件の OS 脆弱性が検出され、そのうち 2 件は中程度の深刻度と分類されました。さらに、このようなシンプルなアプリにもかかわらず、イメージサイズは 77.98MB とかなり大きくなっています。
次に、ディストリビューションレスイメージをベースにした同じアプリを見てみましょう。
8:サードパーティのライブラリの脆弱性をスキャンする
アプリケーションは多くのライブラリを使用するため、そのコード行数は、チームが実際に記述するコードの行数よりも多くなります。つまり、コードの脆弱性だけでなく、関連するすべてのコンポーネントの脆弱性にも注意する必要があります。
幸い、これらの脆弱性は、スキャナーが OS の脆弱性について警告するために使用するのと同じ脆弱性フィードで適切に追跡されています。すべてのツールがイメージ内のライブラリを深くスキャンするわけではないため、イメージスキャナーが十分に深くスキャンし、これらの脆弱性について警告することを確認してください。
9: レイヤーの順序を最適化
Dockerfile 内の RUN
コマンドに注意すると、イメージをさらに最適化できます。RUN
コマンドの順序は、イメージを構成するレイヤーの順序を決定するため、最終的なイメージに大きな影響を与えます。
大きなレイヤー(通常は不変)を最初に、最も変動の大きいファイル(つまり、コンパイル済みのアプリケーション)を最後に配置することで、Docker キャッシュの使用を最適化できます。これにより、既存のレイヤーの再利用が促進され、イメージの構築が高速化され、間接的にイメージのスキャンも高速化されます。
10: Dockerfile の設定ミスをスキャンする
これまで見てきたように、Docker イメージのビルドプロセスは、マニフェストである Dockerfile の指示に従います。
一般的なセキュリティ設定ミスを検出するために、Dockerfile で従うべきベストプラクティスがいくつかあります。
- 必要なリソース以上のアクセス権を持つ特権ユーザー(root)として実行しています。
- コンテナで開いてはいけない ssh ポート 22 などの安全でないポートを公開しています。
- 不適切な「COPY」または「ADD」コマンドによって、プライベートファイルが誤って含まれています。
- 環境変数やマウントを介して注入する代わりに、秘密情報や認証情報を含める(漏洩する)。この Java の例のように、ユーザーが Entrypoint および CMD にオプションを渡せるようにすることも良い方法です。
- ブロックされるソフトウェアパッケージ、許可されるベースイメージ、SUID ファイルが追加されているかどうかなど、特定のポリシーで定義されているその他の多くの事項。
次のようなDockerfileでは:
FROM ubuntu:focal-20200423 USER root RUN curl http://my.service/?auth=ABD54F0356FA0054 EXPOSE 80/tcp EXPOSE 22/tcp ENTRYPOINT ["/main"]
当社のイメージスキャンは、いくつかの問題を自動的に検出することができます。
USER root
root として実行しています。
EXPOSE 22/tcp
ここでは、コンテナには含めるべきではないツールである ssh でよく使用されるポート 22 を公開しています。また、ポート 80 も公開していますが、これは HTTP サーバーの一般的なポートであるため問題ありません。
RUN curl http://my.service/?auth=ABD54F0356FA005432D45D0056AF5400
このコマンドは、誰でも当社に損害を与えるために使用できる認証キーを使用しています。代わりに、何らかの変数を使用する必要があります。このようなキーは、Dockerfile だけでなく、イメージ内のあらゆるファイルで、正規表現を使用して検出することができます。追加の対策として、認証情報を保存することがわかっているファイル名もチェックすることができます。
注意すべき点が多く、圧倒されるかもしれません。幸い、これらのベストプラクティスのほとんどは、NIST や PCI などのセキュリティ基準でカバーされており、多くのイメージスキャンツールには、特定のコンプライアンス制御にマッピングされた、すぐに使えるポリシーが用意されています。
11: Kubernetes 展開全体で脆弱性を迅速にフラグ付け
スキャンに合格したイメージは、完全に安全というわけではありません。イメージをスキャンして展開した後、その直後にそのイメージに新しい脆弱性が発見された場合を想像してみてください。あるいは、ある時点でセキュリティポリシーを強化した場合、すでに実行中のイメージはどうなるのでしょうか?

イメージを継続的にスキャンすることは、イメージスキャンのベストプラクティスです。
- 新しい脆弱性を検出し、ポリシーの変更に対応します。
- その発見結果を適切なチームに報告し、できるだけ早くイメージを修正します。
もちろん、実行時スキャンを実装することで、これらの脆弱性の影響を軽減することができます。CVE-2019-14287 を例にとってみましょう。簡単な Falco ルールを作成するだけで、その脆弱性が悪用されているかどうかを検出することができます。ただし、脆弱性ごとに特定のルールを作成するのは時間がかかり、最後の防衛手段として使用すべきです。
では、クラスタで実行中のイメージを継続的にスキャンする方法についてご説明します。
セキュリティツールは、これを実現するためにさまざまな戦略を採用していますが、最も簡単な方法は、数時間ごとにすべてのイメージを再スキャンすることです。理想的には、脆弱性フィードが更新されたら、影響を受けるイメージをすぐに再スキャンしたいところです。また、一部のツールはイメージのメタデータを保存することができ、完全な再スキャンを行うことなく新しい脆弱性を検出することができます。
そして、実行中のコンテナで脆弱性が発見されたら、できるだけ早く修正する必要があります。ここで重要なのは、脆弱性の報告を効果的に行うことで、各担当者が自分に関連する情報に集中できるようにすることです。
これを実現する 1 つの方法は、DevOps チームとセキュリティチームが、膨大なイメージ、パッケージ、CVE のカタログを整理できる、クエリ可能な脆弱性データベースを使用することです。彼らは、CVE の経過時間、修正プログラムの有無、ソフトウェアのバージョンなどのパラメータを検索したいと思うでしょう。最後に、これらのレポートを脆弱性管理チームや CISO などと共有(PDF/CSV)できると便利です。
例で説明しましょう。次のようなクエリを想像してください:prod namespace 内のseverity がhigh より大きく、CVE が 30 日以上経過しており、修正が利用可能なすべての脆弱性を表示してください。

このような脆弱性報告機能により、チームは実際に修正できる脆弱なイメージを簡単に特定し、脆弱性が悪用される前に解決策の策定に着手することができます。
12: SaaS ベースのスキャンソリューションを選択します
オンプレミスソリューションよりも SaaS ベースのスキャンサービスを選択すると、多くのメリットがあります。
オンデマンドでスケーラブルなリソース:最初は少数のイメージのスキャンから始めて、コンテナアプリケーションの規模拡大に合わせて拡張できます。バックエンドのデータ管理について心配する必要はありません。
迅速な実装:スキャンをCI/CDパイプラインに組み込み、数分で運用を開始できます。オンプレミスアプリケーションのように、インストールや設定に時間がかかることはありません。
簡単なアップグレードとメンテナンス:SaaSプロバイダーがパッチの適用と新機能のアップデートを管理するため、手動でのアップグレードは不要です。
インフラストラクチャや人件費が不要:自社でのハードウェアやソフトウェアライセンスの永久所有権を取得する必要がありません。また、アプリケーションのメンテナンスやサポートのために現地に常駐する必要もありません。
結論
イメージスキャンは、セキュアな DevOps ワークフローの第一防衛線です。イメージスキャンを自動化することで、その可能性を最大限に引き出し、問題になる前に問題を検出することができます。イメージスキャンのベストプラクティスに従うことで、作業効率を低下させることなくセキュリティを組み込むことができます。
また、イメージスキャンは 1 回だけ実施するものではなく、ビルド時、レジストリ登録時、デプロイ前、コンテナの実行開始後など、ワークフローのさまざまな段階で継続的に実施するチェックポイントです。