とことんDevOps | 日本仮想化技術のDevOps技術情報メディア

DevOpsに関連する技術情報を幅広く提供していきます。

日本仮想化技術がお届けする「とことんDevOps」では、DevOpsに関する技術情報や、日々のDevOps業務の中での検証結果、TipsなどDevOpsのお役立ち情報をお届けします。
主なテーマ: DevOps、CI/CD、コンテナ開発、IaCなど
読者登録と各種SNSのフォローもよろしくお願いいたします。

Trivyを使ってみる

DockerやKubernetesが当たり前に使われるようになって、ただこれらのコンテナーツールを便利に使うだけでなく、 安全に使うための工夫が必要になってきています。

コンテナーでアプリケーションを使う上で、コンテナーイメージは重要なパーツの一つです。 コンテナーイメージとはコンテナープラットフォームの上でアプリケーションを動かすための環境を提供するものを指します。

コンテナーではなくネイティブな環境とコンテナーでアプリケーションを実行した場合の関係性を図にしてみると次のようになります。

カーネルと実行ファイル、ライブラリーの上でアプリケーションが実行されているのは共通ですが、コンテナーの場合は間になんらかのコンテナーエンジン(例えばDockerやPodman、containerdやCRI-Oなど)があり、アプリケーションの実行環境(独自の実行ファイルとライブラリー)の上でアプリケーションが実行されます。アプリケーションの実行環境は各コンテナー毎に独立になるので、それぞれの実行環境の変更が他のコンテナーに影響が出ることはありません。

物理マシンや仮想マシンでアプリケーションを実行する場合は、まずOSのセットアップから初めて、アプリケーションの実行に必要な環境(例えばランタイムとかドライバーとか)のセットアップから始めると思います。Ansibleなどを使って自動化すればある程度作業の効率化になりますが、それぞれのセットアップにそれ相応の時間を要します。

一方で、コンテナー技術を使えばアプリケーション刃物の数秒から数十秒程度でアプリケーションの実行を実現できます。アプリケーションの実行環境はコンテナーイメージを使って流し込むだけですし、コンテナーイメージ自体はコンテナーエンジンに対応するものを用意し、かつそのコンテナーエンジンを「環境」に用意すればどこでも動くためです。アプリケーションをコンテナーで実行するにはポータビリティ性の向上というメリットがあります。

アプリケーションの実行に使うベースイメージの選定

まず、アプリケーションを実行する際にそのベースとして使うコンテナーイメージの選定です。

コンテナーイメージはコンテナレジストリーに登録されており、DockerやPodmanといったコンテナー管理ソフトウェアによって、どのレジストリーからダウンロードするか、デフォルトが定義されています。 多くの場合、参照先の追加や変更が可能になっています。特に変更していない場合は多くの場合、Docker Hubのイメージがデフォルトで利用されます。

検索すると、Debian, Ubuntu, CentOS, FedoraというようにLinuxディストリビューションのコンテナーイメージが見つかると思います。 Docker Offical Image, Verified Publisher, Opensource ProgramタグがついたイメージはDockerが公式で提供するイメージか、作成元がきちんと判明しているイメージになっています。

これらのタグがついているイメージは多くの場合は安全なイメージであると考えられますが、脆弱性は日々発見されていますし物凄く頻繁にイメージが更新されているわけでもないので、イメージのスキャニングとパッチ当てした上での利用などを心がける必要があります。

一方でその他のユーザーが作ったイメージには上記のようなタグは設定されていません。そのようなイメージを使う場合はそのイメージに悪意のコードが含まれていないか確認する必要があります。Docker HubではDocker Proライセンス以上のユーザーの場合、GitHubなどと連携してイメージの自動ビルドが可能になっています。この仕組みを使ってビルドされたイメージはDockerfileそのままの内容を人の手を介すことなく自動でビルドされるため、改ざんされていないことを保証できます。自動ビルドされているイメージであればGitソース上にあるDockerfileの内容を確認して不審な点がなければ利用しても特に問題はないでしょう。

逆を言えばDocker Offical Image, Verified Publisher, Opensource Programでもなく、自動ビルドされてもいないイメージは使ってはいけません。 また、自動ビルドされるイメージは大抵の場合はGitのソースが変更されるタイミングで新しいイメージが作成されるため、利用するタイミングではイメージが新鮮な状態でない場合があります。 イメージスキャン機能があるレジストラーを使う、イメージスキャンツールを使ってチェックをすることを心がける必要があります(Docker HubはDocker Proライセンス以上を利用することで、自分がビルドしたイメージの脆弱性スキャンを行うことができます。Herborというコンテナイメージ管理ソフトウェアを使えば独自のコンテナレジストリーを構築でき、ビルドしたイメージの管理、チームへの共有が可能になります)。

