Kubernetes RBAC: Role

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를 지정하면 listcreate 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을 조합하면 수백 명의 개발자가 사용하는 대규모 클러스터도 안전하게 운영할 수 있습니다.

위로 스크롤
WordPress Appliance - Powered by TurnKey Linux