K8s RBAC 권한 관리 심화

K8s RBAC이란?

Kubernetes의 RBAC(Role-Based Access Control)은 “누가(Subject) 무엇을(Resource) 어떻게(Verb) 할 수 있는가”를 정의하는 권한 관리 시스템입니다. 클러스터 보안의 핵심이며, kube-apiserver--authorization-mode=RBAC 플래그로 활성화됩니다. 대부분의 관리형 K8s(EKS, GKE, AKS)에서 기본 활성화되어 있습니다.

4가지 핵심 리소스

RBAC는 4개의 API 오브젝트로 구성됩니다.

리소스 스코프 역할
Role 네임스페이스 특정 네임스페이스 내 권한 정의
ClusterRole 클러스터 전체 클러스터 범위 권한 정의
RoleBinding 네임스페이스 Role/ClusterRole을 주체에 바인딩
ClusterRoleBinding 클러스터 전체 ClusterRole을 클러스터 범위로 바인딩

핵심 포인트: ClusterRoleRoleBinding으로 바인딩하면, 해당 네임스페이스에서만 권한이 적용됩니다. 재사용 가능한 권한 템플릿으로 활용할 수 있습니다.

Role과 ClusterRole 작성

네임스페이스 스코프 Role

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: production
  name: pod-reader
rules:
- apiGroups: [""]           # core API group
  resources: ["pods", "pods/log"]
  verbs: ["get", "list", "watch"]
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["get"]

apiGroups: [""]는 core API(v1)를 의미합니다. Deployment는 apiGroups: ["apps"], Ingress는 apiGroups: ["networking.k8s.io"]입니다.

ClusterRole — 클러스터 전체 또는 재사용 템플릿

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: namespace-admin
rules:
- apiGroups: ["", "apps", "batch"]
  resources: ["*"]
  verbs: ["*"]
- apiGroups: ["networking.k8s.io"]
  resources: ["ingresses"]
  verbs: ["get", "list", "create", "update", "delete"]
# 노드, PV 등 클러스터 리소스는 제외
---
# 읽기 전용 ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: cluster-viewer
rules:
- apiGroups: ["", "apps", "batch", "networking.k8s.io"]
  resources: ["*"]
  verbs: ["get", "list", "watch"]
- apiGroups: [""]
  resources: ["secrets"]
  verbs: []  # 시크릿은 읽기도 차단

Binding 실전 패턴

패턴 1: 개발팀별 네임스페이스 격리

# backend 팀 → backend 네임스페이스 관리 권한
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: backend-team-admin
  namespace: backend
subjects:
- kind: Group
  name: backend-developers    # OIDC 그룹 또는 인증서 O 필드
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: namespace-admin       # ClusterRole을 네임스페이스 범위로 재사용
  apiGroup: rbac.authorization.k8s.io

패턴 2: CI/CD ServiceAccount 최소 권한

# CI/CD 파이프라인용 ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
  name: ci-deployer
  namespace: production
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: deployer
  namespace: production
rules:
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list", "update", "patch"]  # create/delete 불가
- apiGroups: [""]
  resources: ["configmaps", "secrets"]
  verbs: ["get", "list"]
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: ci-deployer-binding
  namespace: production
subjects:
- kind: ServiceAccount
  name: ci-deployer
  namespace: production
roleRef:
  kind: Role
  name: deployer
  apiGroup: rbac.authorization.k8s.io

최소 권한 원칙: CI/CD는 Deployment update/patch만 필요합니다. createdelete는 부여하지 마세요. 실수로 리소스를 삭제하는 사고를 방지합니다.

패턴 3: 특정 리소스 이름 제한

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: specific-configmap-editor
  namespace: production
rules:
- apiGroups: [""]
  resources: ["configmaps"]
  resourceNames: ["app-config", "feature-flags"]  # 이 2개만 수정 가능
  verbs: ["get", "update", "patch"]

resourceNames를 사용하면 특정 이름의 리소스만 접근할 수 있습니다. 단, listcreate verb에는 resourceNames가 적용되지 않으니 주의하세요.

ServiceAccount 토큰과 Pod 권한

Pod 내부의 애플리케이션이 K8s API를 호출할 때 ServiceAccount의 권한을 사용합니다.

# Pod에서 특정 ServiceAccount 사용
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  template:
    spec:
      serviceAccountName: my-app-sa    # 기본값: default SA
      automountServiceAccountToken: true
      containers:
      - name: app
        image: my-app:latest
---
# my-app이 같은 네임스페이스의 ConfigMap만 읽을 수 있도록
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: configmap-reader
  namespace: default
