목차
- Kubernetes PodSecurity 도입이 왜 중요한가요
- Pod Security Standards(파드 보안 표준)를 먼저 이해해야 합니다
- 도입 전 환경에서 실제로 보였던 문제들
- Kubernetes PodSecurity 도입 절차: 제가 실제로 밟은 순서
- Restricted(제한) 수준을 목표로 할 때 매니페스트 예시
- 도입 전후 비교: 무엇이 달라졌나
- ⚠️ 적용하면서 많이 막혔던 문제와 해결 방법
- 1. 기존 이미지가 non-root 실행을 전제로 만들어지지 않은 경우
- 2. 운영 도구와 일반 앱을 같은 기준으로 묶은 경우
- 3. warn만 보고 끝내는 경우
- 검증은 이렇게 했습니다
- 운영 기준을 문서화해야 도입이 끝납니다
- 정리: Kubernetes 보안은 작은 강제에서 시작됩니다
- FAQ: 자주 묻는 질문
- 모든 네임스페이스를 바로 Restricted로 가야 하나요?
- PodSecurity만 있으면 보안 정책이 충분한가요?
- 가장 먼저 점검할 필드는 무엇인가요?
[Kubernetes] Kubernetes PodSecurity 도입 전후 비교: 보안 강화 사례 연구
Kubernetes PodSecurity 도입을 고민하시는 분들은 아마 한 번쯤 이런 순간이 있으셨을 겁니다. 워크로드는 잘 돌아가는데, 막상 보안 점검을 해보면 privileged(특권 모드) 컨테이너가 섞여 있거나, hostPath(호스트 경로 마운트) 같은 위험한 설정이 아무 제어 없이 배포되는 경우 말이죠. 저도 처음엔 "네임스페이스만 잘 나누면 되지 않을까?" 싶었는데, 실제로 운영 환경과 홈랩에서 계속 만져보니 결국 배포 시점의 기본 보안 가드레일이 있어야 하더라고요. 그래서 이번 글에서는 Kubernetes PodSecurity 도입 전후를 어떻게 비교했는지, 어떤 문제가 줄었는지, 그리고 적용하면서 어디서 삽질했는지 경험 기반으로 정리해보겠습니다.
특히 이 글은 Kubernetes 보안, Pod Security Standards(파드 보안 표준), 보안 정책을 실제 운영에 녹이는 관점으로 읽으시면 도움이 됩니다. 이론만 설명하는 글이 아니라, 제가 직접 해보니 어디서 걸리고 어떤 기준으로 정리해야 덜 힘든지 그 흐름을 보여드릴게요.
PodSecurity 도입 전에는 제어가 느슨하고, 도입 후에는 네임스페이스 정책과 허용 범위가 명확해진 구조를 한눈에 보여주는 개요 이미지입니다.
Kubernetes PodSecurity 도입이 왜 중요한가요
쉽게 말해 PodSecurity는 "이 파드(Pod)는 어디까지 위험한 설정을 써도 되는가"를 네임스페이스 단위로 관리하는 방식입니다. 예전에는 팀마다 매니페스트를 제각각 작성하다 보니, 어떤 워크로드는 runAsNonRoot가 잘 들어가 있고, 어떤 건 아예 빠져 있고, 또 어떤 건 디버깅한다고 privileged: true를 넣어둔 채 남아 있기도 했습니다. 이게 한두 개면 눈으로 보겠는데, 서비스가 늘어나면 사람의 기억으로 통제하는 건 불가능하더라고요.
여기서 중요한 포인트! 보안은 좋은 설정을 권장하는 것만으로는 부족합니다. 실제로는 잘못된 설정이 들어왔을 때 막아줘야 합니다. 제가 현장에서 느낀 것도 그거였습니다. 리뷰에서 빠지고, 급한 장애 대응 중엔 원칙이 밀리고, 결국 가장 약한 워크로드가 전체 클러스터 위험도를 끌어올리거든요.
Pod Security Standards(파드 보안 표준)를 먼저 이해해야 합니다
Pod Security Standards는 파드 보안 수준을 세 단계로 나눠서 생각하게 해줍니다. 저도 처음엔 이름만 보고 좀 딱딱하다고 느꼈는데, 실제로 써보니까 기준점이 분명해서 오히려 팀 합의가 빨라졌습니다.
| 수준 | 설명 | 적합한 상황 |
|---|---|---|
| Privileged | 제한이 거의 없는 수준입니다. | 특수한 시스템 워크로드, 아주 제한적인 운영 도구 |
| Baseline | 명백히 위험한 설정을 막는 기본선입니다. | 일반적인 애플리케이션 네임스페이스의 첫 단계 |
| Restricted | 가장 엄격한 수준으로, 안전한 기본값을 강하게 요구합니다. | 보안 민감 서비스, 표준화가 잘 된 앱 워크로드 |
제가 보통 권하는 방식은 이렇습니다.
- 처음부터 전부 Restricted로 밀어붙이지 않습니다.
- 기존 레거시 워크로드가 많다면 먼저 Baseline으로 현재 상태를 정리합니다.
- 새로 만드는 네임스페이스나 표준화된 앱은 Restricted를 기본으로 잡습니다.
이 접근이 중요한 이유는, 보안 정책은 강하면 좋은 게 아니라 운영에 정착해야 좋은 것이기 때문입니다. 너무 급하게 올리면 예외만 늘어나고 결국 정책이 무력화되더라고요.
도입 전 환경에서 실제로 보였던 문제들
제가 PodSecurity를 넣기 전에 먼저 한 일은 "무슨 설정이 돌아다니고 있는지"를 보는 것이었습니다. 막연히 위험할 것 같다가 아니라, 실제 매니페스트와 실행 중인 파드를 기준으로 봐야 하거든요. 그때 자주 보였던 문제는 아래와 같았습니다.
securityContext가 아예 없는 배포가 생각보다 많았습니다.allowPrivilegeEscalation설정이 빠져 있었습니다.- 루트(root) 권한 기반 이미지가 관성적으로 사용되고 있었습니다.
- 운영 중 잠깐 쓰려고 만든 디버그용 파드가 그대로 남아 있었습니다.
- 예외가 문서화되지 않아, 왜 허용했는지 아무도 설명 못 하는 설정이 있었습니다.
이런 상태에서 보안 점검이 들어오면 굉장히 피곤해집니다. "왜 이 컨테이너는 루트로 뜨나요?" 같은 질문이 나오면, 실제 이유가 있어서가 아니라 그냥 기본값이라서 그런 경우가 많거든요. 사실 제일 무서운 건 악의적인 공격보다도 무심코 허용된 기본값입니다.
Kubernetes PodSecurity 도입 절차: 제가 실제로 밟은 순서
여기서는 복잡하게 가지 않고, 운영에서 비교적 덜 아픈 순서로 설명해볼게요. 핵심은 한 번에 차단(enforce)하지 말고 먼저 관찰부터 하는 겁니다.
- 보호할 네임스페이스를 분류합니다.
- audit(감사)와 warn(경고) 중심으로 먼저 적용합니다.
- 실패하는 워크로드를 수정합니다.
- 문제가 없는 네임스페이스부터 enforce(강제)로 전환합니다.
- 예외 워크로드는 별도 네임스페이스로 분리합니다.
예를 들어 애플리케이션용 네임스페이스에 먼저 Baseline 경고와 감사 정책을 걸어볼 수 있습니다.
kubectl label namespace app-team \
pod-security.kubernetes.io/audit=baseline \
pod-security.kubernetes.io/warn=baseline --overwrite
이 상태에서는 배포가 바로 막히지는 않지만, 어떤 설정이 기준을 벗어나는지 확인하기 좋아요. 저는 처음부터 차단했다가 CI/CD 파이프라인이 우르르 깨지는 바람에 한 번 크게 삽질했었습니다. 그 뒤로는 무조건 경고와 감사부터 봅니다.
조금 정리됐다 싶으면 강제로 올립니다.
kubectl label namespace app-team \
pod-security.kubernetes.io/enforce=baseline \
pod-security.kubernetes.io/enforce-version=latest --overwrite
운영에서는 latest 대신 조직에서 검증한 기준 버전을 명시적으로 관리하는 팀도 많습니다. 저는 팀 표준이 아직 흔들릴 때는 버전 기준을 문서화해두는 쪽이 더 낫다고 봅니다. 기준이 자주 바뀌면 개발팀이 체감상 더 힘들어하거든요.
네임스페이스에 audit, warn, enforce 라벨을 붙이면서 단계적으로 정책을 강화하는 흐름을 설명하는 이미지입니다.
Restricted(제한) 수준을 목표로 할 때 매니페스트 예시
다음은 비교적 안전한 기본 형태의 예시입니다. 모든 앱이 이대로 되진 않겠지만, 팀 표준의 시작점으로 잡기 좋습니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
namespace: app-team
spec:
replicas: 2
selector:
matchLabels:
app: web-app
template:
metadata:
labels:
app: web-app
spec:
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: web
image: nginx:stable
ports:
- containerPort: 80
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
여기서 자주 보는 포인트는 아래입니다.
runAsNonRoot: true로 비루트 실행을 강제합니다.allowPrivilegeEscalation: false로 권한 상승 가능성을 줄입니다.capabilities.drop: [ALL]로 불필요한 리눅스 capability(리눅스 커널 권한 집합)를 제거합니다.seccompProfile.type: RuntimeDefault로 기본 시스템 호출 필터를 사용합니다.
도입 전후 비교: 무엇이 달라졌나
정량 수치를 억지로 붙이기보다, 운영 체감 기준으로 말씀드리는 게 더 정확할 것 같습니다. Kubernetes PodSecurity 도입 전에는 "문제 있는 매니페스트가 배포된 뒤 나중에 발견되는 구조"였다면, 도입 후에는 "기준에 맞지 않으면 배포 단계에서 바로 드러나는 구조"로 바뀌었습니다. 이 차이가 생각보다 큽니다.
| 비교 항목 | 도입 전 | 도입 후 |
|---|---|---|
| 보안 기준 | 문서나 리뷰에 의존 | 네임스페이스 정책으로 강제 |
| 위험 설정 발견 시점 | 배포 후 또는 점검 시 | 배포 직전 또는 배포 중 |
| 팀 간 일관성 | 사람마다 다름 | 정책 기준으로 통일 |
| 예외 관리 | 구두 합의가 많음 | 별도 네임스페이스와 문서로 관리 |
제가 직접 해보니 제일 편했던 건, 리뷰에서 감정 소모가 줄어든다는 점이었습니다. 예전엔 "이 설정 위험하지 않나요?" 같은 대화가 사람 대 사람의 의견 충돌처럼 번질 때가 있었는데, 정책을 두고 나면 "이건 Baseline 기준에서 걸립니다"처럼 훨씬 명확해집니다. 이거 진짜 편하더라고요.
⚠️ 적용하면서 많이 막혔던 문제와 해결 방법
이 섹션은 꼭 넣고 싶었습니다. 문서만 보면 간단해 보이는데, 실제로는 여기서 다들 한 번씩 막히거든요.
1. 기존 이미지가 non-root 실행을 전제로 만들어지지 않은 경우
Restricted 수준으로 올리면 가장 먼저 터지는 부분 중 하나입니다. 애플리케이션은 문제 없어 보여도, 이미지 내부 디렉터리 권한이나 엔트리포인트 스크립트가 루트 권한을 전제하는 경우가 있습니다. 저도 처음엔 "분명 앱은 잘 뜨는데 왜 이게 안 되지?" 싶었는데, 알고 보니 컨테이너 시작 시 쓰기 권한이 필요한 경로가 있었어요.
이럴 때는 아래 순서로 봤습니다.
- 이미지가 비루트 실행을 지원하는지 확인합니다.
- 쓰기 경로가 꼭 필요한지 줄입니다.
- 필요하면 Dockerfile 단계에서 권한을 정리합니다.
- 정말 예외가 필요한 워크로드만 별도 분리합니다.
2. 운영 도구와 일반 앱을 같은 기준으로 묶은 경우
로그 수집기, CNI 관련 컴포넌트, 노드 접근이 필요한 에이전트는 일반 앱과 요구 조건이 다를 수 있습니다. 그런데 이걸 한 네임스페이스 정책으로 퉁치면 꼭 어딘가서 충돌이 납니다. 저는 한동안 "왜 어떤 건 꼭 예외가 생기지?" 하다가, 결국 애플리케이션 네임스페이스와 인프라 네임스페이스를 분리하면서 정리가 되더라고요.
3. warn만 보고 끝내는 경우
이건 진짜 자주 봅니다. 경고는 보이는데 바빠서 미루다 보면, 3개월 뒤에도 같은 경고가 그대로 쌓여 있어요. 그래서 저는 warn → audit → enforce 전환 일정을 미리 잡아두는 편입니다. 일정이 없으면 정책은 거의 장식품이 됩니다.
경고 단계에서 어떤 항목이 걸리는지 확인하고, enforce 전환 전에 수정 포인트를 찾는 과정을 보여주는 이미지입니다.
검증은 이렇게 했습니다
보안 정책은 넣는 것보다 검증이 더 중요합니다. 정책이 존재해도 실제로 막히지 않으면 의미가 없으니까요. 제가 보통 하는 검증은 두 가지입니다. 하나는 정상 매니페스트가 잘 배포되는지, 다른 하나는 의도적으로 위험한 설정을 넣었을 때 차단되는지 확인하는 겁니다.
예를 들어 아래처럼 위험한 설정이 들어간 테스트 파드를 만들어 봅니다.
apiVersion: v1
kind: Pod
metadata:
name: privileged-test
namespace: app-team
spec:
containers:
- name: test
image: busybox:1.36
command: ["sh", "-c", "sleep 3600"]
securityContext:
privileged: true
그리고 배포를 시도합니다.
kubectl apply -f privileged-test.yaml
정상이라면 보안 정책 위반으로 거부되는 흐름이 나와야 합니다. 반대로, 팀 표준 매니페스트는 무리 없이 통과해야 하고요. 여기서 중요한 건 단순히 "막혔다"가 아니라, 왜 막혔는지 개발자도 이해할 수 있어야 한다</strong는 점입니다. 그래서 저는 사내 문서나 저장소 README에 자주 걸리는 항목을 같이 정리해두는 편입니다.
추가로 확인했던 항목은 아래와 같습니다.
- 새 네임스페이스 생성 시 기본 라벨 정책이 누락되지 않는지
- CI/CD에서 배포 실패 메시지가 충분히 읽히는지
- 예외 네임스페이스가 무분별하게 늘어나지 않는지
- 운영 문서와 실제 클러스터 라벨 상태가 일치하는지
정상 배포와 정책 위반 배포가 어떻게 구분되는지, 운영자가 어떤 식으로 결과를 확인하는지 보여주는 이미지입니다.
운영 기준을 문서화해야 도입이 끝납니다
사실 PodSecurity는 라벨 몇 개 붙인다고 끝나지 않습니다. 진짜 끝은 팀 운영 기준이 문서로 남고, 다음 사람도 같은 방식으로 적용할 수 있을 때입니다. 저도 처음엔 클러스터에만 반영해두고 안심했었는데, 시간이 지나니 "이 네임스페이스는 왜 baseline이지?" 같은 질문이 계속 나오더라고요.
그래서 저는 아래 항목을 최소 문서 세트로 남깁니다.
- 네임스페이스별 목표 수준: baseline 또는 restricted
- 예외 허용 기준: 누가 승인하고 언제 재검토하는지
- 개발팀용 체크리스트: 보안 컨텍스트 기본 예시
- 배포 실패 시 확인 순서: 어떤 필드를 먼저 볼지
혹시 이런 경험 있으신가요? 정책은 분명 있는데, 새 프로젝트가 생길 때마다 같은 설명을 또 하고 또 하게 되는 상황 말이죠. 이런 반복을 줄이려면 결국 문서화와 템플릿화가 답입니다. 다음 글에서는 OPA Gatekeeper(오픈 정책 에이전트 게이트키퍼)나 Kyverno(카이버노) 같은 정책 엔진과 PodSecurity를 어떻게 역할 분담할지 다뤄볼 예정입니다. 이전 글에서 다룬 네임스페이스 표준화 내용이 있으시다면 같이 연결해서 보시는 것도 좋습니다.
도입 전후의 차이, 운영 체크리스트, 다음 단계까지 한 장으로 요약한 인포그래픽 이미지입니다.
정리: Kubernetes 보안은 작은 강제에서 시작됩니다
Kubernetes PodSecurity 도입은 거창한 보안 프로젝트처럼 보일 수 있지만, 실제로는 "위험한 기본값을 그냥 두지 않겠다"는 아주 현실적인 출발점에 가깝습니다. 제가 여러 번 적용해보니 가장 효과적인 방식은 이렇습니다. 먼저 경고와 감사로 현재 상태를 파악하고, 그다음 Baseline으로 정리한 뒤, 준비된 워크로드부터 Restricted로 올리는 흐름이었습니다. 처음엔 이게 뭔가 싶었는데, 한 번 기준이 잡히고 나면 운영이 훨씬 편해집니다. 드디어 됐다! 싶은 순간이 오더라고요.
마지막으로 한 줄 요약하면 이겁니다. Pod Security Standards는 문서가 아니라 운영 습관으로 들어와야 합니다. 보안 정책이 실제 배포 과정에 녹아들면, 리뷰 품질도 좋아지고 예외도 줄고, 나중에 감사 대응도 덜 힘들어집니다. Kubernetes 보안을 어디서부터 손대야 할지 막막하셨다면, PodSecurity부터 차근차근 시작해보세요.
FAQ: 자주 묻는 질문
모든 네임스페이스를 바로 Restricted로 가야 하나요?
아닙니다. 레거시 워크로드가 많다면 Baseline부터 정리하는 편이 현실적입니다. 무리하게 올리면 예외만 늘어날 수 있습니다.
PodSecurity만 있으면 보안 정책이 충분한가요?
기본 보안 가드레일로는 매우 유용하지만, 조직별 세부 규칙까지 모두 대신하진 못합니다. 필요한 경우 별도 정책 엔진과 역할을 나눠가는 게 좋습니다.
가장 먼저 점검할 필드는 무엇인가요?
runAsNonRoot, allowPrivilegeEscalation, privileged, capabilities, hostPath 같은 항목부터 보는 걸 권합니다.
'IT > k8s' 카테고리의 다른 글
| [k8s] Loki로 Kubernetes 로그 중앙화: 효율적인 모니터링 구축 가이드 (1) | 2026.07.22 |
|---|---|
| [k8s] EKS 운영 비용 최적화 전략: 클라우드 지출 효율 높이기 (1) | 2026.07.21 |
| [k8s] Prometheus Operator vs 직접 설정: Kubernetes 모니터링 실전 비교 분석 (1) | 2026.07.18 |
| [Kubernetes] Karpenter 오토스케일러 1년 사용 후기: 비용 절감과 성능 최적화 회고 (0) | 2026.07.17 |
| [Kubernetes] StatefulSet vs Deployment: 상태 저장 애플리케이션 배포 비교 (0) | 2026.07.17 |
| [k8s] Backstage 운영 후기: 개발자 포털 1년 회고와 정착 과정 (0) | 2026.07.14 |