콘텐츠로 이동

시크릿 관리 정리

쿠버네티스 시크릿 관리 3유형인 Sealed Secrets, External Secrets Operator, Vault의 동작 원리를 비교했다. 상황별로 무엇을 골라야 하는지 적용 사례와 함께 정리한다.

시크릿 관리란

시크릿 관리(Secret Management)란 DB 비밀번호·API 키·인증서 같은 민감 값을 코드와 이미지 밖에서 저장·배포·회전(Rotation)·폐기하는 체계

쿠버네티스 기본 Secret만으로는 부족해서 별도 체계가 필요해짐.

  • base64는 인코딩이지 암호화가 아님 → 매니페스트를 읽을 수 있으면 곧 평문
  • etcd 저장도 기본은 평문 → encryption at rest는 별도로 설정해야 함
  • Git에 한 번 올라간 시크릿은 지워도 커밋 이력에 영구 잔존
  • "모든 것을 Git에 둔다"는 GitOps 전제와 정면 충돌 → GitOps와 ArgoCD 정리에서 남겨둔 숙제

접근 방식 3유형

유형 대표 도구 동작 방식 장점 단점
암호화 후 Git 저장 Sealed Secrets, SOPS 시크릿을 암호화해 Git에 커밋, 클러스터 안에서만 복호화 추가 인프라 없음, GitOps 워크플로 유지 회전·감사 없음, 키 유실 시 복구 불가
외부 스토어 동기화 External Secrets Operator AWS Secrets Manager 등 외부 스토어를 참조해 k8s Secret 생성 원본이 Git 밖, 스토어 교체 유연 외부 스토어 의존, 결과물은 결국 k8s Secret
중앙 시크릿 서버 Vault, OpenBao 전용 서버가 저장·발급·회전·감사를 전담 동적 시크릿, TTL 자동 회수, 전 요청 감사 서버 운영 부담(HA·unseal), 학습 곡선

Sealed Secrets

Sealed Secrets란 시크릿을 비대칭 암호화해 Git에 올릴 수 있게 만드는 Bitnami의 쿠버네티스 컨트롤러

graph LR
    Dev["개발자<br>kubeseal로 암호화"] --> Git["Git 저장소<br>SealedSecret CRD"]
    Git --> Ctrl["컨트롤러<br>개인키로 복호화"]
    Ctrl --> Sec["k8s Secret 생성"]
  • kubeseal CLI가 컨트롤러의 공개키로 암호화 → 개발자는 암호화만 가능하고 복호화는 클러스터만 가능
  • 암호문인 SealedSecret 리소스는 Git에 올려도 안전 → pull request 리뷰·이력 관리가 일반 매니페스트와 동일
  • 컨트롤러 개인키가 단일 장애점 → 키를 백업하지 않으면 클러스터 재구축 시 전부 재암호화
  • 회전 기능 없음 → 값을 바꾸려면 다시 암호화해 커밋하는 수동 절차
  • 2025년 Bitnami 컨테이너 카탈로그 유료화와는 무관하게 독립 배포·유지보수 지속

암호화 구조

  • 값마다 일회용 세션 키(AES-256-GCM)로 암호화하고, 세션 키는 컨트롤러 공개키로 RSA-OAEP(SHA-256) 암호화해 함께 저장하는 하이브리드 구조
  • scope는 RSA-OAEP의 label로 암호문 자체에 바인딩 → 복호화 시점에 네임스페이스·이름이 다르면 풀리지 않음
  • sealing key는 기본 30일마다 새 키가 추가되는 방식 → 구 키가 보존되어 기존 암호문은 계속 풀리고, 새 암호화만 최신 키 사용
scope 복호화 허용 범위 용도
strict(기본) 암호화 때 지정한 네임스페이스·이름 그대로만 대부분의 시크릿
namespace-wide 같은 네임스페이스 안에서 이름 변경 허용 네임스페이스 내 재사용
cluster-wide 어느 네임스페이스든 복호화 허용 클러스터 공용 시크릿

External Secrets Operator

External Secrets Operator(ESO)란 외부 시크릿 스토어의 값을 읽어 k8s Secret으로 동기화하는 오퍼레이터

