Ssoon

HashiCorp Vault - Vault Secrets Operator 본문

CICD Study [1기]

HashiCorp Vault - Vault Secrets Operator

구구달스 2025. 12. 7. 20:30

 

🧩 Vault Secrets Operator란

HashiCorp Vault와 쿠버네티스 환경을 연결해 주는 “Operator”입니다. 즉, Vault에 저장된 시크릿(secret)을 Kubernetes 내에서 네이티브하게 사용하도록 해 줍니다.

  • Vault와 Kubernetes를 통합하는 새로운 공식 방식으로, Vault 1.13 릴리스와 함께 베타로 도입되었습니다.
  • VSO를 사용하면, 애플리케이션은 Kubernetes의 Secret으로만 비밀정보를 참조하면 되고 — Vault API 호출이나 Vault-특화 로직을 애플리케이션에 넣을 필요가 없습니다. 

🛠️ 핵심 동작 방식 (Architecture & Workflow)

▶️ CRD (Custom Resource Definitions) 기반

  • VSO는 Kubernetes의 Custom Resource Definitions (CRD)를 사용합니다. (
  • Vault의 시크릿을 Kubernetes Secret에 동기화하기 위한 명세(spec)는 이 CRD 객체들로 정의됩니다. 
  • VSO는 CRD 객체의 변화(생성, 수정, 삭제 등)를 watch 하여, Vault ↔ Kubernetes Secret 동기화를 유지합니다. 

▶️ Vault → Kubernetes Secret 동기화

  • Vault에 저장된 secret data를 받아서, 대상 Kubernetes Secret에 직접 작성(write)합니다. 그래서 애플리케이션은 단순히 Kubernetes Secret만 참조해도 됩니다. 
  • Vault에서 secret이 변경되면, VSO가 이를 감지하고 자동으로 대상 Secret을 업데이트(sync)합니다.

▶️ 유형별 Secret 지원

VSO는 단순한 정적(secret key/value) 뿐 아니라, Vault가 제공하는 다양한 secret engine을 지원합니다. 

  • Static secrets (예: KV 엔진 기반)
  • Dynamic secrets (DB credential, token 등 Vault 동적 secret)
  • PKI 기반 secrets (TLS 인증서) — 즉, 인증서 발급/갱신/폐기까지 가능

✅ 주요 기능 & 특성 (Features & Capabilities)

  • Vault의 “모든(secret) 엔진(secret engines)”을 지원. 
  • Vault ↔ Kubernetes 간 통신 시 TLS 또는 mTLS 지원. 
  • 여러 Vault 인스턴스로부터 secret 동기화 가능 (multi-Vault support). 
  • Secret drift (Vault vs Kubernetes Secret 간의 불일치) 자동 탐지 및 remediation. 
  • Deployment, ReplicaSet, StatefulSet 리소스에 대해 secret rotation 시 자동 롤링 업데이트 가능. 
  • secret data transformation 지원 (필요시 secret format 변환 등) 
  • Prometheus 계측(instrumentation) 제공 → Operator 상태/metrics 모니터링 가능. 
  • 설치 편의: 공식 Helm chart 또는 Kustomize 사용 가능. 

📦 CRD 종류 및 사용 예

VSO에서는 주요하게 다음 CRD를 사용합니다.

CRD 이름 설명 / 사용 목적

VaultConnection Vault 서버의 주소(Vault endpoint) + TLS 설정 등을 정의. 여러 Vault로의 연결도 설정 가능. 
VaultAuth Vault 인증(authentication) 설정 (예: Kubernetes Auth, AppRole, Cloud-specific 등) 정의. 
VaultStaticSecret Vault에 저장된 정적 secret (KV 등)을 Kubernetes Secret으로 동기화하기 위한 CRD. 
VaultDynamicSecret Vault의 dynamic secret (예: database credential, dynamic token 등) 동기화 위한 CRD. (지원됨)
VaultPKISecret Vault의 PKI secret engine을 통해 발급된 인증서(certificate)를 Kubernetes TLS Secret으로 관리 — 자동 갱신/롤링 포함. 

예를 들어, TLS 인증서가 필요한 Ingress나 Pod에 대해 VaultPKISecret CR을 정의하면, VSO가 Vault에서 인증서를 받아 Kubernetes TLS Secret으로 생성해 주고, 만료 전에 자동 갱신까지 처리합니다. 


🧰 설치 & 구성 방법 (Getting Started)

1. 설치

  • 공식 Helm chart 사용이 가장 일반적입니다. hashicorp/vault-secrets-operator 차트가 제공됩니다. (Bryan Krausen)
  • Helm repo 등록 → helm install 또는 upgrade --install 방식으로 설치. (Bryan Krausen)
  • 설치 후, VSO 전용 네임스페이스가 생성되며, 관련 CRD (VaultConnection, VaultAuth, VaultStaticSecret 등)가 클러스터에 등록됩니다. (Bryan Krausen)

2. Vault 연결 & 인증 설정

  • VaultConnection CR을 생성해 Vault 서버 주소와 TLS 설정을 지정합니다. (Bryan Krausen)
  • VaultAuth CR을 생성해 어떤 방식(authMethod)으로 Vault에 인증할지 정합니다.
    • 일반적으로 Kubernetes Auth Method가 많이 사용됩니다. 이 경우 ServiceAccount를 사용해서 인증합니다. (HashiCorp | An IBM Company)
    • 필요에 따라 AppRole, Cloud-specific auth (AWS IAM, GCP, Azure 등) 도 설정 가능하도록 설계되어 있습니다. (Bryan Krausen)

3. Secret CR 생성 → Kubernetes Secret 자동 생성

  • 예: VaultStaticSecret CR을 생성하면 Vault의 key/value secret이 Kubernetes의 Secret으로 자동 생성됩니다. (Bryan Krausen)
  • 생성된 Secret은 Deployment 혹은 Pod manifest에서 env 또는 volumeMount 등을 통해 참조할 수 있습니다. (벨로그)
  • Vault의 secret이 갱신되면, VSO가 이를 감지하여 Kubernetes Secret도 갱신합니다. 필요하면 Deployment 등 리소스에 롤링 업데이트 트리거도 가능합니다. (HashiCorp | An IBM Company)

🔐 보안 & 운영 고려사항

  • VSO는 Vault ↔ Kubernetes 간 TLS 또는 mTLS 통신을 지원하며, VaultAuth CR 통해 인증 방식을 정의합니다. 
  • Vault 엔진은 Vault Enterprise 또는 Community 버전 (>=1.11)을 공식 지원합니다. 
  • 여러 Vault 인스턴스 지원 → 멀티-클라우드 또는 멀티-환경에서 유용. 
  • Operator 상태, 동기화 현황, 실패율 등을 Prometheus metrics로 수집 가능 → 모니터링 & 알람 체계 구성 가능. 

✅실습 (Vault Secrets Operator (VSO) 설치)

  • K8S (kind) 설치
(⎈|N/A:N/A) ssoon@DESKTOP-72C919S:~$ kind create cluster --name myk8s --image kindest/node:v1.32.8 --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  labels:
    ingress-ready: true
  extraPortMappings:
  - containerPort: 80
    hostPort: 80
    protocol: TCP
  - containerPort: 443
    hostPort: 443
    protocol: TCP
  - containerPort: 30000  # Vault Web UI
    hostPort: 30000
  - containerPort: 30001  # Sample application
    hostPort: 30001
EOF
Creating cluster "myk8s" ...
 ✓ Ensuring node image (kindest/node:v1.32.8) 🖼
 ✓ Preparing nodes 📦
 ✓ Writing configuration 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
Set kubectl context to "kind-myk8s"
You can now use your cluster with:

kubectl cluster-info --context kind-myk8s

Thanks for using kind! 😊
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ docker ps
CONTAINER ID   IMAGE                  COMMAND                  CREATED          STATUS          PORTS                                                                                                       NAMES
f82496892a2b   kindest/node:v1.32.8   "/usr/local/bin/entr…"   27 seconds ago   Up 24 seconds   0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp, 0.0.0.0:30000-30001->30000-30001/tcp, 127.0.0.1:38711->6443/tcp   myk8s-control-plane
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ kubectl get node
NAME                  STATUS     ROLES           AGE   VERSION
myk8s-control-plane   NotReady   control-plane   21s   v1.32.8
  •  Vault Dev 모드 설치 및 Pod 정상 실행
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ helm search repo hashicorp/vault
NAME                                    CHART VERSION   APP VERSION     DESCRIPTION
hashicorp/vault                         0.31.0          1.20.4          Official HashiCorp Vault Chart
hashicorp/vault-secrets-gateway         0.0.2           0.1.0           A Helm chart for Kubernetes
hashicorp/vault-secrets-operator        1.0.1           1.0.1           Official Vault Secrets Operator Chart
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ git clone https://github.com/hashicorp-education/learn-vault-secrets-operator
Cloning into 'learn-vault-secrets-operator'...
remote: Enumerating objects: 208, done.
remote: Counting objects: 100% (93/93), done.
remote: Compressing objects: 100% (47/47), done.
remote: Total 208 (delta 49), reused 62 (delta 46), pack-reused 115 (from 1)
Receiving objects: 100% (208/208), 42.14 KiB | 5.27 MiB/s, done.
Resolving deltas: 100% (113/113), done.
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ cd learn-vault-secrets-operator
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~/learn-vault-secrets-operator$ cat <<EOF > vault-values.yaml
server:
  image:
    repository: "hashicorp/vault"
    tag: "1.19.0"

  dev:
    enabled: true
    devRootToken: "root"

  logLevel: debug

  service:
    enabled: true
    type: ClusterIP
    port: 8200
    targetPort: 8200

ui:
  enabled: true
  serviceType: "NodePort"
  externalPort: 8200
  serviceNodePort: 30000

injector:
  enabled: "false"
EOF
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~/learn-vault-secrets-operator$ helm install vault hashicorp/vault -n vault --create-namespace --values vault-values.yaml --version 0.30.0
NAME: vault
LAST DEPLOYED: Mon Dec  8 21:39:45 2025
NAMESPACE: vault
STATUS: deployed
REVISION: 1
NOTES:
Thank you for installing HashiCorp Vault!

Now that you have deployed Vault, you should look over the docs on using
Vault with Kubernetes available here:

https://developer.hashicorp.com/vault/docs


Your release is named vault. To learn more about the release, try:

  $ helm status vault
  $ helm get manifest vault
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~/learn-vault-secrets-operator$ kubectl get pods -n vault
NAME      READY   STATUS    RESTARTS   AGE
vault-0   1/1     Running   0          22s
  • Vault Dev 모드에서 Kubernetes Auth 및 KV 시크릿 설정 완료
    • Vault는 Dev 모드로 실행 중 (root 토큰 사용).
    • Kubernetes 인증 메서드(demo-auth-mount) 활성화 및 기본 설정 완료 (kubernetes_host=https://kubernetes.default.svc).
    • KV v2 시크릿 엔진(kvv2/) 활성화.
    • 정책(webapp) 생성 → kvv2/data/webapp/config 경로에 read, list 권한 부여.
    • Role(role1) 생성 → app 네임스페이스의 demo-static-app SA에 정책 매핑.
    • 시크릿 저장 완료:
      • 경로: kvv2/webapp/config
      • 값: username=static-user, password=static-password
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ export VAULT_ADDR='http://localhost:30000'
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ vault login
Token (will be hidden):
Success! You are now authenticated. The token information displayed below
is already stored in the token helper. You do NOT need to run "vault login"
again. Future Vault requests will automatically use this token.

Key                  Value
---                  -----
token                root
token_accessor       DDOVnLUT5FserFPjxZyU6VlE
token_duration       ∞
token_renewable      false
token_policies       ["root"]
identity_policies    []
policies             ["root"]
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ vault auth enable -path demo-auth-mount kubernetes
Success! Enabled kubernetes auth method at: demo-auth-mount/
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ vault write auth/demo-auth-mount/config kubernetes_host="https://kubernetes.default.svc"
Success! Data written to: auth/demo-auth-mount/config
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ vault secrets enable -path=kvv2 kv-v2
Success! Enabled the kv-v2 secrets engine at: kvv2/
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ tee webapp.json <<EOF
path "kvv2/data/webapp/config" {
   capabilities = ["read", "list"]
}
EOF
path "kvv2/data/webapp/config" {
   capabilities = ["read", "list"]
}
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ vault policy write webapp webapp.json
Success! Uploaded policy: webapp
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ vault write auth/demo-auth-mount/role/role1 \
   bound_service_account_names=demo-static-app \
   bound_service_account_namespaces=app \
   policies=webapp \
   audience=vault \
   ttl=24h
Success! Data written to: auth/demo-auth-mount/role/role1
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ vault kv put kvv2/webapp/config username="static-user" password="static-password"
===== Secret Path =====
kvv2/data/webapp/config

======= Metadata =======
Key                Value
---                -----
created_time       2025-12-08T12:43:12.51125992Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1
  • Vault Secrets Operator 설치 실패 원인 및 해결 방법
    • Helm 설치 실패 이유: vault/vault-operator-values.yaml 파일이 존재하지 않음 → open vault/vault-operator-values.yaml: no such file or directory.
    • 현재 클러스터에는 Vault(Chart 0.30.0, Dev 모드)만 설치되어 있고, Vault Secrets Operator는 설치되지 않음.
    • helm uninstall 실패 이유: Operator가 설치되지 않았기 때문에 릴리스가 없음.
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ helm search repo hashicorp/vault
NAME                                    CHART VERSION   APP VERSION     DESCRIPTION
hashicorp/vault                         0.31.0          1.20.4          Official HashiCorp Vault Chart
hashicorp/vault-secrets-gateway         0.0.2           0.1.0           A Helm chart for Kubernetes
hashicorp/vault-secrets-operator        1.0.1           1.0.1           Official Vault Secrets Operator Chart
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ cat vault/vault-operator-values.yaml
cat: vault/vault-operator-values.yaml: No such file or directory
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ helm install vault-secrets-operator hashicorp/vault-secrets-operator -n vault-secrets-operator-system --create-namespace --values vault/vault-operator-values.yaml --version 0.7.1
Error: INSTALLATION FAILED: open vault/vault-operator-values.yaml: no such file or directory
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ helm list -A
NAME    NAMESPACE       REVISION        UPDATED                                 STATUS          CHART           APP VERSION
vault   vault           1               2025-12-08 21:39:45.46110372 +0900 KST  deployed        vault-0.30.0    1.19.0
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ helm uninstall -n vault-secrets-operator-system vault-secrets-operator
Error: uninstall: Release not loaded: vault-secrets-operator: release: not found
  • Vault Secrets Operator 설치
    • Helm으로 Vault Secrets Operator 설치 성공 (Chart: vault-secrets-operator-0.10.0, Namespace: vault-secrets-operator-system).
    • vault-operator-values.yaml에서 기본 Vault 연결 설정:
    • 현재 클러스터에는:
      • Vault (Dev 모드, Chart 0.30.0)
      • Vault Secrets Operator (v0.10.0)
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ docker exec -it myk8s-control-plane bash
root@myk8s-control-plane:/# curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 11929  100 11929    0     0   185k      0 --:--:-- --:--:-- --:--:--  187k
Downloading https://get.helm.sh/helm-v3.19.2-linux-amd64.tar.gz
Verifying checksum... Done.
Preparing to install helm into /usr/local/bin
helm installed into /usr/local/bin/helm
root@myk8s-control-plane:/# helm version
version.BuildInfo{Version:"v3.19.2", GitCommit:"8766e718a0119851f10ddbe4577593a45fadf544", GitTreeState:"clean", GoVersion:"go1.24.9"}
root@myk8s-control-plane:/# helm repo add hashicorp https://helm.releases.hashicorp.com
"hashicorp" has been added to your repositories
root@myk8s-control-plane:/# helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "hashicorp" chart repository
Update Complete. ⎈Happy Helming!⎈
root@myk8s-control-plane:/# cat << EOF > vault-operator-values.yaml
defaultVaultConnection:
  enabled: true
  address: "http://vault.vault.svc.cluster.local:8200"
  skipTLSVerify: false
controller:
  manager:
    clientCache:
      persistenceModel: direct-encrypted
      storageEncryption:
        enabled: true
        mount: k8s-auth-mount
        keyName: vso-client-cache
        transitMount: demo-transit
        kubernetes:
          role: auth-role-operator
          serviceAccount: vault-secrets-operator-controller-manager
          tokenAudiences: ["vault"]
EOF
root@myk8s-control-plane:/# helm install vault-secrets-operator hashicorp/vault-secrets-operator -n vault-secrets-operator-system --create-namespace --values vault-operator-values.yaml --version 0.10.0
NAME: vault-secrets-operator
LAST DEPLOYED: Mon Dec  8 12:48:32 2025
NAMESPACE: vault-secrets-operator-system
STATUS: deployed
REVISION: 1
root@myk8s-control-plane:/# helm list -A
NAME                    NAMESPACE                       REVISION        UPDATED                                    STATUS   CHART                           APP VERSION
vault                   vault                           1               2025-12-08 21:39:45.46110372 +0900 +0900   deployed vault-0.30.0                    1.19.0
vault-secrets-operator  vault-secrets-operator-system   1               2025-12-08 12:48:32.647929785 +0000 UTC    deployed vault-secrets-operator-0.10.0   0.10.0
root@myk8s-control-plane:/# exit
exit
  • Vault Secrets Operator —  Vault Secrets Operator가 정상 설치되었고 CRD들도 준비
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ kubectl get all -n vault-secrets-operator-system
NAME                                                             READY   STATUS    RESTARTS   AGE
pod/vault-secrets-operator-controller-manager-7f67cd89fd-j89lm   2/2     Running   0          82s

NAME                                             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/vault-secrets-operator-metrics-service   ClusterIP   10.96.167.190   <none>        8443/TCP   82s

NAME                                                        READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/vault-secrets-operator-controller-manager   1/1     1            1           82s

NAME                                                                   DESIRED   CURRENT   READY   AGE
replicaset.apps/vault-secrets-operator-controller-manager-7f67cd89fd   1         1         1       82s
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ kubectl get crd | grep secrets.hashicorp.com
hcpauths.secrets.hashicorp.com                2025-12-08T12:48:32Z
hcpvaultsecretsapps.secrets.hashicorp.com     2025-12-08T12:48:32Z
secrettransformations.secrets.hashicorp.com   2025-12-08T12:48:32Z
vaultauthglobals.secrets.hashicorp.com        2025-12-08T12:48:32Z
vaultauths.secrets.hashicorp.com              2025-12-08T12:48:32Z
vaultconnections.secrets.hashicorp.com        2025-12-08T12:48:32Z
vaultdynamicsecrets.secrets.hashicorp.com     2025-12-08T12:48:32Z
vaultpkisecrets.secrets.hashicorp.com         2025-12-08T12:48:32Z
vaultstaticsecrets.secrets.hashicorp.com      2025-12-08T12:48:32Z
  • Vault Secrets Operator 컨트롤러 Pod 상태 확인
    • 네임스페이스: vault-secrets-operator-system
    • ServiceAccount: vault-secrets-operator-controller-manager
    • 컨테이너:
      • kube-rbac-proxy → 이미지: quay.io/brancz/kube-rbac-proxy:v0.18.1
      • manager → 이미지: hashicorp/vault-secrets-operator:0.10.0
    • Pod 상태: Running, 준비 완료 (2/2 컨테이너 정상)
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ kubectl describe pod -n vault-secrets-operator-system
...
Service Account:  vault-secrets-operator-controller-manager
...
Containers:
  kube-rbac-proxy:
    Container ID:  containerd://42becec312e2aede9018887df628350776ee19c0b45e1352c54d5f2a36efcb57
    Image:         quay.io/brancz/kube-rbac-proxy:v0.18.1
...
  manager:
    Container ID:  containerd://347e90d35a86db90e0937e3845515d4a04a0bd9c37a19f5c50854c4675b5b76e
    Image:         hashicorp/vault-secrets-operator:0.10.0
...
  • Vault Secrets Operator 기본 리소스 확인
    • 네임스페이스 vault-secrets-operator-system에 기본 CRD 리소스가 생성됨:
      • VaultConnection: default → Vault 주소 및 연결 설정 관리
      • VaultAuth: vault-secrets-operator-default-transit-auth → Operator가 Vault에 인증하는 데 사용
    • Operator는 정상적으로 Vault와 연결할 준비가 되어 있음.
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ kubectl get vaultconnections,vaultauths -n vault-secrets-operator-system
NAME                                            AGE
vaultconnection.secrets.hashicorp.com/default   5m45s

NAME                                                                          AGE
vaultauth.secrets.hashicorp.com/vault-secrets-operator-default-transit-auth   5m45s
  • Vault Secrets Operator 인증 설정 확인
    • Operator는 Vault에 Kubernetes Auth로 로그인하고, 클라이언트 캐시를 Transit 엔진으로 암호화해 안전하게 관리합니다.
    • 기본 연결(VaultConnection)과 인증(VaultAuth)이 준비되었으므로, 이제 VaultStaticSecret 또는 VaultDynamicSecret을 생성해 Vault 시크릿을 Kubernetes Secret으로 동기화할 수 있습니다.
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ kubectl get vaultauth -n vault-secrets-operator-system vault-secrets-operator-default-transit-auth -o jsonpath='{.spec}' | jq
{
  "kubernetes": {
    "audiences": [
      "vault"
    ],
    "role": "auth-role-operator",
    "serviceAccount": "vault-secrets-operator-controller-manager",
    "tokenExpirationSeconds": 600
  },
  "method": "kubernetes",
  "mount": "k8s-auth-mount",
  "storageEncryption": {
    "keyName": "vso-client-cache",
    "mount": "demo-transit"
  },
  "vaultConnectionRef": "default"
}

✅실습 (secret 배포 및 동기화)

  • VaultAuth는 Vault Operator가 Vault 인증을 자동화·관리하기 위해 사용하는 설정 객체이며,
  • Kubernetes 내 파드가 Vault 비밀값을 안전하게 가져올 수 있도록 Kubernetes Auth Method를 선언적으로 정의하는 리소스입니다.
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ kubectl create ns app
namespace/app created
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ kubectl explain vaultauths
GROUP:      secrets.hashicorp.com
KIND:       VaultAuth
VERSION:    v1beta1
...
  • VaultStaticSecret은 Vault의 정적 비밀값을 Kubernetes 내 Secret 리소스로 자동 생성·갱신하는 CRD이며,
  • VaultAuth와 함께 사용되어 애플리케이션이 별도 코드 없이 Secret을 활용할 수 있도록 돕는 구성 요소입니다.
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ mkdir vault
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ cat <<EOF > vault/vault-auth-static.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  # SA bound to the VSO namespace for transit engine auth
  namespace: vault-secrets-operator-system
  name: demo-operator
---
apiVersion: v1
kind: ServiceAccount
metadata:
  namespace: app
  name: demo-static-app
---
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultAuth
metadata:
  name: static-auth
  namespace: app
spec:
  method: kubernetes
  mount: demo-auth-mount
  kubernetes:
    role: role1
    serviceAccount: demo-static-app
    audiences:
      - vault
EOF
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ kubectl apply -f vault/vault-auth-static.yaml
serviceaccount/demo-operator created
serviceaccount/demo-static-app created
vaultauth.secrets.hashicorp.com/static-auth created
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ kubectl get sa,vaultauth -n app
NAME                             SECRETS   AGE
serviceaccount/default           0         4m47s
serviceaccount/demo-static-app   0         3s

NAME                                          AGE
vaultauth.secrets.hashicorp.com/static-auth   3s
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ kubectl explain vaultstaticsecrets
GROUP:      secrets.hashicorp.com
KIND:       VaultStaticSecret
VERSION:    v1beta1
...
  • Vault → Kubernetes Secret 자동 복제·갱신 설정
    1. VaultStaticSecret 생성
    2. Operator가 VaultAuth(static-auth)를 사용해 Vault에 로그인
    3. Vault의 KV v2 경로(kvv2/webapp/config)에 접근
    4. 비밀값을 Kubernetes Secret(secretkv)로 생성
    5. 이후 30초마다 Vault 내용을 체크하여 변경되면 Secret 업데이트
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ cat  <<EOF > vault/static-secret.yaml
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
  name: vault-kv-app
  namespace: app
spec:
  type: kv-v2

  # mount path
  mount: kvv2

  # path of the secret
  path: webapp/config

  # dest k8s secret
  destination:
    name: secretkv
    create: true

  # static secret refresh interval 시크릿 리프레시 주기
  refreshAfter: 30s

  # Name of the CRD to authenticate to Vault
  vaultAuthRef: static-auth
EOF
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ kubectl apply -f vault/static-secret.yaml
vaultstaticsecret.secrets.hashicorp.com/vault-kv-app created
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ kubectl get vaultstaticsecret -n app
NAME           AGE
vault-kv-app   4s

✅실습 (정적 secret 회전)

  •  Vault → Kubernetes Secret 동기화가 정상적으로 성공
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ kubectl get secret -n app
NAME       TYPE     DATA   AGE
secretkv   Opaque   3      2m9s
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ kubectl krew install view-secret
Updated the local copy of plugin index.
  New plugins available:
    * edit-secret
    * get-resources
    * kfilt
    * mittens
Installing plugin: view-secret
Installed plugin: view-secret
\
 | Use this plugin:
 |      kubectl view-secret
 | Documentation:
 |      https://github.com/elsesiy/kubectl-view-secret
/
WARNING: You installed plugin "view-secret" from the krew-index plugin repository.
   These plugins are not audited for security by the Krew maintainers.
   Run them at your own risk.
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ kubectl view-secret -n app secretkv --all
_raw='{"data":{"password":"static-password","username":"static-user"},"metadata":{"created_time":"2025-12-08T12:43:12.51125992Z","custom_metadata":null,"deletion_time":"","destroyed":false,"version":1}}'
password='static-password'
username='static-user'
  • Vault에서 비밀값을 변경하면 Kubernetes Secret도 30초 내 자동으로 최신 상태로 업데이트
    • Vault에 새로운 값 입력 (v2 생성)
      • Vault 경로: kvv2/webapp/config
      • Secret version: 2
    • Kubernetes Secret 즉시 조회 → 아직 이전 값(version 1)
      • 이는 refreshAfter: 30s(갱신 주기) 때문입니다.
        즉, Operator가 Vault에서 30초 간격으로 변경 여부를 확인함.
    • 30초 후 다시 조회 → 최신 값(version 2)으로 자동 업데이트됨
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ vault kv put kvv2/webapp/config username="static-user2" password="static-password2"
===== Secret Path =====
kvv2/data/webapp/config

======= Metadata =======
Key                Value
---                -----
created_time       2025-12-08T13:14:31.447294741Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            2
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ kubectl view-secret -n app secretkv --all
_raw='{"data":{"password":"static-password","username":"static-user"},"metadata":{"created_time":"2025-12-08T12:43:12.51125992Z","custom_metadata":null,"deletion_time":"","destroyed":false,"version":1}}'
password='static-password'
username='static-user'
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ kubectl view-secret -n app secretkv --all
_raw='{"data":{"password":"static-password2","username":"static-user2"},"metadata":{"created_time":"2025-12-08T13:14:31.447294741Z","custom_metadata":null,"deletion_time":"","destroyed":false,"version":2}}'
password='static-password2'
username='static-user2'
(⎈|kind-myk8s:N/A) ssoon@DESKTOP-72C919S:~$ kubectl get secret -n app
NAME       TYPE     DATA   AGE
secretkv   Opaque   3      4m15s
Comments