쿠버네티스 배포판 — 직접 세운다 · RKE2 와 CIS 프로필 · 실습
profile: cis 한 줄에 RKE2 가 시작을 거부했다
목표
VM 안의 RKE2 v1.36.4+rke2r1 서버 한 대에서 기본 구성을 읽고, profile: cis 로 바꿀 때 호스트가 갖춰야 할 것을 실제 실패 메시지로 만나며,
Pod Security Admission 이 privileged 에서 restricted 로 바뀌어 같은 특권 파드가 거절되는 것과 etcd 스냅숏까지 확인합니다.
왜 중요한가
RKE2 는 k3s 와 같은 계열의 단일 바이너리 배포판이지만, 저장소는 처음부터 내장 etcd 이고 보안 하드닝을 프로필 하나로 켜게 설계되어 있습니다.
그래서 옮겨 갈 때 막히는 곳은 기능이 아니라 정책과 호스트 준비입니다. cis 프로필은 etcd 를 전용 사용자로 돌리고, kubelet 이 커널 값을
바꾸지 못하게 하며, 모든 네임스페이스에 restricted 기준을 강제합니다. 프로필은 이런 요구를 대신 만족시켜 주지 않고 확인만 합니다 —
준비가 안 된 호스트에서는 시작 자체를 거부합니다. 또 기본 프로필로 이미 돌던 클러스터에 나중에 켜면, 서비스만 멈추고 남겨 둔 root etcd 가 새 문제를 만듭니다.
어떤 기본값이 바뀌는지 알고 옮기면 "어제까지 뜨던 파드가 오늘 거절된다" 는 사고를 미리 막을 수 있습니다.
단계
1. /root/rke2/layout.json 에 version(rke2 --version 첫 줄의 판, 예: v0.0.0+rke2r0 형식), rke2_binary(rke2 실행 파일의 절대 경로), kubectl(RKE2 가 함께 푼 kubectl 의 절대 경로), kubeconfig(관리자 kubeconfig 경로), kubectl_on_default_path(기본 PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 만으로 kubectl 을 찾을 수 있는지, 불리언)를 적으세요.
2. /root/rke2/components.json 에 cni(kube-system 의 CNI DaemonSet 으로 판단한 이름, 소문자), ingress_class(기본 IngressClass 이름), datastore(컨트롤 플레인 저장소: etcd 또는 sqlite), storage_classes(StorageClass 이름을 정렬한 배열), cluster_cidr, service_cidr, cluster_dns(CoreDNS 서비스 IP), overlaps_host(두 대역 중 하나라도 호스트 클러스터의 10.244.0.0/16·10.96.0.0/12 와 겹치는지, 불리언)를 적으세요.
3. 네임스페이스 legacy 를 만들고 /root/rke2/priv-legacy.yaml 로 특권 파드 priv(이미지 public.ecr.aws/docker/library/busybox:1.37, sleep 86400, privileged: true)를 띄워 Ready 가 되게 하세요. 그다음 /root/rke2/pss-default.json 에 pss_file(RKE2 가 쓴 Pod Security Admission 설정 파일 경로), enforce(그 파일의 defaults.enforce 값), etcd_process_user(지금 etcd 프로세스를 돌리는 사용자 이름), priv_uid(파드 priv 의 UID)를 적으세요.
4. /etc/rancher/rke2/config.yaml 에 profile: cis 를 넣고 systemctl restart rke2-server 를 실행하세요. 실패하면 rke2-server 저널에서 level=fatal 줄 하나를 그대로 /root/rke2/cis-fatal.txt 에 저장하고, 호스트 준비를 마칠 때까지 재시도 루프가 돌지 않게 systemctl stop rke2-server 로 멈추고, 서비스를 멈춰도 계속 도는 정적 파드(root 로 도는 etcd 포함)를 rke2-killall.sh 로 정리하세요.
5. 문서의 호스트 요구사항대로 시스템 사용자·그룹 etcd(홈 없음, 셸 nologin)를 만들고, RKE2 가 함께 푼 rke2-cis-sysctl.conf 를 /etc/sysctl.d/60-rke2-cis.conf 로 복사해 적용하세요(systemd-sysctl 재시작 대신 sysctl -p <파일>). 이 클러스터는 기본 프로필로 만들어졌으므로 시작하기 전에 /var/lib/rancher/rke2/server/db/etcd/member/snap/db 의 소유자를 확인해 /root/rke2/host-prep.json 에 etcd_uid, etcd_gid, db_owner_before(사용자:그룹)를 적은 뒤, 소유권은 손대지 말고 systemctl start --no-block rke2-server 로 서비스를 시작하세요.
6. API 서버가 /readyz 에 ok 를 낼 때까지 기다린 뒤 /root/rke2/cis-state.json 에 etcd_process_user, enforce(지금 rke2-pss.yaml 의 defaults.enforce), exempt_namespaces(그 파일의 exemptions.namespaces 를 정렬한 배열), db_owner_after(지금 member/snap/db 의 사용자:그룹), netpol_namespaces(NetworkPolicy 가 하나라도 있는 네임스페이스를 정렬한 배열), protect_kernel_defaults(kubelet 이 --config-dir 로 읽는 설정 디렉터리의 파일에 protectKernelDefaults: true 가 있는지, 불리언)를 적으세요.
7. 네임스페이스 shop 을 만들고 3단계와 같은 특권 파드를 /root/rke2/priv-shop.yaml(네임스페이스만 shop)로 적용해, 거절 출력 전체를 /root/rke2/denied.txt 에 저장하세요. 그다음 restricted 기준을 만족하는 파드 ok(이미지 같음, sleep 86400)를 /root/rke2/ok.yaml 로 shop 에 띄워 Ready 로 만드세요. legacy 의 priv 는 지우지 않습니다.
8. rke2 etcd-snapshot save --name before-shop 으로 스냅숏을 만들고 /root/rke2/snapshot.json 에 name(RKE2 가 붙인 전체 스냅숏 이름), path(파일의 절대 경로), size(바이트, 숫자), sha256(파일 해시)을 적으세요.
9. /root/rke2/report.json 에 profile(config.yaml 의 값), enforce_before, enforce_after, start_blocker(4단계 fatal 이 요구한 것: etcd-user·sysctl·selinux 중 하나), legacy_priv_running(legacy 의 priv 가 지금 Running 인지, 불리언), snapshot_dir(스냅숏이 저장된 디렉터리), default_sa_automount_disabled(shop 네임스페이스 default ServiceAccount 에 automountServiceAccountToken: false 가 설정됐는지, 불리언)를 적으세요.
참고
- 로그인 셸에는
KUBECONFIG와/var/lib/rancher/rke2/bin이 PATH 에 들어 있습니다. 채점기는 그 설정 없이 경로를 직접 씁니다. - 서비스 로그:
journalctl -u rke2-server --no-pager | tail - 4단계에서 정리한 뒤 6단계에서 다시 올라올 때까지 API 는 응답하지 않습니다.
systemctl stop만 하면 정적 파드가 계속 돌아 응답이 섞여 보입니다. - 흔한 실수: 호스트 준비를 하며
systemctl restart systemd-sysctl을 치는 것. 문서가 돌고 있는 클러스터에서는 부작용을 경고합니다. - 흔한 실수:
systemctl stop만 하고 정적 파드를 남긴 채 다시 시작하는 것. 옛 root etcd 가 조각 모음을 받아 db 를 root 소유로 남기고, etcd 사용자로 뜬 새 etcd 가 db 를 열지 못해 panic 합니다(실측). - [RKE2 CIS 하드닝 가이드](https://docs.rke2.io/security/hardening_guide) · [Pod Security Standards](https://docs.rke2.io/security/pod_security_standards) · [백업과 복구](https://docs.rke2.io/datastore/backup_restore)
단계 9개
- RKE2 는 무엇을 어디에 두었나
- 기본으로 무엇이 들어왔나
- 기본 프로필에서는 특권 파드가 뜬다
- profile: cis 한 줄에 RKE2 가 시작을 거부했다
- etcd 사용자와 커널 값을 준비하고 다시 시작한다
- cis 프로필로 다시 올라온 클러스터를 확인한다
- 같은 매니페스트가 이번에는 거절됐다
- 프로필을 바꾼 뒤의 etcd 스냅숏
- 옮기기 전에 확인할 목록으로 정리한다