🚨

Trivy を使って kubernetes のセキュリティについて見てみる

2024/01/18に公開

はじめに

k8s を扱っていく上でセキュリティ対策は重要ですが、k8s 公式の Security に多くのページがある通りセキュリティを意識しなければならない箇所は多岐に渡ります。一番上のページにある Overview of Cloud Native Security ではセキュリティを考慮すべき箇所を 4 つのレイヤーに分類し、各レイヤーの頭文字をとって 4C と呼んでいます。

Cloud

Cloud は主に k8s クラスタのコントロールプレーン (etcd や API server への通信) および各ノード自体のセキュリティに関する部分を指します。
各クラウドプロバイダー上で k8s クラスタを構築している際は各プロバイダーが公開しているセキュリティのベストプラクティスに従うことが推奨されています。一方、自宅で self-managed な環境の k8s クラスタを構築している場合などは上記リソースに関するセキュリティを自分で管理する必要があります。

Cluster

Cluster は主に k8s クラスタを構成するリソースに関するセキュリティを指します。クラスタレベルのセキュリティでは考慮すべき項目が多いですが、主に以下のような項目があります。

  • pod に必要以上の権限を付加しない。
  • secret 等の機密情報を適切に管理する。
  • ClusterRole, Role, ServiceAccount の権限を適切に管理する。

Container

Container は主に pod で使用するコンテナイメージに関するセキュリティとなります。自作アプリケーションを k8s クラスタ上で稼働させる場合はコンテナイメージを作成することになりますが、その際に追加でインストールする依存ライブラリで脆弱性が報告されているものを使用しない、root ユーザを使用せず必要な権限のみを付加したユーザでコードを実行するように設定するなどの点を意識する必要があります。

Code

code は自作アプリケーションを作成する場合にコード内に機密情報を書き込まない、脆弱性のあるライブラリを使用しない等が考えられます。code に関するセキュリティは実装する言語によるため、言語毎のセキュリティツールなどを活用するのが良いです(例えば python では bandit など)。

trivy

上記を読んで各レイヤーにおいて注目すべき点があることはわかりましたが、結局どのようにセキュリティが充分であるかを検証する方法についてはイメージが掴みにくいと思います。幸いなことに k8s 用のセキュリティツールは数多く存在しているため、それらを活用することで事前知識が薄い状態でもセキュリティにおいて意識すべき項目を確認することができます。
今回はセキュリティツールの中でも使用者の多い trivy をメインに使っていきます。

概要

https://trivy.dev/

Trivy は OSS のセキュリティスキャナツールであり、コンテナイメージ、イメージレジストリ、ファイルシステム、構成ファイル等の様々な対象に対してセキュリティスキャンを実行し脆弱性や不備を検出することが可能となっています。trivy 自体は k8s 専用のツールというわけではないですが、k8s のマニフェストやクラスタ構成のセキュリティスキャンを実行できるため 4 C のうち Cloud ~ Container レイヤーのセキュリティをカバーできます。

インストール

trivy はいくつかの方法でインストール可能となっており、installation にまとまっています。
今回は ubuntu 上で CLI として実行するため以下の方法でインストールします。

sudo apt-get install wget apt-transport-https gnupg lsb-release
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | sudo tee -a /etc/apt/sources.list.d/trivy.list
sudo apt-get update
sudo apt-get install trivy

使ってみる

さっそく trivy を使ってセキュリティについて見ていきます。
trivy を使うには対象となる k8s クラスタやアプリケーションが必要となるため、ここでは flask を使った簡単な自作 python アプリケーションを作成して k8s クラスタ上で稼働させるというシナリオを考え、その際のセキュリティについて実際に確認していきます。

コンテナイメージの検証

はじめに、python アプリケーション用のベースイメージとして Dockerhub で公開されている公式 python イメージのうち比較的新しい以下のものを使用します。

  • イメージ名: python:3.12.1-slim-bookworm
  • Digest: python@sha256:ee9a59cfdad294560241c9a8c8e40034f165feb4af7088c1479c2cdd84aafbed
  • Created: 2023-12-08T04:49:21Z

trivy ではコンテナイメージに対してセキュリティスキャンを行い、コンテナ内のパッケージやライブラリで確認されている脆弱性を検出できます。詳細は Container Image を参照。
まずこのイメージに対してスキャンを実行して脆弱性がないか確認します。

$ trivy image python:3.12.1-slim-bookworm

python:3.12.1-slim-bookworm (debian 12.4)

Total: 84 (UNKNOWN: 0, LOW: 66, MEDIUM: 15, HIGH: 2, CRITICAL: 1)

このイメージは 2023/12/8 とつい最近に更新されていますが、この時点で 84 件とかなりの脆弱性が検出されています。

上記のスキャン結果では少し数が多いので Severity low を除いたものを見ていきます。実行時に -s オプションで検出する severity をフィルタできるため、low 以外の値を設定してスキャンを実行します。

実行結果
$ trivy image python:3.12.1-slim-bookworm  -s UNKNOWN,MEDIUM,HIGH,CRITICAL

python:3.12.1-slim-bookworm (debian 12.4)

Total: 18 (UNKNOWN: 0, MEDIUM: 15, HIGH: 2, CRITICAL: 1)

