[CI/CD Study 8주차] Vault - Vault Secret Operator
- -
CoudNet@ 팀의 가시다님께서 리딩하시는 CI/CD Study 8주차 스터디 내용 정리
이번 주차는 스터디의 마지막 8주차로 Vault 에 대해 더 자세히 배운 시간이었습니다.
이번 글에서는 Vault Secret Operator 에 대해서 학습한 내용을 서술했습니다.
1. Vault Secret Operator (VSO)

VSO 는 HashiCorp Vault에 저장된 비밀 정보(Secret)를 Kubernetes 리소스로 자동 동기화해주는 오퍼레이터
1.1. 핵심 개념
- Vault Secret 을 Kubernetes Secret 으로 변환/동기화
- Kubernetes CRD(Custom Resource Definition) 기반으로 동작
- 애플리케이션이 Vault 를 직접 호출하지 않고 VSO 가 대신 수행
사용 이유
- 애플리케이션 코드에서 Vault 연동 로직 제거 가능
- Secret Rotation 시 자동 반영
- Kubernetes Native 방식의 Secret 관리
1.2. 주요 구성 요소
1. VaultSecretsOperator
- VSO 컨트롤러
2. VaultAuth
- Vault 인증 방식 정의 (AppRole, Kubernetes Auth)
3. VaultStaticSecret
- 고정 Secret 요청
4. VaultDynamicSecret
- 동적 Secret 요청 (TTL)
1.3. VSO 동작
1. kubernetes 에 VaultAuth, VaultStaticSecret 등의 VSO CRD 배포
2. VSO 가 VaultAuth CRD 를 통해 Vault 인증
3. 배포된 VaultSaticSecret, VaultDynamicSecret Vault Secret 조회
4. Kubernetes Secret 으로 생성/변경
2. 실습 환경 구성
2.1 Kind 구성
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
# kind 노드에 기본 툴 설치
docker exec -it myk8s-control-plane sh -c 'apt update && apt install tree psmisc lsof wget net-tools dnsutils tcpdump ngrep iputils-ping git vim -y'
2.2. Vault 배포 - Dev 모드
# VSO 실습 코드
git clone https://github.com/hashicorp-education/learn-vault-secrets-operator
# Vault Values 생성
cat <<EOF > ./learn-vault-secrets-operator/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
# VSO 배포
helm install vault hashicorp/vault -n vault --create-namespace --values ./learn-vault-secrets-operator/vault-values.yaml --version 0.30.0
Vault 실습용 설정
# Vault Login
export VAULT_ADDR='http://localhost:30000'
vault login
### 암호는 root 로 지정
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 ulsQ0rcAQR9CaevdXSGGgzZp
token_duration ∞
token_renewable false
token_policies ["root"]
identity_policies []
policies ["root"]
# k8s 인증 활성화
vault auth enable -path demo-auth-mount kubernetes
vault write auth/demo-auth-mount/config kubernetes_host="https://kubernetes.default.svc"
# Vault Secret 엔진 활성화
vault secrets enable -path=kvv2 kv-v2
# Vault Policy 생성
tee webapp.json <<EOF
path "kvv2/data/webapp/config" {
capabilities = ["read", "list"]
}
EOF
vault policy write webapp webapp.json
# k8s vault 인증 연동
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
# Vault 시크릿 생성
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-13T12:44:06.335642586Z
custom_metadata <nil>
deletion_time n/a
destroyed false
version 1
2.3. VSO 배포 - Helm
Helm 설치할 때, Helm Version 4 이상을 사용하시는 분들은 종종 오류가 발생할 수 있습니다.
우선 실습인만큼 Helm Version3 으로 설치를 하면 문제없이 배포가 됩니다.
# Helm Install
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
# 배포 확인
kubectl get-all -n vault-secrets-operator-system

3. Static Secret 활용

정적 시크릿은 이때까지 활용했던 Vault 내용과 동일합니다.
다만, VSO 가 대신 Vault 를 접근한다는 것이 다릅니다.
Static Secret 시나리오
1. Vault 에 Secret/Policy/Role 생성
2. VSO 가 Vault 에 로그인 후 Token 받음
3. VSO 가 전달받은 Token 으로 Vault 에 Secret 요청 후 받음
4. VSO 는 K8S Secret 에 값 업데이트
5. VSO 는 주기적으로 Vault 에 Secret 요청 후 받음
주요 사용 CRD
- VaultAuth : VSO 가 Vault 로그인 시 사용
- VaultStaticSecret : VSO 가 Vault 에 StaticSecret 요청 시 사용
3.1. 정적 시크릿 배포 및 연동
# 네임스페이스 배포
kubectl create ns app
# VaultAuth CRD 배포
kubectl apply -f vault/vault-auth-static.yaml
# VaultStaticSecret CRD 배포
kubectl apply -f vault/static-secret.yaml
3.2. 정적 시크릿 변경
# K8S Secret 값 확인 - Krew 플러그인
kubectl krew install view-secret
kubectl view-secret -n app secretkv --all
'''
_raw='{"data":{"password":"static-password","username":"static-user"},"metadata":{"created_time":"2025-12-13T12:44:06.335642586Z","custom_metadata":null,"deletion_time":"","destroyed":false,"version":1}}'
password='static-password'
username='static-user'
'''
# Secret 변경
vault kv put kvv2/webapp/config username="static-user2" password="static-password2"
# Secret 값 재조회
kubectl view-secret -n app secretkv --all
'''
_raw='{"data":{"password":"static-password2","username":"static-user2"},"metadata":{"created_time":"2025-12-13T13:00:28.941140014Z","custom_metadata":null,"deletion_time":"","destroyed":false,"version":2}}'
password='static-password2'
username='static-user2'
'''
4. Dynamic Secret 활용

