TT Lab
はじめる
学ぶ 学習パス コース

Kubernetesディストリビューション — 自分で立てる

RKE2 — 既定値がセキュリティ寄りのディストリビューション

TT Labで続きを見る

一言でいうと

RKE2はk3sのようにバイナリ1つで構築しますが、データストアは最初からetcdで、profile: cisの1行でrestrictedのPodセキュリティやetcd専用ユーザーのようなハードニングを有効にするディストリビューションです。その1行は、ホストの準備ができているときにだけ有効になります。

なぜ必要なのか

k3sはエッジや開発環境のために軽量に作られたディストリビューションなので、データストアのデフォルトはsqliteで、部品も楽なほうが入ってきます。ところが、公共機関や金融のように、CISベンチマークのチェックリストを持ってくる顧客の前では、「楽なデフォルト」がかえって監査項目になります。点検のたびにAPIサーバーのフラグを1つずつ直し、etcdディレクトリの権限を合わせ、ネームスペースごとにPodセキュリティのラベルを付ける作業を人が繰り返すと、必ず1か所が抜けます。

RKE2は、その繰り返しをディストリビューションの中に移したものです。RKE2 CISハードニングガイドの説明のとおり、profileを有効にすると、RKE2がPSAの設定ファイルとデフォルトのネームスペースのNetworkPolicyを自分で書き込み、etcdをetcdユーザーで動かし、ファイルの権限を絞ります。その代わり、ホスト側の準備(カーネル値、etcdユーザー)は運用者の担当として残し、起動時にその準備ができているかを検査します。設計が「勝手に直してくれる」ではなく「できていなければ止まる」である理由は、ホストの設定をディストリビューションがこっそり変えると、そのホストの他のソフトウェアを壊すおそれがあるからです。

どう動くのか

まず、インストールの形がk3sと異なります。クイックスタートのドキュメントのとおり、kubeconfigは/etc/rancher/rke2/rke2.yaml、kubectl・crictl・ctrは/var/lib/rancher/rke2/binにあり、デフォルトのPATHには入っていません。コントロールプレーンはk3sのように1つのプロセスにまとまらず、kubeletが起動する静的Pod(etcd、kube-apiserverなど)です。

このVMでデフォルトのプロファイルで構築した結果(実測、v1.36.4+rke2r1):

cni             canal (문서의 기본값)
ingress class   traefik (v1.36 부터 새 클러스터의 기본, 문서)
datastore       etcd 정적 파드
storage class   없음 — k3s 의 local-path 같은 것이 들어오지 않는다
대역            파드 10.42.0.0/16 · 서비스 10.43.0.0/16 · DNS 10.43.0.10
PSA 설정        /etc/rancher/rke2/rke2-pss.yaml, enforce: privileged
etcd 프로세스   root

このコードブロックの韓国語の部分は、CNIのcanalがドキュメント上のデフォルトであること、IngressClassのtraefikがv1.36から新しいクラスターのデフォルトであること(ドキュメント)、データストアのetcdが静的Podであること、StorageClassはなく、k3sのlocal-pathのようなものは入ってこないこと、CIDR範囲がPod・Service・DNSの順であること、そしてetcdプロセスがrootで動いていることを表しています。

CIDR範囲はサーバー設定リファレンスのデフォルトと同じで、このラボのVMを動かしているホストクラスター(10.244と10.96)とは重ならないので、そのまま使います。

profile: cisを有効にすると何が変わるかは、Pod Security Standardsのドキュメントにまとめられています。同じファイルが次のように書き直されました(実測)。

defaults:
  enforce: "restricted"
  audit: "restricted"
  warn: "restricted"
exemptions:
  namespaces: [kube-system, compliance-operator-system, tigera-operator]

そのほか、kubeletでprotect-kernel-defaultsが有効になり、カーネル値が期待と異なるとkubeletが止まります(このバージョンではフラグではなく、/var/lib/rancher/rke2/agent/etc/kubelet.conf.d/00-rke2-defaults.confのprotectKernelDefaults: trueとして入りました。実測)。また、default・kube-public・kube-systemにNetworkPolicyが作られます。default ServiceAccountのトークンの自動マウントも、RKE2がシステムのネームスペースでは無効にします(このVMでは、default・kube-public・kube-system・kube-node-leaseがfalseでした。実測)。ただしガイドは、運用者があとから作ったネームスペースのNetworkPolicyとdefault ServiceAccountを「運用者の対応が必要」として残しています。ラボで作った2つのネームスペースには、NetworkPolicyもなく、automountの値も空でした(実測)。