なお、非常にいいコンテンツをNECさんが用意してくれているので、そちらも一読することをおすすめします。

Trivyを使ったイメージスキャンを実行

例えばTrivyを使うと、次のようにコンテナーイメージのスキャニングが可能です。

% trivy image --ignore-unfixed ubuntu:22.04
2022-06-24T14:32:04.422+0900    INFO    Vulnerability scanning is enabled
...
ubuntu:22.04 (ubuntu 22.04)
Total: 6 (UNKNOWN: 0, LOW: 0, MEDIUM: 6, HIGH: 0, CRITICAL: 0)
┌─────────────┬───────────────┬──────────┬───────────────────┬───────────────────┬────────────────────────────────────────────────────────────┐
│   Library   │ Vulnerability │ Severity │ Installed Version │   Fixed Version   │                           Title                            │
├─────────────┼───────────────┼──────────┼───────────────────┼───────────────────┼────────────────────────────────────────────────────────────┤
│ e2fsprogs   │ CVE-2022-1304 │ MEDIUM   │ 1.46.5-2ubuntu1   │ 1.46.5-2ubuntu1.1 │ e2fsprogs: out-of-bounds read/write via crafted filesystem │
│             │               │          │                   │                   │ https://avd.aquasec.com/nvd/cve-2022-1304                  │
├─────────────┤               │          │                   │                   │                                                            │
│ libcom-err2 │               │          │                   │                   │                                                            │
│             │               │          │                   │                   │                                                            │
├─────────────┤               │          │                   │                   │                                                            │
│ libext2fs2  │               │          │                   │                   │                                                            │
│             │               │          │                   │                   │                                                            │
├─────────────┤               │          │                   │                   │                                                            │
│ libss2      │               │          │                   │                   │                                                            │
│             │               │          │                   │                   │                                                            │
├─────────────┼───────────────┤          ├───────────────────┼───────────────────┼────────────────────────────────────────────────────────────┤
│ libssl3     │ CVE-2022-2068 │          │ 3.0.2-0ubuntu1.2  │ 3.0.2-0ubuntu1.5  │ openssl: the c_rehash script allows command injection      │
│             │               │          │                   │                   │ https://avd.aquasec.com/nvd/cve-2022-2068                  │
├─────────────┼───────────────┤          ├───────────────────┼───────────────────┼────────────────────────────────────────────────────────────┤
│ logsave     │ CVE-2022-1304 │          │ 1.46.5-2ubuntu1   │ 1.46.5-2ubuntu1.1 │ e2fsprogs: out-of-bounds read/write via crafted filesystem │
│             │               │          │                   │                   │ https://avd.aquasec.com/nvd/cve-2022-1304                  │
└─────────────┴───────────────┴──────────┴───────────────────┴───────────────────┴────────────────────────────────────────────────────────────┘

Trivyを使ってビルドしたイメージのスキャン

このイメージをそのまま使ってアプリケーションイメージを生成すると、どうなるでしょうか?

