Sysdigがコンテナイメージスキャニングの12のベストプラクティスを公式ブログで解説した。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
クラウドネイティブアプリケーションを本番環境で保護、運用するためのソリューションを提供するSysdigは2020年7月21日(米国時間)、コンテナイメージスキャニングのベストプラクティスを公式ブログで解説した。
コンテナイメージスキャニングは、開発から本番環境の運用までのアプリケーションライフサイクル全体にセキュリティとモニタリングを統合する「DevSecOps」の主要ワークフローの一つだという。
Sysdigは、DevOpsの大きな課題の一つとして、アプリケーションデリバリーを遅らせることなく、セキュリティリスクを管理することを挙げ、この課題に迅速に対処する方法の一つが、DevSecOpsワークフローの導入だと説明している。
コンテナイメージスキャニングは、セキュリティ上の問題点や、脆弱(ぜいじゃく)性、バッドプラクティスの検出を目的に、コンテナイメージ(以下、イメージ)の内容とビルドプロセスを分析することを指す。
コンテナイメージスキャニングのためのツールは通常、NVD(National Vulnerability Database)やAlpine、Canonicalなど、さまざまなソースからCVE(Common Vulnerabilities and Exposures)情報を収集し、イメージが脆弱かどうかチェックする。一部のツールは、よくあるセキュリティ問題やバッドプラクティスをチェックするスキャニングルールも標準で提供する。
イメージスキャニングは、DevSecOpsワークフローの幾つかのステップに統合できる。例えば、「CI/CD(継続的インテグレーション/継続的デプロイ)パイプラインに統合し、脆弱性がレジストリに到達するのを防ぐ」「レジストリに統合し、サードパーティーイメージの脆弱性を防御する」「ランタイムに統合し、新たに発見されたCVEから保護する」といったことが可能だ。
Sysdigは、イメージスキャニングの12のベストプラクティスを自動化して実行することで、セキュリティを確保しながら、アプリケーションを迅速にデプロイできるとしている。
イメージをビルドする際は、公開前に注意深くスキャンする必要がある。そのためには、DevOpsワークフローの既存のCI/CDパイプラインに、イメージスキャニングを行うステップを追加すればよい。
コードをテストし、ビルドしたら、イメージをステージングリポジトリにプッシュし、このイメージに対してイメージスキャニングツールを実行する。こうしたツールは通常、見つかった問題を列挙し、それぞれの深刻度を評価したレポートを返す。こうしたイメージスキャニング結果をチェックし、重大な問題がある場合は、そのビルドを「失敗」とする。
このプロセスを自動化してCI/CDパイプラインに組み込むことで、脆弱性がレジストリに混入するのを防げる。
ステージングリポジトリを使わずに、CI/CDパイプラインからイメージを直接スキャンするインラインイメージスキャニングを行うこともできる。
インラインイメージスキャニングでは、スキャン用のメタデータだけがスキャニングツールに送られる。この手法はプライバシー管理に役立つ。
Sysdigは、一般的なCI/CDツールでインラインイメージスキャニングを実行する方法のガイドを提供している。これらのツールには、「Gitlab」「GitHub Actions」「AWS CodePipeline」「Azure Pipelines」「CircleCI」「Jenkins」「Atlassian Bamboo」「Tekton」が含まれる。
デプロイするイメージは、全てレジストリからプルされる。レジストリでイメージをスキャンすれば、少なくとも、実行するイメージは、前もってスキャンされていることが分かる。
CI/CDパイプラインで脆弱なイメージをブロックしても、こうしたイメージが本番環境にデプロイされる可能性は残る。また、イメージをレジストリでスキャンしても、外部開発者が作成したイメージがブロックされない恐れがある。
Kubernetesがイメージのスケジューリング前にイメージをチェックし、スキャンされていないイメージや、脆弱なイメージがクラスタにデプロイされないようにすることができれば、理想的だ。アドミッション(許可)コントローラーを使えば、このポリシーを実装できる。
Kubernetesの強力なネイティブ機能である「Kubernetesアドミッションコントローラー」は、「クラスタで何を実行するか」の定義とカスタマイズを支援する。Kubernetes APIへのリクエストが認証、認可された後で、オブジェクトの永続化前に、このリクエストをインターセプトし、処理する機能を果たす。
スキャニングツールは通常、オンデマンドでイメージスキャニングをトリガできる検証Webフックを提供し、検証による判断を返す。アドミッションコントローラーは、イメージのスケジューリング前にこのWebフックを呼び出せる。Webフックによって返されるセキュリティ検証による判断は、APIサーバに反映される。APIサーバは、最初のリクエスト元に応答し、イメージがチェックを通った場合にのみ、etcd(etc distributed)データベースにオブジェクトを永続させる。
ただし、このセキュリティ検証はイメージスキャナーによって、「クラスタ内で何が起こっているか」のコンテキストなしで実行される。このソリューションは、OPA(オープンポリシーエージェント)によって改善できる。
OPAは、オープンソースの汎用(はんよう)ポリシーエンジンであり、「rego」という宣言型の高水準言語を使用する。OPAの重要な考え方の一つが、判断とポリシー強制を分離することだ。OPAにより、Kubernetesクラスタにおけるアドミッションの判断を、イメージスキャナーの代わりに行える。名前空間やPodメタデータなどのクラスタ情報を使うと、この判断が可能となる。例えば、「dev」名前空間と「production」名前空間で、制限が異なるポリシーを適用できる。
「latest」「staging」など、対象がすぐ変わるタグを使うと、スキャンしたバージョンとは異なるバージョンのイメージを、Kubernetesクラスタにデプロイしてしまうことがある。
この問題を避けるには、例えば、可能であれば、「ubuntu:focal」ではなく、「ubuntu:focal-20200423」といったタグを使う。
ただし、一部のイメージでは、バージョンを示すタグは、互換性を破らないマイナーな変更をイメージに加えただけでも、更新される傾向がある。このため、冗長になるものの、再現性を確保する唯一の選択肢は、イメージIDを使うことだ。だが、その場合、次のようなタグを使うことになる可能性がある。
ubuntu:@sha256:d5a6519d9f048100123c568eb83f7ef5bfcad69b01424f420f17c932b00dea76
当然のことながら、このタグには問題があるが、それはイメージIDに起因している。イメージIDについては、Dockerfileのベストプラクティスに従う必要がある。その上で、Kubernetesアドミッションコントローラー、イメージスキャナー、OPAエンジンの組み合わせにおいて、イメージIDをタグに使うというポリシーを強制するとよい。
新しいDockerイメージは通常、既存のベースイメージから作られる。このベースイメージは、イメージのDockerfileのFROMステートメントで定義される。これにより、階層化されたアーキテクチャ設計を実現し、ほとんどの一般的なタスクにおいて、多くの時間を節約する。イメージスキャニングにおいては、ベースイメージは1回だけスキャンすれば済む。もし親イメージが脆弱であれば、それを基に構築されるイメージも全て脆弱になる。
このため、イメージスキャニングツールは、既知の脆弱なイメージの脆弱性データベースを継続的にチェックし、イメージに既知の脆弱性が見つかったら、通知する必要がある。
一般的なLinuxディストリビューションに見られるパッケージマネジャー、シェル、その他のプログラムを含まないベースイメージである“ディストリビューションレス”イメージを利用することで、アプリケーションとその依存関係のみを軽量コンテナイメージにパッケージできる。ランタイムコンテナの内容を、必要なものだけに限定することで、攻撃対象領域を最小化できる。また、スキャナーのSN比が改善されることにもなる。
下の例は、Ubuntuで動作する「Hello world」アプリのDockerfileと、それをベースにしたイメージのスキャン結果を示している。
FROM ubuntu:focal COPY main / ENTRYPOINT ["/main"]
このイメージスキャンにより、OSの脆弱性が24件あり、そのうち2件の深刻度が「Medium」であることが分かった。またイメージサイズは、こうしたシンプルなアプリにしては大きい77.98MB。
次に、ディストリビューションレスのベースイメージに基づく同じアプリのDockerfileと、それをベースにしたイメージのスキャン結果を以下に示す。
FROM gcr.io/distroless/static-debian10 COPY main / ENTRYPOINT ["/main"]
今度は、無視できる程度のOS脆弱性が2件だけ見つかった。イメージサイズも、このアプリにふさわしい6.93MBにとどまっている。
アプリケーションは多くのライブラリを使用する。そのため、コードの行数がチームで作成したコードよりも多くなり、コードの脆弱性だけではなく、コードの全ての依存関係にも注意する必要が生じる。
幸い、こうした脆弱性は、イメージスキャナーがOSの脆弱性をチェックするのに使うのと同じ脆弱性データベースで追跡できる。ただし、全てのスキャンツールがイメージ内のライブラリをスキャンし、脆弱性を警告するとは限らない。使用するイメージスキャナーがこの機能を備えているかどうかを確認する必要がある。
DockerfileのRUNコマンドを注意深く使えば、イメージの最適化を推し進めることができる。RUNコマンドの順序が、イメージを構成するレイヤーの順序を規定し、最終イメージに大きく影響するからだ。
大きなレイヤー(通常、不変)を最初に配置し、大きく変化するファイル(コンパイルされたアプリケーション)を最後に配置することで、Dockerのキャッシュ利用を最適化できる。これは、既存レイヤーの再利用に役立ち、イメージのビルドを高速化し、イメージスキャニングも間接的にスピードアップする。
Dockerイメージのビルドプロセスは、Dockerfileの命令に従う。Dockerfileにおけるセキュリティ上の一般的な構成ミスを検出するには、Dockerfileのベストプラクティスに反する下記のような問題の有無をチェックするとよい。
こうした問題の大部分は、NIST(米国立標準技術研究所)やPCI(Payment Card Industry)のようなセキュリティ基準でカバーされており、多くのイメージスキャニングツールが、具体的なコンプライアンス管理にマッピングされたポリシーを標準で提供する。
スキャンを経たイメージも、完全に安全なわけではない。例えば、イメージをスキャンし、デプロイした直後に新しい脆弱性が見つかることもある。また、ある時点でセキュリティポリシーを強化した場合、既に実行されているイメージの中で、新しいポリシーに適合しないものが出てくる恐れがある。
このため、Kubernetesクラスタで実行されているイメージについても、下記2点を目的として継続的にスキャンを行うことが、イメージスキャニングのベストプラクティスとなる。
オンプレミスソリューションに代えてSaaSベースのスキャニングサービスを選択することで、下記のメリットが得られる。
Copyright © ITmedia, Inc. All Rights Reserved.