現場での姿

デフォルトのプロファイルで数か月動いていたクラスターに、点検を前にprofile: cisを入れて再起動したとしましょう。このVMで順に起きたことです(実測)。

1) level=fatal msg="missing required: user: unknown user etcd ..."   ← 시작 거부
2) systemctl stop → etcd 사용자·sysctl 준비 → 다시 시작
   → etcd 컨테이너: failed to open database .../member/snap/db ... panic
3) 데이터 디렉터리는 etcd:etcd 로 바뀌었는데 member/snap/db 만 root:root
4) rke2-killall.sh 로 남은 컨테이너까지 내리고 다시 시작 → 9초 만에 readyz ok, db 도 etcd:etcd

このコードブロックの韓国語の部分は、順に、1)起動の拒否、2)サービスを停止してetcdユーザーとsysctlを準備してから再び起動すると、etcdコンテナがデータベースを開けずにpanicすること、3)データディレクトリはetcd:etcdに変わったのにmember/snap/dbだけがroot:rootのままであること、4)rke2-killall.shで残ったコンテナまで停止して再び起動すると9秒でreadyzがokになり、dbもetcd:etcdになることを述べています。

原因は、systemctl stopがrke2プロセスだけを停止し、静的Podのコンテナは残すことにありました。stopの後もrootで動くetcdがそのまま残っていて(実測)、新しく起動したRKE2が送ったDefragmenting etcd databaseをその古いetcdが受け取り、dbファイルをroot所有で書き直しました。そのあとetcdユーザーで起動した新しいetcdは、そのファイルを開けませんでした。ログの時刻とファイルの所有者から見た因果関係です。逆に、古いコンテナを片付けた後は、chownをしなくても、RKE2が起動時に所有権を合わせました(実測)。ガイドが言う「etcdのデータディレクトリをetcd所有にする」が、これです。

APIが戻った後も、終わりではありませんでした。RKE2がNetworkPolicyを書き込んだのは起動の62秒後で、再起動後に最初に作ったネームスペースにdefault ServiceAccountができたのは、ネームスペースを作って16秒後(別のVMでは43秒後)でした(実測)。その間にPodを入れると、PodSecurityではなくserviceaccount "default" not foundで拒否され、ポリシーのせいで止められたように見えます。

2)が特にわかりにくいところです。設定ファイルはすべて正しく直してあり、サービスはactivatingなのに、kubectlは接続のタイムアウトしか出しません。原因はサービスのログではなく、etcdコンテナのログ(/var/log/pods/kube-system_etcd-*)にしかありません。

Podについては、デフォルトのプロファイルでそのまま起動した特権Podと同じマニフェストが、新しいネームスペースでviolates PodSecurity "restricted:latest"として拒否されます。ただしすでに起動していた特権Podは引き続きRunningです。PSAは作成リクエストしか検査しないので、プロファイルを有効にした日は静かで、そのPodが再デプロイされる日に突然止まります。k3sから移してきたワークロードも同様で、RKE2だからではなく、cisプロファイルが有効なRKE2だから拒否されます。デフォルトのプロファイルのRKE2なら受け入れます。

もう1つ、インストールパスも環境に左右されます。インストールスクリプトは、/usr/localが読み取り専用か別のマウントポイントの場合、/opt/rke2に展開します(クイックスタートのドキュメントとスクリプトのコメント)。このラボのVMでは、/usr/localが大きなスクラッチディスクで、/optがあるルートは2.4GiBですでに89%でした(実測、インストールしたものは128M)。そのままにするとルートがほぼいっぱいになるので、INSTALL_RKE2_TAR_PREFIXを明示しました。

実務で本当に大切なこと

次のラボですること

VMのRKE2で、インストール場所とデフォルトのコンポーネントを記録し、デフォルトのプロファイルで特権Podを起動してから、profile: cisに変更します。起動拒否のメッセージを受け取り、静的Podまで片付けたうえで、etcdユーザーとsysctlを準備して再び起動し、所有権がどう変わったかを確認したあと、同じマニフェストが拒否されることと、既存のPodが残ることを確認し、スナップショットを取得してレポートにまとめます。