☁️ 클라우드

Secret

민감한 정보를 저장하는 Kubernetes 리소스

📖 상세 설명

Kubernetes Secret은 비밀번호, OAuth 토큰, SSH 키, TLS 인증서 등 민감한 정보를 저장하고 관리하는 리소스입니다. ConfigMap과 유사하지만, 민감한 데이터를 위해 설계되었으며 base64로 인코딩되어 저장됩니다.

Secret 타입:

  • Opaque: 임의의 사용자 정의 데이터 (기본값)
  • kubernetes.io/tls: TLS 인증서와 개인키 (tls.crt, tls.key)
  • kubernetes.io/dockerconfigjson: Docker 레지스트리 인증 정보
  • kubernetes.io/basic-auth: 기본 인증 (username, password)
  • kubernetes.io/ssh-auth: SSH 인증 (ssh-privatekey)
  • kubernetes.io/service-account-token: ServiceAccount 토큰 (자동 생성)

Secret 사용 방식:

  • 환경변수: Pod의 env 또는 envFrom으로 주입
  • 볼륨 마운트: 파일로 마운트하여 접근
  • imagePullSecrets: 프라이빗 레지스트리 인증

보안 고려사항:

기본 Secret은 etcd에 평문으로 저장됩니다. 프로덕션 환경에서는 다음을 권장합니다:

  • etcd 암호화: EncryptionConfiguration으로 저장 시 암호화
  • RBAC: Secret 접근 권한을 최소화
  • External Secrets: AWS Secrets Manager, HashiCorp Vault 등 외부 시크릿 관리자 연동
  • Sealed Secrets: 암호화된 Secret을 Git에 저장 (GitOps)

Secret vs ConfigMap: ConfigMap은 민감하지 않은 설정 데이터용이고, Secret은 민감한 데이터용입니다. Secret은 tmpfs에 마운트되어 디스크에 쓰이지 않고, 메모리에 로드됩니다.

💻 코드 예제

Secret 생성 및 사용

# 1. kubectl로 Secret 생성 (권장)
kubectl create secret generic db-credentials \
  --from-literal=username=admin \
  --from-literal=password='S3cur3P@ss!'

# 2. YAML 파일로 생성 (base64 인코딩 필요)
# echo -n 'admin' | base64  → YWRtaW4=
# echo -n 'S3cur3P@ss!' | base64  → UzNjdXIzUEBzcyE=

apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
  namespace: production
type: Opaque
data:
  username: YWRtaW4=        # base64 인코딩
  password: UzNjdXIzUEBzcyE=

# 3. stringData 사용 (평문, 자동 인코딩)
apiVersion: v1
kind: Secret
metadata:
  name: api-keys
type: Opaque
stringData:
  api-key: "sk-1234567890abcdef"
  webhook-secret: "whsec_abcdef123456"

---
# Pod에서 Secret 사용
apiVersion: v1
kind: Pod
metadata:
  name: app-with-secrets
spec:
  containers:
    - name: app
      image: my-app:latest
      env:
        # 개별 키를 환경변수로
        - name: DB_USERNAME
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: username
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: password

      # 전체 Secret을 환경변수로
      envFrom:
        - secretRef:
            name: api-keys
            optional: false

      # 파일로 마운트
      volumeMounts:
        - name: db-creds-volume
          mountPath: /etc/secrets
          readOnly: true

  volumes:
    - name: db-creds-volume
      secret:
        secretName: db-credentials
        defaultMode: 0400  # 읽기 전용 권한

TLS Secret (HTTPS 인증서)

# TLS Secret 생성
kubectl create secret tls my-tls-secret \
  --cert=./tls.crt \
  --key=./tls.key

# YAML 형식
apiVersion: v1
kind: Secret
metadata:
  name: my-tls-secret
type: kubernetes.io/tls
data:
  tls.crt: LS0tLS1CRUdJTi...  # base64 인코딩된 인증서
  tls.key: LS0tLS1CRUdJTi...  # base64 인코딩된 개인키

---
# Ingress에서 TLS Secret 사용
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app-ingress
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  tls:
    - hosts:
        - my-app.example.com
      secretName: my-tls-secret
  rules:
    - host: my-app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-app
                port:
                  number: 80

External Secrets Operator (AWS Secrets Manager)

# External Secrets Operator 설치
helm install external-secrets \
  external-secrets/external-secrets \
  -n external-secrets \
  --create-namespace

# SecretStore 설정 (AWS Secrets Manager 연결)
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: aws-secretsmanager
  namespace: production
spec:
  provider:
    aws:
      service: SecretsManager
      region: ap-northeast-2
      auth:
        jwt:
          serviceAccountRef:
            name: external-secrets-sa

---
# ExternalSecret 정의 (AWS에서 동기화)
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: db-credentials
  namespace: production
spec:
  refreshInterval: 1h  # 1시간마다 동기화
  secretStoreRef:
    name: aws-secretsmanager
    kind: SecretStore
  target:
    name: db-credentials  # 생성될 K8s Secret 이름
    creationPolicy: Owner
  data:
    - secretKey: username
      remoteRef:
        key: production/database  # AWS Secret 이름
        property: username
    - secretKey: password
      remoteRef:
        key: production/database
        property: password

# kubectl get externalsecret -n production
# NAME             STORE                REFRESH   STATUS
# db-credentials   aws-secretsmanager   1h        SecretSynced

🗣️ 실무 대화 예시

보안 리뷰

보안팀: "DB 비밀번호가 Git에 올라간 거 같은데요?"

개발자: "Secret YAML에 base64로 인코딩해서 올렸는데..."

보안팀: "base64는 암호화가 아니에요. 누구나 디코딩할 수 있어요. Sealed Secrets이나 External Secrets Operator를 써서 Git에는 암호화된 상태로만 저장하세요."

개발자: "알겠습니다. AWS Secrets Manager로 옮기고 External Secrets로 동기화하겠습니다."

기술 면접

면접관: "Kubernetes Secret의 보안 한계점은?"

지원자: "첫째, 기본적으로 etcd에 평문 저장됩니다. EncryptionConfiguration으로 저장 시 암호화를 활성화해야 해요. 둘째, base64는 인코딩일 뿐 암호화가 아닙니다. 셋째, Secret에 접근 가능한 모든 Pod와 사용자가 값을 볼 수 있어 RBAC 설정이 중요합니다."

면접관: "그래서 어떻게 개선하나요?"

지원자: "HashiCorp Vault나 AWS Secrets Manager 같은 전문 Secret 관리 도구를 사용합니다. External Secrets Operator로 외부 Secret을 K8s Secret으로 동기화하면 소스 저장소에 민감 정보를 저장하지 않아도 됩니다."

운영 이슈

운영팀: "Secret 업데이트했는데 Pod에 반영이 안 돼요."

개발자: "환경변수로 주입한 Secret은 Pod 재시작이 필요해요. 볼륨 마운트 방식이면 kubelet이 주기적으로 업데이트하지만 기본 1분 정도 걸려요. 즉시 반영하려면 Pod를 롤링 재시작하세요."

운영팀: "자동으로 재시작되게 할 수 없나요?"

개발자: "Reloader 같은 컨트롤러를 설치하면 Secret 변경 시 자동으로 Deployment를 롤아웃해요. 아니면 Secret hash를 annotation에 넣어서 변경 시 Pod가 재생성되게 할 수도 있어요."

⚠️ 주의사항

🔗 관련 용어

ConfigMap HashiCorp Vault External Secrets Sealed Secrets SOPS RBAC

📚 더 배우기