2026. 6. 8. 09:58ㆍKubernetes
Kubernetes Secret은 민감 데이터를 저장하는 기본 메커니즘이지만, 기본 설정만으로는 안전하지 않습니다. Base64 인코딩은 암호화가 아니며, etcd에 평문으로 저장될 수 있습니다. 운영 환경에서는 암호화, 접근 제어, 외부 Secret 관리 도구를 조합하여 보안을 강화해야 합니다.
핵심 요약
- Kubernetes Secret은 Base64 인코딩만 적용됩니다. 이는 난독화일 뿐 암호화가 아닙니다.
- 기본 설정에서 Secret은 etcd에 평문으로 저장됩니다. EncryptionConfiguration을 통해 저장 시 암호화(Encryption at Rest)를 활성화해야 합니다.
- RBAC로 Secret 접근을 최소화하고, 감사 로그로 접근 이력을 추적합니다.
- 운영 환경에서는 외부 Secret 관리 도구(External Secrets Operator, Sealed Secrets, HashiCorp Vault)를 사용하여 Git 저장소에 Secret이 노출되지 않도록 합니다.
- 매니지드 Kubernetes(EKS, AKS, GKE)는 기본적으로 etcd 암호화를 제공하지만, 클러스터 내부 접근 제어는 별도로 설계해야 합니다.
1. Kubernetes Secret의 보안 한계
팀에서 데이터베이스 비밀번호를 Kubernetes Secret으로 관리합니다. YAML 파일을 Git에 커밋하고, 새 팀원이 합류할 때마다 kubectl로 Secret을 확인합니다. 어느 날 보안 감사에서 이런 질문을 받습니다: "이 Secret은 누가 언제 조회했는지 추적할 수 있나요? etcd에 암호화되어 저장되나요?"
대부분의 경우 대답은 "아니오"입니다. Kubernetes Secret의 기본 보안 수준이 낮은 이유를 먼저 이해해야 합니다.
1.1 Base64 ≠ 암호화
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
namespace: production
type: Opaque
data:
username: YWRtaW4= # echo -n "admin" | base64
password: cEBzc3cwcmQ= # echo -n "p@ssw0rd" | base64
base64 --decode로 누구나 원본 값을 복원할 수 있습니다. Git에 이 YAML이 커밋되면 사실상 평문과 동일합니다.
1.2 etcd 평문 저장
기본 설정에서 Kubernetes API Server는 Secret을 etcd에 그대로 저장합니다. etcd 백업 파일에 접근하거나, etcd를 직접 조회할 수 있는 사람은 모든 Secret을 평문으로 읽을 수 있습니다.
1.3 과도한 접근 권한
view ClusterRole을 가진 사용자는 Secret을 읽을 수 없지만, edit이나 admin ClusterRole은 Secret 읽기/쓰기가 가능합니다. 네임스페이스에 edit 권한을 부여하면 의도치 않게 Secret 접근이 허용됩니다.
| 위험 | 원인 | 영향 |
|---|---|---|
| Git에 Secret YAML 노출 | Base64가 암호화라고 착각 | 리포지토리 접근자 전원이 Secret 열람 가능 |
| etcd 평문 저장 | EncryptionConfiguration 미설정 | etcd 백업/접근으로 전체 Secret 유출 |
| 과도한 RBAC 권한 | 네임스페이스 단위 edit/admin 부여 | 불필요한 서비스까지 Secret 조회 가능 |
| 감사 추적 불가 | Audit Log 미설정 | 누가 언제 Secret을 읽었는지 확인 불가 |
| Pod에서 환경 변수 노출 | env로 Secret 주입 | kubectl describe pod로 값 노출 |
2. 보안 강화 전략 개요
Secret 보안은 단일 도구로 해결되지 않습니다. 여러 계층을 조합하여 방어 깊이(Defense in Depth)를 확보해야 합니다.
| 계층 | 목적 | 도구/설정 |
|---|---|---|
| 저장 시 암호화 | etcd에 암호화된 상태로 저장 | EncryptionConfiguration, 클라우드 KMS |
| 접근 제어 | Secret 읽기/쓰기 권한 최소화 | RBAC, 전용 ServiceAccount |
| 전송 시 암호화 | API Server ↔ etcd 통신 암호화 | TLS (기본 활성화) |
| 외부 Secret 관리 | Git에 Secret을 저장하지 않음 | External Secrets Operator, Sealed Secrets, Vault |
| 감사 | 접근 이력 추적 | Kubernetes Audit Log |
| 런타임 보호 | Pod 내부에서 Secret 노출 최소화 | Volume mount 방식, automountServiceAccountToken: false |
3. etcd 저장 시 암호화 (Encryption at Rest)
3.1 EncryptionConfiguration 설정
자체 관리 클러스터(kubeadm 등)에서는 API Server에 --encryption-provider-config 플래그로 암호화를 활성화합니다.
# /etc/kubernetes/encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <BASE64_ENCODED_32_BYTE_KEY>
- identity: {} # 암호화되지 않은 기존 Secret 읽기용 fallback
# 32바이트 키 생성
head -c 32 /dev/urandom | base64
# API Server 설정에 추가
# /etc/kubernetes/manifests/kube-apiserver.yaml
# --encryption-provider-config=/etc/kubernetes/encryption-config.yaml
암호화 Provider 선택:
| Provider | 특징 | 권장 용도 |
|---|---|---|
aescbc |
AES-CBC 256비트 암호화. 키를 로컬에 저장 | 소규모 클러스터, 테스트 환경 |
aesgcm |
AES-GCM 인증 암호화. 키 로테이션 시 주의 필요 | 성능이 중요한 경우 |
kms v2 |
외부 KMS와 연동 (봉투 암호화) | 운영 환경 권장 |
secretbox |
NaCl SecretBox 사용. 인증+암호화 동시 제공 | 단순한 환경에서 안전한 선택 |
identity provider는 암호화를 적용하지 않습니다. providers 목록에서 첫 번째 항목이 쓰기에 사용됩니다. identity가 첫 번째이면 암호화가 적용되지 않으므로, 반드시 암호화 provider를 먼저 배치해야 합니다.3.2 기존 Secret 재암호화
EncryptionConfiguration을 적용해도 기존 Secret은 자동으로 재암호화되지 않습니다. 명시적으로 업데이트해야 합니다.
# 모든 네임스페이스의 Secret을 재암호화
kubectl get secrets --all-namespaces -o json | \
kubectl replace -f -
3.3 매니지드 Kubernetes에서의 etcd 암호화
| 플랫폼 | 기본 암호화 | 추가 옵션 |
|---|---|---|
| EKS | 기본 비활성화, 콘솔에서 활성화 가능 | AWS KMS 키를 지정하여 봉투 암호화 |
| AKS | 기본 활성화 (Azure 관리 키) | Customer-Managed Key (CMK) 지정 가능 |
| GKE | 기본 활성화 (Google 관리 키) | CMEK(Customer-Managed Encryption Key) 지정 가능 |
EKS에서 Secret 암호화를 활성화하는 경우:
# EKS 클러스터에 KMS 기반 Secret 암호화 활성화
aws eks associate-encryption-config \
--cluster-name my-cluster \
--encryption-config '[{"resources":["secrets"],"provider":{"keyArn":"arn:aws:kms:ap-northeast-2:123456789012:key/key-id"}}]'
4. RBAC로 Secret 접근 제한
4.1 Secret 전용 Role 설계
네임스페이스 내에서도 Secret 접근은 별도 Role로 분리해야 합니다. 개발자에게 edit ClusterRole 대신 Secret을 제외한 커스텀 Role을 부여하는 패턴이 안전합니다.
# Secret 읽기를 제외한 개발자 Role
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: production
name: developer-no-secrets
rules:
- apiGroups: [""]
resources: ["pods", "services", "configmaps"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["apps"]
resources: ["deployments", "replicasets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# Secret에 대한 규칙이 없으므로 접근 불가
# Secret 읽기만 허용하는 별도 Role (필요한 사람에게만 부여)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: production
name: secret-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list"]
resourceNames: ["db-credentials", "api-key"] # 특정 Secret만 허용
4.2 감사 로그 설정
Secret 접근 이력을 추적하려면 Kubernetes Audit Policy에서 Secret 관련 이벤트를 기록해야 합니다.
# audit-policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# Secret 읽기/쓰기 모든 동작을 RequestResponse 수준으로 기록
- level: Metadata
resources:
- group: ""
resources: ["secrets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
Secret 감사 로그에서
level: RequestResponse를 사용하면 Secret 값 자체가 로그에 기록될 수 있습니다. level: Metadata는 "누가, 언제, 어떤 Secret에 접근했는가"만 기록하므로 보안과 감사 모두를 만족합니다.5. 외부 Secret 관리 도구 비교
Git 저장소에 Secret YAML을 직접 저장하지 않으려면 외부 Secret 관리 도구를 사용해야 합니다. 각 도구의 동작 방식과 trade-off가 다릅니다.
| 도구 | 방식 | Git에 저장하는 것 | Secret 원본 위치 | 복잡도 |
|---|---|---|---|---|
| Sealed Secrets | 비대칭 암호화로 암호화된 Secret을 Git에 저장 | 암호화된 SealedSecret YAML | 클러스터 내부 (컨트롤러가 복호화) | 낮음 |
| External Secrets Operator | 외부 저장소에서 Secret을 동기화 | ExternalSecret 참조 YAML (값 미포함) | AWS Secrets Manager, Vault, Azure Key Vault 등 | 중간 |
| HashiCorp Vault + VSO | Vault에서 직접 Secret을 주입 | VaultStaticSecret/VaultDynamicSecret CRD | HashiCorp Vault | 높음 |
| Secrets Store CSI Driver | CSI 볼륨으로 Secret을 Pod에 마운트 | SecretProviderClass YAML (값 미포함) | 클라우드 KMS, Vault | 중간 |
의사결정 기준
Q1. Secret 원본을 어디에 저장할 것인가?
├── 클라우드 Secret 서비스 (Secrets Manager, Key Vault 등) → External Secrets Operator
├── 자체 관리 Vault → HashiCorp Vault + VSO
└── 별도 인프라 없이 Git 기반으로 → Sealed Secrets
Q2. 동적 Secret(TTL 기반 자동 갱신)이 필요한가?
├── 예 → HashiCorp Vault (Dynamic Secrets)
└── 아니오 → External Secrets Operator 또는 Sealed Secrets
Q3. 팀 규모와 운영 복잡도를 감당할 수 있는가?
├── 소규모 팀, 단일 클러스터 → Sealed Secrets
├── 중규모, 멀티 클라우드 → External Secrets Operator
└── 대규모, 엄격한 보안 요구사항 → HashiCorp Vault
6. Sealed Secrets: Git 친화적 암호화
Sealed Secrets는 Bitnami에서 개발한 오픈소스 도구입니다. 비대칭 암호화를 사용하여 클러스터의 컨트롤러만 복호화할 수 있는 SealedSecret을 생성합니다. 암호화된 YAML은 Git에 안전하게 커밋할 수 있습니다.
6.1 동작 원리
- 클러스터에 Sealed Secrets 컨트롤러를 설치합니다. 컨트롤러가 RSA 키 쌍을 생성합니다.
- 개발자가
kubesealCLI로 일반 Secret을 SealedSecret으로 암호화합니다. - 암호화된 SealedSecret YAML을 Git에 커밋합니다.
- 클러스터에 SealedSecret이 적용되면, 컨트롤러가 복호화하여 일반 Kubernetes Secret을 생성합니다.
6.2 설치 및 사용
# 컨트롤러 설치 (Helm)
helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets
helm install sealed-secrets sealed-secrets/sealed-secrets \
--namespace kube-system
# kubeseal CLI 설치 (v0.27.x 기준)
# macOS
brew install kubeseal
# Linux
wget https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.27.3/kubeseal-0.27.3-linux-amd64.tar.gz
tar -xvzf kubeseal-0.27.3-linux-amd64.tar.gz
sudo install -m 755 kubeseal /usr/local/bin/kubeseal
# 일반 Secret을 SealedSecret으로 암호화
kubectl create secret generic db-credentials \
--namespace production \
--from-literal=username=admin \
--from-literal=password='p@ssw0rd' \
--dry-run=client -o yaml | \
kubeseal --format yaml > sealed-db-credentials.yaml
# sealed-db-credentials.yaml (Git에 안전하게 커밋 가능)
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: db-credentials
namespace: production
spec:
encryptedData:
username: AgBy3i4OJSWK+P... (암호화된 값)
password: AgCtr8sMGh2DJKL... (암호화된 값)
template:
metadata:
name: db-credentials
namespace: production
type: Opaque
6.3 키 로테이션
Sealed Secrets 컨트롤러는 기본적으로 30일마다 새 키를 생성합니다. 이전 키는 삭제되지 않으므로 기존 SealedSecret은 계속 복호화됩니다.
# 현재 사용 중인 키 확인
kubectl get secrets -n kube-system -l sealedsecrets.bitnami.com/sealed-secrets-key
# 키를 수동으로 로테이션한 후 기존 SealedSecret 재암호화
kubeseal --re-encrypt < sealed-db-credentials.yaml > sealed-db-credentials-new.yaml
6.4 trade-off
| 장점 | 단점 |
|---|---|
| 설치/운영이 단순 | 클러스터 간 키가 다르므로 멀티 클러스터에서 관리 복잡 |
| Git에 안전하게 저장 가능 | Secret 값 변경 시 re-seal 필요 |
| 외부 인프라 불필요 | 동적 Secret(자동 갱신) 미지원 |
| GitOps와 자연스럽게 통합 | 컨트롤러 키 분실 시 모든 Secret 복구 불가 |
Sealed Secrets 컨트롤러의 개인 키를 안전하게 백업해야 합니다. 이 키를 잃으면 기존 SealedSecret을 복호화할 수 없습니다. 키는
kube-system 네임스페이스의 Secret으로 저장되므로, 클러스터 재생성 시 이 키를 먼저 복원해야 합니다.7. External Secrets Operator: 외부 저장소 연동
External Secrets Operator(ESO)는 AWS Secrets Manager, Azure Key Vault, GCP Secret Manager, HashiCorp Vault 등 외부 Secret 저장소에서 값을 읽어와 Kubernetes Secret으로 동기화합니다.
7.1 동작 원리
- 외부 저장소(예: AWS Secrets Manager)에 Secret을 생성합니다.
- 클러스터에
SecretStore(또는ClusterSecretStore)를 정의하여 외부 저장소 연결 정보를 설정합니다. ExternalSecret리소스를 생성하여 "어떤 외부 Secret을 어떤 Kubernetes Secret으로 동기화할지" 정의합니다.- ESO 컨트롤러가 주기적으로 외부 저장소에서 값을 가져와 Kubernetes Secret을 생성/갱신합니다.
7.2 설치 및 설정
# External Secrets Operator 설치 (Helm)
helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets external-secrets/external-secrets \
--namespace external-secrets \
--create-namespace
7.3 AWS Secrets Manager 연동 예시
# SecretStore 정의 — AWS Secrets Manager 연결
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: aws-secrets-manager
namespace: production
spec:
provider:
aws:
service: SecretsManager
region: ap-northeast-2
auth:
jwt:
serviceAccountRef:
name: external-secrets-sa # IRSA로 인증
# ExternalSecret 정의 — 외부 Secret을 K8s Secret으로 동기화
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-credentials
namespace: production
spec:
refreshInterval: 1h # 동기화 주기
secretStoreRef:
name: aws-secrets-manager
kind: SecretStore
target:
name: db-credentials # 생성될 K8s Secret 이름
creationPolicy: Owner
data:
- secretKey: username # K8s Secret의 key
remoteRef:
key: production/db-creds # AWS Secrets Manager의 Secret 이름
property: username # JSON 내 특정 필드
- secretKey: password
remoteRef:
key: production/db-creds
property: password
7.4 Azure Key Vault 연동 예시
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: azure-key-vault
namespace: production
spec:
provider:
azurekv:
vaultUrl: "https://my-vault.vault.azure.net"
authType: WorkloadIdentity
serviceAccountRef:
name: external-secrets-sa
7.5 trade-off
| 장점 | 단점 |
|---|---|
| Secret 원본이 클러스터 외부에 있어 분리됨 | 외부 저장소 장애 시 Secret 갱신 불가 |
| 다양한 Provider 지원 (15+ 종류) | 초기 설정(IRSA, Workload Identity) 필요 |
| 자동 동기화로 Secret 갱신 자동화 | 동기화 지연(refreshInterval)으로 즉시 반영 안 될 수 있음 |
| Git에는 참조만 저장 (값 미포함) | 외부 저장소 비용 발생 |
8. HashiCorp Vault 연동
HashiCorp Vault는 동적 Secret 생성, 자동 갱신, 세밀한 접근 정책을 제공하는 Secret 관리 플랫폼입니다. Kubernetes와의 연동 방식은 세 가지가 있습니다.
| 연동 방식 | 설명 | 특징 |
|---|---|---|
| Vault Secrets Operator (VSO) | CRD 기반으로 Vault Secret을 K8s Secret으로 동기화 | 권장 방식, Operator 패턴 |
| Vault Agent Injector | Init/Sidecar 컨테이너로 Secret을 Pod에 주입 | 애플리케이션 코드 변경 불필요 |
| Secrets Store CSI Driver | CSI 볼륨으로 Secret을 파일 시스템에 마운트 | 표준 CSI 인터페이스 사용 |
8.1 Vault Secrets Operator (VSO) 방식
# VaultAuth — Vault 인증 설정
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultAuth
metadata:
name: vault-auth
namespace: production
spec:
method: kubernetes
mount: kubernetes
kubernetes:
role: production-app
serviceAccount: app-sa
# VaultStaticSecret — 정적 Secret 동기화
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
name: db-credentials
namespace: production
spec:
type: kv-v2
mount: secret
path: production/db-creds
destination:
name: db-credentials # 생성될 K8s Secret 이름
create: true
refreshAfter: 30s # 갱신 주기
vaultAuthRef: vault-auth
# VaultDynamicSecret — 동적 Secret (TTL 기반 자동 갱신)
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultDynamicSecret
metadata:
name: db-dynamic-creds
namespace: production
spec:
mount: database
path: creds/production-readonly
destination:
name: db-dynamic-credentials
create: true
renewalPercent: 67 # TTL의 67% 지점에서 갱신
vaultAuthRef: vault-auth
8.2 Vault Agent Injector 방식
Pod에 annotation을 추가하면 Vault Agent가 sidecar로 주입되어 Secret을 파일로 제공합니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: production
spec:
template:
metadata:
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "production-app"
vault.hashicorp.com/agent-inject-secret-db-creds: "secret/data/production/db-creds"
vault.hashicorp.com/agent-inject-template-db-creds: |
{{- with secret "secret/data/production/db-creds" -}}
export DB_USER="{{ .Data.data.username }}"
export DB_PASS="{{ .Data.data.password }}"
{{- end }}
spec:
serviceAccountName: app-sa
containers:
- name: app
image: my-app:1.0
# Secret은 /vault/secrets/db-creds 파일로 마운트됨
8.3 trade-off
| 장점 | 단점 |
|---|---|
| 동적 Secret으로 자동 로테이션 가능 | Vault 자체의 운영 부담 (HA, unseal, 백업) |
| 세밀한 접근 정책 (Policy, AppRole) | 러닝 커브가 높음 |
| 감사 로그 내장 | 추가 인프라 비용 (Vault 클러스터) |
| 다양한 Secret Engine (DB, PKI, AWS 등) | 단일 장애점(SPOF) 위험 (HA 필수) |
Vault를 자체 운영하기 어려운 경우, HCP Vault(HashiCorp Cloud Platform)나 클라우드 네이티브 Secret 관리 서비스(AWS Secrets Manager, Azure Key Vault)를 External Secrets Operator로 연동하는 방식이 운영 부담을 줄일 수 있습니다.
9. 실무 시나리오: 스타트업 프로덕션 환경 설계
시나리오
팀원 10명의 스타트업에서 EKS 클러스터를 운영합니다. 데이터베이스 비밀번호, 외부 API 키, TLS 인증서를 관리해야 합니다. GitOps(Argo CD)를 사용 중이며, Secret을 Git에 저장하면 안 됩니다.
요구사항
- Secret이 Git 리포지토리에 노출되면 안 됨
- Secret 변경 시 Pod 재시작 없이 반영되면 좋음
- 운영 부담이 적어야 함 (Vault 자체 운영은 부담)
- AWS 서비스를 이미 사용 중
설계 결정
| 결정 | 이유 | 대안 |
|---|---|---|
| External Secrets Operator 채택 | AWS Secrets Manager와 직접 연동, Vault 운영 불필요 | Sealed Secrets (동기화/갱신 미지원) |
| IRSA로 인증 | IAM Role을 ServiceAccount에 직접 연결, 키 관리 불필요 | Access Key 사용 (보안 위험) |
| refreshInterval: 1h 설정 | Secret 변경 시 1시간 내 자동 반영 | 수동 kubectl rollout restart |
| EKS Secret 암호화 활성화 | KMS로 etcd 암호화, 추가 비용 최소 | 기본 설정 유지 (보안 위험) |
구현
# 1. IRSA용 ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: external-secrets-sa
namespace: production
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/ExternalSecretsRole
# 2. ClusterSecretStore (모든 네임스페이스에서 재사용)
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
name: aws-secrets-manager
spec:
provider:
aws:
service: SecretsManager
region: ap-northeast-2
auth:
jwt:
serviceAccountRef:
name: external-secrets-sa
namespace: production
# 3. ExternalSecret (Git에 이것만 커밋)
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: app-secrets
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: ClusterSecretStore
target:
name: app-secrets
data:
- secretKey: DB_PASSWORD
remoteRef:
key: production/app/db
property: password
- secretKey: API_KEY
remoteRef:
key: production/app/external-api
property: key
이 구성에서 Git에 저장되는 것은 ExternalSecret YAML뿐입니다. 실제 Secret 값은 AWS Secrets Manager에만 존재하고, ESO가 클러스터에 동기화합니다.
10. Pod에서 Secret을 안전하게 사용하기
Secret을 Pod에 주입하는 방법은 두 가지입니다: 환경 변수(env)와 볼륨 마운트(volume). 보안 관점에서 볼륨 마운트가 권장됩니다.
10.1 환경 변수 vs 볼륨 마운트
| 방식 | 장점 | 단점 |
|---|---|---|
| 환경 변수 (env) | 코드에서 바로 접근 가능 | kubectl describe pod로 노출, 프로세스 목록에서 보일 수 있음, Secret 변경 시 Pod 재시작 필요 |
| 볼륨 마운트 (volume) | 파일 시스템 권한으로 보호, 자동 갱신 가능 | 파일 읽기 로직 필요 |
# 권장: 볼륨 마운트 방식
apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
containers:
- name: app
image: my-app:1.0
volumeMounts:
- name: secrets
mountPath: /etc/secrets
readOnly: true
volumes:
- name: secrets
secret:
secretName: db-credentials
defaultMode: 0400 # 소유자만 읽기 가능
10.2 불필요한 Secret 마운트 방지
# API 접근이 불필요한 Pod에서 ServiceAccount 토큰 마운트 비활성화
apiVersion: v1
kind: Pod
metadata:
name: simple-worker
spec:
automountServiceAccountToken: false
containers:
- name: worker
image: worker:1.0
11. 보안 체크리스트
운영 환경에서 Kubernetes Secret을 관리할 때 확인해야 하는 항목입니다.
| # | 항목 | 확인 방법 |
|---|---|---|
| 1 | etcd 암호화가 활성화되어 있는가 | kubectl get secrets -o json의 annotation 확인, 매니지드의 경우 콘솔 확인 |
| 2 | Secret YAML이 Git에 커밋되어 있지 않은가 | git log --all -p -- '*.yaml' | grep 'kind: Secret' |
| 3 | Secret 접근 RBAC가 최소 권한인가 | kubectl auth can-i get secrets --as=<user> -n <ns> |
| 4 | 감사 로그가 Secret 접근을 기록하는가 | Audit Policy에 secrets 리소스 포함 여부 확인 |
| 5 | 환경 변수 대신 볼륨 마운트를 사용하는가 | Deployment YAML의 envFrom vs volumeMounts 확인 |
| 6 | 불필요한 ServiceAccount 토큰이 마운트되지 않는가 | automountServiceAccountToken: false 확인 |
| 7 | Secret 로테이션 전략이 있는가 | 외부 Secret 관리 도구의 refreshInterval 또는 수동 절차 확인 |
| 8 | 컨트롤러 키(Sealed Secrets) 또는 Vault unseal 키를 백업했는가 | 백업 절차 및 복구 테스트 여부 확인 |
12. 비용/운영 고려사항
| 항목 | 비용 | 운영 부담 |
|---|---|---|
| EKS Secret 암호화 (KMS) | KMS API 호출당 $0.03/10,000건 | 거의 없음 (한 번 설정) |
| AWS Secrets Manager | Secret당 $0.40/월 + API 호출 비용 | Secret 생성/갱신 관리 |
| Azure Key Vault | 표준 티어 기준 $0.03/10,000 작업 | Secret 생성/갱신 관리 |
| HashiCorp Vault (자체 운영) | 인프라 비용 (EC2/EKS) | HA 구성, unseal, 백업, 업그레이드 관리 |
| HCP Vault | 월 $0.03/시간~ (인스턴스 기준) | 관리형으로 운영 부담 낮음 |
| Sealed Secrets | 무료 (오픈소스) | 키 백업, re-seal 관리 |
| External Secrets Operator | 무료 (오픈소스) + 외부 저장소 비용 | Provider 인증 설정, 동기화 모니터링 |
소규모 팀에서 시작한다면 Sealed Secrets로 충분할 수 있습니다. Secret 수가 늘어나고 자동 갱신이 필요해지면 External Secrets Operator로 전환하는 단계적 접근이 실용적입니다. Vault는 동적 Secret, PKI 인증서, 데이터베이스 자격증명 자동 로테이션 등 고급 기능이 필요한 경우에 도입을 검토합니다.
13. 정리
- Kubernetes Secret은 Base64 인코딩만 적용되며, 기본 설정에서는 etcd에 평문으로 저장됩니다. 단독으로는 보안 수준이 낮습니다.
- 저장 시 암호화(EncryptionConfiguration 또는 클라우드 KMS)를 반드시 활성화하여 etcd 수준에서 보호해야 합니다.
- RBAC로 Secret 접근을 최소화하고, 감사 로그로 접근 이력을 추적합니다.
- Git에 Secret을 저장하지 않기 위해 외부 Secret 관리 도구를 사용합니다. 팀 규모와 요구사항에 따라 Sealed Secrets, External Secrets Operator, HashiCorp Vault 중 선택합니다.
- Pod에서는 환경 변수보다 볼륨 마운트 방식이 보안상 안전합니다. 불필요한 토큰 마운트는 비활성화합니다.
- Secret 보안은 단일 도구가 아닌 여러 계층(암호화, 접근 제어, 외부 관리, 감사)의 조합으로 달성됩니다.
참고 문서
'Kubernetes' 카테고리의 다른 글
| EKS vs AKS vs GKE: 매니지드 Kubernetes 비교 (0) | 2026.06.08 |
|---|---|
| Kubernetes HPA가 동작하지 않는 이유 (0) | 2026.06.07 |
| Kubernetes RBAC란 무엇인가: Role, ClusterRole, Binding으로 권한 설계하기 (0) | 2026.06.01 |
| Kubernetes Service 종류: ClusterIP, NodePort, LoadBalancer, Ingress 비교 (0) | 2026.05.31 |
| Kubernetes 아키텍처 기본 구조: Control Plane과 Worker Node (0) | 2026.05.31 |