FROM docker.io/ubuntu:22.04
# Runs as root:
RUN apt-get update && apt-get install -y python3 && rm -rf /var/lib/apt/lists/*

# Switch to non-root user:
RUN useradd --create-home appuser
WORKDIR /home/appuser
USER appuser
ADD test.py /home/appuser/

# Runs as non-root user:
ENTRYPOINT ["python3", "test.py"]

当然ながら作成したイメージには脆弱性が残ったままになります。Trivyを使ってイメージをスキャンしてみましょう。

% docker build --compress -t python3-demo:latest -f Dockerfile .
% trivy image --ignore-unfixed python3-demo:latest
2022-06-24T15:00:03.800+0900    INFO    Vulnerability scanning is enabled
...
python3-demo:latest (ubuntu 22.04)
Total: 6 (UNKNOWN: 0, LOW: 0, MEDIUM: 6, HIGH: 0, CRITICAL: 0)
┌─────────────┬───────────────┬──────────┬───────────────────┬───────────────────┬────────────────────────────────────────────────────────────┐
│   Library   │ Vulnerability │ Severity │ Installed Version │   Fixed Version   │                           Title                            │
├─────────────┼───────────────┼──────────┼───────────────────┼───────────────────┼────────────────────────────────────────────────────────────┤
│ e2fsprogs   │ CVE-2022-1304 │ MEDIUM   │ 1.46.5-2ubuntu1   │ 1.46.5-2ubuntu1.1 │ e2fsprogs: out-of-bounds read/write via crafted filesystem │
│             │               │          │                   │                   │ https://avd.aquasec.com/nvd/cve-2022-1304                  │
├─────────────┤               │          │                   │                   │                                                            │
│ libcom-err2 │               │          │                   │                   │                                                            │
│             │               │          │                   │                   │                                                            │
├─────────────┤               │          │                   │                   │                                                            │
│ libext2fs2  │               │          │                   │                   │                                                            │
│             │               │          │                   │                   │                                                            │
├─────────────┤               │          │                   │                   │                                                            │
│ libss2      │               │          │                   │                   │                                                            │
│             │               │          │                   │                   │                                                            │
├─────────────┼───────────────┤          ├───────────────────┼───────────────────┼────────────────────────────────────────────────────────────┤
│ libssl3     │ CVE-2022-2068 │          │ 3.0.2-0ubuntu1.2  │ 3.0.2-0ubuntu1.5  │ openssl: the c_rehash script allows command injection      │
│             │               │          │                   │                   │ https://avd.aquasec.com/nvd/cve-2022-2068                  │
├─────────────┼───────────────┤          ├───────────────────┼───────────────────┼────────────────────────────────────────────────────────────┤
│ logsave     │ CVE-2022-1304 │          │ 1.46.5-2ubuntu1   │ 1.46.5-2ubuntu1.1 │ e2fsprogs: out-of-bounds read/write via crafted filesystem │
│             │               │          │                   │                   │ https://avd.aquasec.com/nvd/cve-2022-1304                  │
└─────────────┴───────────────┴──────────┴───────────────────┴───────────────────┴────────────────────────────────────────────────────────────┘

これを防ぐにはベースイメージは最新のイメージを使うことが重要になってくるのですが、脆弱性の発見とイメージのメンテナンス時期がマッチしていればこのような問題は起きないものの、そんなに物事がうまくいくことは滅多にありません。

コンテナイメージの脆弱性への対策としては、最新の「Best practices for writing Dockerfiles」に従ってイメージを作成することが重要です。かつてのドキュメントにはaptのようなパッケージ管理ツールでOSとライブラリの更新(つまりapt upgradeやapt dist-upgrade)のようなコマンドは実行すべきではないと書かれていたことがありましたが、「そのアドバイスはひどいアドバイスであり、適切ではない」として問題定義され、現在のベストプラクティスからは取り除かれています。

もし昔そのようなベストプラクティスを見てOSのアップデートをDockerfileに含めていない場合は、今後はその構築方法は改めた方が良いと思います。

ということで、先ほどの例にapt-get -y upgradeを加えてみます。

% cat Dockerfile-fixed
FROM docker.io/ubuntu:22.04
# Runs as root:
RUN apt-get update && apt-get -y upgrade && apt-get install -y python3 && rm -rf /var/lib/apt/lists/*

# Switch to non-root user:
RUN useradd --create-home appuser
WORKDIR /home/appuser
USER appuser
ADD test.py /home/appuser/

# Runs as non-root user:
ENTRYPOINT ["python3", "test.py"]

ビルドしたイメージをTrivyでスキャンしてみると、イメージには脆弱性がなくなったことがわかります。

% docker build --compress -t python3-demo2:latest -f Dockerfile-fixed .
% trivy image --ignore-unfixed python3-demo2:latest
2022-06-24T15:13:22.528+0900    INFO    Vulnerability scanning is enabled
...
python3-demo2:latest (ubuntu 22.04)
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)

当然ではありますが、アプリケーションは想定通り動作します。

% docker run python3-demo2:latest
hello world

Trivyのその他の使い道

Trivyを「GUI」で使う

最近のDocker Desktopを使えば、Docker ExtensionのTrivyを導入することで、イメージスキャンを使いやすいGUIで利用できます。

また、同様のツールとしてRancher Labs + SUSEが提供するRancher Desktopにも、Trivyを使ったコンテナイメージスキャニング機能が存在します。

これらは無料かつ追加でソフトウェアの導入は必要なく使えるため、積極的に使っておきたい機能です。

Trivyを使ったKubernetesの脆弱性スキャン

現在のTrivyはKubernetesクラスターの脆弱性スキャン機能が用意されています。 これを使うことで、現在利用しているKubernetesクラスターの弱点を知るきっかけになると思います。

Kubernetesクラスターのスキャンツールとしては他にKubescapeやkube-hunterなどといったツールが存在します。 その他にも探せばいくつもソリューションが見つかります。

まとめ

Trivyを使えばコンテナイメージのスキャンを手軽に行うことができます。Rancher Desktopを使えば、標準機能でコンテナイメージのスキャンが可能です。 Docker Desktopの場合はDocker ExtensionのTrivyを追加することで、イメージスキャンが可能になります。Trivy以外のイメージスキャンもいくつか入手できるようです。

TrivyはKubernetesクラスターのセキュリティチェックにも使えるので、KubernetesやDocker,Podmanといったようなコンテナーを使う場合には使っておきたいツールです。