K8s RBAC 권한 설계 실전 가이드

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 보안의 첫 번째 방어선이다. 설정 한 줄의 실수가 클러스터 전체를 위험에 노출시킬 수 있으므로, 코드 리뷰와 자동 감사를 일상 워크플로에 반드시 포함시켜야 한다.

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