동적 암호 주기 관리는 Vault 가 자동으로 암호를 갱신(삭제/재생성)하고,
VSO가 해당 암호를 K8S Secret 에 동기화하는 방식으로 구성됩니다.
주요 사용 CRD
- VaultAuth : VSO 가 Vault 로그인 시 사용
- VaultDynamicSecret : VSO 가 Vault 에 DynamicSecret 요청 시 사용
4.1. PostgreSQL 파드 배포 및 Vault Database Secret Engine 설정

# NS 배포
kubectl create ns postgres
# PostgreSQL 배포 : 암호 secret-pass
helm upgrade --install postgres bitnami/postgresql --namespace postgres --set auth.audit.logConnections=true --set auth.postgresPassword=secret-pass
# DB 엔진 활성화
vault secrets enable -path=demo-db database
# vault 에 DB 정보 설정
vault write demo-db/config/demo-db \
plugin_name=postgresql-database-plugin \
allowed_roles="dev-postgres" \
connection_url="postgresql://{{username}}:{{password}}@postgres-postgresql.postgres.svc.cluster.local:5432/postgres?sslmode=disable" \
username="postgres" \
password="secret-pass"
# DB 사용자 동적 Role 생성
vault write demo-db/roles/dev-postgres \
db_name=demo-db \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \
GRANT ALL PRIVILEGES ON DATABASE postgres TO \"{{name}}\";" \
revocation_statements="REVOKE ALL ON DATABASE postgres FROM \"{{name}}\";" \
backend=demo-db \
name=dev-postgres \
default_ttl="10m" \
max_ttl="20m"
# Vault 정책 생성
vault policy write demo-auth-policy-db - <<EOF
path "demo-db/creds/dev-postgres" {
capabilities = ["read"]
}
EOF
4.2. Dynamic Secret 을 활용하는 Application 배포
# NS 배포
kubectl create ns demo-ns
# App 배포
kubectl apply -f dynamic-secrets/.
# Username 시크릿 정보 확인
kubectl exec -it deploy/vso-db-demo -n demo-ns -- cat /etc/secrets/username ; echo
# Password 시크릿 정보 확인
kubectl exec -it deploy/vso-db-demo -n demo-ns -- cat /etc/secrets/password ; echo
5. PKI Secret 활용
VSO 에서 애플리케이션이 PKI 인증서를 사용할 때, 동적으로 변경된 PKI 인증서를 사용할 수 있도록 지원
5.1. PKI Secret 생성
# PKI 엔진 활성화
vault secrets enable pki
# PKI TTL 설정
vault secrets tune -max-lease-ttl=8760h pki
# PKI 생성
vault write pki/root/generate/internal \
common_name=example.com \
ttl=8760h

PKI 추가 설정
# 인증서 발급자(CA) 접근 URL, CRL(인증서 폐기 목록) 접근 URL 설정
vault write pki/config/urls \
issuing_certificates="http://vault.vault.svc:8200/v1/pki/ca" \
crl_distribution_points="http://vault.vault.svc:8200/v1/pki/crl"
# 인증서 생성
vault write pki/roles/example-dot-com \
allowed_domains=example.com \
allow_subdomains=true \
max_ttl=72h
# Vault Policy 생성
vault policy write pki - <<EOF
path "pki*" { capabilities = ["read", "list"] }
path "pki/sign/example-dot-com" { capabilities = ["create", "update"] }
path "pki/issue/example-dot-com" { capabilities = ["create"] }
EOF
5.2. PKI Secret 활용
# k8s 인증 활성화
vault auth enable kubernetes
vault write auth/kubernetes/config kubernetes_host="https://kubernetes.default.svc"
# K8S 인증 Role 생성
vault write auth/kubernetes/role/issuer \
bound_service_account_names=issuer \
bound_service_account_namespaces=default \
policies=pki \
ttl=20m
Cert Manager 배포
# Cert Manager CRD 배포
kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v1.12.3/cert-manager.crds.yaml
# NS 생성
kubectl create namespace cert-manager
helm repo add jetstack https://charts.jetstack.io && helm repo update
# Cert Manager 설치
helm install cert-manager --namespace cert-manager --version v1.12.3 jetstack/cert-manager
인증서 활용
# NS 생성
kubectl create serviceaccount issuer
# Issuer Secret 배포
cat >> issuer-secret.yaml <<EOF
apiVersion: v1
kind: Secret
metadata:
name: issuer-token-lmzpj
annotations:
kubernetes.io/service-account.name: issuer
type: kubernetes.io/service-account-token
EOF
kubectl apply -f issuer-secret.yaml
# Vault Issuer 배포
ISSUER_SECRET_REF=$(kubectl get secrets --output=json | jq -r '.items[].metadata | select(.name|startswith("issuer-token-")).name')
cat > vault-issuer.yaml <<EOF
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: vault-issuer
namespace: default
spec:
vault:
server: http://vault.vault.svc:8200
path: pki/sign/example-dot-com
auth:
kubernetes:
mountPath: /v1/auth/kubernetes
role: issuer
secretRef:
name: $ISSUER_SECRET_REF
key: token
EOF
kubectl apply --filename vault-issuer.yaml
# example-com 인증서 생성
cat > example-com-cert.yaml <<EOF
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: example-com
namespace: default
spec:
secretName: $ISSUER_SECRET_REF
issuerRef:
name: vault-issuer
commonName: www.example.com
dnsNames:
- www.example.com
EOF
kubectl apply --filename example-com-cert.yaml
# 인증서 확인
kubectl get certificate.cert-manager.io/example-com -owide
UI 에서도 인증서를 확인할 수 있습니다.