rules:
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["get", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: my-app-configmap
  namespace: default
subjects:
- kind: ServiceAccount
  name: my-app-sa
  namespace: default
roleRef:
  kind: Role
  name: configmap-reader
  apiGroup: rbac.authorization.k8s.io

보안 팁: API 접근이 불필요한 Pod는 automountServiceAccountToken: false를 설정하세요. default ServiceAccount의 토큰이 자동 마운트되면 불필요한 공격 표면이 됩니다.

RBAC 디버깅과 감사

kubectl auth can-i — 권한 확인

# 내 권한 확인
kubectl auth can-i create deployments -n production
# yes / no

# 특정 사용자의 권한 확인 (관리자)
kubectl auth can-i get secrets -n production --as=user@example.com
kubectl auth can-i list pods -n backend --as=system:serviceaccount:backend:ci-deployer

# 모든 권한 나열
kubectl auth can-i --list -n production
kubectl auth can-i --list --as=system:serviceaccount:default:my-app-sa

누가 어떤 권한을 갖고 있는지 역추적

# 특정 네임스페이스의 모든 RoleBinding 확인
kubectl get rolebindings -n production -o wide

# ClusterRoleBinding 전체 조회
kubectl get clusterrolebindings -o wide | grep -v system:

# 특정 Role에 바인딩된 주체 확인
kubectl describe rolebinding deployer-binding -n production

위험한 권한 탐지

# 와일드카드(*) 권한을 가진 ClusterRoleBinding 찾기
kubectl get clusterrolebindings -o json | 
  jq '.items[] | select(.roleRef.name == "cluster-admin") | .subjects'

# escalate, bind verb 확인 — 권한 상승 가능
kubectl get clusterroles -o json | 
  jq '.items[] | select(.rules[]?.verbs[]? == "escalate" or .rules[]?.verbs[]? == "bind") | .metadata.name'

반드시 확인해야 할 위험 권한:

  • cluster-admin — 모든 리소스에 모든 작업 가능
  • secrets에 대한 get/list — 인증 정보 탈취 가능
  • pods/exec — 컨테이너 내부 접근 가능
  • escalate, bind verb — RBAC 자체를 수정해 권한 상승

Aggregated ClusterRole

여러 ClusterRole을 하나로 합치는 패턴입니다. aggregationRule의 라벨 셀렉터로 자동 결합됩니다.

# 기본 뷰어 역할
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring-viewer
  labels:
    rbac.theokei.com/aggregate-to-viewer: "true"
rules:
- apiGroups: ["monitoring.coreos.com"]
  resources: ["prometheusrules", "servicemonitors"]
  verbs: ["get", "list", "watch"]
---
# 커스텀 리소스 뷰어
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: custom-resource-viewer
  labels:
    rbac.theokei.com/aggregate-to-viewer: "true"
rules:
- apiGroups: ["myapp.theokei.com"]
  resources: ["widgets"]
  verbs: ["get", "list", "watch"]
---
# 자동 집계 — 위 2개의 rules가 합쳐짐
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: aggregate-viewer
aggregationRule:
  clusterRoleSelectors:
  - matchLabels:
      rbac.theokei.com/aggregate-to-viewer: "true"
rules: []  # 자동 채워짐

CRD를 추가할 때마다 기존 ClusterRole을 수정하지 않고, 라벨만 붙인 새 ClusterRole을 만들면 자동으로 포함됩니다. K8s 시크릿 관리와 함께 사용하면 보안 체계가 더욱 단단해집니다.

실전 RBAC 설계 원칙

원칙 실천 방법
최소 권한 필요한 verb만, 필요한 리소스만, 필요한 네임스페이스에만
그룹 바인딩 개인이 아닌 팀/그룹 단위로 바인딩
SA 토큰 최소화 불필요한 Pod는 automountServiceAccountToken: false
와일드카드 금지 resources: [“*”], verbs: [“*”] 사용 자제
정기 감사 kubectl auth can-i –list로 주기적 점검

RBAC는 NetworkPolicy와 함께 K8s 보안의 양대 축입니다. NetworkPolicy가 네트워크 레벨 격리라면, RBAC는 API 레벨 격리입니다. 둘 다 적용해야 진정한 멀티테넌트 환경을 구축할 수 있습니다.

정리

K8s RBAC의 핵심은 “기본 거부, 명시 허용”입니다. Role/ClusterRole로 권한을 정의하고, Binding으로 주체에 연결하는 단순한 모델이지만, 최소 권한 원칙을 지키려면 세밀한 설계가 필요합니다. kubectl auth can-i로 정기적으로 감사하고, CI/CD ServiceAccount는 필요한 verb만 부여하며, 위험 권한(cluster-admin, secrets, pods/exec)은 반드시 추적하세요.

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