┌──────────────┬────────────────┬──────────┬──────────────┬───────────────────┬───────────────┬──────────────────────────────────────────────────────────────┐
│   Library    │ Vulnerability  │ Severity │    Status    │ Installed Version │ Fixed Version │                            Title                             │
├──────────────┼────────────────┼──────────┼──────────────┼───────────────────┼───────────────┼──────────────────────────────────────────────────────────────┤
│ gcc-12-base  │ CVE-2023-4039  │ MEDIUM   │ affected     │ 12.2.0-14         │               │ gcc: -fstack-protector fails to guard dynamic stack          │
│              │                │          │              │                   │               │ allocations on ARM64                                         │
│              │                │          │              │                   │               │ https://avd.aquasec.com/nvd/cve-2023-4039                    │
├──────────────┤                │          │              │                   ├───────────────┤                                                              │
│ libgcc-s1    │                │          │              │                   │               │                                                              │
│              │                │          │              │                   │               │                                                              │
│              │                │          │              │                   │               │                                                              │
├──────────────┼────────────────┤          │              ├───────────────────┼───────────────┼──────────────────────────────────────────────────────────────┤
│ libncursesw6 │ CVE-2023-50495 │          │              │ 6.4-4             │               │ ncurses: segmentation fault via _nc_wrap_entry()             │
│              │                │          │              │                   │               │ https://avd.aquasec.com/nvd/cve-2023-50495                   │
├──────────────┼────────────────┼──────────┤              ├───────────────────┼───────────────┼──────────────────────────────────────────────────────────────┤
│ libsqlite3-0 │ CVE-2023-7104  │ HIGH     │              │ 3.40.1-2          │               │ sqlite: heap-buffer-overflow at sessionfuzz                  │
│              │                │          │              │                   │               │ https://avd.aquasec.com/nvd/cve-2023-7104                    │
├──────────────┼────────────────┼──────────┤              ├───────────────────┼───────────────┼──────────────────────────────────────────────────────────────┤
│ libssl3      │ CVE-2023-5678  │ MEDIUM   │              │ 3.0.11-1~deb12u2  │               │ openssl: Generating excessively long X9.42 DH keys or        │
│              │                │          │              │                   │               │ checking excessively long X9.42...                           │
│              │                │          │              │                   │               │ https://avd.aquasec.com/nvd/cve-2023-5678                    │
│              ├────────────────┤          │              │                   ├───────────────┼──────────────────────────────────────────────────────────────┤
│              │ CVE-2023-6129  │          │              │                   │               │ openssl: POLY1305 MAC implementation corrupts vector         │
│              │                │          │              │                   │               │ registers on PowerPC                                         │
│              │                │          │              │                   │               │ https://avd.aquasec.com/nvd/cve-2023-6129                    │
├──────────────┼────────────────┤          │              ├───────────────────┼───────────────┼──────────────────────────────────────────────────────────────┤
│ libstdc++6   │ CVE-2023-4039  │          │              │ 12.2.0-14         │               │ gcc: -fstack-protector fails to guard dynamic stack          │
│              │                │          │              │                   │               │ allocations on ARM64                                         │
│              │                │          │              │                   │               │ https://avd.aquasec.com/nvd/cve-2023-4039                    │
├──────────────┼────────────────┤          │              ├───────────────────┼───────────────┼──────────────────────────────────────────────────────────────┤
│ libsystemd0  │ CVE-2023-7008  │          │              │ 252.19-1~deb12u1  │               │ systemd-resolved: Unsigned name response in signed zone is   │
│              │                │          │              │                   │               │ not refused when DNSSEC=yes...                               │
│              │                │          │              │                   │               │ https://avd.aquasec.com/nvd/cve-2023-7008                    │
├──────────────┼────────────────┤          │              ├───────────────────┼───────────────┼──────────────────────────────────────────────────────────────┤
│ libtinfo6    │ CVE-2023-50495 │          │              │ 6.4-4             │               │ ncurses: segmentation fault via _nc_wrap_entry()             │
│              │                │          │              │                   │               │ https://avd.aquasec.com/nvd/cve-2023-50495                   │
├──────────────┼────────────────┤          │              ├───────────────────┼───────────────┼──────────────────────────────────────────────────────────────┤
│ libudev1     │ CVE-2023-7008  │          │              │ 252.19-1~deb12u1  │               │ systemd-resolved: Unsigned name response in signed zone is   │
│              │                │          │              │                   │               │ not refused when DNSSEC=yes...                               │
│              │                │          │              │                   │               │ https://avd.aquasec.com/nvd/cve-2023-7008                    │
├──────────────┼────────────────┤          │              ├───────────────────┼───────────────┼──────────────────────────────────────────────────────────────┤
│ login        │ CVE-2023-4641  │          │              │ 1:4.13+dfsg1-1+b1 │               │ shadow-utils: possible password leak during passwd(1) change │
│              │                │          │              │                   │               │ https://avd.aquasec.com/nvd/cve-2023-4641                    │
├──────────────┼────────────────┤          │              ├───────────────────┼───────────────┼──────────────────────────────────────────────────────────────┤
│ ncurses-base │ CVE-2023-50495 │          │              │ 6.4-4             │               │ ncurses: segmentation fault via _nc_wrap_entry()             │
│              │                │          │              │                   │               │ https://avd.aquasec.com/nvd/cve-2023-50495                   │
├──────────────┤                │          │              │                   ├───────────────┤                                                              │
│ ncurses-bin  │                │          │              │                   │               │                                                              │
│              │                │          │              │                   │               │                                                              │
├──────────────┼────────────────┤          │              ├───────────────────┼───────────────┼──────────────────────────────────────────────────────────────┤
│ openssl      │ CVE-2023-5678  │          │              │ 3.0.11-1~deb12u2  │               │ openssl: Generating excessively long X9.42 DH keys or        │
│              │                │          │              │                   │               │ checking excessively long X9.42...                           │
│              │                │          │              │                   │               │ https://avd.aquasec.com/nvd/cve-2023-5678                    │
│              ├────────────────┤          │              │                   ├───────────────┼──────────────────────────────────────────────────────────────┤
│              │ CVE-2023-6129  │          │              │                   │               │ openssl: POLY1305 MAC implementation corrupts vector         │
│              │                │          │              │                   │               │ registers on PowerPC                                         │
│              │                │          │              │                   │               │ https://avd.aquasec.com/nvd/cve-2023-6129                    │
├──────────────┼────────────────┤          │              ├───────────────────┼───────────────┼──────────────────────────────────────────────────────────────┤
│ passwd       │ CVE-2023-4641  │          │              │ 1:4.13+dfsg1-1+b1 │               │ shadow-utils: possible password leak during passwd(1) change │
│              │                │          │              │                   │               │ https://avd.aquasec.com/nvd/cve-2023-4641                    │
├──────────────┼────────────────┼──────────┤              ├───────────────────┼───────────────┼──────────────────────────────────────────────────────────────┤
│ perl-base    │ CVE-2023-31484 │ HIGH     │              │ 5.36.0-7+deb12u1  │               │ perl: CPAN.pm does not verify TLS certificates when          │
│              │                │          │              │                   │               │ downloading distributions over HTTPS...                      │
│              │                │          │              │                   │               │ https://avd.aquasec.com/nvd/cve-2023-31484                   │
├──────────────┼────────────────┼──────────┼──────────────┼───────────────────┼───────────────┼──────────────────────────────────────────────────────────────┤
│ zlib1g       │ CVE-2023-45853 │ CRITICAL │ will_not_fix │ 1:1.2.13.dfsg-1   │               │ zlib: integer overflow and resultant heap-based buffer       │
│              │                │          │              │                   │               │ overflow in zipOpenNewFileInZip4_6                           │
│              │                │          │              │                   │               │ https://avd.aquasec.com/nvd/cve-2023-45853                   │
└──────────────┴────────────────┴──────────┴──────────────┴───────────────────┴───────────────┴──────────────────────────────────────────────────────────────┘
2024-01-13T22:20:11.305+0900    INFO    Table result includes only package filenames. Use '--format json' option to get the full path to the package file.

Python (python-pkg)

Total: 1 (UNKNOWN: 0, MEDIUM: 1, HIGH: 0, CRITICAL: 0)

┌────────────────┬───────────────┬──────────┬────────┬───────────────────┬───────────────┬──────────────────────────────────────────────────────────┐
│    Library     │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │                          Title                           │
├────────────────┼───────────────┼──────────┼────────┼───────────────────┼───────────────┼──────────────────────────────────────────────────────────┤
│ pip (METADATA) │ CVE-2023-5752 │ MEDIUM   │ fixed  │ 23.2.1            │ 23.3          │ pip: Mercurial configuration injectable in repo revision │
│                │               │          │        │                   │               │ when installing via pip                                  │
│                │               │          │        │                   │               │ https://avd.aquasec.com/nvd/cve-2023-5752                │
└────────────────┴───────────────┴──────────┴────────┴───────────────────┴───────────────┴──────────────────────────────────────────────────────────┘

severity low 以外では 19 件の脆弱性が検出されています。検出されたもの関しては Vulnerability に CVE の番号、title に対応するリンクがあるので詳細を確認できます。