graph LR
    ES["ExternalSecret<br>무엇을 가져올지"] --> Ctrl["컨트롤러"]
    SS["SecretStore<br>어디서·어떤 인증으로"] --> Ctrl
    Ctrl -->|"refreshInterval마다 조회"| Ext["외부 스토어<br>(Secrets Manager 등)"]
    Ctrl -->|"생성·갱신"| Sec["k8s Secret"]
  • SecretStore CRD가 "어디서"(스토어 종류·인증), ExternalSecret CRD가 "무엇을"(키 경로) 정의
  • SecretStore는 네임스페이스 스코프, ClusterSecretStore는 클러스터 전역 → 팀별 위임이냐 중앙 관리냐에 따라 선택
  • 프로바이더: AWS Secrets Manager·SSM Parameter Store, GCP Secret Manager, Azure Key Vault, Vault 등 다수
  • Git에는 참조(CRD)만 커밋되고 실제 값은 외부 스토어에 존재 → GitOps와 양립
  • refreshInterval 주기로 재동기화 → 스토어에서 값을 회전하면 클러스터로 자동 전파
  • CNCF Sandbox 프로젝트(2022) → 2025년 5월 v1 API 안정화, 같은 해 여름 유지보수 공백으로 릴리스가 한 달 넘게 멈췄다가 9월 재개
  • 결과물은 여전히 k8s Secret → etcd 암호화와 RBAC 최소화는 별도로 챙겨야 함

Vault

Vault란 HashiCorp가 만든 중앙 시크릿 관리 서버로, 저장을 넘어 발급·회전·폐기·감사까지 시크릿 생명주기 전체를 다루는 도구

소속과 라이선스가 자주 바뀌어 선택 전 확인이 필요함.

  • 2023년 오픈소스(MPL 2.0)에서 BUSL 1.1로 전환 → 경쟁 호스팅 서비스 구축만 제한, 일반 사용은 무료
  • 2025년 IBM의 HashiCorp 인수 완료 → 현재는 IBM 소프트웨어 제품군
  • 라이선스 전환에 반발한 커뮤니티가 마지막 MPL 버전을 포크 → Linux Foundation 산하 OpenBao로 발전 중

핵심 구성 요소

구성 요소 설명
Secret Engine 시크릿 저장·생성 백엔드. KV(정적 저장), database(임시 계정 발급), transit(암호화 대행), pki(인증서 발급)
Auth Method 클라이언트 인증 방식. kubernetes(ServiceAccount 토큰 검증), approle, oidc 등
Policy 경로 기반 접근 제어. 어떤 주체가 어떤 시크릿 경로를 읽을지 정의
Lease/TTL 발급된 시크릿의 유효 기간. 만료 시 자동 폐기
Seal/Unseal 재시작 시 unseal 키 조각(Shamir)을 모아 루트 키를 여는 봉인 해제 절차. 클라우드 KMS에 맡기는 auto-unseal 가능
Audit Device 모든 요청·응답을 감사 로그로 기록
  • 키는 3계층 구조로 보호됨: 데이터는 키링의 암호화 키로, 키링은 루트 키로, 루트 키는 unseal 키로 암호화 → unseal은 이 사슬을 여는 절차
  • Shamir 방식은 unseal 키를 여러 조각으로 나눠 임계값 이상이 모여야 재구성 → auto-unseal은 루트 키 복호화를 클라우드 KMS에 위임해 재시작을 자동화

인증 흐름

  • Pod가 자신의 ServiceAccount 토큰으로 로그인 요청 → Vault가 쿠버네티스 TokenReview API로 위조·만료를 검증
  • role이 "어느 네임스페이스의 어느 ServiceAccount인지"를 policy에 매핑 → 검증 통과 시 해당 policy가 묶인 Vault 토큰 발급
  • 이후 요청은 Vault 토큰으로 진행 → policy에 허용된 경로만 접근 가능

동적 시크릿

동적 시크릿(Dynamic Secrets)이란 요청 시점에 실제 계정을 만들어 TTL과 함께 발급하고, 만료되면 자동 회수하는 방식

sequenceDiagram
    participant App
    participant Vault
    participant DB
    App->>Vault: 로그인(ServiceAccount 토큰)
    Vault-->>App: Vault 토큰 발급
    App->>Vault: DB 자격증명 요청
    Vault->>DB: CREATE USER(임시 계정 생성)
    Vault-->>App: 임시 계정 + TTL
    Note over App,DB: TTL 동안만 유효한 계정으로 접속
    Vault->>DB: TTL 만료시 DROP USER(자동 회수)
  • 고정 비밀번호가 존재하지 않음 → 유출돼도 TTL이 지나면 무효
  • 회전을 사람이 아니라 시스템이 수행 → "비밀번호 언제 바꿨더라"가 사라짐
  • 감사 로그에 누가 언제 어떤 계정을 발급받았는지 전부 남음 → 유출 시 추적 범위가 계정 단위로 좁혀짐

