TT Lab
开始
学习 学习路径 课程

Kubernetes 发行版 — 自己搭

RKE2——默认值偏向安全的发行版

在 TT Lab 中继续学习

一句话总结

RKE2 和 k3s 一样用一个二进制文件搭建,但存储从一开始就是 etcd,并且只需 profile: cis 这一行,就能开启 restricted Pod 安全以及 etcd 专用用户之类的加固。这一行只有在主机已准备就绪时才会生效。

为什么需要它

k3s 是为边缘和开发环境而打造的轻量发行版,所以存储默认值是 sqlite,组件也往方便的方向引入。然而,面对公共机构或金融行业这类带着 CIS 基准检查清单前来的客户,“方便的默认值”反而会成为审计项。每次检查都要由人反复地逐个修改 API 服务器标志、调整 etcd 目录权限、给每个命名空间加上 Pod 安全标签,总会漏掉某一处。

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 那样集中在一个进程中,而是由 kubelet 启动的静态 Pod(etcd、kube-apiserver 等)。

在这台 VM 上用默认 profile 搭建的结果(实测,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(文档中的默认值);ingress class 是 traefik(从 v1.36 起成为新集群的默认值,依据文档);datastore 是 etcd 静态 Pod;storage class 没有——不会像 k3s 的 local-path 那样自动带入;网段为 Pod 10.42.0.0/16、Service 10.43.0.0/16、DNS 10.43.0.10;PSA 配置为 /etc/rancher/rke2/rke2-pss.yaml,enforce 为 privileged;etcd 进程以 root 运行。

网段与服务器配置参考中的默认值相同,而且不与运行本实验 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,标注为“需要运维人员介入”,留了下来。实验中创建的两个命名空间既没有 NetworkPolicy,automount 的值也是空的(实测)。

在现场相遇的样子

假设一个用默认 profile 运行了几个月的集群,在检查之前写入 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 步,日志出现 missing required: user: unknown user etcd 的 fatal,表示拒绝启动;第 2 步,执行 systemctl stop、准备 etcd 用户和 sysctl 后重新启动,etcd 容器因 failed to open database 而 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,会因为 serviceaccount "default" not found 而不是 PodSecurity 被拒绝,看起来像是被策略阻止了。

第 2 步尤其容易让人困惑。配置文件都改对了,服务处于 activating,但 kubectl 只报连接超时。原因不在服务日志中,只存在于 etcd 容器的日志(/var/log/pods/kube-system_etcd-*)里。

在 Pod 方面,在默认 profile 下能正常启动的特权 Pod,同样的清单在新命名空间中会因 violates PodSecurity "restricted:latest" 被拒绝。但是,已经在运行的特权 Pod 仍然保持 Running。PSA 只检查创建请求,所以开启 profile 的当天风平浪静,到了那个 Pod 被重新部署的日子,会突然被阻止。从 k3s 迁移过来的工作负载也一样,被拒绝不是因为它是 RKE2,而是因为它是开启了 cis profile 的 RKE2——默认 profile 的 RKE2 是会接受它的。

还有一点,安装路径也取决于环境。如果 /usr/local 是只读的或是单独的挂载点,安装脚本会解压到 /opt/rke2(快速入门文档和脚本注释)。这台实验 VM 的 /usr/local 是一块大的暂存磁盘,而 /opt 所在的根分区只有 2.4GiB,已经用了 89%(实测,安装物为 128M)。如果保持原样,根分区几乎会被占满,所以明确指定了 INSTALL_RKE2_TAR_PREFIX。

实际工作中真正重要的事

下一项实验要做什么

在 VM 的 RKE2 上记录安装位置和默认组件,在默认 profile 下启动特权 Pod,然后改为 profile: cis。收到拒绝启动的消息之后,清理到静态 Pod 为止,准备好 etcd 用户和 sysctl 再重新启动,确认所有权如何变化,接着确认同样的清单被拒绝、已有的 Pod 仍然保留,最后生成快照并整理成报告。