例えば severity CRITICAL に分類されている zlib1g の CVE を確認してみます。

https://avd.aquasec.com/nvd/cve-2023-45853

CVE の内容についてはリンク先の概要や Weakness に記載されており、対応状況については検出されたパッケージ名と CVE を元に検索するか、CVE によってはリンク先の References で github PR や各ライブラリの forum, blog 等で対応状況に関する投稿やコメントを見て確認します。zlib1g では Reference から Github PR が作成されていることが確認でき、こちらのコメントを見る限り既にパッチが作成、マージされているようです。

ではパッチが適用されたバージョンに更新するため、イメージ上で apt install zlib1g で最新バージョンがインストールできるか試してみます。

$ docker run --rm -it python:3.12.1-slim-bookworm bash

root@9cf0d7559dc0:/# apt update && apt install -y zlib1g
Get:1 http://deb.debian.org/debian bookworm InRelease [151 kB]
Get:2 http://deb.debian.org/debian bookworm-updates InRelease [52.1 kB]
Get:3 http://deb.debian.org/debian-security bookworm-security InRelease [48.0 kB]
Get:4 http://deb.debian.org/debian bookworm/main amd64 Packages [8787 kB]
Get:5 http://deb.debian.org/debian bookworm-updates/main amd64 Packages [12.7 kB]
Get:6 http://deb.debian.org/debian-security bookworm-security/main amd64 Packages [134 kB]
Fetched 9185 kB in 3s (3498 kB/s)
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
All packages are up to date.
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
zlib1g is already the newest version (1:1.2.13.dfsg-1).
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.

apt では既に最新バージョンの 1:1.2.13.dfsg-1 がインストール済みと表示されます。そのため、debian で公開されているパッケージのバージョンを以下で検索します。

https://packages.debian.org/search?keywords=zlib1g

bookworm (stable) (libs): 圧縮ライブラリ - ランタイム
1:1.2.13.dfsg-1: amd64 arm64 armel armhf i386 mips64el mipsel ppc64el s390x

コンテナイメージ python:3.12.1-slim-bookworm では文字通りベースイメージに debian bookworm が使用されていますが、bookworm のパッケージとして配布されている zlib1g の最新バージョンは 1:1.2.13.dfsg-1となっており、一方で trivy スキャンの実行結果から CVE が検出されているバージョンも 1:1.2.13.dfsg-1 であることがわかります。そのため、bookworm においては CVE 対応のパッチが適用されたバージョンはまだパッケージとして配布されていないことなります。

trixie (testing) (libs): 圧縮ライブラリ - ランタイム
1:1.3.dfsg-3: amd64 arm64 armel armhf i386 mips64el ppc64el s390x

一方、debian の最新バージョン trixie では zlib1g のより新しいバージョン 1:1.3.dfsg-3 が公開されているということもわかります。


別の脆弱性として、 severity HiGH で検出されている libsqlite3-0 も見てみます。

https://avd.aquasec.com/nvd/2023/cve-2023-7104/

A vulnerability was found in SQLite SQLite3 up to 3.43.0 and classified as critical. This issue affects the function sessionReadRecord of the file ext/session/sqlite3session.c of the component make alltest Handler. The manipulation leads to heap-based buffer overflow. It is recommended to apply a patch to fix this issue. The associated identifier of this vulnerability is VDB-248999.

上記に示されているように、この CVE は 3.43.0 以降のバージョンでは修正されているようです。こちらも配布されているパッケージのバージョンを確認します。

https://packages.debian.org/search?suite=default&section=all&arch=any&searchon=names&keywords=libsqlite3-0

bookworm (stable) (libs): SQLite 3 共有ライブラリ
3.40.1-2: amd64 arm64 armel armhf i386 mips64el mipsel ppc64el s390x
trixie (testing) (libs): SQLite 3 共有ライブラリ
3.44.2-1: amd64 arm64 armel armhf i386 mips64el ppc64el s390x

bookworm では修正前のバージョン 3.40.1-2 までしか取得できず、trixie では修正済みの 3.44.2-1 が取得できます。

このように報告されてから日の浅い CVE に関しては対応したパッチがそもそも作成されていなかったり、使用している OS ではパッケージ配布されていないような場合があります。この場合は CVE サイトの Potential Mitigations に示されている回避方法で該当するライブラリや機能を使用しないようにする、最新のバージョンを手動で適用する、そもそも使用している OS やベースイメージを切り替える等の方法で対応することが必要となります。

上で確認した CVE では、 Debian trixie であれば新しいバージョンがパッケージ配布されているのでこちらを使えば解決できそうです。なので python:3.12.1-slim-bookworm の代わりに debian trixie をベースイメージに使う方向に切り替えます。trixie も Dockerhub で公式のイメージが公開されているので以下のものを使用します。

  • イメージ名: debian:trixie
  • Digest: debian@sha256:6b29b1669aab3975150883173fb9247044223f185dff2017bc3cdd3a7eda620a
  • Created: 2024-01-11T02:40:38.837523872Z

これに対して trivy スキャンを実行します。

$ trivy image debian:trixie

debian:trixie (debian trixie/sid)

Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)

こちらは 2024/1/11 に更新されているだけあって CVEが 1 件も検出されませんでした。

公式の debian イメージではデフォルトで最小限のパッケージしかインストールされていないため、python が使用できるように apt でインストールします。また、後に flask を使用するためこちらも pip でインストールします。最新バージョンは 3 系ですが、試しに古いバージョンである 2.3.0 をインストールします。

Dockerfile
FROM debian:trixie

RUN apt update && apt install -y python3 python3-pip
RUN pip install --break-system-packages flask==2.3.0

ビルドしたイメージを再度スキャンすると、flask で 1 件の CVE が検出されました。

Python (python-pkg)

Total: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 1, CRITICAL: 0)

┌──────────────────┬────────────────┬──────────┬────────┬───────────────────┬───────────────┬────────────────────────────────────────────────────────────┐
│     Library      │ Vulnerability  │ Severity │ Status │ Installed Version │ Fixed Version │                           Title                            │
├──────────────────┼────────────────┼──────────┼────────┼───────────────────┼───────────────┼────────────────────────────────────────────────────────────┤
│ Flask (METADATA) │ CVE-2023-30861 │ HIGH     │ fixed  │ 2.3.0             │ 2.3.2, 2.2.5  │ flask: Possible disclosure of permanent session cookie due │
│                  │                │          │        │                   │               │ to missing Vary: Cookie...                                 │
│                  │                │          │        │                   │               │ https://avd.aquasec.com/nvd/cve-2023-30861                 │
└──────────────────┴────────────────┴──────────┴────────┴───────────────────┴───────────────┴────────────────────────────────────────────────────────────┘

上記の通り 2.3.2 以降で修正されているため、flask に関しても特段理由がなければ最新バージョンを使うのが良さそうです。

前置きがかなり長くなりましたがベースイメージとして使用するコンテナイメージの脆弱性対応ができたので、次に python アプリケーションを作成します。ここに関しては / にアクセスしたら文字を返すだけの非常に単純な作りにします。

app.py
from flask import Flask

app = Flask(__name__)


