LabHub
배우기 러닝패스 코스

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

profile: cis 한 줄에 RKE2 가 시작을 거부했다

LabHub 에서 이어서 보기

목표

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.jsonversion(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.jsoncni(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.jsonpss_file(RKE2 가 쓴 Pod Security Admission 설정 파일 경로), enforce(그 파일의 defaults.enforce 값), etcd_process_user(지금 etcd 프로세스를 돌리는 사용자 이름), priv_uid(파드 priv 의 UID)를 적으세요.
4. /etc/rancher/rke2/config.yamlprofile: 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.jsonetcd_uid, etcd_gid, db_owner_before(사용자:그룹)를 적은 뒤, 소유권은 손대지 말고 systemctl start --no-block rke2-server 로 서비스를 시작하세요.
6. API 서버가 /readyz 에 ok 를 낼 때까지 기다린 뒤 /root/rke2/cis-state.jsonetcd_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.jsonname(RKE2 가 붙인 전체 스냅숏 이름), path(파일의 절대 경로), size(바이트, 숫자), sha256(파일 해시)을 적으세요.
9. /root/rke2/report.jsonprofile(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 가 설정됐는지, 불리언)를 적으세요.

참고

단계 9개

  1. RKE2 는 무엇을 어디에 두었나
  2. 기본으로 무엇이 들어왔나
  3. 기본 프로필에서는 특권 파드가 뜬다
  4. profile: cis 한 줄에 RKE2 가 시작을 거부했다
  5. etcd 사용자와 커널 값을 준비하고 다시 시작한다
  6. cis 프로필로 다시 올라온 클러스터를 확인한다
  7. 같은 매니페스트가 이번에는 거절됐다
  8. 프로필을 바꾼 뒤의 etcd 스냅숏
  9. 옮기기 전에 확인할 목록으로 정리한다