쿠버네티스 연동 3종

연동 방식 동작 특징
Agent Injector 사이드카가 시크릿을 파일로 주입 앱 수정 없음, Pod마다 사이드카 오버헤드
CSI Provider 볼륨 마운트 시점에 주입 k8s Secret 객체를 만들지 않는 구성 가능
Vault Secrets Operator CRD 선언으로 k8s Secret 동기화 셋 중 최신, GitOps 친화

3유형 비교

비교 항목 Sealed Secrets External Secrets Operator Vault
시크릿 원본 위치 Git(암호문) 외부 스토어 Vault 서버
동적 시크릿 미지원 미지원(스토어 기능에 의존) 지원
회전 수동(재암호화 후 커밋) 스토어 회전 → 자동 동기화 TTL 기반 자동 회수
감사 로그 Git 이력이 전부 스토어 측 로그 전 요청 감사 로그
운영 부담 컨트롤러 1개 오퍼레이터 + 외부 스토어 서버 클러스터(HA·unseal) 운영
도입 난도 낮음 중간 높음

사용 사례

상황 추천 방식 추천 사유
소규모 클러스터, 외부 의존 최소화 Sealed Secrets 컨트롤러 하나로 끝나고 Git 워크플로 유지
클라우드 시크릿 스토어를 이미 운영 External Secrets Operator 기존 스토어를 단일 원본으로 재사용
DB 계정 자동 발급·회전, 감사 요구 Vault 동적 시크릿과 전 요청 감사 로그
멀티 클라우드·수백 서비스 규모 Vault 스토어 종속 없는 중앙 정책·발급 체계
BUSL 라이선스가 걸림 OpenBao MPL 2.0을 유지하는 Vault 포크

ArgoCD + Sealed Secrets

소규모 팀이 저장소 하나로 앱과 시크릿을 함께 배포하는 구성

  • 개발자가 kubeseal로 암호화해 앱 매니페스트와 같은 저장소에 커밋 → ArgoCD가 SealedSecret까지 함께 배포
  • 시크릿 변경도 pull request 리뷰와 커밋 이력을 거침 → 배포 이력과 시크릿 이력이 한 곳에 모임
  • 컨트롤러 개인키는 클러스터 밖에 정기 백업 → 키를 잃으면 전체 재암호화 외에 복구 수단이 없음

Secrets Manager + ESO

EKS에서 클라우드 시크릿 스토어를 단일 원본으로 쓰는 구성

  • RDS 비밀번호를 Secrets Manager에 저장하고 자동 회전 설정 → ESO가 IRSA(IAM Roles for Service Accounts)로 인증해 주기 조회
  • 회전된 값은 refreshInterval마다 k8s Secret으로 전파 → 애플리케이션 쪽은 갱신 반영(재시작·리로드)만 챙기면 됨
  • 액세스 키 발급이 필요 없음 → 오퍼레이터 ServiceAccount에 IAM Role을 연결하는 IRSA로 해결

Vault 동적 시크릿

서비스 수십 개가 각자 DB 계정을 받아 쓰는 MSA 구성

  • 서비스마다 Vault role을 만들어 네임스페이스·ServiceAccount와 policy를 매핑
  • 기동 시 kubernetes auth로 로그인 → database engine에서 TTL 계정을 발급받아 접속
  • 유출 사고 시 해당 lease만 revoke → 전체 비밀번호 교체 작업이 사라짐
  • Vault가 멈추면 신규 발급도 막힘 → HA 구성과 TTL 갱신 여유가 전제 조건

Vault PKI + cert-manager

내부 서비스 간 TLS 인증서를 자동 발급·갱신하는 구성

  • pki engine을 내부 CA로 두고 cert-manager의 Vault issuer가 sign 엔드포인트로 발급 요청
  • cert-manager의 Vault 인증도 kubernetes auth로 처리 가능 → 클러스터 안에서 토큰 관리가 자동
  • Certificate 리소스 선언만으로 발급·갱신이 자동화 → 유효기간 짧은 인증서를 부담 없이 순환

결론

  • 셋은 대체재가 아니라 층위가 다른 도구 → 작게 시작하고 회전·감사 요구가 생기는 시점에 위 층위로 이행
  • 어느 방식이든 결과물이 k8s Secret이면 etcd 암호화·RBAC 최소화는 공통 숙제
  • Sealed Secrets는 "Git에 안전하게 넣는 법", ESO는 "밖에 두고 가져오는 법", Vault는 "쓸 때마다 발급받는 법"