@app.route("/")
def root():
    return "App is running."


if __name__ == "__main__":
    app.run(host="0.0.0.0")

これをコンテナ化します。debian イメージのデフォルトユーザは root になっていますが、このままでは k8s で動作させる際に指摘されるので事前にアプリケーション実行用のユーザ python を作成しておきます。最終的な Dockerfile は以下のようになります。

Dockerfile
FROM debian:trixie

RUN apt update && apt install -y python3 python3-pip
RUN pip install --break-system-packages flask==3.0.0
RUN useradd -d /home/python -s /bin/bash -m -b /home/python -u 10001 python
COPY app.py /home/python/app.py

USER python
WORKDIR /home/python
EXPOSE 5000

ENTRYPOINT [ "python3" ]
CMD [ "/home/python/app.py" ]

ビルド

docker build -t myflask-trivy-test:1.0.0 .

最後に念の為再度 trivy スキャンを実行

myflask-trivy-test:1.0.0 (debian trixie/sid)

Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)

Total: 0 なので自作アプリケーションを追加しても CVE は検出されていません。これでコンテナレベルで脆弱性対応を行った自作アプリケーションのコンテナイメージが作成できました。

マニフェストの検証

アプリケーションのイメージを作成したので、次は k8s クラスタにデプロイするためのマニフェストを作成します。
マニフェストでは上記のイメージを使った deployment, service, 外部からのアクセス用の ingress を定義します。まずはあまり深く考えず必要な項目のみを設定します。

app.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myflask
  namespace: trivy
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myflask
  template:
    metadata:
      labels:
        app: myflask
    spec:
      containers:
        - name: myflask
          image: myflask-trivy-test
          ports:
            - containerPort: 5000

---
apiVersion: v1
kind: Service
metadata:
  name: myflask
  namespace: trivy
spec:
  selector:
    app: myflask
  ports:
  - protocol: TCP
    port: 5000
    targetPort: 5000

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myflask
  namespace: trivy
spec:
  rules:
  - host: myflask.ops.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: myflask
            port:
              number: 5000

trivy では Misconfiguration Scanning の機能により k8s マニフェストを含む IaC 全般の configuration ファイルの検証が可能となっているため、こちらで確認してみます。

$ trivy config app.yml

app.yml (kubernetes)

Tests: 168 (SUCCESSES: 151, FAILURES: 17, EXCEPTIONS: 0)
Failures: 17 (UNKNOWN: 0, LOW: 13, MEDIUM: 4, HIGH: 0, CRITICAL: 0)

Failures: 17 より上記のマニフェストでは 17 件の問題点が検出されています。こちらも CVE と同様に severity low を除いたものについて見ていきます。

実行結果
$ trivy config app.yml -s UNKNOWN,MEDIUM,HIGH,CRITICAL

app.yml (kubernetes)

Tests: 96 (SUCCESSES: 92, FAILURES: 4, EXCEPTIONS: 0)
Failures: 4 (UNKNOWN: 0, MEDIUM: 4, HIGH: 0, CRITICAL: 0)

MEDIUM: Container 'myflask' of Deployment 'myflask' should set 'securityContext.allowPrivilegeEscalation' to false
════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════出された項目は主に k8s Pod Security Standards に該当し、基本的に pod に必要以上の権限を与えない考え方に基づくものになります。═════════════
A program inside the container can elevate its own privileges and run as root, which might give the program control over the container and node.

See https://avd.aquasec.com/misconfig/ksv001
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 app.yml:17-20
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
  17 ┌         - name: myflask
  18 │           image: myflask-trivy-test
  19 │           ports:
  20 └             - containerPort: 5000
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


MEDIUM: Container 'myflask' of Deployment 'myflask' should set 'securityContext.runAsNonRoot' to true
═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Force the running image to run as a non-root user to ensure least privileges.

See https://avd.aquasec.com/misconfig/ksv012
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 app.yml:17-20
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
  17 ┌         - name: myflask
  18 │           image: myflask-trivy-test
  19 │           ports:
  20 └             - containerPort: 5000
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


MEDIUM: Container 'myflask' of Deployment 'myflask' should specify an image tag
═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
It is best to avoid using the ':latest' image tag when deploying containers in production. Doing so makes it hard to track which version of the image is running, and hard to roll back the version.

See https://avd.aquasec.com/misconfig/ksv013
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 app.yml:17-20
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
  17 ┌         - name: myflask
  18 │           image: myflask-trivy-test
  19 │           ports:
  20 └             - containerPort: 5000
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


MEDIUM: container myflask of deployment myflask in trivy namespace should specify a seccomp profile
═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
A program inside the container can bypass Seccomp protection policies.

See https://avd.aquasec.com/misconfig/ksv104
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

実行結果の See: https://avd.aquasec.com/misconfig/ksv104 等のリンク先を見ると各項目の詳細を確認できます。大体はリンク先の Links で提示されている k8s 関連のドキュメントを見ていくことになります。
今回検出された項目は主に k8s Pod Security Standards に該当し、基本的に pod に必要以上の権限を与えない考え方に基づくものになります。

実行結果や上記ドキュメントを参考に securityContext を追加して修正します。

app.yml
-          image: myflask-trivy-test
+           image: myflask-trivy-test:1.0.0

+           securityContext:
+             allowPrivilegeEscalation: false
+             runAsNonRoot: true
+             runAsUser: 10001
+             runAsGroup: 10001
+             readOnlyRootFilesystem: true
+             seccompProfile:
+               type: RuntimeDefault

これで再度 trivy config app.yml -s UNKNOWN,MEDIUM,HIGH,CRITICAL を実行すると何も表示されないため、指摘された項目が修正できたことがわかります。

クラスタの検証

マニフェストの検証もできたので上記リソースを k8s クラスタにデプロイします。今回は検証用に新規に作成した k8s クラスタにデプロイしました。

kubectl create namespace trivy
kubectl apply -f app.yml

trivy では k8s クラスタ上の各種リソースや各ノードの kubelet 等の検証も行うことができます。詳細は Kubernetes を参照。
特定の namespace のみに絞ってリソースを検証することもできるので、アプリケーションをデプロイした trivy namespace 上のリソースに対してスキャンを実行してみます。

$ trivy k8s  --namespace trivy all --report summary -s UNKNOWN,MEDIUM,HIGH,CRITICAL
6 / 6 [------------------------------------------------------------------------------------------------] 100.00% 2 p/s

Summary Report for kubernetes-admin@kubernetes


Workload Assessment
┌───────────┬──────────┬─────────────────┬───────────────────┬───────────────┐
│ Namespace │ Resource │ Vulnerabilities │ Misconfigurations │    Secrets    │
│           │          ├───┬───┬───┬─────┼────┬────┬────┬────┼───┬───┬───┬───┤
│           │          │ C │ H │ M │  U  │ C  │ H  │ M  │ U  │ C │ H │ M │ U │
└───────────┴──────────┴───┴───┴───┴─────┴────┴────┴────┴────┴───┴───┴───┴───┘
Severities: C=CRITICAL H=HIGH M=MEDIUM U=UNKNOWN

検出された脆弱性は上記の table 形式にカウント数が表示されますが、今回の場合はマニフェストを検証した段階で対応済みであるためクラスタレベルのスキャンでも検出されませんでした。

