Pod 안에서 kubectl 비슷한 작업을 하거나, 사이드카가 다른 리소스를 조회할 때 "누구 권한으로" 그 일을 하는 걸까요? 사람이 kubeconfig로 인증하듯, Pod는 ServiceAccount(SA)로 인증합니다. 문제는 아무 설정도 안 하면 모든 Pod가 default SA를 쓰고, 거기에 토큰이 자동으로 마운트된다는 점입니다. 이 토큰이 탈취되면 클러스터 권한이 통째로 새어 나갈 수 있습니다.
ServiceAccount가 하는 일
SA는 Pod의 신분증입니다. Pod가 생성되면 SA에 연결된 토큰이 컨테이너 안 고정 경로에 마운트됩니다.
# Pod 안에서 마운트된 토큰 경로
/var/run/secrets/kubernetes.io/serviceaccount/token
/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
/var/run/secrets/kubernetes.io/serviceaccount/namespace
애플리케이션이 API 서버를 호출할 때 이 토큰을 Authorization: Bearer 헤더에 실어 보내면, API 서버가 "이 SA가 누구인지"를 확인합니다. 그 다음 이 SA가 무엇을 할 수 있는지는 RBAC(Role/RoleBinding)가 결정합니다. 즉 인증(누구냐)은 SA가, 인가(뭘 할 수 있냐)는 RBAC가 담당합니다.
전용 SA + 최소 권한 만들기
default에 권한을 붙이지 말고, 워크로드마다 전용 SA를 만들어 딱 필요한 권한만 줍니다.
apiVersion: v1
kind: ServiceAccount
metadata:
name: reader-sa
namespace: app
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: app
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"] # 읽기만
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
namespace: app
name: reader-binding
subjects:
- kind: ServiceAccount
name: reader-sa
namespace: app
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
Pod에서는 spec.serviceAccountName으로 이 SA를 지정합니다.
spec:
serviceAccountName: reader-sa
automountServiceAccountToken: true # 토큰이 필요 없으면 false
API 접근이 전혀 필요 없는 워크로드라면 automountServiceAccountToken: false로 토큰 마운트 자체를 꺼서 공격 표면을 줄이는 게 좋습니다.
권한 확인
kubectl auth can-i로 SA의 실제 권한을 사람이 직접 추측하지 말고 검증합니다.
| 명령 | 확인 내용 |
|---|---|
kubectl auth can-i list pods --as=system:serviceaccount:app:reader-sa -n app | 의도한 권한이 있는가 |
kubectl auth can-i delete pods --as=system:serviceaccount:app:reader-sa -n app | 없어야 할 권한이 없는가 |
kubectl get sa reader-sa -n app -o yaml | SA 정의 확인 |
--as=system:serviceaccount:<namespace>:<sa이름> 형식이 SA를 흉내 내는 표준 방식입니다.
보안 체크리스트
# default SA에 RoleBinding이 붙어 있지 않은지
kubectl get rolebindings,clusterrolebindings -A -o wide | grep ':default'
# 과도한 권한(cluster-admin) SA 찾기
kubectl get clusterrolebindings -o json \
| jq '.items[] | select(.roleRef.name=="cluster-admin") | .subjects'
# 워크로드가 전용 SA를 쓰는지
kubectl get pods -A -o jsonpath='{range .items[*]}{.spec.serviceAccountName}{"\n"}{end}' | sort | uniq -c
default SA에 권한이 붙어 있거나 cluster-admin이 남발돼 있으면 가장 먼저 정리해야 할 대상입니다.
ServiceAccount와 RBAC를 직접 만들어 can-i로 권한을 검증하는 실습은 쿠버네티스 트랙에서 회원가입 없이 무료로 해볼 수 있습니다.