K8s RBAC란? 왜 중요한가
Kubernetes RBAC(Role-Based Access Control)은 클러스터 내 리소스에 대한 접근 권한을 역할(Role) 기반으로 제어하는 메커니즘이다. 멀티테넌트 환경, CI/CD 파이프라인, 개발자 셀프서비스 등 모든 운영 시나리오에서 최소 권한 원칙(Principle of Least Privilege)을 구현하는 핵심 보안 계층이다.
RBAC 없이 운영하면 모든 ServiceAccount가 cluster-admin 수준의 권한을 갖게 되어, 하나의 Pod 탈취로 전체 클러스터가 위험에 노출된다. 실제 CNCF 보안 보고서에 따르면 Kubernetes 보안 사고의 40% 이상이 과도한 권한 설정에서 비롯된다.
RBAC 4대 리소스 구조
RBAC는 4개의 API 리소스로 구성된다. Role/ClusterRole이 “무엇을 할 수 있는가”를 정의하고, RoleBinding/ClusterRoleBinding이 “누가 그 권한을 갖는가”를 연결한다.
# 1. Role — 네임스페이스 범위 권한
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: backend
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list"]
resourceNames: ["api-server", "worker"] # 특정 리소스만 허용
# 2. ClusterRole — 클러스터 전체 범위
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: node-viewer
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]
- apiGroups: ["metrics.k8s.io"]
resources: ["nodes", "pods"]
verbs: ["get", "list"]
# 3. RoleBinding — 네임스페이스에 권한 부여
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
namespace: backend
name: dev-pod-reader
subjects:
- kind: User
name: dev-alice
apiGroup: rbac.authorization.k8s.io
- kind: ServiceAccount
name: ci-runner
namespace: ci
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
# 4. ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: global-node-viewer
subjects:
- kind: Group
name: platform-team
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: node-viewer
apiGroup: rbac.authorization.k8s.io
핵심 개념: apiGroups · resources · verbs
RBAC 규칙의 세 축을 정확히 이해해야 실수 없이 권한을 설계할 수 있다.
| 항목 | 설명 | 예시 |
|---|---|---|
| apiGroups | API 그룹 (core=””) | “”, “apps”, “batch”, “networking.k8s.io” |
| resources | 리소스 종류 | “pods”, “deployments”, “secrets”, “configmaps” |
| verbs | 허용 동작 | “get”, “list”, “watch”, “create”, “update”, “patch”, “delete” |
| resourceNames | 특정 이름만 허용 (선택) | [“my-configmap”, “api-deploy”] |
서브리소스도 별도로 지정해야 한다. pods/log, pods/exec, deployments/scale 등은 상위 리소스 권한이 있어도 자동 포함되지 않는다.
실전 패턴 1: 멀티테넌트 네임스페이스 격리
팀별 네임스페이스에 개발자 권한을 부여하되, 다른 팀 네임스페이스에는 접근할 수 없게 설계한다.
# ClusterRole로 권한 "템플릿"을 한 번만 정의
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: namespace-developer
rules:
- apiGroups: ["", "apps", "batch"]
resources: ["pods", "deployments", "services", "configmaps", "jobs", "cronjobs"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: [""]
resources: ["pods/log", "pods/portforward"]
verbs: ["get", "create"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list"] # secrets는 읽기만
---
# 팀별 RoleBinding으로 네임스페이스에 바인딩
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
namespace: team-alpha
name: team-alpha-dev
subjects:
- kind: Group
name: team-alpha-devs
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole # ClusterRole을 RoleBinding에 연결
name: namespace-developer # → 해당 네임스페이스 범위로 축소됨
apiGroup: rbac.authorization.k8s.io
핵심 트릭: ClusterRole을 RoleBinding에 연결하면, ClusterRole의 규칙이 해당 네임스페이스 범위로 자동 축소된다. 권한 템플릿을 한 번만 정의하고 여러 네임스페이스에 재사용할 수 있다.
실전 패턴 2: CI/CD ServiceAccount 최소 권한
CI/CD 파이프라인에 cluster-admin을 부여하는 것은 가장 흔한 안티패턴이다. 배포에 필요한 최소 권한만 부여한다.
apiVersion: v1
kind: ServiceAccount
metadata:
name: github-actions-deployer
namespace: ci
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: production
name: ci-deployer
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "patch", "update"]
- apiGroups: ["apps"]
resources: ["deployments/rollback"]
verbs: ["create"]
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "create", "update", "patch"]
# Secret create/update는 제외 — Sealed Secrets나 External Secrets로 관리
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
namespace: production
name: ci-deployer-binding
subjects:
- kind: ServiceAccount
name: github-actions-deployer
namespace: ci
roleRef:
kind: Role
name: ci-deployer
apiGroup: rbac.authorization.k8s.io
실전 패턴 3: 읽기 전용 모니터링 계정
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: monitoring-reader
labels:
rbac.theokei.com/tier: "readonly"
rules:
- apiGroups: [""]
resources: ["pods", "services", "endpoints", "namespaces", "nodes"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments", "replicasets", "statefulsets", "daemonsets"]
verbs: ["get", "list", "watch"]
- apiGroups: ["batch"]
resources: ["jobs", "cronjobs"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get"]
# secrets, configmaps 조회 권한 없음 — 민감 데이터 보호
Aggregated ClusterRole: 권한 자동 병합
Kubernetes의 강력한 기능 중 하나인 Aggregated ClusterRole은 라벨 셀렉터로 여러 ClusterRole을 자동 병합한다. CRD 기반 오퍼레이터나 플러그인 시스템에서 특히 유용하다.
# 부모: aggregation 규칙만 정의
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: platform-admin
aggregationRule:
clusterRoleSelectors:
- matchLabels:
rbac.theokei.com/aggregate-to-platform-admin: "true"
rules: [] # 자동 채워짐
---
# 자식: 라벨로 자동 병합됨
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: platform-admin-core
labels:
rbac.theokei.com/aggregate-to-platform-admin: "true"
rules:
- apiGroups: [""]
resources: ["namespaces"]
verbs: ["get", "list", "create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: platform-admin-cert-manager
labels:
rbac.theokei.com/aggregate-to-platform-admin: "true"
rules:
- apiGroups: ["cert-manager.io"]
resources: ["certificates", "issuers"]
verbs: ["get", "list", "create", "update", "delete"]
새로운 CRD 권한이 필요할 때 자식 ClusterRole만 추가하면 부모에 자동 병합된다. K8s StatefulSet 가이드에서 다룬 오퍼레이터 패턴과 함께 사용하면 효과적이다.
권한 감사: kubectl auth can-i
RBAC 설정 후 반드시 검증해야 한다. kubectl auth can-i는 권한 확인의 핵심 도구다.
# 특정 사용자의 권한 확인
kubectl auth can-i create deployments
--namespace production
--as system:serviceaccount:ci:github-actions-deployer
# yes
kubectl auth can-i delete secrets
--namespace production
--as system:serviceaccount:ci:github-actions-deployer
# no
# 전체 권한 목록 확인
kubectl auth can-i --list
--as system:serviceaccount:ci:github-actions-deployer
--namespace production
# 모든 네임스페이스에서 위험 권한 감사
kubectl auth can-i '*' '*' --all-namespaces
--as system:serviceaccount:kube-system:default
# cluster-admin 여부 빠르게 체크
자동화 감사 스크립트
운영 환경에서는 주기적으로 과도한 권한을 감사해야 한다.
#!/bin/bash
# rbac-audit.sh — cluster-admin 바인딩 감사
echo "=== ClusterRoleBinding → cluster-admin ==="
kubectl get clusterrolebindings -o json |
jq -r '.items[] | select(.roleRef.name=="cluster-admin") |
"(.metadata.name): ([.subjects[]? | "(.kind)/(.name)((.namespace // "cluster"))"]) | join(", ")"'
echo ""
echo "=== ServiceAccounts with Secret access ==="
for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
kubectl auth can-i get secrets -n "$ns"
--as system:serviceaccount:"$ns":default 2>/dev/null |
grep -q "yes" && echo " WARNING: $ns/default can read secrets"
done
echo ""
echo "=== Wildcard rules (verbs: ['*'] or resources: ['*']) ==="
kubectl get clusterroles -o json |
jq -r '.items[] | select(.rules[]? |
(.verbs[]? == "*") or (.resources[]? == "*")) |
.metadata.name' | sort -u
흔한 실수와 방지 방법
| 실수 | 위험 | 해결 |
|---|---|---|
| CI에 cluster-admin 부여 | 전체 클러스터 탈취 | 네임스페이스 범위 Role 사용 |
| verbs: [“*”] 남용 | 의도치 않은 delete 허용 | 필요한 verb만 명시 |
| default SA 방치 | Pod에 불필요한 API 접근 | automountServiceAccountToken: false |
| Secret 읽기 과도 허용 | DB 비밀번호 유출 | resourceNames로 제한 |
| pods/exec 무분별 허용 | 컨테이너 내부 침투 | 개발 환경에만 제한 부여 |
Pod ServiceAccount 보안 강화
apiVersion: v1
kind: Pod
metadata:
name: secure-app
spec:
serviceAccountName: app-sa
automountServiceAccountToken: false # API 접근 불필요 시 토큰 마운트 차단
containers:
- name: app
image: myapp:latest
securityContext:
runAsNonRoot: true
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
대부분의 애플리케이션 Pod는 Kubernetes API를 호출할 필요가 없다. automountServiceAccountToken: false를 기본으로 설정하면 토큰이 마운트되지 않아 공격 표면이 줄어든다. K8s Probes 가이드의 보안 설정과 함께 적용하면 Pod 보안이 크게 강화된다.
RBAC + OPA Gatekeeper 연동
RBAC는 “누가 무엇을 할 수 있는가”를 제어하지만, “어떤 값으로 생성할 수 있는가”는 제어하지 못한다. OPA Gatekeeper와 결합하면 더 세밀한 정책을 적용할 수 있다.
# ConstraintTemplate: ClusterRoleBinding에서 cluster-admin 사용 금지
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8sblockclusteradmin
spec:
crd:
spec:
names:
kind: K8sBlockClusterAdmin
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8sblockclusteradmin
violation[{"msg": msg}] {
input.review.object.roleRef.name == "cluster-admin"
msg := sprintf("cluster-admin binding not allowed: %v",
[input.review.object.metadata.name])
}
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sBlockClusterAdmin
metadata:
name: block-cluster-admin-binding
spec:
match:
kinds:
- apiGroups: ["rbac.authorization.k8s.io"]
kinds: ["ClusterRoleBinding"]
정리: RBAC 설계 체크리스트
- 최소 권한 원칙: 필요한 verb, resource만 명시. 와일드카드(*) 금지
- 네임스페이스 격리: ClusterRole을 RoleBinding으로 축소 바인딩
- SA 토큰 차단: automountServiceAccountToken: false 기본화
- Aggregation 활용: CRD 권한은 라벨 기반 자동 병합
- 주기적 감사: kubectl auth can-i + 감사 스크립트 자동화
- 정책 보완: OPA Gatekeeper로 RBAC가 커버하지 못하는 값 검증
- Secret 접근 최소화: resourceNames로 필요한 Secret만 허용
RBAC는 Kubernetes 보안의 첫 번째 방어선이다. 설정 한 줄의 실수가 클러스터 전체를 위험에 노출시킬 수 있으므로, 코드 리뷰와 자동 감사를 일상 워크플로에 반드시 포함시켜야 한다.