また、クラスタ全体のリソースに対してもスキャンを実行できます。チェックする項目が多くなるためスキャンに時間がかかります。

結果
$ trivy k8s cluster --report summary -s UNKNOWN,MEDIUM,HIGH,CRITICAL
226 / 226 [--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------] 100.00% 2 p/s

Summary Report for kubernetes-admin@kubernetes


Workload Assessment
┌───────────────┬──────────────────────────────────────────────┬─────────────────┬───────────────────┬───────────────┐
│   Namespace   │                   Resource                   │ Vulnerabilities │ Misconfigurations │    Secrets    │
│               │                                              ├───┬────┬────┬───┼───┬───┬────┬──────┼───┬───┬───┬───┤
│               │                                              │ C │ H  │ M  │ U │ C │ H │ M  │  U   │ C │ H │ M │ U │
├───────────────┼──────────────────────────────────────────────┼───┼────┼────┼───┼───┼───┼────┼──────┼───┼───┼───┼───┤
│ kube-system   │ DaemonSet/kube-proxy                         │ 5 │ 18 │ 18 │   │   │ 2 │ 4  │      │   │   │   │   │
│ kube-system   │ Pod/kube-controller-manager-k8s-sec-master   │   │    │    │   │   │ 1 │ 4  │      │   │   │   │   │
│ kube-system   │ Pod/kube-scheduler-k8s-sec-master            │   │    │    │   │   │ 1 │ 4  │      │   │   │   │   │
│ kube-system   │ ConfigMap/extension-apiserver-authentication │   │    │    │   │   │   │ 1  │      │   │   │   │   │
│ kube-system   │ Deployment/coredns                           │   │ 3  │ 4  │   │   │ 1 │ 3  │      │   │   │   │   │
│ kube-system   │ Pod/kube-apiserver-k8s-sec-master            │   │    │    │   │   │ 1 │ 4  │      │   │   │   │   │
│ kube-system   │ Pod/etcd-k8s-sec-master                      │   │    │    │   │   │ 1 │ 4  │      │   │   │   │   │
│ kube-flannel  │ DaemonSet/kube-flannel-ds                    │ 2 │    │ 4  │   │   │ 2 │ 11 │      │   │   │   │   │
│ ingress-nginx │ Deployment/ingress-nginx-controller          │ 2 │ 13 │ 41 │   │   │ 1 │ 4  │      │   │   │   │   │
│ ingress-nginx │ Job/ingress-nginx-admission-create           │   │    │    │   │   │   │ 1  │      │   │   │   │   │
│ ingress-nginx │ Job/ingress-nginx-admission-patch            │   │    │    │   │   │   │ 1  │      │   │   │   │   │
└───────────────┴──────────────────────────────────────────────┴───┴────┴────┴───┴───┴───┴────┴──────┴───┴───┴───┴───┘
Severities: C=CRITICAL H=HIGH M=MEDIUM U=UNKNOWN


RBAC Assessment
┌───────────────┬─────────────────────────────────────────────────────────────────┬─────────────────┐
│   Namespace   │                            Resource                             │ RBAC Assessment │
│               │                                                                 ├───┬───┬───┬─────┤
│               │                                                                 │ C │ H │ M │  U  │
├───────────────┼─────────────────────────────────────────────────────────────────┼───┼───┼───┼─────┤
│ kube-system   │ Role/system::leader-locking-kube-scheduler                      │   │   │ 1 │     │
│ kube-system   │ Role/system:controller:token-cleaner                            │   │   │ 1 │     │
│ kube-system   │ Role/system:controller:bootstrap-signer                         │   │   │ 1 │     │
│ kube-system   │ Role/system::leader-locking-kube-controller-manager             │   │   │ 1 │     │
│ kube-system   │ Role/system:controller:cloud-provider                           │   │   │ 1 │     │
│ kube-public   │ Role/system:controller:bootstrap-signer                         │   │   │ 1 │     │
│ kube-public   │ RoleBinding/kubeadm:bootstrap-signer-clusterinfo                │ 1 │   │   │     │
│ ingress-nginx │ Role/ingress-nginx-admission                                    │   │   │ 1 │     │
│ ingress-nginx │ Role/ingress-nginx                                              │   │   │ 1 │     │
│               │ ClusterRole/system:controller:root-ca-cert-publisher            │   │   │ 1 │     │
│               │ ClusterRole/system:kube-scheduler                               │   │   │ 1 │     │
│               │ ClusterRole/ingress-nginx                                       │ 1 │   │   │     │
│               │ ClusterRole/system:controller:resourcequota-controller          │ 1 │   │   │     │
│               │ ClusterRole/system:controller:replicaset-controller             │   │   │ 2 │     │
│               │ ClusterRoleBinding/cluster-admin                                │   │   │ 1 │     │
│               │ ClusterRole/system:controller:generic-garbage-collector         │ 1 │   │   │     │
│               │ ClusterRole/system:controller:node-controller                   │   │   │ 1 │     │
│               │ ClusterRole/system:node                                         │ 1 │   │ 1 │     │
│               │ ClusterRole/system:controller:persistent-volume-binder          │ 1 │ 2 │ 1 │     │
│               │ ClusterRole/system:controller:endpointslice-controller          │   │ 1 │   │     │
│               │ ClusterRole/system:controller:endpointslicemirroring-controller │   │ 1 │   │     │
│               │ ClusterRole/system:kube-controller-manager                      │ 5 │   │   │     │
│               │ ClusterRole/system:controller:pod-garbage-collector             │   │   │ 1 │     │
│               │ ClusterRole/system:aggregate-to-admin                           │ 1 │   │   │     │
│               │ ClusterRole/system:controller:cronjob-controller                │   │   │ 3 │     │
│               │ ClusterRole/system:controller:ttl-after-finished-controller     │   │   │ 1 │     │
│               │ ClusterRole/system:controller:horizontal-pod-autoscaler         │ 2 │   │   │     │
│               │ ClusterRole/cluster-admin                                       │ 2 │   │   │     │
│               │ ClusterRole/system:controller:deployment-controller             │   │   │ 3 │     │
│               │ ClusterRole/system:controller:expand-controller                 │ 1 │   │   │     │
│               │ ClusterRole/ingress-nginx-admission                             │ 1 │   │   │     │
│               │ ClusterRole/system:aggregate-to-edit                            │ 2 │ 4 │ 6 │     │
│               │ ClusterRole/system:controller:replication-controller            │   │   │ 2 │     │
│               │ ClusterRole/system:controller:statefulset-controller            │   │   │ 1 │     │
│               │ ClusterRole/system:controller:job-controller                    │   │   │ 2 │     │
│               │ ClusterRole/system:controller:namespace-controller              │ 1 │   │   │     │
│               │ ClusterRole/system:controller:endpoint-controller               │   │ 1 │   │     │
│               │ ClusterRole/admin                                               │ 3 │ 4 │ 6 │     │
│               │ ClusterRole/edit                                                │ 2 │ 4 │ 6 │     │
│               │ ClusterRole/system:controller:daemon-set-controller             │   │   │ 1 │     │
└───────────────┴─────────────────────────────────────────────────────────────────┴───┴───┴───┴─────┘
Severities: C=CRITICAL H=HIGH M=MEDIUM U=UNKNOWN


