Kubespray「Cluster Hardening」 (セキュリティに気をつけてKubesprayを使おう！)

# はじめに

なぜか公式ドキュメントページっぽい[kubespray.io](https://kubespray.io/#/docs)には、該当ページへのリンクがまだない（ベータなのかな？）んですが、KubesprayのGithubには[2022/05/30](https://github.com/kubernetes-sigs/kubespray/commits/master/docs/hardening.md)からKubesprayでのデプロイが[CIS Benchmarks](https://learn.cisecurity.org/benchmarks)をクリアできるように「Cluster Hardening」というページが追加されています。
これの紹介がてら、ちょっとしたTipsを共有をしよう！というのが今回の趣旨です。
私の観測範囲では日本語で解説してそうな記事が見つからなかったので。。。

該当リンク：
https://kubespray.io/#/docs/hardening
https://github.com/kubernetes-sigs/kubespray/blob/master/docs/hardening.md

## そもそもKubesprayって？

マネージドではないKubernetes環境を作成するための手段の一つで、ansible-playbookとして構築されています（参考：[Ansible Playbook とは](https://www.redhat.com/ja/topics/automation/what-is-an-ansible-playbook)）。
クラウド環境に用意したVMに対しても利用できますが、~~おうちKubernetes勢という変わり者たちが~~オンプレ環境に用意した実機やVMに対しても利用することができます。
ansibleについての解説やKubesprayそのものに関しては、先人の方々の記事をご参照ください。

## 解説

とりあえず、以下が「Cluster Hardening」の中身になります（2022/12/17 現在）。
Kubesprayの「Cluster Hardening」は、セキュリティを高めるための設定集として用意されています。
後述のようにplaybookの呼び出し時に設定値をhardeningの設定で上書きすることで適用しますので、自分の環境に合わせて若干の変更が必要になる場合があります。
RBACやAuditの有効化、secretの暗号化、簡易FWの有効化などが設定されており、用語の調査をするだけで、Kubernetesにおけるセキュリティについて知る良いとっかかりになると思います。

hardening.yaml:
```yaml
# Hardening
---

## kube-apiserver
authorization_modes: ['Node', 'RBAC']
# AppArmor-based OS
# kube_apiserver_feature_gates: ['AppArmor=true']
kube_apiserver_request_timeout: 120s
kube_apiserver_service_account_lookup: true

# enable kubernetes audit
kubernetes_audit: true
audit_log_path: "/var/log/kube-apiserver-log.json"
audit_log_maxage: 30
audit_log_maxbackups: 10
audit_log_maxsize: 100

tls_min_version: VersionTLS12
tls_cipher_suites:
  - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
  - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305

# enable encryption at rest
kube_encrypt_secret_data: true
kube_encryption_resources: [secrets]
kube_encryption_algorithm: "secretbox"

kube_apiserver_enable_admission_plugins:
  - EventRateLimit
  - AlwaysPullImages
  - ServiceAccount
  - NamespaceLifecycle
  - NodeRestriction
  - LimitRanger
  - ResourceQuota
  - MutatingAdmissionWebhook
  - ValidatingAdmissionWebhook
  - PodNodeSelector
  - PodSecurity
kube_apiserver_admission_control_config_file: true
# EventRateLimit plugin configuration
kube_apiserver_admission_event_rate_limits:
  limit_1:
    type: Namespace
    qps: 50
    burst: 100
    cache_size: 2000
  limit_2:
    type: User
    qps: 50
    burst: 100
kube_profiling: false

## kube-controller-manager
kube_controller_manager_bind_address: 127.0.0.1
kube_controller_terminated_pod_gc_threshold: 50
# AppArmor-based OS
# kube_controller_feature_gates: ["RotateKubeletServerCertificate=true", "AppArmor=true"]
kube_controller_feature_gates: ["RotateKubeletServerCertificate=true"]

## kube-scheduler
kube_scheduler_bind_address: 127.0.0.1
kube_kubeadm_scheduler_extra_args:
  profiling: false
# AppArmor-based OS
# kube_scheduler_feature_gates: ["AppArmor=true"]

## etcd
etcd_deployment_type: kubeadm

## kubelet
kubelet_authentication_token_webhook: true
kube_read_only_port: 0
kubelet_rotate_server_certificates: true
kubelet_protect_kernel_defaults: true
kubelet_event_record_qps: 1
kubelet_rotate_certificates: true
kubelet_streaming_connection_idle_timeout: "5m"
kubelet_make_iptables_util_chains: true
kubelet_feature_gates: ["RotateKubeletServerCertificate=true", "SeccompDefault=true"]
kubelet_seccomp_default: true
kubelet_systemd_hardening: true
# In case you have multiple interfaces in your
# control plane nodes and you want to specify the right
# IP addresses, kubelet_secure_addresses allows you
# to specify the IP from which the kubelet
# will receive the packets.
kubelet_secure_addresses: "192.168.10.110 192.168.10.111 192.168.10.112"

# additional configurations
kube_owner: root
kube_cert_group: root

# create a default Pod Security Configuration and deny running of insecure pods
# kube_system namespace is exempted by default
kube_pod_security_use_default: true
kube_pod_security_default_enforce: restricted
```

適用コマンド例:
```shell
ansible-playbook -v cluster.yml \
        -i inventory.ini \
        -b --become-user=root \
        --private-key ~/.ssh/id_ecdsa \
        -e "@vars.yaml" \
        -e "@hardening.yaml"
```

## ちょっとしたTipsについて（「Cluster Hardening」やった上でKubernetes上で「Prometheus」等をセルフホストするときの注意点）

上記のように「Cluster Hardening」を行なっておくことは、最低限のセキュリティを確保することになりますし、ぜひ検討していただきたいのですが、注意点があります。
それは、セキュリティ向上に重きを置いて設定値が記述されているので、そのままだと正常に機能しないアプリケーションが出てきたりします。
例えば、Prometheusは構成上スクレイピングのためにkubeletと通信しますが、上記設定をそのまま適用するとFWにシャットアウトされて動きません。
この辺のために、修正した箇所について紹介していきます。

### kube-controller-managerのbind設定

```yaml
# kube_controller_manager_bind_address: 127.0.0.1
kube_controller_manager_bind_address: 0.0.0.0 # 127.0.0.1の場合、prometheusからのscrapeに失敗するので、変更します。その代わりに別途FWやNetworkPolicy等で保護する必要があるので注意。
```

### kube-schedulerのbind設定

```yaml
# kube_scheduler_bind_address: 127.0.0.1
kube_scheduler_bind_address: 0.0.0.0 # 127.0.0.1の場合、prometheusからのscrapeに失敗するので、変更します。その代わりに別途FWやNetworkPolicy等で保護する必要があるので注意。
```

### etcdのbind設定

下記設定を追加しない場合、127.0.0.1(localhostだったかも。。。)にbindされてしまい、prometheusからのscrapeに失敗するので、変更します。その代わりに別途FWやNetworkPolicy等で保護する必要があるので注意。
```yaml
etcd_extra_vars:
  listen-metrics-urls: http://0.0.0.0:2381
```

### kubeletのFWについて

kubelet_secure_addressesは、systemdとiptablesの連携機能を用いて、kubeletに接続できるアプリケーションを接続元IPアドレスによって制限することができる設定になっています。
単にcontrol-planeが各ワーカーノードのkubeletに接続できれば良いだけであれば、敢えて設定せずにコメントアウトすれば、kubesprayが自動的にcontrol-planeのIPアドレスを設定してくれます。
ここに設定が必要になるのは、control-plane以外のIPアドレスからの通信を許可するときのみです。
が、PrometheusをKubernetesクラスタ内でセルフホストする場合、PrometheusのPodからkubeletへの通信を許可する必要があるため、それを許可する追加設定が必要になります。
kubesprayセットアップ時点では、PrometheusのPodに割り当てられるIPアドレスは予測できないため、Podに割り当てるCIDRをまとめて設定することになります。これだけだとセキュリティ強度が落ちてしまうため、別途FWやNetworkPolicy等で保護する必要がありますので、注意してください。
私の場合はCNIにCiliumを利用しているので、CiliumClusterwideNetworkPolicyにNodeSelectorを利用した[Host Policies](https://docs.cilium.io/en/v1.12/policy/language/#host-policies)を用いて「Cluster Hardening」とは別に通信の制御を行なっています(entityとかマジで便利)。

```yaml
kubelet_secure_addresses: "[control-plane１のIP] [control-plane2のIP] [control-plane3のIP] [PodのCIDR]"
```

## まとめ

以上のように、KubesprayでのKubernetes環境デプロイにおいてのセキュリティについて紹介してみました。
特にbindやFW設定のTipsについては、「Cluster Hardening」をそのまま適用すると引っかかるかもしれないので、どなたかのお役に立てれば幸いです。
また、気が向いたら今回同時に設定した「CiliumClusterwideNetworkPolicy」の内容についても解説するかもしれません。もしご興味がある方がいらっしゃいましたら期待せずにお待ちください。