Kubernetes RBAC란? Role-Based Access Control의 핵심 개념
Kubernetes 클러스터를 운영하다 보면 “누가 어떤 리소스에 어떤 작업을 할 수 있는가?”라는 질문이 반드시 등장합니다. 개발자에게는 자신의 네임스페이스 Pod 조회만, CI/CD 파이프라인에는 Deployment 배포만, 모니터링 시스템에는 읽기 전용 접근만 허용해야 합니다. Kubernetes RBAC(Role-Based Access Control)는 이런 최소 권한 원칙(Principle of Least Privilege)을 구현하는 핵심 메커니즘입니다.
RBAC는 Kubernetes 1.8부터 GA(General Availability)로 안정화되었으며, 현재 모든 관리형 Kubernetes 서비스(EKS, GKE, AKS)에서 기본 활성화되어 있습니다. 이 글에서는 RBAC의 4가지 핵심 리소스부터 ServiceAccount 연동, 실무 설계 패턴, 흔한 함정과 감사(Audit) 전략까지 운영 수준에서 완전히 다룹니다.
RBAC의 4가지 핵심 리소스: Role, ClusterRole, RoleBinding, ClusterRoleBinding
RBAC는 “무엇을 할 수 있는가”를 정의하는 Role 계열과, “누구에게 부여하는가”를 정의하는 Binding 계열, 총 4가지 리소스로 구성됩니다.
| 리소스 | 스코프 | 역할 |
|---|---|---|
| Role | 네임스페이스 | 특정 네임스페이스 내 권한 규칙 정의 |
| ClusterRole | 클러스터 전체 | 클러스터 범위 권한 규칙 정의 (노드, PV 등 포함) |
| RoleBinding | 네임스페이스 | Role/ClusterRole을 주체(Subject)에 바인딩 |
| ClusterRoleBinding | 클러스터 전체 | ClusterRole을 클러스터 범위로 주체에 바인딩 |
핵심 포인트: RoleBinding은 ClusterRole을 참조할 수 있습니다. 이 조합이 실무에서 가장 강력한 패턴입니다 — ClusterRole로 권한 규칙을 한 번 정의하고, 각 네임스페이스의 RoleBinding으로 스코프를 제한하는 것이죠.
Role과 ClusterRole: 권한 규칙(rules) 상세 해부
Role의 핵심은 rules 배열입니다. 각 규칙은 apiGroups, resources, verbs 세 가지 축으로 구성됩니다.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: dev
name: pod-reader
rules:
- apiGroups: [""] # core API group (빈 문자열)
resources: ["pods"]
verbs: ["get", "watch", "list"]
- apiGroups: ["apps"] # apps API group
resources: ["deployments"]
verbs: ["get", "list"]
resourceNames: ["my-app"] # 특정 리소스만 허용
apiGroups 이해하기
Kubernetes API는 그룹별로 나뉩니다. kubectl api-resources 명령으로 확인할 수 있습니다:
| apiGroup | 포함 리소스 |
|---|---|
"" (core) |
pods, services, configmaps, secrets, namespaces, nodes, persistentvolumes |
apps |
deployments, statefulsets, daemonsets, replicasets |
batch |
jobs, cronjobs |
networking.k8s.io |
ingresses, networkpolicies |
rbac.authorization.k8s.io |
roles, clusterroles, rolebindings, clusterrolebindings |
verbs 완전 목록
RBAC에서 사용 가능한 verb는 HTTP 메서드와 매핑됩니다:
| Verb | HTTP 메서드 | 설명 |
|---|---|---|
| get | GET (단일) | 개별 리소스 조회 |
| list | GET (목록) | 리소스 목록 조회 |
| watch | GET (watch) | 변경 사항 스트리밍 |
| create | POST | 리소스 생성 |
| update | PUT | 리소스 전체 교체 |
| patch | PATCH | 리소스 부분 수정 |
| delete | DELETE (단일) | 개별 리소스 삭제 |
| deletecollection | DELETE (목록) | 리소스 일괄 삭제 |
⚠️ 주의: * 와일드카드는 모든 verb를 의미합니다. 편리하지만 최소 권한 원칙에 위배되므로 프로덕션에서는 절대 사용하지 마세요.
서브리소스(subresource) 접근 제어
Pod의 로그 조회(kubectl logs)나 exec(kubectl exec)은 서브리소스에 대한 접근입니다:
rules:
- apiGroups: [""]
resources: ["pods/log"] # kubectl logs 허용
verbs: ["get"]
- apiGroups: [""]
resources: ["pods/exec"] # kubectl exec 허용 (위험!)
verbs: ["create"]
- apiGroups: ["apps"]
resources: ["deployments/scale"] # 스케일링만 허용
verbs: ["get", "patch"]
pods/exec은 컨테이너에 직접 접근하는 것이므로 매우 민감합니다. 개발 환경에서만 제한적으로 허용하는 것이 좋습니다.
RoleBinding과 ClusterRoleBinding: 주체(Subject)에게 권한 부여
Binding은 Role과 Subject를 연결합니다. Subject는 세 가지 종류가 있습니다:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: dev-pod-reader
namespace: dev
subjects:
# 1. User: X.509 인증서의 CN(Common Name) 또는 OIDC 토큰의 sub
- kind: User
name: "jane@example.com"
apiGroup: rbac.authorization.k8s.io
# 2. Group: X.509의 O(Organization) 또는 OIDC의 groups 클레임
- kind: Group
name: "dev-team"
apiGroup: rbac.authorization.k8s.io
# 3. ServiceAccount: Pod에 마운트되는 자동 생성 계정
- kind: ServiceAccount
name: "ci-deployer"
namespace: ci # ServiceAccount는 namespace 필수!
roleRef:
kind: Role # Role 또는 ClusterRole
name: pod-reader
apiGroup: rbac.authorization.k8s.io
중요: roleRef는 생성 후 변경할 수 없습니다(immutable). 참조 대상을 바꾸려면 Binding을 삭제하고 다시 만들어야 합니다.
ClusterRole + RoleBinding 패턴 (가장 실용적)
실무에서 가장 많이 쓰는 패턴입니다. ClusterRole로 규칙을 한 번 정의하고, 네임스페이스별 RoleBinding으로 적용합니다:
# 1. ClusterRole: 한 번 정의
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: namespace-admin
rules:
- apiGroups: ["", "apps", "batch"]
resources: ["*"]
verbs: ["*"]
---
# 2. dev 네임스페이스에만 적용
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: dev-ns-admin
namespace: dev
subjects:
- kind: Group
name: "dev-team"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole # ClusterRole 참조!
name: namespace-admin
apiGroup: rbac.authorization.k8s.io
---
# 3. staging 네임스페이스에도 동일 규칙 적용
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: staging-ns-admin
namespace: staging
subjects:
- kind: Group
name: "dev-team"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: namespace-admin
apiGroup: rbac.authorization.k8s.io
이 패턴의 장점: 권한 규칙이 중복되지 않고, 네임스페이스 격리가 유지되며, 새 네임스페이스 추가 시 RoleBinding만 만들면 됩니다.
ServiceAccount와 RBAC: Pod 레벨 권한 설계
ServiceAccount는 Pod 내부에서 실행되는 애플리케이션의 신원(identity)입니다. 모든 네임스페이스에는 default ServiceAccount가 자동 생성되지만, 이를 그대로 사용하면 안 됩니다.
# 1. 전용 ServiceAccount 생성
apiVersion: v1
kind: ServiceAccount
metadata:
name: log-collector
namespace: monitoring
automountServiceAccountToken: true # 기본값, 필요 없으면 false
---
# 2. 필요한 최소 권한만 부여
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: pod-log-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get"]
---
# 3. 바인딩
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: log-collector-binding
subjects:
- kind: ServiceAccount
name: log-collector
namespace: monitoring
roleRef:
kind: ClusterRole
name: pod-log-reader
apiGroup: rbac.authorization.k8s.io
---
# 4. Pod에서 사용
apiVersion: apps/v1
kind: Deployment
metadata:
name: log-collector
namespace: monitoring
spec:
template:
spec:
serviceAccountName: log-collector # 전용 SA 지정
containers:
- name: collector
image: log-collector:latest
토큰 자동 마운트 비활성화
Kubernetes API에 접근할 필요가 없는 Pod는 토큰 마운트를 꺼야 합니다. 공격 표면을 줄이는 기본 보안 조치입니다:
# ServiceAccount 레벨에서 비활성화
apiVersion: v1
kind: ServiceAccount
metadata:
name: web-app
automountServiceAccountToken: false # 모든 Pod에 적용
# 또는 Pod 레벨에서 비활성화
spec:
automountServiceAccountToken: false
containers:
- name: web
image: nginx:latest
기본 제공 ClusterRole: system: 접두사의 비밀
Kubernetes는 설치 시 다양한 기본 ClusterRole을 제공합니다. 실무에서 자주 활용하는 것들입니다:
| ClusterRole | 용도 | 권한 수준 |
|---|---|---|
cluster-admin |
전체 클러스터 관리자 | 모든 리소스, 모든 verb (슈퍼유저) |
admin |
네임스페이스 관리자 | 네임스페이스 내 대부분 리소스 (Role/RoleBinding 포함) |
edit |
개발자 | CRUD 가능, Role/RoleBinding 제외 |
view |
읽기 전용 | 대부분 리소스 조회 (Secrets 제외) |
실무 팁: view ClusterRole은 Secrets를 포함하지 않습니다. 민감 정보 접근이 필요한 경우 별도 Role을 추가해야 합니다.
Aggregated ClusterRole: 라벨 기반 규칙 자동 합산
Kubernetes의 기본 ClusterRole(admin, edit, view)은 Aggregation 기능을 사용합니다. CRD를 만들 때 이 메커니즘을 활용하면 기본 역할에 자동으로 권한이 추가됩니다:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: crontab-view
labels:
# 이 라벨이 있으면 'view' ClusterRole에 자동 합산
rbac.authorization.k8s.io/aggregate-to-view: "true"
rbac.authorization.k8s.io/aggregate-to-edit: "true"
rules:
- apiGroups: ["stable.example.com"]
resources: ["crontabs"]
verbs: ["get", "list", "watch"]
이렇게 하면 기존에 view 역할을 가진 모든 사용자가 자동으로 CRD 리소스를 조회할 수 있게 됩니다. Operator 개발 시 반드시 알아야 할 패턴입니다.
실무 RBAC 설계 패턴 5가지
패턴 1: 팀별 네임스페이스 격리
# 각 팀에 전용 네임스페이스 + admin 권한
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: backend-team-admin
namespace: backend-prod
subjects:
- kind: Group
name: "oidc:backend-team"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: admin # 기본 제공 admin 역할 재사용
apiGroup: rbac.authorization.k8s.io
패턴 2: CI/CD 파이프라인 최소 권한
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: ci-deployer
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "patch", "update"]
- apiGroups: [""]
resources: ["configmaps", "secrets"]
verbs: ["get", "list", "create", "update", "patch"]
- apiGroups: [""]
resources: ["services"]
verbs: ["get", "list"]
# 의도적으로 delete 미포함 → 삭제 불가
패턴 3: 읽기 전용 모니터링 계정
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: monitoring-reader
rules:
- apiGroups: [""]
resources: ["pods", "nodes", "services", "endpoints", "namespaces"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments", "statefulsets", "daemonsets"]
verbs: ["get", "list", "watch"]
- nonResourceURLs: ["/metrics", "/healthz"]
verbs: ["get"]
nonResourceURLs는 API 리소스가 아닌 HTTP 경로에 대한 접근을 제어합니다. Prometheus 같은 메트릭 수집기에 필수적입니다.
패턴 4: 긴급 대응(Break Glass) 계정
# 평소에는 비활성, 장애 시에만 사용하는 긴급 계정
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: emergency-admin
annotations:
description: "장애 대응 전용. 사용 후 즉시 삭제할 것"
subjects:
- kind: Group
name: "oncall-sre"
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
이 Binding은 평소에는 생성하지 않고, 장애 발생 시에만 kubectl apply로 즉시 적용합니다. 장애 복구 후 즉시 삭제하고, Audit Log로 사용 기록을 추적합니다.
패턴 5: resourceNames로 특정 리소스 제한
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["app-config", "db-credentials"] # 이 2개만 접근 가능
verbs: ["get"]
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["app-settings"]
verbs: ["get", "update", "patch"]
함정: resourceNames를 지정하면 list와 create verb가 무시됩니다. 리소스 이름을 미리 알아야 하고, 목록 조회는 불가능합니다.
RBAC 디버깅과 감사: kubectl auth 명령 활용
kubectl auth can-i: 권한 확인
# 내 권한 확인
kubectl auth can-i create deployments --namespace dev
# yes
# 다른 사용자의 권한 확인 (관리자만 가능)
kubectl auth can-i delete pods --namespace prod --as jane@example.com
# no
# ServiceAccount 권한 확인
kubectl auth can-i list secrets
--as system:serviceaccount:monitoring:log-collector
# no
# 현재 사용자의 모든 권한 목록
kubectl auth can-i --list --namespace dev
kubectl auth whoami: 현재 신원 확인 (v1.27+)
kubectl auth whoami
# ATTRIBUTE VALUE
# Username jane@example.com
# Groups [dev-team system:authenticated]
Audit Policy로 RBAC 이벤트 추적
프로덕션에서는 반드시 Audit 로그를 활성화하여 누가 언제 무엇을 했는지 추적해야 합니다:
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# RBAC 변경은 항상 전체 기록
- level: RequestResponse
resources:
- group: "rbac.authorization.k8s.io"
resources: ["roles", "clusterroles", "rolebindings", "clusterrolebindings"]
# Secrets 접근은 메타데이터만 기록 (본문은 위험)
- level: Metadata
resources:
- group: ""
resources: ["secrets"]
# 나머지는 요청 수준만
- level: Request
resources:
- group: ""
- group: "apps"
RBAC 흔한 실수와 안티패턴 6가지
1. default ServiceAccount에 권한 부여
# ❌ 위험: 네임스페이스의 모든 Pod가 이 권한을 가짐
subjects:
- kind: ServiceAccount
name: default
namespace: production
# ✅ 안전: 전용 ServiceAccount 사용
subjects:
- kind: ServiceAccount
name: my-app-sa
namespace: production
2. 와일드카드 남용
# ❌ 모든 리소스에 모든 권한 (cluster-admin과 동일)
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
# ✅ 필요한 것만 명시
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "update"]
3. ClusterRoleBinding 과다 사용
클러스터 전체에 권한을 부여하는 ClusterRoleBinding은 최소화해야 합니다. 대부분의 경우 네임스페이스별 RoleBinding으로 충분합니다.
4. escalate/bind verb 무시
RBAC 자체의 권한도 제어할 수 있습니다. bind verb가 있으면 자신보다 높은 권한의 Role을 바인딩할 수 있는 위험이 있으므로 주의해야 합니다:
# ⚠️ 위험: 이 역할을 가진 사용자는 cluster-admin도 바인딩 가능
rules:
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["clusterrolebindings"]
verbs: ["create"] # bind 없이 create만 있어도 위험
5. 그룹 미활용
개별 User에게 직접 바인딩하면 관리가 어렵습니다. OIDC 프로바이더(Keycloak, Dex, Google Workspace)의 그룹을 활용하세요.
6. 정기 감사 미실시
# 모든 ClusterRoleBinding 감사 (cluster-admin 바인딩 체크)
kubectl get clusterrolebindings -o json |
jq '.items[] | select(.roleRef.name=="cluster-admin") | .subjects'
# 미사용 ServiceAccount 찾기
kubectl get serviceaccounts --all-namespaces -o json |
jq '.items[] | select(.metadata.name != "default") |
"(.metadata.namespace)/(.metadata.name)"'
OIDC 연동: 외부 IdP로 사용자 인증 통합
프로덕션 환경에서는 X.509 인증서 대신 OIDC(OpenID Connect)를 사용하여 중앙 IdP(Identity Provider)와 연동하는 것이 표준입니다. kube-apiserver 설정 예시:
# kube-apiserver 플래그
--oidc-issuer-url=https://keycloak.example.com/realms/k8s
--oidc-client-id=kubernetes
--oidc-username-claim=email
--oidc-groups-claim=groups
--oidc-username-prefix="oidc:"
--oidc-groups-prefix="oidc:"
이제 RBAC의 Subject에서 OIDC 그룹을 사용할 수 있습니다:
subjects:
- kind: Group
name: "oidc:platform-team" # OIDC 그룹 + prefix
apiGroup: rbac.authorization.k8s.io
사용자 온보딩/오프보딩이 IdP에서 관리되므로, Kubernetes 측 RBAC 변경 없이 팀원 추가/제거가 가능합니다. 이것이 Kubernetes Secrets 관리와 결합되면 완전한 보안 체계가 됩니다.
RBAC 자동화: Namespace Provisioning과 Policy Engine
네임스페이스가 많아지면 RBAC를 수동 관리하기 어렵습니다. 두 가지 자동화 방법을 소개합니다.
방법 1: Namespace Controller (초기화 자동화)
네임스페이스 생성 시 기본 RoleBinding을 자동 생성하는 컨트롤러를 만들 수 있습니다. 혹은 ArgoCD App of Apps 패턴으로 GitOps 기반 자동화가 가능합니다.
방법 2: OPA/Kyverno로 RBAC 정책 강제
# Kyverno: cluster-admin 바인딩 금지 정책
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: restrict-cluster-admin
spec:
validationFailureAction: Enforce
rules:
- name: block-cluster-admin-binding
match:
any:
- resources:
kinds:
- ClusterRoleBinding
validate:
message: "cluster-admin 직접 바인딩은 금지됩니다."
pattern:
roleRef:
name: "!cluster-admin"
정리: RBAC 설계 체크리스트
| 항목 | 체크 |
|---|---|
| default ServiceAccount에 추가 권한 부여하지 않음 | ☐ |
| API 접근 불필요 Pod는 automountServiceAccountToken: false | ☐ |
| ClusterRoleBinding 대신 RoleBinding 우선 사용 | ☐ |
| 와일드카드(*) 미사용 | ☐ |
| OIDC 그룹 기반 바인딩 (개별 User 바인딩 최소화) | ☐ |
| pods/exec 권한 제한적 부여 | ☐ |
| Audit 로그 활성화 및 RBAC 변경 추적 | ☐ |
| cluster-admin 바인딩 정기 감사 | ☐ |
| ClusterRole + RoleBinding 패턴으로 규칙 재사용 | ☐ |
| Aggregated ClusterRole로 CRD 권한 자동 통합 | ☐ |
Kubernetes RBAC는 단순한 권한 관리가 아니라 클러스터 보안의 첫 번째 방어선입니다. 최소 권한 원칙을 지키고, 정기적으로 감사하며, OIDC와 Policy Engine을 조합하면 수백 명의 개발자가 사용하는 대규모 클러스터도 안전하게 운영할 수 있습니다.