kube-bench で kubernetes のハードニング状況をチェックする


#初めに

kubernetes の Security に関して勉強を始めたところ、CISベンチマークというのを知りました。
[Center for Internet Security](https://www.cisecurity.org/) が出している 各種S/W やプラットフォームのハードニングのガイドラインです。 
[CIS Benchmark for kubernetes](https://www.cisecurity.org/benchmark/kubernetes/) 以外にも 各種 OS などのガイドが存在します。
それについて、確認した内容をまとめました。

2021年2月時点で入手できる CIS Benchmark for kubernetes は v1.6.0 ( 2020年7月23日リリース。 kubernetes 1.16 - 1.18 が対象 )で、PDF で約 270 ページあり、それをすべて読むのは大変だというのが本音です。
実際は、各セキュリティ項目ごとに概要、設定確認方法、設定方法、影響、デフォルト、参考情報等という形でまとめられているのでそう難しくはないですが。。。
目次を見るだけでも概要はわかりますので、興味があればご覧ください。

ベンチマークの大きな構成としては、以下のようになっています。

1. Control Plane Components
2. etcd
3. Control Plane Configuration
4. Worker Nodes
5. Policies

これらを1つ1つ確認していくためにはかなりの労力が必要になりそうですが、自動的にチェックする [kube-bench](https://github.com/aquasecurity/kube-bench) というツールが公開されています。
[kube-bench](https://github.com/aquasecurity/kube-bench) を実行すると、CIS kubernetes Benchmark に沿って各種設定の状況を確認してくれます。
この記事では、セキュリティについて全く意識せずに作った　自宅 kubernetes 環境を kube-bench でチェックしてみよう、という内容になります。（なお、あくまで実行してみるところまでで、結果を受けたセキュリティ強化はここでは記載していません。← 一番重要なところをやらないという。。。　徐々に勉強しながら対応予定です。）

kube-bench の Readme.md では Noteとして注意事項が挙げられています
- kube-bench の検査内容は CIS ベンチマークに 1:1 で対応しているわけではない
- kubernetes のマネージドサービス (GKEなど) のコントロールプレーンの検査はできない

少し話がそれますが、マネージドサービスの「マネージド」部分については各種クラウド事業者がセキュリティを検査しているようなので、通常意識する必要はなさそうです。
また、CIS ベンチマークを公開している各種クラウドについて、「不合格」となっている項目も多々ありますが「不合格」イコール「ダメ」という話ではないと認識しておく必要があります。そもそも想定されている構成が異なる、などによるものだと思われます。

クラウドによっては CISベンチマークの評価自体をしていないものもあります。
例えば、Amazon EKS に関しては、 [2020/7/11 のブログ](https://aws.amazon.com/jp/about-aws/whats-new/2020/07/announcing-cis-benchmark-for-amazon-eks/) で以下のように述べられています。
（ただし、kube-bench の EKS 用モジュールは存在しています）

>Center for Internete Security (CIS) Kubernetes Benchmark は、セルフマネージド Kubernetes クラスターのセキュリティ設定に関して優れたプラクティスガイダンスを提供していますが、Amazon EKS が実行する AWS マネージド Kubernetes クラスターのセキュリティ設定ステータスを正確に評価するのに役立ちませんでした。

他に見つけられた 各種マネージドクラウドの CIS ベンチマーク結果は　参考）部分に記載しておきます。

#環境

さて、マネージドサービスの話はおいておいて、自宅 kubernetes 環境は以下の構成です。
自宅 kubernetes 環境は ノートPC 上の CentOS 上に KVM 環境を準備し、その上の VM として kubernetes の各ノードを構成しています。

- KVM ホスト
    - CentOS Linux release 8.2.2004 (Core)
- Master / Node  ( 1 Master / 3 Worker 構成 )
    - Ubuntu 20.04.1 LTS
    - containerd 1.4.3-1
    - calico 3.11
    - kubernetes 1.20.1

また、自宅内 LAN 上の 勉強用 kubernetes Cluster なので、セキュリティは考慮しておらず、
コントローラー、ノードそれぞれ、デフォルトのセキュリティ状態です。

kubernetes cluster の構築方法としては、 kubernetes docs の kubeadm 利用パターンで、Containerd を利用しています。
　（※ CIS Kubernetes Benchmark では Container 部分までチェックはされていなようです）
CIS Benchmark の対象は kubernetes 1.18 までのようですが、適用できる部分も多いと思いますので、この環境で確かめてみます。

# kube-bench 導入

導入方法は kube-bench の Readme.md には以下の4種類の記載があります。

- コンテナ内から実行
- kube-bench インストールコンテナを実行しホストにインストール
- バイナリ、コンフィグ、テスト関連ファイルをダウンロード
- ソースからのコンパイル

ここでは、一番お手軽そうに見える 「コンテナ内から実行」を試してみたいと思います。
（実際には kubernetes 上の Pod として実行しています。）

# コンテナからの kube-bench 実行の概要

Readme.md の [Running in a kubernetes cluster](https://github.com/aquasecurity/kube-bench#running-in-a-kubernetes-cluster) に従い、job.yaml を実行します。

Redame.md では job.yaml を実行すれば、 master / node それぞれ検査されるように見受けられますが、当環境でやってみたところ、ノードの検証だけが実行されました。
ノード、マスター用の、　job-node.yaml,  job-master.yaml を利用すると、それぞれ確認可能なのですが、実際に確認できたセクションは、 1. Control Plane Components, 4. Worker Nodes,  5. Policies だけでした。
2. etcd,  3. Control Plane Configuration が確認できないのはなぜだろう、と思い試行錯誤しました。
最終的な結論としては、master 用の job-master.yaml をベースに kube-bench --version=1.18 指定をすることにより、全項目確認することができました。（修正内容は後述）
kube-bench で --version オプションを指定しないと、バージョンを自動判別するようになっていますが、 kubernetes 1.20 ではうまく判定できていないのだと思います。（そもそも対象が 1.16 - 1.18 です）

yaml では、大まかには以下のような設定が行われています。　（master 用と node 用で異なります。以下は master の例）

- kubernetes の job として Pod を実行
- job.spec.template.spec.hostPID を true に設定することにより、コンテナホストと PID namespace を共有する　（それにより、ホスト上のプロセスの詳細(ptrace)を確認することができるようになる）
- nodeSelector で実行対象ノードを選択する
- toleration を用いて master ノードでも稼働できるようにする
- aquasec/kube-bench:latest　イメージを利用する
- command に kube-bench を指定　（引数に node/master を指定する）
- コンテナホスト上の kubernetes 関連ファイルのパスを hostPath でコンテナ内に ReadOnly でマウントし、確認できるようにする

当環境 (kubernets v1.20.1) は、kube-bench の対象バージョンから外れているため、kube-bench のバージョン指定(1.18) を行っています。その修正内容は以下に記載します。（ # のコメント部分を参照 ）

```yaml
---
apiVersion: batch/v1
kind: Job
metadata:
  name: kube-bench-master
spec:
  template:
    spec:
      hostPID: true
      nodeSelector:
        node-role.kubernetes.io/master: ""
      tolerations:
        - key: node-role.kubernetes.io/master
          operator: Exists
          effect: NoSchedule
      containers:
        - name: kube-bench
          image: aquasec/kube-bench:latest
#          command: ["kube-bench", "master"]    # 元の command 部分をコメントアウト
          command: ["kube-bench"]               # command は kube-bench のみに変更
          args: ["--version=1.18"]              # 引数に --version=1.18 を追加
          volumeMounts:
…略
```


#kube-bench 実行例

実行は難しくなく、yaml を apply するだけです。
job が作成され、 Pod で処理が実行されます。　（※下記の job.yaml は　上記セクションで job-master.yaml ベースに修正したものになります。）

```shell-session
# kubectl apply -f job.yaml
job.batch/kube-bench-master created

# kubectl get jobs -o wide
NAME                COMPLETIONS   DURATION   AGE   CONTAINERS   IMAGES                      SELECTOR
kube-bench-master   1/1           4s         5s    kube-bench   aquasec/kube-bench:latest   controller-uid=fe19f9fe-e752-487f-bf5f-fe7f2b42d7c4

# kubectl get pods -o wide
NAME                      READY   STATUS      RESTARTS   AGE   IP              NODE      NOMINATED NODE   READINESS GATES
kube-bench-master-zr44r   0/1     Completed   0          9s    10.200.163.35   k8sctl0   <none>           <none>
```

ここで注意しておく必要があるのは、実行ノードです。
上記の例では k8sctl0 で実行されています。（Control-plane 1台構成なので当たり前ですが）
複数ノードある場合にどのノードで実行されるかは kube-scheduler の選択次第です。
特定のノードの設定修正したはずなのにチェックが [PASS] にならない、と思っていたら違うノードでチェックされていました、ということがあります。　はい。私自身の経験です。

実行結果は、Pod のログで確認します。

```shell-session
# kubectl logs kube-bench-master-zr44r

[INFO] 1 Master Node Security Configuration
[INFO] 1.1 Master Node Configuration Files
[PASS] 1.1.1 Ensure that the API server pod specification file permissions are set to 644 or more restrictive (Automated)

...

[WARN] 1.1.9 Ensure that the Container Network Interface file permissions are set to 644 or more restrictive (Manual)
[WARN] 1.1.10 Ensure that the Container Network Interface file ownership is set to root:root (Manual)
[PASS] 1.1.11 Ensure that the etcd data directory permissions are set to 700 or more restrictive (Automated)
[FAIL] 1.1.12 Ensure that the etcd data directory ownership is set to etcd:etcd (Automated)
[PASS] 1.1.13 Ensure that the admin.conf file permissions are set to 644 or more restrictive (Automated)

...

1.1.12 On the etcd server node, get the etcd data directory, passed as an argument --data-dir,
from the below command:
ps -ef | grep etcd
Run the below command (based on the etcd data directory found above).
For example, chown etcd:etcd /var/lib/etcd

...

== Summary master ==
45 checks PASS
10 checks FAIL
10 checks WARN
0 checks INFO

== Summary total ==
45 checks PASS
10 checks FAIL
10 checks WARN
0 checks INFO

```

各セクションの項目ごとに [PASS]、[WARN]、[FAIL] が表示されます。
実行例では割愛していますが、 １～５すべてのセクションについてチェックが行われました。

また、項目中 Manual となっているものについては、確認できれば [PASS]、できなければ [WARN] になるようで、[WARN] の場合は自分で確認する必要があります。
例えば、 1.1.9 の CNI 関連ファイルのパーミッションですが、当環境では Calico で、`/etc/cni/net.d/` 以下のファイルは 644 より厳しいパーミッションでしたが、読み取れていないようです。

注意事項として、 5. の Policies では、 RBAC、Pod Security Policy、Network Policy、Secret などが対象になっていますが、
いずれも、必要最低限の RBAC にするべきというようなガイドになっています。
自動的に識別できるような内容ではないため、ほぼすべて [WARNING] になっています。　
つまり、結局は自分できちんと管理する必要がある、ということです。

[WARN]、[FAIL] の物に関しては、　続いて簡単な対応方法が提示されます。
1.1.12 の例では、 etcd の --data-dir のオーナー・グループを etcd:etcd にするようガイドされています。
確認してみたところ、オーナー・グループが root になっていました。

```shell-session
# ps -ef | grep etcd | grep data-dir
root        1378    1313  1 00:48 ?        00:06:09 etcd --advertise-client-urls=https://192.168.199.200:2379 --cert-file=/etc/kubernetes/pki/etcd/server.crt --client-cert-auth=true --data-dir=/var/lib/etcd --initial-advertise-peer-urls=https://192.168.199.200:2380 --initial-cluster=k8sctl0=https://192.168.199.200:2380 --key-file=/etc/kubernetes/pki/etcd/server.key --listen-client-urls=https://127.0.0.1:2379,https://192.168.199.200:2379 --listen-metrics-urls=http://127.0.0.1:2381 --listen-peer-urls=https://192.168.199.200:2380 --name=k8sctl0 --peer-cert-file=/etc/kubernetes/pki/etcd/peer.crt --peer-client-cert-auth=true --peer-key-file=/etc/kubernetes/pki/etcd/peer.key --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt --snapshot-count=10000 --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt

# ls -ld /var/lib/etcd
drwx------ 3 root root 4096 Feb 11 00:48 /var/lib/etcd

```

このように、対応方法は比較的わかりやすく書かれていると思います。
もし内容がわからなかったり、より詳細を知りたかったりする場合は、
CIS kubernetes benchmark の PDF ファイルの該当セクションを確認するとより詳細な記述や確認方法、参考情報へのリンクなどが記載されています。

こういった情報に基づき、ハードニング設定を入れていけばセキュリティを強化することができます。

注意事項として、該当の設定を入れた場合に正しく動くかどうかは保証されていないようです。
例えば、ノード側でチェックした際に、 `--protect-kernel-defaults` を true にするように表示されます。
ただ、その対応をすると kubelet が動かなくなり、kernel パラメータを tuning する必要が出てくるようです。
その対応をまとめてくださっている方がいました。

[kubeletの--protect-kernel-defaultsをtrueにする際に必要なkernel tuning](https://qiita.com/gashirar/items/25b96192e7a30a911688)

このように、ガイドに沿って単純に設定すればそれで OK というわけではありませんが、
ハードニングのガイドラインとツールがあるだけで対応の難しさは全く違ってくると思います。

#終わりに

CIS によりハードニングの指針がまとめられ、それをベースに kube-bench という診断ツールがあることにより
kubernetes のハードニングのハードルが下がっていると思います。
ただし、ハードニングすればOK、というわけではなくあくまでセキュリティを強化するための手段の1つ、ということを忘れずに利用・運用する必要がありそうです。

---
参考）
■ CIS
https://www.cisecurity.org/

■ kube-bench
https://github.com/aquasecurity/kube-bench

■ CISベンチマーク（GKE / anthos）
https://cloud.google.com/kubernetes-engine/docs/concepts/cis-benchmarks?hl=ja
https://cloud.google.com/anthos/gke/docs/on-prem/1.5/concepts/cis-benchmarks?hl=ja

■ CISベンチマーク (AKS)
https://docs.microsoft.com/ja-jp/azure/aks/security-hardened-vm-host-image

■ CISベンチマーク (IKS)
https://cloud.ibm.com/docs/containers?topic=containers-cis-benchmark




----