CKA — 쿠버네티스 관리자 · 인증·인가와 RBAC · 이론
인증과 인가는 다른 층이다
한 줄 요약
apiserver 에 들어온 요청은 인증(너는 누구냐) → 인가(그 사람이 이걸 해도 되냐) → 어드미션(그 내용이 규칙에 맞냐) 순서로 통과합니다. RBAC 는 두 번째 층 하나이고, 첫 번째 층은 인증서와 토큰의 세계입니다. 이 둘을 섞으면 진단이 엉킵니다.
왜 이게 필요했나
쿠버네티스에는 사용자 오브젝트가 없습니다. kubectl get users 는 없습니다. 왜냐면 apiserver 는 신원 발급자가 아니라 검증자이기 때문입니다. 신원의 출처는 밖에 있습니다.
- 클라이언트 인증서 — CN 이 사용자 이름, O 가 그룹이 됩니다.
kubeadm이 만들어 주는 admin.conf 가 이 방식입니다. - ServiceAccount 토큰 — 파드 안 워크로드의 신원.
system:serviceaccount:<네임스페이스>:<이름>이라는 이름과system:serviceaccounts그룹을 갖습니다. - OIDC, 웹훅, 부트스트랩 토큰 등
그래서 "그 사용자를 지워 주세요" 라는 요청은 성립하지 않습니다. 인증서를 폐기하거나 바인딩을 지우는 것이 전부입니다.
인가 쪽은 네 종류의 오브젝트뿐입니다.
| | 네임스페이스 범위 | 클러스터 범위 |
| --- | --- | --- |
| 권한 정의 | Role | ClusterRole |
| 권한 부여 | RoleBinding | ClusterRoleBinding |
여기서 딱 하나 헷갈리는 조합이 있습니다. RoleBinding 이 ClusterRole 을 참조하는 것은 허용되며, 그 경우 ClusterRole 의 규칙이 그 RoleBinding 이 있는 네임스페이스 안에서만 적용됩니다. view, edit 같은 내장 ClusterRole 을 네임스페이스별로 재사용하는 표준 방법입니다. 반대로 ClusterRoleBinding 을 쓰면 전 클러스터로 퍼지므로, 네임스페이스 경계를 지키려다 여기서 실수하는 일이 많습니다.
RBAC 는 누적만 됩니다. 거부 규칙이 없습니다. 어떤 바인딩이든 한 번 허용하면 허용입니다. 그래서 "왜 이 계정이 이걸 할 수 있지" 를 추적할 때는 모든 바인딩을 훑어야 하고, kubectl auth can-i --as= 가 그 지름길입니다. 이 명령은 SubjectAccessReview 를 만들어 apiserver 에 물어볼 뿐 실제 요청을 보내지 않습니다. 부작용 없이 안전하게 확인할 수 있습니다.
어떻게 동작하나
Aggregated ClusterRole 은 규칙을 직접 쓰지 않는 ClusterRole 입니다. aggregationRule 에 라벨 셀렉터를 적으면 컨트롤러가 그 라벨이 붙은 다른 ClusterRole 들을 찾아 rules 를 채워 넣습니다. CRD 로 새 리소스를 추가했을 때 기존 view/edit 역할에 라벨 하나로 얹을 수 있는 확장점입니다. 여기에 손으로 rules 를 쓰면 컨트롤러가 덮어씁니다.
automountServiceAccountToken 은 파드에 API 토큰을 넣을지 결정합니다. 기본은 넣습니다. 그러면 API 를 전혀 쓰지 않는 nginx 파드에도 토큰이 들어가고, 컨테이너가 털리면 그 토큰이 곧 클러스터 접근권입니다. ServiceAccount 나 파드 스펙에서 false 로 두면 kube-api-access-* 투영 볼륨 자체가 주입되지 않습니다.
현장에서 만나는 모습
사례 1 — CA 개인키의 노출 창을 2시간으로 자른다. 홈랩을 3노드에서 7노드로 늘릴 때, 워커 조인과 컨트롤 플레인 조인의 차이가 인증 구조를 그대로 보여 줬습니다. 워커는 토큰 하나면 끝납니다.
kubeadm token create --print-join-command컨트롤 플레인은 다릅니다. 새 노드도 인증서를 발급하는 주체가 되어야 하므로 CA 개인키가 필요합니다. kubeadm 은 이 민감한 파일을 scp 로 나르게 하지 않고, kubeadm init phase upload-certs --upload-certs 로 CA 키 묶음을 암호화해 클러스터 안의 Secret 으로 올립니다. 조인 명령에 주는 64자리 certificate-key 가 그 암호문을 푸는 대칭키입니다.
그리고 이 Secret 은 2시간 뒤 자동 삭제됩니다. 암호화됐다고는 해도 클러스터의 루트 신뢰가 안에 들어 있으니 노출 창을 최소화하려는 설계입니다. 만료되면 upload-certs 를 다시 실행하면 되고 기존 클러스터에는 영향이 없습니다. 인증의 핵심은 이렇게 "신뢰의 뿌리를 얼마나 짧게 노출하느냐" 입니다.
사례 2 — 인증서 SAN 이 곧 접근 권한이다. 같은 클러스터에서 컨트롤 플레인 노드를 셋으로 늘렸는데도 첫 노드가 죽으면 아무도 붙지 못했습니다. apiserver 인증서의 SAN 에 나머지 두 노드의 IP 가 없었기 때문입니다. RBAC 를 아무리 잘 짜도 TLS 검증을 통과하지 못하면 인가 층에 도달조차 못 합니다. 반대로 이 성질을 역이용해, 죽은 노드의 IP 를 살아 있는 노드의 인터페이스에 얹으면 인증서 재발급도 kubeconfig 수정도 없이 모든 클라이언트가 그대로 재접속합니다. 클라이언트가 검증하는 것은 노드가 아니라 접속한 주소가 SAN 에 있는가 이기 때문입니다.
사례 3 — 권한이 아니라 설정이 문제일 때도 있다. GPU Operator 를 올리다 파드가 전부 Init 에서 멈춘 적이 있습니다. kubectl get runtimeclass 에는 nvidia 가 멀쩡히 있었지만 노드의 containerd 설정에는 nvidia 가 한 글자도 없었습니다. 이름표는 쿠버네티스 오브젝트로 존재하고 실체는 노드에 있어야 하는데, 후자가 없었던 것입니다. 오브젝트가 있다는 것과 실제로 동작한다는 것은 다른 명제 — 인가 문제를 진단할 때도 같은 습관이 필요합니다. can-i 가 yes 를 뱉었다면 그 다음은 인증서, 네트워크, 어드미션 웹훅 순으로 내려가야 합니다.
다음 실습에서 할 것
ServiceAccount 를 만들고 Role/RoleBinding 으로 읽기 권한만 주고, kubectl auth can-i --as= 로 된다는 것과 안 된다는 것을 둘 다 확인합니다. 그 다음 ClusterRole/ClusterRoleBinding 으로 클러스터 스코프 리소스를 열고, Aggregated ClusterRole 을 구성하고, 마지막으로 토큰이 아예 주입되지 않는 파드를 만듭니다.