클러스터 스코프 리소스
Node, PersistentVolume, StorageClass, Namespace, ClusterRole — 네임스페이스가 없는 것들.
신원이 확인되어도 모든 작업을 허용할 수는 없다. RBAC(Role-Based Access Control, 역할 기반 접근 제어)은 누가 어느 범위에서 무엇을 할 수 있는지 정한다. 이 페이지에서는 권한 정의부터 연결과 검산까지 다룬다.
인가를 “이 사람에게 이 권한”으로 하나씩 붙이면, 사람이 늘 때마다 권한 목록을 통째로 복사해야 하고 정책이 바뀌면 전부를 찾아 고쳐야 한다. RBAC은 역할에 권한을 정의해 두고, 주체를 그 역할에 묶는 방식이다. 권한 정의는 한 곳에 모이고, 사람이 늘어나는 일은 묶기 하나를 더하는 일이 된다.
핵심 구조는 하나다 — 주체 → 바인딩 → 역할 → 리소스.
권한을 정의한다 — Role(네임스페이스 안) · ClusterRole(클러스터 전체)
권한을 부여한다 — RoleBinding(네임스페이스 안) · ClusterRoleBinding(클러스터 전체)
NetworkPolicy와 같은 철학이다 — “막는다”가 아니라 “허용하지 않는다”.
apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: pod-reader namespace: devrules: - apiGroups: [""] # "" = core 그룹 (Pod, Service, ConfigMap …) resources: ["pods", "pods/log"] verbs: ["get", "list", "watch"] - apiGroups: ["apps"] resources: ["deployments"] verbs: ["get", "list", "update", "patch"] - apiGroups: [""] resources: ["secrets"] resourceNames: ["db-secret"] # 특정 이름만 verbs: ["get"]kubectl create role pod-reader --verb=get,list,watch --resource=pods -n devkubectl create role pod-reader --verb=get --resource=pods --resource-name=web -n devresourceNames는 지정한 이름의 오브젝트로 권한을 좁힌다 — 위 예는 dev 네임스페이스의
Secret 중 db-secret만 읽게 한다. get으로 이름을 지정하는 요청과 전체 목록 요청은 다르므로,
이 규칙만으로 kubectl get secrets를 실행하면 403이다.
list·watch에도 이름 제한을 적용할 수 있지만, 허용된 동사와 함께
metadata.name 필드 셀렉터가 필요하다. 자세한 조건은
공식 RBAC 문서에서 확인한다.
주요 verb
| verb | HTTP |
|---|---|
get | GET (단일) |
list | GET (목록) |
watch | GET (스트림) |
create | POST |
update | PUT |
patch | PATCH |
delete | DELETE |
deletecollection | DELETE (다수) |
* | 전부 |
apiGroups
| 값 | 리소스 |
|---|---|
"" | Pod, Service, ConfigMap, Secret, Node, PV… |
"apps" | Deployment, ReplicaSet, StatefulSet, DaemonSet |
"batch" | Job, CronJob |
"networking.k8s.io" | Ingress, NetworkPolicy |
"rbac.authorization.k8s.io" | Role, RoleBinding |
"storage.k8s.io" | StorageClass, CSIDriver |
kubectl api-resources # APIVERSION 열에서 그룹을 확인kubectl api-resources --api-group=apps일부 동작은 별도의 하위 리소스로 취급된다.
| 하고 싶은 것 | 필요한 리소스 |
|---|---|
kubectl logs | pods/log |
kubectl exec | pods/exec (verb는 create) |
kubectl port-forward | pods/portforward |
kubectl scale deploy | deployments/scale |
| Pod 상태 갱신 | pods/status |
- apiGroups: [""] resources: ["pods/exec"] verbs: ["create"] # exec은 create 동사다apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: name: dev-can-read-pods namespace: devsubjects: - kind: User # 인증서의 CN name: dev apiGroup: rbac.authorization.k8s.io - kind: Group # 인증서의 O name: developers apiGroup: rbac.authorization.k8s.io - kind: ServiceAccount # SA는 apiGroup을 쓰지 않는다 name: deploy-bot namespace: devroleRef: kind: Role # Role 또는 ClusterRole name: pod-reader apiGroup: rbac.authorization.k8s.iokubectl create rolebinding dev-can-read-pods --role=pod-reader --user=dev -n devkubectl create rolebinding sa-binding --role=pod-reader --serviceaccount=dev:deploy-bot -n devRole은 네임스페이스 안의 리소스만 가리킬 수 있다. 그런데 Node·PV·StorageClass처럼 네임스페이스가 아예 없는 리소스가 있고, “모든 네임스페이스에서 Pod을 볼 수 있는 사람”도 필요하다. Role로는 표현할 수 없는 이 범위를 맡는 것이 ClusterRole이다.
kubectl create clusterrole node-reader --verb=get,list,watch --resource=nodeskubectl create clusterrolebinding ops-nodes --clusterrole=node-reader --user=opsClusterRole이 필요한 경우
클러스터 스코프 리소스
Node, PersistentVolume, StorageClass, Namespace, ClusterRole — 네임스페이스가 없는 것들.
여러 네임스페이스 재사용
같은 권한 정의를 여러 곳에서 RoleBinding으로 붙여 쓴다.
비(非)리소스 URL
/healthz, /metrics 같은
리소스가 아닌 경로.
rules: - nonResourceURLs: ["/healthz", "/metrics"] verbs: ["get"]nonResourceURLs는 API 오브젝트가 아닌 경로를 다룬다. /healthz·/metrics·/version처럼
API 서버가 그냥 응답하는 엔드포인트에는 apiGroup도 resource도 없어서, 보통의 rules로는
가리킬 수 없기 때문이다. 모니터링 시스템에 권한을 줄 때 쓰이며,
깊게 팔 필요는 없다 — ClusterRole에서만 쓸 수 있다는 것만 기억한다.
| roleRef | 바인딩 종류 | 결과 |
|---|---|---|
Role | RoleBinding | 그 네임스페이스 안에서만 |
ClusterRole | RoleBinding | 그 네임스페이스 안에서만 (권한 정의를 재사용) |
ClusterRole | ClusterRoleBinding | 모든 네임스페이스 + 클러스터 스코프 |
Role | ClusterRoleBinding | 불가능 — 허용되지 않는다 |
세 번째 줄(파란 칸)이 핵심이다. ClusterRole을 RoleBinding으로 묶으면 권한 범위는 그 네임스페이스로 좁혀진다.
“읽기 전용 권한”이나 “네임스페이스 안에서 다 할 수 있는 권한”은 어느 클러스터에나 필요하다. 그래서 Kubernetes가 미리 만들어 둔 ClusterRole이 있다 — 시험에서도 Role을 새로 쓰는 것보다 이걸 RoleBinding으로 붙이는 쪽이 빠를 때가 많다.
kubectl get clusterroleskubectl describe clusterrole view| 이름 | 권한 |
|---|---|
cluster-admin | 전부. * on * |
admin | 네임스페이스 안의 거의 전부 (ResourceQuota·Namespace 자체는 제외) |
edit | 대부분의 리소스 읽기·쓰기. RBAC은 못 만진다 |
view | 읽기 전용. Secret은 못 본다 |
kubectl create clusterrolebinding me-admin --clusterrole=cluster-admin --user=alicekubectl create rolebinding dev-edit --clusterrole=edit --user=bob -n devsystem: 접두사가 붙은 것들은 컴포넌트용이다 (system:node, system:kube-scheduler …).
수정하지 말 것. kubelet·스케줄러·컨트롤러 매니저가 이 역할로 API 서버를 부르므로,
잘못 건드리면 클러스터 자체가 멈춘다.
게다가 API 서버는 기동할 때 기본 역할에서 빠진 권한을 도로 채워 넣는다 —
지운 규칙은 살아 돌아오고 더한 규칙은 그대로 남아, 의도와 다른 상태가 되기 쉽다.
CRD(CustomResourceDefinition)로 새 리소스를 들여오면, view 권한을 가진 사람들은 그것을 못 본다 —
view의 규칙에 그 리소스가 없기 때문이다. 그렇다고 내장 역할을 직접 고치는 것은 위험하다(바로 위).
집계(aggregation)가 그 사이를 메운다. 라벨이 붙은 ClusterRole을 만들어 두면
API 서버가 그 규칙을 내장 역할 안으로 자동으로 합쳐 준다.
apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: monitoring labels: rbac.authorization.k8s.io/aggregate-to-view: "true" # view에 자동 합쳐진다rules: - apiGroups: ["monitoring.coreos.com"] resources: ["prometheuses"] verbs: ["get", "list", "watch"]view/edit/admin에 규칙이 자동으로 합쳐진다admin/edit/view의 aggregationRule이 이 라벨을 수집한다kubectl get clusterrole view -o yaml | grep -A5 aggregationRuleaggregationRule이 있는 ClusterRole의 rules는 컨트롤러가 채우는 자리다 — 손으로 적어도 덮어써진다.
직접 만들 일은 드물지만, view의 rules가 왜 저절로 늘어나는지를 설명해 주는 장치라
이름과 동작 방향만 알아두면 된다.
RBAC은 여러 바인딩의 합이라, YAML을 눈으로 읽어서는 “그래서 이 사람이 지금 무엇을 할 수 있는가”가
잘 안 보인다. kubectl auth can-i는 그 계산을 API 서버에게 직접 물어보는 명령이다 —
실제 인가 판정과 같은 코드가 답하므로, 내 해석이 아니라 클러스터의 결론이 나온다.
impersonation(가장·--as)까지 쓰면 로그인하지 않고도 남의 관점에서 확인할 수 있다.
kubectl auth can-i create deployments -n devkubectl auth can-i delete pods --all-namespaceskubectl auth can-i '*' '*' # 클러스터 관리자인가
# 남의 권한을 대신 확인 (impersonation)kubectl auth can-i list secrets -n dev --as=devkubectl auth can-i get pods --as=system:serviceaccount:dev:deploy-bot -n dev
# 내가 가진 권한 전부kubectl auth can-i --list -n devkubectl auth whoami# impersonation으로 테스트 (kubeconfig 전환 없이)kubectl --as=dev get pods -n devkubectl --as=dev --as-group=developers get pods -n dev
# SA 토큰으로 Pod 안에서kubectl run tmp --image=curlimages/curl --rm -it --restart=Never \ --overrides='{"spec":{"serviceAccountName":"deploy-bot"}}' -- sh TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) curl -s --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt \ -H "Authorization: Bearer $TOKEN" \ https://kubernetes.default.svc/api/v1/namespaces/dev/pods에러 메시지를 정확히 읽는다 — 필요한 정보가 다 있다
Error from server (Forbidden): pods is forbidden: User "dev" cannot list resource "pods" in API group "" in the namespace "prod"실제로 안 되는지 확인
kubectl auth can-i list pods -n prod --as=dev바인딩이 있는지
kubectl get rolebinding,clusterrolebinding -A -o wide | grep dev역할의 내용 확인
kubectl describe role pod-reader -n prodkubectl describe clusterrole view| 흔한 원인 | 확인 |
|---|---|
| 바인딩의 네임스페이스가 다르다 | RoleBinding은 대상 네임스페이스에 있어야 한다 |
| apiGroup 오타 | apps vs "" — Deployment는 apps |
| 하위 리소스 누락 | pods/log, pods/exec |
| SA 이름 형식 | system:serviceaccount:ns:name |
roleRef를 고치려 했다 | 변경 불가 — 지우고 다시 |
auth can-i --as로 검산한다.