티스토리 뷰
Kubernetes 민감정보 관리 완벽 가이드: Secret, 암호화, 그리고 실전 전략
pak2251 2025. 10. 22. 13:11Kubernetes 민감정보 관리 완벽 가이드: Secret, 암호화, 그리고 실전 전략
작성일: 2025-10-22
태그: Kubernetes, Security, Secret Management, DevOps, GitOps
난이도: 중급~고급
들어가며
Kubernetes 환경에서 가장 신경 써야 할 부분 중 하나가 바로 민감정보(Sensitive Data) 관리입니다. 데이터베이스 비밀번호, API 키, OAuth 클라이언트 시크릿, JWT 토큰 등 수많은 민감정보가 클러스터 곳곳에 흩어져 있습니다.
이 글에서는 imprun.dev 플랫폼을 구축하면서 겪은 실전 경험을 바탕으로, 프로덕션 수준의 민감정보 관리 전략을 소개합니다.
우리가 관리해야 하는 민감정보
imprun.dev에서 관리하는 민감정보 목록:
민감정보 카테고리:
인증/보안:
- JWT Secret (64자 랜덤 문자열)
- GitHub OAuth (Client ID + Secret)
- Google OAuth (Client ID + Secret)
- Admin 계정 초기 비밀번호
데이터베이스:
- MongoDB Root 비밀번호
- MongoDB 연결 URI
- Redis 비밀번호
인프라:
- TLS/SSL 인증서 (cert-manager)
- Cloudflare API 토큰 (DNS Challenge)
- Container Registry 인증정보
런타임:
- 사용자 앱 전용 DB 접근 토큰
- Runtime Exporter 인증 토큰
문제: 이 모든 정보를 안전하게 저장하면서도, 운영 편의성을 유지해야 합니다.
Part 1: Kubernetes Secret 기초와 한계
Kubernetes Secret이란?
민감정보를 Base64로 인코딩하여 저장하는 Kubernetes 리소스입니다.
apiVersion: v1
kind: Secret
metadata:
name: mongodb-auth
namespace: imprun-system
type: Opaque
data:
username: cm9vdA== # base64("root")
password: bXlwYXNzd29yZA== # base64("mypassword")
Secret의 장점
✅ 환경 변수 주입 간편:
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: mongodb-auth
key: password
✅ RBAC 연동: 네임스페이스/역할별 접근 제어
✅ 자동 마운트: Pod에 파일로 자동 마운트 가능
Secret의 한계와 위험
❌ Base64는 암호화가 아님 (디코딩만으로 원본 노출)
❌ etcd에 평문 저장 (etcd 암호화 미활성화 시)
❌ Git에 커밋 위험 (실수로 values-production.yaml 커밋 시 유출)
❌ Secret Sprawl: Secret이 여러 곳에 분산되면 관리 복잡도 폭발
Part 2: imprun.dev의 Secret 관리 전략
전략 1: 중앙 집중식 Secret 생성
문제: 5개의 서브차트가 각각 Secret을 생성하면 관리 불가능
해결: Umbrella Chart의 templates/secrets.yaml에서 모든 Secret을 생성
k8s/imprun/templates/secrets.yaml:
{{- if .Values.secrets.jwt.secret }}
---
apiVersion: v1
kind: Secret
metadata:
name: imprun-jwt-secret
namespace: {{ .Release.Namespace }}
annotations:
helm.sh/resource-policy: keep # ⭐ 핵심: Helm 업그레이드 시 유지
type: Opaque
data:
JWT_SECRET: {{ .Values.secrets.jwt.secret | b64enc }}
{{- end }}
{{- if .Values.secrets.github.clientSecret }}
---
apiVersion: v1
kind: Secret
metadata:
name: imprun-github-oauth
namespace: {{ .Release.Namespace }}
annotations:
helm.sh/resource-policy: keep
type: Opaque
data:
GITHUB_CLIENT_ID: {{ .Values.secrets.github.clientId | b64enc }}
GITHUB_CLIENT_SECRET: {{ .Values.secrets.github.clientSecret | b64enc }}
{{- end }}
핵심 포인트:
helm.sh/resource-policy: keep: 업그레이드 시 Secret 재생성 방지- 조건부 생성: 값이 제공된 경우만 생성 (
{{- if }}) - 단일 진실 공급원: 모든 Secret이 한 파일에 정의됨
전략 2: values-production.yaml 분리
절대 Git에 커밋하지 않는 파일: values-production.yaml
# k8s/imprun/values-production.yaml (⚠️ .gitignore 필수)
secrets:
jwt:
secret: "abcd1234...64자 랜덤 문자열" # openssl rand -base64 48
github:
clientId: "Ov23liOCvWeKTz6TO6Pn"
clientSecret: "ghp_xxxxxxxxxxxxxxxxxxxx"
google:
clientId: "123456789-xxxxxxx.apps.googleusercontent.com"
clientSecret: "GOCSPX-xxxxxxxxxxxxxxxx"
adminSeed:
username: "admin@imprun.dev"
password: "초기_관리자_비밀번호"
mongodb:
auth:
rootPassword: "몽고_루트_비밀번호"
redis:
auth:
password: "레디스_비밀번호"
보안 원칙:
# .gitignore
values-production.yaml
values-*.secret.yaml
*.pem
*.key
전략 3: 자동 생성 vs 명시적 제공
선택지 제공:
| 방법 | 장점 | 단점 | 사용 시기 |
|---|---|---|---|
| 자동 생성 | 편리함, 충돌 없음 | 값 추적 어려움 | 개발 환경 |
| 명시적 제공 | 예측 가능, 백업 용이 | 초기 설정 번거로움 | 프로덕션 |
Helm Template 예시 (자동 생성):
{{- if not .Values.secrets.jwt.secret }}
---
apiVersion: v1
kind: Secret
metadata:
name: imprun-jwt-secret
annotations:
helm.sh/resource-policy: keep
data:
JWT_SECRET: {{ randAlphaNum 64 | b64enc }} # 64자 랜덤 생성
{{- end }}
주의: 자동 생성 시 helm.sh/resource-policy: keep 필수 (재설치 시 값 변경 방지)
Part 3: KubeBlocks와 자동 생성 Secret
문제: 외부 Operator가 생성하는 Secret
MongoDB를 KubeBlocks로 관리하면, KubeBlocks Operator가 Secret을 자동 생성합니다.
# k8s/imprun/templates/mongodb-cluster.yaml
apiVersion: apps.kubeblocks.io/v1alpha1
kind: Cluster
metadata:
name: mongodb
spec:
clusterDefinitionRef: mongodb
terminationPolicy: DoNotTerminate
componentSpecs:
- name: mongodb
componentDefRef: mongodb
KubeBlocks가 생성하는 Secret:
$ kubectl get secret mongodb-conn-credential -n imprun-system
NAME TYPE DATA AGE
mongodb-conn-credential Opaque 4 2m
포함 내용:
username: root
password: values에서 지정한 rootPassword
endpoint: mongodb-mongodb:27017
port: "27017"
해결: Secret 이름 표준화
문제: 우리가 mongodb-auth를 생성했지만, KubeBlocks는 mongodb-conn-credential 생성
해결: KubeBlocks의 표준 이름을 사용하도록 모든 참조 변경
# imprun-server Deployment
env:
- name: MONGODB_ROOT_USER
valueFrom:
secretKeyRef:
name: mongodb-conn-credential # ✅ KubeBlocks 표준 이름
key: username
- name: MONGODB_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mongodb-conn-credential
key: password
교훈: 외부 Operator 사용 시 해당 Operator의 Secret 네이밍 규칙 따르기
Part 4: Secret 라이프사이클 관리
helm install: 초기 생성
# 1차: Secret이 존재하지 않으므로 생성
helm install imprun ./k8s/imprun -n imprun-system \
-f values-production.yaml
helm upgrade: 기존 유지
annotations:
helm.sh/resource-policy: keep # ⭐ 이 annotation 덕분에 유지됨
# 2차: Secret은 그대로 유지, Deployment만 업데이트
helm upgrade imprun ./k8s/imprun -n imprun-system \
-f values-production.yaml
장점:
- 비밀번호가 변경되지 않음 (서비스 안정성)
- 롤링 업데이트 중에도 인증 문제 없음
helm uninstall: 삭제하지 않음
helm uninstall imprun -n imprun-system
# ✅ Secret은 여전히 존재
kubectl get secrets -n imprun-system
# NAME TYPE DATA AGE
# imprun-jwt-secret Opaque 1 10d
# imprun-github-oauth Opaque 2 10d
재설치 시:
# Secret이 이미 존재하므로 재생성하지 않음 ({{- if }} 조건)
helm install imprun ./k8s/imprun -n imprun-system
Secret 완전 삭제 (신중히!)
# ⚠️ 프로덕션에서는 절대 실행 금지
kubectl delete secret imprun-jwt-secret -n imprun-system
# 재설치 시 새 값으로 생성됨
helm upgrade imprun ./k8s/imprun -n imprun-system
Part 5: 민감정보 노출 방지 전략
1. .gitignore 철저히 관리
# k8s/imprun/.gitignore
values-production.yaml
values-*.secret.yaml
*.pem
*.key
*.crt
secrets/
# k8s/victoriametrics/.gitignore
values-production.yaml
검증:
# Git에 커밋되지 않았는지 확인
git status --ignored | grep values-production
2. Pre-commit Hook으로 자동 검증
# .git/hooks/pre-commit
#!/bin/bash
# 민감정보 패턴 검색
if git diff --cached | grep -E "(password|secret|token|apiKey).*[:=].*['\"]" ; then
echo "❌ 민감정보가 커밋에 포함되어 있습니다!"
exit 1
fi
# values-production.yaml 커밋 차단
if git diff --cached --name-only | grep -E "values-production\.yaml" ; then
echo "❌ values-production.yaml은 커밋할 수 없습니다!"
exit 1
fi
3. Secret 백업 전략
안전한 백업:
# 1. Secret을 YAML로 추출
kubectl get secrets -n imprun-system -o yaml > secrets-backup.yaml
# 2. GPG로 암호화
gpg --encrypt --recipient admin@imprun.dev secrets-backup.yaml
# 3. 암호화된 파일만 백업 스토리지에 저장
aws s3 cp secrets-backup.yaml.gpg s3://imprun-backups/secrets/
# 4. 원본 삭제
rm secrets-backup.yaml
복구:
# 1. 다운로드 및 복호화
aws s3 cp s3://imprun-backups/secrets/secrets-backup.yaml.gpg .
gpg --decrypt secrets-backup.yaml.gpg > secrets-backup.yaml
# 2. 복원
kubectl apply -f secrets-backup.yaml
# 3. 복호화된 파일 삭제
rm secrets-backup.yaml*
4. RBAC으로 접근 제한
# k8s/imprun/templates/rbac.yaml
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: secret-reader
namespace: imprun-system
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames:
- imprun-jwt-secret
- imprun-github-oauth
verbs: ["get", "list"] # create, update, delete 제외
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: imprun-server-secret-access
namespace: imprun-system
subjects:
- kind: ServiceAccount
name: imprun-server
namespace: imprun-system
roleRef:
kind: Role
name: secret-reader
apiGroup: rbac.authorization.k8s.io
원칙:
- Pod는 필요한 Secret만 접근 가능
get,list만 허용 (update,delete금지)- ServiceAccount별로 세밀한 권한 분리
Part 6: 고급 전략 - External Secret Operator
현재의 한계
현재 방식 (values-production.yaml):
- ❌ 여전히 평문 파일로 존재
- ❌ 개발자 로컬에 민감정보 저장
- ❌ Secret Rotation 수동 작업
해결: External Secrets Operator
AWS Secrets Manager / HashiCorp Vault 연동:
# k8s/imprun/templates/external-secret.yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: imprun-secrets
namespace: imprun-system
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secretsmanager
kind: SecretStore
target:
name: imprun-jwt-secret
creationPolicy: Owner
data:
- secretKey: JWT_SECRET
remoteRef:
key: imprun/production/jwt-secret
장점:
- ✅ 중앙 집중식 Secret 관리 (AWS Secrets Manager)
- ✅ 자동 Rotation 지원
- ✅ 감사 로그 (누가 언제 접근했는지 추적)
- ✅ 다중 클러스터 Secret 동기화
단점:
- 추가 복잡도
- 외부 서비스 의존성
- 비용 발생 (AWS Secrets Manager: $0.40/secret/month)
Part 7: 실전 체크리스트
배포 전 검증
# ✅ 1. .gitignore 확인
git check-ignore -v k8s/imprun/values-production.yaml
# .gitignore:10:values-production.yaml k8s/imprun/values-production.yaml
# ✅ 2. Secret annotation 확인
kubectl get secret imprun-jwt-secret -n imprun-system -o yaml | grep "resource-policy"
# helm.sh/resource-policy: keep
# ✅ 3. Secret 값 검증 (Base64 디코딩)
kubectl get secret imprun-jwt-secret -n imprun-system \
-o jsonpath='{.data.JWT_SECRET}' | base64 -d
# (64자 랜덤 문자열 출력 확인)
# ✅ 4. RBAC 권한 확인
kubectl auth can-i get secrets --as=system:serviceaccount:imprun-system:imprun-server -n imprun-system
# yes
kubectl auth can-i delete secrets --as=system:serviceaccount:imprun-system:imprun-server -n imprun-system
# no
보안 감사
# 1. 모든 Secret 목록
kubectl get secrets --all-namespaces
# 2. 특정 Secret 사용처 추적
kubectl get pods -n imprun-system -o json | \
jq -r '.items[] | select(.spec.containers[].env[]?.valueFrom.secretKeyRef.name == "imprun-jwt-secret") | .metadata.name'
# 3. Secret 생성 일자 확인 (오래된 Secret은 Rotation 고려)
kubectl get secrets -n imprun-system -o json | \
jq -r '.items[] | [.metadata.name, .metadata.creationTimestamp] | @tsv'
Part 8: MongoDB Secret 특수 사례 - terminationPolicy
문제: MongoDB 데이터 보존
# KubeBlocks Cluster
spec:
terminationPolicy: DoNotTerminate # ⭐ 핵심 설정
시나리오:
# 1. Helm Chart 삭제
helm uninstall imprun -n imprun-system
# 2. MongoDB Cluster는?
kubectl get cluster -n imprun-system
# NAME STATUS AGE
# mongodb Running 10d ← 여전히 존재!
# 3. Secret도 유지됨
kubectl get secret mongodb-conn-credential -n imprun-system
# NAME TYPE DATA AGE
# mongodb-conn-credential Opaque 4 10d
장점:
- ✅ 실수로 helm uninstall 해도 데이터 보존
- ✅ Secret도 함께 유지되어 재설치 시 즉시 연결 가능
완전 삭제 방법:
# 방법 1: Cluster만 삭제 (권장)
kubectl delete cluster mongodb -n imprun-system
# 방법 2: 네임스페이스 전체 삭제
kubectl delete namespace imprun-system
결론: 민감정보 관리의 핵심 원칙
1. 분리 (Separation)
- 코드와 민감정보 분리 (values.yaml vs values-production.yaml)
- 환경별 Secret 분리 (dev/staging/production)
2. 최소 권한 (Least Privilege)
- RBAC으로 Pod별 필요한 Secret만 접근
get,list만 허용 (update,delete금지)
3. 암호화 (Encryption)
- etcd 암호화 활성화 (--encryption-provider-config)
- 백업 시 GPG 암호화
- 전송 중 TLS 필수
4. 감사 (Auditing)
- Secret 접근 로그 기록
- 정기적인 Secret Rotation
- 사용하지 않는 Secret 정리
5. 자동화 (Automation)
- Helm으로 Secret 생성 자동화
helm.sh/resource-policy: keep로 안전한 업그레이드- External Secrets Operator로 고급 관리
다음 단계
단계별 개선 로드맵
Level 1 (현재): Helm + values-production.yaml
- ✅ 구현 간단
- ✅ 대부분의 스타트업/중소기업에 적합
Level 2 (6개월 내): External Secrets Operator
- 중앙 집중식 관리
- 자동 Rotation
Level 3 (1년 내): Vault + Dynamic Secrets
- 동적 Secret 생성 (DB 계정 자동 생성/삭제)
- 단기 유효 토큰
- 완벽한 감사 로그
참고 자료
공식 문서
imprun.dev 관련 문서
- k8s/imprun/SECRETS.md - Secret 관리 상세 가이드
- k8s/FAQ.md - MongoDB Secret 관련 FAQ
- Helm Chart Best Practices
추가 학습
글쓴이: imprun.dev Team
피드백: GitHub Issues 또는 이메일로 문의해주세요
변경 이력
- 2025-10-22: 초안 작성
- Helm 기반 Secret 관리 전략
- KubeBlocks Secret 처리 방법
- terminationPolicy: DoNotTerminate 패턴
- 보안 체크리스트 및 RBAC 설정
'실제 경험과 인사이트를 AI와 함께 정리한 글' 카테고리의 다른 글
| Kubernetes 기반 데이터베이스 Operator - MongoDB Replica Set 관리 (1) | 2025.10.22 |
|---|---|
| Kubernetes Namespace 삭제 전쟁: 18시간의 Terminating과의 싸움 (0) | 2025.10.22 |
| Next.js SSR 환경에서 API URL 환경변수 관리 전략 (0) | 2025.10.22 |
| CI/CD 파이프라인 구축: GitHub Actions로 완전 자동화하기 (0) | 2025.10.22 |
| Kubernetes 리소스 최적화: ARM64 환경에서 효율적으로 운영하기 (0) | 2025.10.22 |
- Total
- Today
- Yesterday
- AI
- SHACL
- react
- knowledge graph
- Tax Analysis
- api gateway
- Kubernetes
- AI Development
- LangChain
- LLM
- 개발 도구
- security
- Rag
- troubleshooting
- Developer Tools
- claude code
- Go
- frontend
- AI agent
- authorization
- ai 개발 도구
- Ontology
- architecture
- authentication
- Next.js
- workflow
- Tailwind CSS
- backend
- Claude
- PYTHON
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
