콘텐츠로 이동
Study Note온프렘 쿠버네티스

Vault와 VSO의 동작 원리

결론부터
Vault + VSO 경로에서 값은 Git에도 클러스터에도 살지 않고 금고에만 산다 — Git에는 참조만 두고, VSO가 쿠버네티스가 보증하는 신원으로 금고에서 값을 받아 Secret으로 동기화한다. 신뢰의 근거가 Sealed Secrets의 암호화 수학에서 인증과 정책으로 바뀌는 것이 이 경로의 본질이다
이 장에서 처음 나오는 말8개
Vault
HashiCorp의 비밀 금고 서버. 값을 암호화해 저장하고, 모든 접근이 인증 → 정책 → 감사 로그 한 문을 지나게 한다.
봉인 · 개봉Seal · Unseal
Vault는 재시작하면 잠긴(sealed) 상태로 떠서 아무 요청도 못 받는다. 데이터 암호화 키를 보호하는 장치라, 개봉 수단을 대야만 서비스가 시작된다.
시크릿 엔진Secret Engine
비밀의 종류별 백엔드. 키-값 저장(KV), DB 자격증명 발급(database), 인증서 발급(PKI)이 각각 다른 엔진이다.
동적 시크릿Dynamic Secret
미리 저장해 둔 값이 아니라 요청 때마다 새로 발급되고 수명이 있는 자격증명. DB 계정을 그때그때 만들어 주는 식이다.
lease리스
동적 시크릿의 수명 계약. 만료 전에 갱신하거나 새로 발급받아야 하고, 만료되면 Vault가 원천에서 그 자격증명을 회수한다.
정책Policy
Vault의 경로 기반 접근 규칙. "이 신원은 kv/data/loki/*를 읽을 수 있다"처럼 누가 어느 경로를 읽고 쓰는지를 정한다.
VSOVault Secrets Operator
HashiCorp가 만든 클러스터 안 오퍼레이터. Git에 올라간 참조 CR을 보고 Vault에서 값을 읽어 평범한 Secret으로 만들어 동기화한다.
TokenReview
쿠버네티스 API에 "이 ServiceAccount 토큰이 진짜냐"를 물어보는 검증 API. Vault가 클러스터 신원을 믿는 뿌리다.

시크릿 장의 세 방식 표에서 “ESO + 외부 금고” 행이 이 경로다. ESO가 금고 종류를 가리지 않는 범용 오퍼레이터라면, VSO는 HashiCorp가 만든 Vault 전용 오퍼레이터라는 차이일 뿐 구조는 같다 — 이 페이지는 Vault를 골랐을 때를 기준으로, 금고 쪽(Vault)과 클러스터 쪽(VSO)이 각각 무엇을 하는지를 본다.

큰 그림 — Git에는 값이 아예 없다

섹션 제목: “큰 그림 — Git에는 값이 아예 없다”

Sealed Secrets는 값을 암호문으로 바꿔 Git에 뒀다. 이 경로는 한 발 더 간다 — Git에는 값이 어떤 형태로도 올라가지 않는다. 올라가는 건 “Vault의 이 경로에 있는 값을 이 Secret으로 만들어 달라”는 참조 CR뿐이고, 참조는 평문이어도 비밀이 아니다.

Git에는 값 없는 참조 CR만 올라가고, Argo CD가 적용한 참조를 VSO가 보고 Vault에 인증해 값을 읽어 런타임 Secret으로 동기화하는 흐름

Sealed Secrets와 나란히 놓으면 바뀌는 것이 선명하다.

Sealed SecretsVault + VSO
Git에 올라가는 것암호문참조만 — 값은 어떤 형태로도 없다
값이 사는 곳Git(잠긴 채) + 런타임 SecretVault + 런타임 Secret
신뢰의 근거암호화 수학 — 개인키를 가진 컨트롤러만 푼다신원 — 인증된 대상에게 정책이 허락한 경로만
값 변경재봉인해서 커밋금고에서 바꾸면 자동 전파 — 커밋이 필요 없다
누가 언제 읽었나알 수 없다감사 로그에 남는다
추가로 운영할 것컨트롤러 하나Vault 서버 자체 — HA · 개봉 · 백업

Vault 쪽 — 금고는 무엇으로 이루어지나

섹션 제목: “Vault 쪽 — 금고는 무엇으로 이루어지나”

VSO를 보기 전에 금고 자체를 잡아야 한다. Vault는 크게 네 조각이다.

개봉 — 재시작하면 잠긴 채로 뜬다

섹션 제목: “개봉 — 재시작하면 잠긴 채로 뜬다”

Vault는 저장하는 모든 데이터를 자체 키로 암호화하고, 그 키를 보호하는 root key를 메모리에만 둔다. 그래서 프로세스가 재시작하면 root key가 사라진 잠긴 상태로 뜨고, 개봉 수단을 대야만 요청을 받기 시작한다. 기본 방식은 root key를 여러 조각으로 나눠 (예: 5조각 중 3조각이 모여야 복원) 운영자들이 나눠 갖는 Shamir 분할이다.