Infra Assessment
┌─────────────┬───────────────────────────────────┬─────────────────────────────┐
│  Namespace  │             Resource              │ Kubernetes Infra Assessment │
│             │                                   ├──────┬──────┬──────┬────────┤
│             │                                   │  C   │  H   │  M   │   U    │
├─────────────┼───────────────────────────────────┼──────┼──────┼──────┼────────┤
│ kube-system │ Pod/kube-apiserver-k8s-sec-master │      │      │ 1    │        │
│             │ NodeInfo/k8s-sec-worker           │      │ 2    │      │        │
│             │ NodeInfo/k8s-sec-master           │  1   │ 4    │      │        │
└─────────────┴───────────────────────────────────┴──────┴──────┴──────┴────────┘
Severities: C=CRITICAL H=HIGH M=MEDIUM U=UNKNOWN

クラスタ全体では様々な severity の問題点が検出されています。上記のクラスタは kubeadm を使って新規に構築し、flannel と nginx ingress controller のみインストールしたほぼクリーンな状態のクラスタではありますが、この段階でも vulnerability や RBAC の問題性が検出されています。

例えば Workload Assessment では、flannel は現時点での最新バージョン v0.24.0 を使用していますが、イメージ内で使用されている以下のライブラリで CVE が報告されています。

docker.io/flannel/flannel:v0.24.0 (alpine 3.18.4)

Total: 1 (CRITICAL: 1)

┌────────────┬────────────────┬──────────┬────────┬───────────────────┬───────────────┬─────────────────────────────────────────────────────────────┐
│  Library   │ Vulnerability  │ Severity │ Status │ Installed Version │ Fixed Version │                            Title                            │
├────────────┼────────────────┼──────────┼────────┼───────────────────┼───────────────┼─────────────────────────────────────────────────────────────┤
│ strongswan │ CVE-2023-41913 │ CRITICAL │ fixed  │ 5.9.10-r1         │ 5.9.12-r0     │ strongSwan before 5.9.12 has a buffer overflow and possible │
│            │                │          │        │                   │               │ unauthenti ...                                              │
│            │                │          │        │                   │               │ https://avd.aquasec.com/nvd/cve-2023-41913                  │
└────────────┴────────────────┴──────────┴────────┴───────────────────┴───────────────┴─────────────────────────────────────────────────────────────┘

docker.io/flannel/flannel:v0.24.0 (alpine 3.18.4)

Total: 1 (CRITICAL: 1)

┌────────────┬────────────────┬──────────┬────────┬───────────────────┬───────────────┬─────────────────────────────────────────────────────────────┐
│  Library   │ Vulnerability  │ Severity │ Status │ Installed Version │ Fixed Version │                            Title                            │
├────────────┼────────────────┼──────────┼────────┼───────────────────┼───────────────┼─────────────────────────────────────────────────────────────┤
│ strongswan │ CVE-2023-41913 │ CRITICAL │ fixed  │ 5.9.10-r1         │ 5.9.12-r0     │ strongSwan before 5.9.12 has a buffer overflow and possible │
│            │                │          │        │                   │               │ unauthenti ...                                              │
│            │                │          │        │                   │               │ https://avd.aquasec.com/nvd/cve-2023-41913                  │
└────────────┴────────────────┴──────────┴────────┴───────────────────┴───────────────┴─────────────────────────────────────────────────────────────┘

RBAC Assessment では主に role や clusterrole の権限をチェックします。例えば CRITICAL として報告されている以下のものでは、kube-controller-manager という role に secret のアクセス権限が設定されていることで検出されています。

-ClusterRole-system:kube-controller-manager-3561865151.yaml (kubernetes)

Tests: 26 (SUCCESSES: 21, FAILURES: 5, EXCEPTIONS: 0)
Failures: 5 (CRITICAL: 5)

CRITICAL: ClusterRole 'system:kube-controller-manager' shouldn't have access to manage resource 'secrets'
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Viewing secrets at the cluster-scope is akin to cluster-admin in most clusters as there are typically at least one service accounts (their token stored in a secret) bound to cluster-admin directly or a role/clusterrole that gives similar permissions.

See https://avd.aquasec.com/misconfig/ksv041
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
 -ClusterRole-system:kube-controller-manager-3561865151.yaml:52-58
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
  52 ┌     - apiGroups:
  53 │         - ""
  54 │       resources:
  55 │         - secrets
  56 │         - serviceaccounts
  57 │       verbs:
  58 └         - create
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Infra Assessment では主に node に関する項目がチェックされます。severity high で検出されている項目を確認すると、node 上に存在する CNI のファイルや CA 証明書に必要以上のパーミッションが設定されているため、600 に設定してより厳しく絞るべきという指摘となっています。

NodeInfo/k8s-sec-master (kubernetes)

Tests: 51 (SUCCESSES: 47, FAILURES: 4, EXCEPTIONS: 0)
Failures: 4 (HIGH: 4)

HIGH: Ensure that the Container Network Interface specification file permissions is set to 600 or more restrictive
═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Ensure that the container network interface file has permissions of 600 or more restrictive.

See https://avd.aquasec.com/misconfig/kcv0056
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


HIGH: Ensure that the Kubernetes PKI certificate file permission is set to 600
═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Ensure that the Kubernetes PKI certificate file permission is set to 600.

See https://avd.aquasec.com/misconfig/kcv0068
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


HIGH: Ensure that the kubelet service file permissions are set to 600 or more restrictive
═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Ensure that the kubelet service file has permissions of 600 or more restrictive.

See https://avd.aquasec.com/misconfig/kcv0069
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


HIGH: Ensure that if the kubelet refers to a configuration file with the --config argument, that file has permissions of 600 or more restrictive.
═════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Ensure that if the kubelet refers to a configuration file with the --config argument, that file has permissions of 600 or more restrictive.

See https://avd.aquasec.com/misconfig/kcv0077
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

このように trivy によってクラスタ上に存在するリソースやクラスタを構成する node に対して包括的にセキュリティスキャンを実行でき、脆弱性を含む箇所の検出や必要以上の権限が設定されているリソース、ファイルの検出が行えるようになっています。

その他のセキュリティツール

kubescape

https://kubescape.io/

kubescape は CNCF の sandbox project に位置づけられている OSS の k8s security platform です。Github star も 9 k となっているため、trivy と同様に幅広く使われている様子が伺えます。
trivy と同様にマニフェストの misconfiguration スキャンや稼働中の k8s クラスタ全体に対してセキュリティスキャンが実行可能となっています。

インストール

Install では CLI やクラスタ内に operator としてインストールする方法が用意されています。
ここでは k8s のプライグインマネージャーである krew を使ってインストールします。

kubectl krew update
kubectl krew install kubescape

スキャンの実行

kubescape も trivy と同様に Scanning files によって k8s マニフェストのセキュリティスキャンが実行できます。ここでは マニフェストの検証 で作成したマニフェストに対してスキャンを実行し、指摘が検出されるか見てみます。

$ kubectl kubescape scan app.yml

