Kubernetes 클러스터 RBAC 감사은(는) 무엇을 하나요?
사용 시기
- Kubernetes 클러스터(EKS, GKE, AKS 또는 자체 관리형)의 보안 평가를 수행할 때
- RBAC 정책이 사용자 및 서비스 계정에 대해 최소 권한 원칙을 적용하는지 확인할 때
- Kubernetes 클러스터 내에서 잠재적인 수평 이동 또는 권한 상승을 조사할 때
- 컴플라이언스 감사에서 액세스 제어 및 권한에 대한 문서화가 필요할 때
- 신규 팀을 공유 클러스터에 온보딩하고 적절한 RBAC 정책을 정의할 때
다음 경우에는 사용하지 마세요: 네트워크 정책 감사(Cilium 또는 Calico 네트워크 정책 도구 사용), 컨테이너 이미지 스캔(Trivy 또는 Grype 사용), 런타임 보안 모니터링(Falco 또는 Sysdig Secure 사용).
전제 조건
- kubectl이 대상 클러스터에 대해 cluster-admin 또는 이에 준하는 읽기 권한으로 설정되어 있어야 함
- rbac-tool 설치(
kubectl krew install rbac-tool또는 GitHub 바이너리) - KubiScan 설치(
pip install kubiscan) - Kubeaudit 설치(
brew install kubeaudit또는 GitHub 릴리스) - RBAC 결과와 실제 API 액세스를 연계 분석하기 위한 클러스터의 감사 로그 접근 권한
워크플로우
1단계: 위험한 권한이 있는 ClusterRole 및 Role 열거
와일드카드 권한, 시크릿 접근, 파드 exec, 권한 상승 기능이 있는 역할을 식별합니다.
# 와일드카드 동사 접근 권한이 있는 모든 ClusterRole 나열
kubectl get clusterroles -o json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for role in data['items']:
name = role['metadata']['name']
for rule in role.get('rules', []):
verbs = rule.get('verbs', [])
resources = rule.get('resources', [])
if '*' in verbs or '*' in resources:
print(f'ClusterRole: {name}')
print(f' Verbs: {verbs}')
print(f' Resources: {resources}')
print(f' API Groups: {rule.get(\"apiGroups\", [])}')
print()
"
# 시크릿을 읽을 수 있는 역할 찾기
kubectl get clusterroles -o json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for role in data['items']:
name = role['metadata']['name']
for rule in role.get('rules', []):
resources = rule.get('resources', [])
verbs = rule.get('verbs', [])
if ('secrets' in resources or '*' in resources) and ('get' in verbs or 'list' in verbs or '*' in verbs):
if not name.startswith('system:'):
print(f'ClusterRole: {name} -> can access secrets (verbs: {verbs})')
"
# pods/exec 권한이 있는 역할 찾기 (컨테이너 탈출 위험)
kubectl get clusterroles -o json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for role in data['items']:
name = role['metadata']['name']
for rule in role.get('rules', []):
resources = rule.get('resources', [])
if 'pods/exec' in resources or 'pods/*' in resources:
print(f'ClusterRole: {name} -> has pods/exec access')
"
2단계: ClusterRoleBinding 및 RoleBinding 감사
바인딩을 검토하여 누가 상승된 권한을 가지고 있는지 식별하고 지나치게 광범위한 그룹 할당을 탐지합니다.
# 모든 ClusterRoleBinding과 해당 대상을 나열
kubectl get clusterrolebindings -o json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for binding in data['items']:
name = binding['metadata']['name']
role = binding['roleRef']['name']
subjects = binding.get('subjects', [])
for subject in subjects:
kind = subject.get('kind', '')
subj_name = subject.get('name', '')
ns = subject.get('namespace', 'cluster-wide')
print(f'{name} -> Role: {role} | {kind}: {subj_name} ({ns})')
" | sort
# cluster-admin에 바인딩된 항목 찾기
kubectl get clusterrolebindings -o json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for binding in data['items']:
if binding['roleRef']['name'] == 'cluster-admin':
print(f\"Binding: {binding['metadata']['name']}\")
for subject in binding.get('subjects', []):
print(f\" {subject.get('kind')}: {subject.get('name')} (ns: {subject.get('namespace', 'N/A')})\")
"
# 모든 인증된 사용자에게 액세스를 부여하는 바인딩 찾기
kubectl get clusterrolebindings -o json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for binding in data['items']:
for subject in binding.get('subjects', []):
if subject.get('name') in ['system:authenticated', 'system:unauthenticated']:
print(f\"WARNING: {binding['metadata']['name']} grants {binding['roleRef']['name']} to {subject['name']}\")
"
3단계: rbac-tool을 사용한 종합 분석
자동화된 RBAC 분석을 위해 rbac-tool을 사용합니다. who-can 쿼리와 정책 생성을 포함합니다.
# 모든 네임스페이스에서 시크릿을 가져올 수 있는 주체 조회
kubectl rbac-tool who-can get secrets
# 파드를 생성할 수 있는 주체 조회 (컨테이너 탈출 가능성)
kubectl rbac-tool who-can create pods
# 파드에 exec할 수 있는 주체 조회
kubectl rbac-tool who-can create pods/exec
# 권한을 상승시킬 수 있는 주체 조회 (bind/escalate 동사)
kubectl rbac-tool who-can bind clusterroles
kubectl rbac-tool who-can escalate clusterroles
# RBAC 정책 보고서 생성
kubectl rbac-tool analysis
# RBAC 관계 시각화
kubectl rbac-tool viz --outformat dot > rbac-graph.dot
dot -Tpng rbac-graph.dot -o rbac-graph.png
4단계: KubiScan을 사용한 위험한 권한 탐지
KubiScan을 사용하여 위험한 서비스 계정, 파드 및 RBAC 구성을 자동으로 식별합니다.
# KubiScan을 실행하여 위험한 역할 찾기
python3 -m kubiscan -rroles # 위험한 Roles 나열
python3 -m kubiscan -rcr # 위험한 ClusterRoles 나열
python3 -m kubiscan -rrb # 위험한 RoleBindings 나열
python3 -m kubiscan -rcrb # 위험한 ClusterRoleBindings 나열
# 위험한 서비스 계정 찾기
python3 -m kubiscan -rs # 위험한 서비스 계정
# 위험한 서비스 계정으로 실행 중인 파드 찾기
python3 -m kubiscan -rp # 위험한 파드
# 권한 상승 경로 확인
python3 -m kubiscan -pe # 권한 상승 벡터
# 전체 보고서 생성
python3 -m kubiscan -a # 모든 검사 실행
5단계: 서비스 계정 토큰 마운팅 및 사용 감사
불필요한 서비스 계정 토큰 마운트를 확인하여 손상된 파드로부터의 수평 이동을 방지합니다.
# 자동 마운트된 서비스 계정 토큰이 있는 파드 찾기
kubectl get pods --all-namespaces -o json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for pod in data['items']:
name = pod['metadata']['name']
ns = pod['metadata']['namespace']
sa = pod['spec'].get('serviceAccountName', 'default')
automount = pod['spec'].get('automountServiceAccountToken', True)
if automount and sa != 'default':
print(f'{ns}/{name} -> SA: {sa} (token auto-mounted)')
"
# 기본값이 아닌 토큰 시크릿이 있는 서비스 계정 찾기
kubectl get serviceaccounts --all-namespaces -o json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for sa in data['items']:
name = sa['metadata']['name']
ns = sa['metadata']['namespace']
secrets = sa.get('secrets', [])
if name != 'default' and len(secrets) > 0:
print(f'{ns}/{name}: {len(secrets)} secret(s) bound')
"
# 특권 모드 또는 호스트 액세스 권한으로 실행 중인 파드 확인
kubectl get pods --all-namespaces -o json | python3 -c "
import json, sys
data = json.load(sys.stdin)
for pod in data['items']:
name = pod['metadata']['name']
ns = pod['metadata']['namespace']
for container in pod['spec'].get('containers', []):
sc = container.get('securityContext', {})
if sc.get('privileged', False) or sc.get('runAsUser', 1) == 0:
print(f'RISK: {ns}/{name}/{container[\"name\"]} - privileged={sc.get(\"privileged\",False)} runAsRoot={sc.get(\"runAsUser\",\"not set\")==0}')
"
6단계: Kubeaudit을 사용한 RBAC 및 보안 정책 검증
RBAC 관련 결과를 포함한 종합적인 보안 검사를 위해 Kubeaudit을 실행합니다.
# 모든 kubeaudit 검사 실행
kubeaudit all --kubeconfig ~/.kube/config
# 특정 RBAC 관련 검사 실행
kubeaudit privesc # allowPrivilegeEscalation 확인
kubeaudit rootfs # readOnlyRootFilesystem 확인
kubeaudit nonroot # runAsNonRoot 확인
kubeaudit capabilities # 위험한 capabilities 확인
# 처리를 위해 JSON으로 출력
kubeaudit all --kubeconfig ~/.kube/config -f json > kubeaudit-results.json
핵심 개념
| 용어 | 정의 |
|---|---|
| RBAC | Kubernetes의 역할 기반 액세스 제어(Role-Based Access Control), 개별 사용자 또는 서비스 계정의 역할에 따라 클러스터 리소스에 대한 액세스를 규제하는 방법 |
| ClusterRole | 클러스터 전체 역할 정의로, 모든 네임스페이스에 적용되는 리소스에 대한 권한(동사 및 리소스)을 지정 |
| ClusterRoleBinding | ClusterRole을 클러스터 범위의 주체(사용자, 그룹, 서비스 계정)와 연결 |
| Service Account | 파드가 Kubernetes API 서버에 인증하기 위한 식별자로, 비활성화하지 않으면 자동으로 마운트됨 |
| automountServiceAccountToken | 서비스 계정 토큰을 파드 파일시스템에 자동으로 마운트할지 여부를 제어하는 파드 스펙 필드 |
| Privilege Escalation | 사용자가 자신 또는 다른 사용자에게 상승된 권한을 부여할 수 있게 하는 RBAC 동사(bind, escalate, impersonate) |
도구 및 시스템
- kubectl: Kubernetes RBAC 리소스(역할, 바인딩, 서비스 계정)를 조회하는 기본 CLI
- rbac-tool: who-can 쿼리, 시각화 및 정책 생성을 포함한 RBAC 분석용 kubectl 플러그인
- KubiScan: 위험한 권한 및 권한 상승 경로를 스캔하는 Python 도구
- Kubeaudit: RBAC 문제를 포함한 보안 안티패턴에 대해 파드 및 워크로드를 검사하는 보안 감사 도구
- rakkess: 현재 사용자의 모든 리소스 유형에 대한 액세스 매트릭스를 보여주는 kubectl 플러그인
일반적인 시나리오
시나리오: 여러 개발 팀이 공유하는 EKS 클러스터 감사
컨텍스트: 공유 EKS 클러스터가 4개의 개발 팀을 지원합니다. RBAC는 초기 설정 시 구성되었지만 12개월 동안 검토되지 않았습니다. 팀들은 다른 팀의 네임스페이스에 접근할 수 있다고 보고합니다.
접근 방식:
- 모든 ClusterRoleBinding을 나열하여 인증된 사용자에게 광범위한 액세스를 부여하는 바인딩 식별
kubectl rbac-tool who-can get secrets를 실행하여 네임스페이스 전체에서 시크릿을 읽을 수 있는 주체 찾기- ClusterRoleBinding이
edit권한을system:authenticated에 부여하여 모든 사용자에게 클러스터 전체 쓰기 액세스를 제공하는 것을 발견 - KubiScan을 실행하여 위험한 권한이 있는 서비스 계정 및 상승된 서비스 계정으로 실행되는 파드 식별
- ClusterRoleBinding을 각 팀의 네임스페이스 범위 RoleBinding으로 교체
- API 액세스가 필요하지 않은 워크로드에 대해 automountServiceAccountToken 비활성화
- 팀 간 네임스페이스 트래픽을 격리하는 NetworkPolicy 생성
주의사항: ClusterRoleBinding을 제거하면 클러스터 전체 액세스에 의존하는 CI/CD 파이프라인 및 운영자가 중단될 수 있습니다. 바인딩을 제거하기 전에 어떤 워크로드가 이를 사용하는지 항상 감사하세요. EKS는 IAM 역할을 aws-auth ConfigMap을 통해 Kubernetes 그룹에 매핑하므로 RBAC 변경은 IAM 역할 매핑과 조정되어야 합니다.
출력 형식
Kubernetes RBAC Audit Report
===============================
Cluster: production-eks (EKS 1.28)
Audit Date: 2026-02-23
Namespaces: 12
RBAC INVENTORY:
ClusterRoles: 48 (18 custom, 30 system)
ClusterRoleBindings: 32 (12 custom, 20 system)
Roles (namespaced): 24
RoleBindings (namespaced): 36
Service Accounts: 67
CRITICAL FINDINGS:
[RBAC-001] ClusterRoleBinding Grants edit to system:authenticated
Binding: authenticated-edit
Effect: ALL authenticated users have edit access across ALL namespaces
Risk: Any user can modify resources in any namespace
Remediation: Replace with namespace-scoped RoleBindings per team
[RBAC-002] Custom ClusterRole with Wildcard Permissions
ClusterRole: developer-admin
Rules: verbs=["*"], resources=["*"], apiGroups=["*"]
Bindings: 4 users via developer-admin-binding
Risk: Equivalent to cluster-admin without the name
Remediation: Scope to specific resources and verbs needed
SUMMARY:
Principals with cluster-admin: 6 (recommended: <= 3)
Roles with wildcard permissions: 4
Service accounts with secret access: 12
Pods with auto-mounted tokens: 45 / 67
Privileged containers: 8