저장 — 상태가 어느 바닥에 떨어지나

섹션 제목: “저장 — 상태가 어느 바닥에 떨어지나”

Vault의 상태는 이 덱의 두 바닥(오브젝트 스토리지 · Postgres)이 아니라 자체 통합 스토리지(Raft) 에 떨어진다 — Vault 파드 여럿이 각자 블록 볼륨(2장의 스토리지 계층)에 복제본을 들고 합의하는 구조라, 별도 DB 없이 HA가 된다. 디스크 위 데이터는 암호화돼 있으므로 볼륨을 통째로 읽혀도 값은 새지 않는다. 백업은 Raft 스냅샷을 떠서 클러스터 밖(예: MinIO 버킷)에 두는 것이고, 이 스냅샷 + 개봉 수단이 복구의 전부다.

시크릿 엔진 — 저장만 하는 금고가 아니다

섹션 제목: “시크릿 엔진 — 저장만 하는 금고가 아니다”
엔진하는 일Sealed Secrets 대비
KV버전이 남는 키-값 저장. 잘못 덮어써도 이전 버전으로 돌아간다같은 자리 — 정적 값 보관
databaseDB 계정을 요청 때마다 발급하고 lease 만료 시 회수대응물이 없다 — 발급 자체가 회전이다
PKI단기 인증서를 그 자리에서 발급cert-manager와 겹치는 자리 — 4장 경로가 이미 있으면 굳이 옮기지 않는다

정적 값만 쓸 거라면 Vault의 이점은 절반이다 — 동적 시크릿과 감사가 필요해질 때 이 경로의 운영 비용이 정당화된다는 게 시크릿 장의 “회전이 업무가 되면”의 실체다.

정책과 감사 — 모든 접근이 한 문을 지난다

섹션 제목: “정책과 감사 — 모든 접근이 한 문을 지난다”