Network
┌────────────────────────┬───────────┬─────────────────────────────────────┐
│ Control name           │ Resources │ View details                        │
├────────────────────────┼───────────┼─────────────────────────────────────┤
│ Missing network policy │     1     │ $ kubescape scan control C-0260  -v │
└────────────────────────┴───────────┴─────────────────────────────────────┘

スキャンを実行した結果、trivy では検出されなかった項目として Missing network policy が検出されました。kubescape で検出される項目は Control と呼ばれており、 armosec control に検出される項目の一覧と各項目の説明が記載されています。上記で検出された項目は C-0206 - Ensure that all Namespaces have Network Policies defined であり、namespace ではネットワークポリシーを設定して必要なトラフィックのみを許可するようにすべきという内容になります。

これを修正するため app.yml の中に NetworkPolicy の定義を追加します。

app.yml
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: myflask
  namespace: trivy
spec:
  podSelector:
    matchLabels:
      app: myflask
  policyTypes:
  - Ingress
  ingress:
  - from:
    - ipBlock:
        cidr: 0.0.0.0/0
    - namespaceSelector:
        matchLabels:
          app: myflask
    - podSelector:
        matchLabels:
          app: myflask
    ports:
    - protocol: TCP
      port: 5000

これで再度スキャンを実行したところ検出されていた項目は解消されました。

$ kubectl kubescape scan app.yml
 ✅  Initialized scanner
 ✅  Loaded policies
 ✅  Loaded exceptions
 ✅  Loaded account configurations
 ✅  Done accessing local objects
Control: C-0009 100% |████████████████████████████████████████████████████████████████████| (33/33, 209 it/s)
 ✅  Done scanning File
 ✅  Done aggregating results


Kubescape security posture overview for cluster: kubernetes-admin-kubernetes

In this overview, Kubescape shows you a summary of your cluster security posture, including the number of users who can perform administrative actions. For each result greater than 0, you should evaluate its need, and then define an exception to allow it. This baseline can be used to detect drift in future.

また、kubescape moクラスタ全体に対してセキュリティスキャンを実行することが可能です。

実行結果
$ kubectl kubescape scan
 ✅  Initialized scanner
 ✅  Loaded policies
 ✅  Loaded exceptions
 ✅  Loaded account configurations
 ✅  Accessed Kubernetes objects
Control: C-0036 100% |█████████████████████████████████████████████████████████████████████| (45/45, 78 it/s)
 ✅  Done scanning. Cluster: kubernetes-admin-kubernetes
 ✅  Done aggregating results


Kubescape security posture overview for cluster: kubernetes-admin-kubernetes

In this overview, Kubescape shows you a summary of your cluster security posture, including the number of users who can perform administrative actions. For each result greater than 0, you should evaluate its need, and then define an exception to allow it. This baseline can be used to detect drift in future.

Control plane
┌────┬─────────────────────────────────────┬────────────────────────────────────┐
│    │ Control name                        │ Docs                               │
├────┼─────────────────────────────────────┼────────────────────────────────────┤
│ ✅ │ API server insecure port is enabled │ https://hub.armosec.io/docs/c-0005 │
│ ❌ │ Anonymous access enabled            │ https://hub.armosec.io/docs/c-0262 │
│ ❌ │ Audit logs enabled                  │ https://hub.armosec.io/docs/c-0067 │
│ ✅ │ RBAC enabled                        │ https://hub.armosec.io/docs/c-0088 │
│ ❌ │ Secret/etcd encryption enabled      │ https://hub.armosec.io/docs/c-0066 │
└────┴─────────────────────────────────────┴────────────────────────────────────┘

Access control
┌─────────────────────────────────────────────────┬───────────┬────────────────────────────────────┐
│ Control name                                    │ Resources │ View details                       │
├─────────────────────────────────────────────────┼───────────┼────────────────────────────────────┤
│ Cluster-admin binding                           │     0     │ $ kubescape scan control C-0035 -v │
│ Data Destruction                                │     0     │ $ kubescape scan control C-0007 -v │
│ Exec into container                             │     0     │ $ kubescape scan control C-0002 -v │
│ List Kubernetes secrets                         │     3     │ $ kubescape scan control C-0015 -v │
│ Minimize access to create pods                  │     0     │ $ kubescape scan control C-0188 -v │
│ Minimize wildcard use in Roles and ClusterRoles │     0     │ $ kubescape scan control C-0187 -v │
│ Portforwarding privileges                       │     0     │ $ kubescape scan control C-0063 -v │
│ Validate admission controller (mutating)        │     0     │ $ kubescape scan control C-0039 -v │
│ Validate admission controller (validating)      │     1     │ $ kubescape scan control C-0036 -v │
└─────────────────────────────────────────────────┴───────────┴────────────────────────────────────┘

Secrets
┌─────────────────────────────────────────────────┬───────────┬────────────────────────────────────┐
│ Control name                                    │ Resources │ View details                       │
├─────────────────────────────────────────────────┼───────────┼────────────────────────────────────┤
│ Applications credentials in configuration files │     1     │ $ kubescape scan control C-0012 -v │
└─────────────────────────────────────────────────┴───────────┴────────────────────────────────────┘

Network
┌────────────────────────┬───────────┬────────────────────────────────────┐
│ Control name           │ Resources │ View details                       │
├────────────────────────┼───────────┼────────────────────────────────────┤
│ Missing network policy │     7     │ $ kubescape scan control C-0260 -v │
└────────────────────────┴───────────┴────────────────────────────────────┘

Workload
┌─────────────────────────┬───────────┬────────────────────────────────────┐
│ Control name            │ Resources │ View details                       │
├─────────────────────────┼───────────┼────────────────────────────────────┤
│ Host PID/IPC privileges │     1     │ $ kubescape scan control C-0038 -v │
│ HostNetwork access      │     1     │ $ kubescape scan control C-0041 -v │
│ HostPath mount          │     2     │ $ kubescape scan control C-0048 -v │
│ Non-root containers     │     3     │ $ kubescape scan control C-0013 -v │
│ Privileged container    │     0     │ $ kubescape scan control C-0057 -v │
└─────────────────────────┴───────────┴────────────────────────────────────┘


Highest-stake workloads
───────────────────────

High-stakes workloads are defined as those which Kubescape estimates would have the highest impact if they were to be exploited.

1. namespace: ingress-nginx, name: ingress-nginx-controller, kind: Deployment
   '$ kubescape scan workload Deployment/ingress-nginx-controller --namespace ingress-nginx'
2. namespace: kube-flannel, name: kube-flannel-ds, kind: DaemonSet
   '$ kubescape scan workload DaemonSet/kube-flannel-ds --namespace kube-flannel'
3. namespace: trivy, name: kube-bench, kind: Job
   '$ kubescape scan workload Job/kube-bench --namespace trivy'


Compliance Score
────────────────

The compliance score is calculated by multiplying control failures by the number of failures against supported compliance frameworks. Remediate controls, or configure your cluster baseline with exceptions, to improve this score.

* MITRE: 73.61%
* NSA: 66.57%

View a full compliance report by running '$ kubescape scan framework nsa' or '$ kubescape scan framework mitre'

