쿠버네티스 마스터 노트 시리즈 8편. RBAC 4 객체(Role/ClusterRole/RoleBinding/ClusterRoleBinding)의 권한 모델, ServiceAccount가 Pod 인증 주체가 되는 메커니즘, NetworkPolicy로 Pod 사이 방화벽, SecurityContext로 컨테이너 권한 제어(runAsNonRoot·readOnlyRootFilesystem), Pod Security Standards 3 단계, 이미지 스캔까지.
이 글은 쿠버네티스 마스터 노트 시리즈의 여덟 번째 편입니다. 1~7편이 기능이었다면, 이번엔 운영 환경의 보안 — RBAC·NetworkPolicy·SecurityContext.
K8s는 강력한 도구라 권한 관리 안 하면 사고 폭발. RBAC으로 누구·뭘·어디서, NetworkPolicy로 Pod 방화벽, SecurityContext로 컨테이너 권한.
처음 K8s Security가 어렵게 느껴지는 이유
처음 이 단원이 어렵게 느껴지는 이유는 두 가지예요. 첫째, RBAC 4 객체가 한 번에 등장합니다. Role·ClusterRole·RoleBinding·ClusterRoleBinding. 둘째, NetworkPolicy 문법이 직관적이지 않습니다.
해결법은 한 가지예요. "Role = 권한 묶음 / Binding = 사용자에게 부여" 한 줄. ClusterRole·ClusterRoleBinding은 클러스터 전체 버전. 둘 곱하기로 4개. 이 그림이 잡히면 끝.
RBAC — Role-Based Access Control
K8s 보안의 토대. 4 객체로 권한 부여.
[Role / ClusterRole] = 권한 묶음
↑
[RoleBinding / ClusterRoleBinding]
↓
[Subject = User / Group / ServiceAccount]
Role vs ClusterRole
| 종류 | 범위 |
|---|---|
| Role | 특정 namespace |
| ClusterRole | 클러스터 전체 (또는 namespace 자원에도) |
Role 예시
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: dev
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
dev namespace 안 Pod을 읽기만 가능.
RoleBinding — 사용자에게 부여
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: alice-pod-reader
namespace: dev
subjects:
- kind: User
name: alice
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
alice에게 dev namespace의 pod-reader Role 부여.
ClusterRoleBinding — 전역
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: alice-cluster-admin
subjects:
- kind: User
name: alice
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
alice에게 클러스터 전체 admin.
Verbs — 작업 종류
get, list, watch # 읽기
create, update, patch # 쓰기
delete, deletecollection # 삭제
apiGroups·resources
rules:
- apiGroups: [""] # core API (Pod·Service 등)
resources: ["pods"]
- apiGroups: ["apps"] # apps API (Deployment·StatefulSet)
resources: ["deployments"]
- apiGroups: [""]
resources: ["pods/log"] # 하위 리소스
verbs: ["get"]
여기서 정말 중요한 시험 함정 — apiGroups: [""] = core API (Pod·Service·Node·Namespace 등 K8s 1.0부터). 그 외엔 그룹 명시 (apps·batch·networking.k8s.io 등).
ServiceAccount — Pod의 인증 주체
각 Pod은 ServiceAccount(SA)로 API Server 인증.
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-sa
namespace: dev
spec:
serviceAccountName: my-sa
containers:
- name: app
image: my-app
기본은 default SA (namespace당 자동 생성). 권한 거의 없음.
여기서 시험 함정이 하나 있어요. default SA는 너무 약함. 운영 권장 = 각 앱별 전용 SA + 최소 권한 RoleBinding.
SA 토큰 자동 마운트
/var/run/secrets/kubernetes.io/serviceaccount/
├── token # JWT 토큰
├── ca.crt
└── namespace
K8s API 호출 시 이 토큰으로 인증. 클러스터 안 자동.
토큰 자동 마운트 비활성
spec:
serviceAccountName: my-sa
automountServiceAccountToken: false # API 안 호출하는 Pod
실전 RBAC 패턴
개발자 — namespace 안 모든 권한
kind: ClusterRole
metadata:
name: developer
rules:
- apiGroups: ["", "apps", "batch", "networking.k8s.io"]
resources: ["*"]
verbs: ["*"]
kind: RoleBinding
metadata:
name: dev-team
namespace: dev
subjects:
- kind: Group
name: developers
roleRef:
kind: ClusterRole
name: developer
CI/CD — 특정 Deployment만 업데이트
kind: Role
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
resourceNames: ["my-app"]
verbs: ["get", "patch", "update"]
특정 리소스만 (resourceNames).
NetworkPolicy — Pod 방화벽
기본은 모든 Pod끼리 통신 가능. NetworkPolicy로 제한.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db-policy
namespace: prod
spec:
podSelector:
matchLabels:
app: db
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: api
ports:
- protocol: TCP
port: 5432
egress:
- to:
- namespaceSelector:
matchLabels:
name: kube-system
ports:
- protocol: UDP
port: 53
해석:
- 대상 —
app: dbPod - Ingress —
app: apiPod에서 5432만 - Egress — kube-system DNS 53만
여기서 정말 중요한 시험 함정 — NetworkPolicy 적용 시 명시 안 된 트래픽 모두 차단. 처음 적용 시 정상 통신 끊김 위험. 점진적 적용·테스트 필수.
Default Deny
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector: {} # 모든 Pod
policyTypes:
- Ingress
- Egress
namespace의 모든 트래픽 차단 (allow 명시 안 한 것). 보안 강한 환경.
여기서 시험 함정이 하나 있어요. NetworkPolicy는 CNI 지원 필요. Calico·Cilium 지원, Flannel 기본 X. 클러스터 생성 시 결정.
SecurityContext — 컨테이너 권한
Pod 레벨
spec:
securityContext:
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
runAsNonRoot: true
Container 레벨
containers:
- name: app
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: [ALL]
add: [NET_BIND_SERVICE]
privileged: false
핵심 옵션
| 옵션 | 권장 |
|---|---|
| runAsNonRoot: true | Root 실행 X |
| runAsUser | 명시적 사용자 ID |
| readOnlyRootFilesystem: true | 루트 파일시스템 읽기 전용 |
| allowPrivilegeEscalation: false | sudo 같은 권한 상승 X |
| capabilities.drop: [ALL] | 모든 Linux capability 제거 |
| privileged: false | 호스트 권한 X |
여기서 정말 중요한 시험 함정 — 운영 환경 SecurityContext 표준:
securityContext:
runAsNonRoot: true
runAsUser: 1000
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: [ALL]
이미지가 root로 실행되면 위 설정 충돌 → 컨테이너 실패. 이미지 측에서 non-root 사용자 설정 필요.
Pod Security Standards — PSS (1.25+)
이전 PodSecurityPolicy 대체. 3 레벨:
| 레벨 | 의미 |
|---|---|
| Privileged | 제한 없음 |
| Baseline | 알려진 위협 차단 |
| Restricted | 강력한 제한 (운영 권장) |
namespace 라벨로 적용:
apiVersion: v1
kind: Namespace
metadata:
name: prod
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: latest
enforce (강제) / audit (로그) / warn (경고).
이미지 보안
이미지 출처 검증
spec:
containers:
- name: app
image: my-registry.com/my-app:v1.2.3 # 명시적 태그
# latest 절대 X
imagePullPolicy: IfNotPresent
Admission Controller로 강제
- ImagePolicyWebhook
- OPA Gatekeeper
- Kyverno
특정 레지스트리만 허용·서명 검증·취약점 스캔 통과한 것만.
이미지 스캔
도구:
- Trivy — 오픈소스
- Snyk
- Aqua Security
- Clair
CI 파이프라인에서 이미지 빌드 후 스캔.
Secret 보안 (5편 보충)
# Secret을 etcd에 평문 저장 X
EncryptionConfiguration 필수 (5편)
# 운영 = External Secrets + Vault
Audit Log
# /etc/kubernetes/audit-policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata
resources:
- group: ""
resources: ["secrets"]
API Server에서 audit log 활성화. 모든 작업 추적.
보안 체크리스트
✓ RBAC 활성·최소 권한
✓ 각 앱별 ServiceAccount
✓ default SA 사용 X
✓ NetworkPolicy default deny + 명시 allow
✓ SecurityContext — runAsNonRoot·readOnlyRoot·dropAll
✓ Pod Security Standards (restricted)
✓ 이미지 명시 태그 (latest X)
✓ 이미지 스캔 (Trivy 등)
✓ Secret 외부 관리 (Vault·Sealed)
✓ etcd 암호화
✓ Audit Log
✓ 노드 OS 업데이트
시험 직전 한 번 더 — 자주 헷갈리는 함정 모음
여기까지가 8편의 핵심입니다. 시험 직전 또는 실무에서 헷갈릴 때 다시 펼쳐 볼 수 있게 압축 노트로 마무리할게요.
- RBAC = K8s 보안 토대
- 4 객체 — Role / ClusterRole / RoleBinding / ClusterRoleBinding
- Role = namespace / ClusterRole = 클러스터
- Binding = Subject(User·Group·SA)에게 Role 부여
- Verbs — get·list·watch (읽기) / create·update·patch (쓰기) / delete
apiGroups: [""]= core API- ServiceAccount = Pod의 인증 주체
- 토큰 자동 마운트 (
/var/run/secrets/.../token) - default SA = 너무 약함, 운영 = 앱별 전용 SA
- NetworkPolicy = Pod 방화벽
- 기본은 모두 통신 → NetworkPolicy로 제한
- CNI 지원 필요 (Calico·Cilium / Flannel X)
- Default Deny + 명시 Allow 패턴
- SecurityContext — Pod / Container 레벨
- 운영 표준 — runAsNonRoot·readOnlyRootFilesystem·dropAll·allowPrivilegeEscalation false
- 이미지가 root면 충돌
- Pod Security Standards (1.25+) — 3 레벨 (Privileged·Baseline·Restricted)
- namespace 라벨로 적용
- 이미지 — 명시 태그 (latest X) / 이미지 스캔 (Trivy)
- Admission Controller — OPA Gatekeeper·Kyverno
- etcd 암호화 + Audit Log
- 보안 체크리스트 12개 항목
시리즈 다른 편
- 1편 — 아키텍처·Control Plane
- 2편 — Pod
- 3편 — Workloads
- 4편 — Services·Networking
- 5편 — ConfigMap·Secret
- 6편 — Storage
- 7편 — Scaling·Scheduling
- 8편 — Security (현재 글)
- 9편 — Helm
- 10편 — 실전 (매니지드·GitOps·Observability)
공식 문서: Kubernetes RBAC / NetworkPolicy / Pod Security Standards 에서 더 깊이.
다음 글(9편)에서는 Helm — 패키지 매니저, Chart 구조, 템플릿, 운영 배포 패턴까지 풀어 갑니다.