LabHub
배우기 러닝패스 코스

쿠버네티스 배포판 — 직접 세운다 · RKE2 와 CIS 프로필 · 이론

RKE2 — 기본값이 보안 쪽으로 기운 배포판

LabHub 에서 이어서 보기

한 줄 요약

RKE2 는 k3s 처럼 바이너리 하나로 세우지만 저장소는 처음부터 etcd 이고, profile: cis 한 줄로 restricted 파드 보안과 etcd 전용 사용자 같은 하드닝을 켜는 배포판입니다. 그 한 줄은 호스트가 준비돼 있을 때만 켜집니다.

왜 이게 필요했나

k3s 는 엣지와 개발 환경을 위해 가볍게 만든 배포판이라, 저장소 기본값이 sqlite 이고 부품도 편한 쪽으로 들어옵니다. 그런데 공공기관이나 금융처럼 CIS 벤치마크 점검표를 들고 오는 고객 앞에서는 "편한 기본값" 이 오히려 감사 항목이 됩니다. 점검할 때마다 API 서버 플래그를 하나씩 고치고, etcd 디렉터리 권한을 맞추고, 네임스페이스마다 파드 보안 라벨을 붙이는 일을 사람이 반복하면 반드시 한 곳이 빠집니다.

RKE2 는 그 반복을 배포판 안으로 옮긴 것입니다. [RKE2 CIS 하드닝 가이드](https://docs.rke2.io/security/hardening_guide)의 설명대로, profile 을 켜면 RKE2 가 PSA 설정 파일과 기본 네임스페이스의 NetworkPolicy 를 직접 써 넣고, etcd 를 etcd 사용자로 돌리며, 파일 권한을 좁힙니다. 대신 호스트 쪽 준비(커널 값, etcd 사용자)는 운영자 몫으로 남기고, 시작할 때 그 준비가 됐는지 검사합니다. 설계가 "알아서 고쳐 주기" 가 아니라 "안 되어 있으면 멈추기" 인 이유는, 호스트 설정을 배포판이 몰래 바꾸면 그 호스트의 다른 소프트웨어를 깨뜨릴 수 있기 때문입니다.

어떻게 동작하나

먼저 설치 모양이 k3s 와 다릅니다. [빠른 시작 문서](https://docs.rke2.io/install/quickstart)대로 kubeconfig 는 /etc/rancher/rke2/rke2.yaml, kubectl·crictl·ctr 은 /var/lib/rancher/rke2/bin 에 있고 기본 PATH 에 들어 있지 않습니다. 컨트롤 플레인은 k3s 처럼 한 프로세스에 묶이지 않고 kubelet 이 띄우는 정적 파드(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.10PSA 설정        /etc/rancher/rke2/rke2-pss.yaml, enforce: privilegedetcd 프로세스   root

대역은 [서버 설정 참조](https://docs.rke2.io/reference/server_config)의 기본값과 같고, 이 실습 VM 을 돌리는 호스트 클러스터(10.244/10.96)와 겹치지 않아 그대로 씁니다.

profile: cis 를 켜면 바뀌는 것은 [Pod Security Standards 문서](https://docs.rke2.io/security/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.confprotectKernelDefaults: 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: 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 ... panic3) 데이터 디렉터리는 etcd:etcd 로 바뀌었는데 member/snap/db 만 root:root4) rke2-killall.sh 로 남은 컨테이너까지 내리고 다시 시작 → 9초 만에 readyz ok, db 도 etcd:etcd

원인은 systemctl stop 이 rke2 프로세스만 멈추고 정적 파드 컨테이너는 남긴다는 데 있었습니다. 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초)였습니다(실측). 그 사이에 파드를 넣으면 PodSecurity 가 아니라 serviceaccount "default" not found 로 거절되어, 정책 때문에 막힌 것처럼 보입니다.

2번이 특히 헷갈립니다. 설정 파일은 전부 맞게 고쳤고 서비스는 activating 인데 kubectl 은 연결 시간 초과만 냅니다. 원인은 서비스 로그가 아니라 etcd 컨테이너 로그(/var/log/pods/kube-system_etcd-*)에만 있습니다.

파드 쪽에서는, 기본 프로필에서 그대로 떴던 특권 파드와 같은 매니페스트가 새 네임스페이스에서 violates PodSecurity "restricted:latest" 로 거절됩니다. 하지만 이미 떠 있던 특권 파드는 계속 Running 입니다. PSA 는 생성 요청만 검사하기 때문에, 프로필을 켠 날에는 조용하다가 그 파드가 재배포되는 날 갑자기 막힙니다. k3s 에서 옮겨 온 워크로드도 마찬가지로, RKE2 라서가 아니라 cis 프로필이 켜진 RKE2 라서 거절됩니다 — 기본 프로필의 RKE2 는 받아 줍니다.

또 하나, 설치 경로도 환경을 탑니다. 설치 스크립트는 /usr/local 이 읽기 전용이거나 별도 마운트 지점이면 /opt/rke2 에 풉니다([빠른 시작 문서](https://docs.rke2.io/install/quickstart)와 스크립트 주석). 이 실습 VM 은 /usr/local 이 큰 스크래치 디스크이고 /opt 가 있는 루트는 2.4GiB 에 이미 89% 였습니다(실측, 설치물은 128M). 그대로 두면 루트가 거의 차므로 INSTALL_RKE2_TAR_PREFIX 를 명시했습니다.

실무에서 진짜 중요한 것

다음 실습에서 할 것

VM 의 RKE2 에서 설치 위치와 기본 구성요소를 기록하고, 기본 프로필에서 특권 파드를 띄운 뒤 profile: cis 로 바꿉니다. 시작 거부 메시지를 받고, 정적 파드까지 정리한 뒤 etcd 사용자·sysctl 을 준비해 다시 올리고 소유권이 어떻게 바뀌었는지 확인한 다음, 같은 매니페스트가 거절되는 것과 기존 파드가 남는 것을 확인하고 스냅숏을 떠서 보고서로 정리합니다.