やはり trivy でスキャンを実行したときと同様に多くの項目が検出されています。各項目については View details に記載されているコマンドを実行することでどのリソースが該当しているか確認できます。
例えば List Kubernetes secrets の項目がどのリソースで検出されているかを確認するには kubectl kubescape scan control C-0015 -v を実行します。

List Kubernetes secrets
$ kubectl kubescape scan control C-0015 -v
 ✅  Initialized scanner
 ✅  Loaded policies
 ✅  Loaded exceptions
 ✅  Loaded account configurations
 ✅  Accessed Kubernetes objects
 100% |███████████████████████████████████████████████████████████████████████████████████| (1/1, 14830 it/s)
 ✅  Done scanning. Cluster: kubernetes-admin-kubernetes
 ✅  Done aggregating results


──────────────────────────────────────────────────


################################################################################
ApiVersion:
Kind: ServiceAccount
Name: ingress-nginx
Namespace: ingress-nginx

Controls: 1 (Failed: 1, action required: 0)

┌────────────────────────────────────────────────────────────────┐
│ Resources                                                      │
├────────────────────────────────────────────────────────────────┤
│ Severity             : High                                    │
│ Control Name         : List Kubernetes secrets                 │
│ Docs                 : https://hub.armosec.io/docs/c-0015      │
│ Assisted Remediation : relatedObjects[1].rules[1].resources[2] │
│                        relatedObjects[1].rules[1].verbs[0]     │
│                        relatedObjects[1].rules[1].verbs[1]     │
│                        relatedObjects[1].rules[1].verbs[2]     │
│                        relatedObjects[1].rules[1].apiGroups[0] │
│                        relatedObjects[0].subjects[0]           │
│                        relatedObjects[0].roleRef.name          │
└────────────────────────────────────────────────────────────────┘

################################################################################
ApiVersion:
Kind: ServiceAccount
Name: ingress-nginx
Namespace: ingress-nginx

Controls: 1 (Failed: 1, action required: 0)

┌────────────────────────────────────────────────────────────────┐
│ Resources                                                      │
├────────────────────────────────────────────────────────────────┤
│ Severity             : High                                    │
│ Control Name         : List Kubernetes secrets                 │
│ Docs                 : https://hub.armosec.io/docs/c-0015      │
│ Assisted Remediation : relatedObjects[1].rules[0].resources[4] │
│                        relatedObjects[1].rules[0].verbs[0]     │
│                        relatedObjects[1].rules[0].verbs[1]     │
│                        relatedObjects[1].rules[0].apiGroups[0] │
│                        relatedObjects[0].subjects[0]           │
│                        relatedObjects[0].roleRef.name          │
└────────────────────────────────────────────────────────────────┘

################################################################################
ApiVersion:
Kind: ServiceAccount
Name: ingress-nginx-admission
Namespace: ingress-nginx

Controls: 1 (Failed: 1, action required: 0)

┌────────────────────────────────────────────────────────────────┐
│ Resources                                                      │
├────────────────────────────────────────────────────────────────┤
│ Severity             : High                                    │
│ Control Name         : List Kubernetes secrets                 │
│ Docs                 : https://hub.armosec.io/docs/c-0015      │
│ Assisted Remediation : relatedObjects[1].rules[0].resources[0] │
│                        relatedObjects[1].rules[0].verbs[0]     │
│                        relatedObjects[1].rules[0].apiGroups[0] │
│                        relatedObjects[0].subjects[0]           │
│                        relatedObjects[0].roleRef.name          │
└────────────────────────────────────────────────────────────────┘


┌─────────────────┬───┐
│        Controls │ 1 │
│          Passed │ 0 │
│          Failed │ 1 │
│ Action Required │ 0 │
└─────────────────┴───┘

Failed resources by severity:

┌──────────┬───┐
│ Critical │ 0 │
│     High │ 3 │
│   Medium │ 0 │
│      Low │ 0 │
└──────────┴───┘

┌──────────┬─────────────────────────┬──────────────────┬───────────────┬──────────────────┐
│ Severity │ Control name            │ Failed resources │ All Resources │ Compliance score │
├──────────┼─────────────────────────┼──────────────────┼───────────────┼──────────────────┤
│   High   │ List Kubernetes secrets │        3         │      72       │       96%        │
├──────────┼─────────────────────────┼──────────────────┼───────────────┼──────────────────┤
│          │    Resource Summary     │        3         │      72       │      95.83%      │
└──────────┴─────────────────────────┴──────────────────┴───────────────┴──────────────────┘

実行結果より nginx ingress controller の ServiceAccount で検出されていることがわかります。control の内容を先程の一覧から確認すると c-0015 より、対象の ServiceAccount に対して secret の list 等の権限が許可されている場合に警告されるとのことです。実際に ingress-nginx を見てみると確かに get list watch の実行が許可されています。

$ kubectl -n ingress-nginx describe roles.rbac.authorization.k8s.io ingress-nginx

PolicyRule:
  Resources                           Non-Resource URLs  Resource Names          Verbs
  ---------                           -----------------  --------------          -----
  secrets                             []                 []                      [get list watch]

また Accepting risk with exceptions に記載されているように、json で対象のリソースや control を記述することで特定の警告を無視することもできます。

ignore.json
ignore.json
[
  {
      "name": "exclude-List-Kubernetes-secrets",
      "policyType": "postureExceptionPolicy",
      "actions": [
          "alertOnly"
      ],
      "resources": [
          {
              "designatorType": "Attributes",
              "attributes": {
                  "kind": ".*"
              }
          }
      ],
      "posturePolicies": [
          {
              "controlID": "C-0015"
          }
      ]
  }
]

ファイルを指定して実行すると、先程 3 件になっていた List Kubernetes secrets が 0 件になっていることが確認できます。

$ kubectl kubescape scan  --exceptions ignore.json

...
│ List Kubernetes secrets                         │     0     │ $ kubescape scan control C-0015 -v │

このように kubescape でもマニフェストの検証やクラスタ全体の包括的なセキュリティスキャンが実行できます。

kubescape と trivy の使い分けに関しては、それぞれスキャン時にチェックする項目が異なることから両方を並行して活用するのが良さそうです。両方とも CI/CD への統合や operator による k8s クラスタ上へのデプロイが可能であるため、両者を使うことで定期的なセキュリティスキャンの実行やより網羅的なセキュリティリスクの検出が実現できるかと思われます。

まとめ

セキュリティツールの trivy を使って k8s セキュリティの 4 C のうち主に Container, Cluster の部分のセキュリティチェックを検証しました。
普段はセキュリティについて意識することはあまりないですが、trivy ではドキュメントに大体の使用例が乗っていること、および検出された結果の問題点と修正方法が詳細に記載されていることから、事前知識がなくてもどのリソースが検出されているか、またどのように修正を行えばよいかが分かりやすく容易に対応できました。そのため、trivy を使って既存のリソースのセキュリティ対応を行うことで、どのような点を注意していけばよいか実際に使いながら学べるようになっていると感じました。
また、同じセキュリティツールの kubescape の検証も試しました。こちらも手軽にインストール・実行することができ、検出結果に対する対応が可能となっています。
このようなセキュリティツールを有効に活用することで無駄な労力をかけずに継続的なセキュリティ対応が実現できそうです。

Discussion