Vault의 모든 요청은 인증(누구냐) → 정책(그 경로를 읽어도 되냐) → 감사 로그(기록) 를 지난다. 정책은 경로 기반이라 “Loki의 신원은 kv/data/loki/*만 읽는다”처럼 좁게 자를 수 있고, 사람의 로그인은 OIDC로 Keycloak에 붙일 수 있다 — 플랫폼의 다른 도구들과 같은 SSO 문으로 들어오게 된다.

VSO 쪽 — 참조를 보고 값을 나른다

섹션 제목: “VSO 쪽 — 참조를 보고 값을 나른다”

VSO는 Sealed Secrets 컨트롤러와 같은 오퍼레이터 패턴이다. 다른 점은 감시하는 CR에 값이 없다는 것 — CR은 “어디의 무엇을 어느 Secret으로”라는 배선 정보만 담고, 값은 매번 Vault에서 읽어 온다. CR은 역할별로 세 축이다.

CR답하는 질문Git에 두나
VaultConnection어느 Vault에 접속하나 (주소 · CA)둔다
VaultAuth누구로서 인증하나 (인증 방식 · 사용할 ServiceAccount · Vault role)둔다
VaultStaticSecret 등어디의 값을 어느 Secret으로 만드나 (경로 · 대상 이름 · 갱신 주기)둔다 — 전부 값이 없는 평문이다
# Git에 올라가는 참조의 예 — 비밀이 하나도 없다
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
name: loki-s3
namespace: observability
spec:
vaultAuthRef: platform-auth
mount: kv
path: loki/s3
refreshAfter: 60s
destination:
name: loki-s3 # 이 이름의 Secret이 만들어진다
create: true
rolloutRestartTargets:
- kind: Deployment
name: loki

인증 — 시드 시크릿 없이 신원으로 들어간다

섹션 제목: “인증 — 시드 시크릿 없이 신원으로 들어간다”

여기가 이 경로의 가장 좋은 성질이다. “VSO가 Vault에 로그인할 비밀번호는 또 어디에 두나?”라는 되풀이 문제가 생기지 않는다 — VSO는 저장된 자격증명이 아니라 쿠버네티스가 그 자리에서 발급·보증하는 ServiceAccount 토큰으로 인증하기 때문이다.

VSO가 쿠버네티스 API에서 단기 ServiceAccount 토큰을 발급받아 Vault에 제출하면, Vault가 TokenReview로 진위를 되물어 확인한 뒤 정책이 묶인 자기 토큰을 내주는 인증 순서

Vault 쪽에는 미리 role을 만들어 둔다 — “observability 네임스페이스의 loki SA가 오면 kv/data/loki/* 읽기 정책을 준다”는 식으로, 쿠버네티스 신원과 Vault 정책을 묶는 자리다. 그래서 참조 CR을 훔쳐 다른 네임스페이스에 붙여 넣어도 소용없다 — 그 네임스페이스의 SA는 role에 묶여 있지 않아 인증 단계에서 끊긴다. Sealed Secrets가 OAEP label 수학으로 막던 것을 여기서는 신원 검증이 막는 것이다.

이 위임의 뿌리는 클라우드의 IAM 역할 연결(IRSA)과 같은 아이디어다 — 시크릿 장에서 “온프렘에는 신원 기반 접근이 없다”고 했는데, Vault + kubernetes auth가 바로 그 빈칸을 채우는 온프렘 구현이다.

동기화 — 회전이 커밋 없이 끝난다

섹션 제목: “동기화 — 회전이 커밋 없이 끝난다”
  1. VSO가 VaultStaticSecret을 보고 Vault에서 값을 읽어 대상 Secret을 만든다. 이후 refreshAfter 주기로 다시 읽는다.

  2. 금고에서 값이 바뀌면 다음 주기에 VSO가 차이를 감지하고 Secret을 갱신한다. 재봉인도, 커밋도 없다 — Git의 참조는 그대로이기 때문이다.

  3. rolloutRestartTargets에 적어 둔 워크로드를 자동으로 롤링 재시작한다. 시크릿 장에서 Reloader가 맡던 “파드가 새 값을 읽게 하기”가 오퍼레이터에 내장돼 있는 셈이다.

  4. 누가 Secret을 손으로 고쳐도 다음 동기화 때 금고 기준으로 덮어써진다. 소스는 Git이 아니라 Vault다.

동적 시크릿은 여기서 한 단계 더 간다 — VaultDynamicSecret은 database 엔진에서 수명 있는 계정을 발급받아 Secret으로 두고, lease 만료 전에 새 계정을 받아 갈아끼우는 것까지 오퍼레이터가 돈다. “90일마다 DB 비밀번호 교체”라는 업무 자체가 사라지는 지점이다. CR 종류와 필드의 정본은 Vault Secrets Operator 공식 문서다.

급소의 이동 — 부트스트랩과 복구

섹션 제목: “급소의 이동 — 부트스트랩과 복구”

Sealed Secrets의 급소가 sealing key 세트였다면, 이 경로의 급소는 Raft 스냅샷 + 개봉 수단이다. 그리고 Vault가 클러스터 안에 있다면 백업 장의 복구 순서에 순환이 하나 생긴다 — 앱 시크릿은 Vault에서 오는데, 그 Vault부터 되살려야 하는 것이다.

  1. 개봉 수단(Shamir 조각 등)을 클러스터 밖에서 꺼낸다. sealing key 백업이 차지하던 “잃으면 끝”의 자리를 이것이 물려받는다

  2. Vault를 설치하고 Raft 스냅샷을 복원한 뒤 개봉한다

  3. VSO와 참조 CR을 Argo CD로 동기화한다 → VSO가 인증하고 값을 읽어 Secret들이 되살아난다

  4. Vault로 아직 못 옮겼거나 Vault 이전에 필요한 값(예: Vault 스냅샷이 든 버킷의 접근 키)은 이 경로로 못 되살린다 — 그 몇 개가 Sealed Secrets나 암호화 백업으로 남겨 둘 몫이다

그래서 실제 그림은 “VSO가 Sealed Secrets를 밀어낸다”보다 역할 분담에 가깝다 — 앱 시크릿은 금고 경로로 옮기고, Sealed Secrets는 금고를 되살리는 데 필요한 최소한으로 줄어든다.

  • 이 경로의 본질은 신뢰 근거의 교체다 — Sealed Secrets의 암호화 수학 대신 신원 · 정책 · 감사. Git에는 값이 어떤 형태로도 올라가지 않는다
  • Vault는 저장만 하는 금고가 아니다 — 재시작하면 잠기는 개봉 모델, 자체 Raft 저장, KV·database·PKI 엔진, 경로 기반 정책과 감사 로그가 한 몸이다
  • 온프렘의 첫 관문은 개봉이다 — KMS가 없으니 재시작마다 사람이 낄지, 별도 장치를 둘지를 설계 단계에서 정한다
  • VSO의 인증에는 시드 시크릿이 없다 — 쿠버네티스가 보증하는 SA 토큰을 Vault가 TokenReview로 되물어 확인하고, role이 그 신원에 정책을 묶는다
  • 회전이 커밋 없이 끝난다 — 금고에서 바꾸면 VSO가 Secret을 갱신하고 지정한 워크로드를 재시작한다. 동적 시크릿은 발급 자체가 회전이다
  • 다만 런타임 Secret의 노출면은 그대로다 — VSO도 결국 평범한 Secret을 만들므로 클러스터 안쪽은 RBAC과 저장 시 암호화가 맡는다
  • 급소는 sealing key에서 Raft 스냅샷 + 개봉 수단으로 옮겨 간다. Vault 이전에 필요한 값 몇 개는 여전히 Sealed Secrets 몫이다 — 대체가 아니라 역할 분담이다