[Kubernetes] Vault Secrets Operator(VSO) 활용해 Kubernetes Secrets를 유출 없이 사용하기
![[Kubernetes] Vault Secrets Operator(VSO) 활용해 Kubernetes Secrets를 유출 없이 사용하기](/content/images/size/w1200/2025/02/img.jpg)
도입 배경
현재 인프라는 Kubernetes Manifest 파일을 GitHub에 저장하고, ArgoCD를 통해 자동 배포가 이루어지도록 구성되어 있다.
하지만 Secrets를 생성하려면 Manifest 파일에 민감한 정보를 작성해야 하며, 이를 Git에 Push하면 민감정보가 Public하게 공개되는 문제가 발생하였다.
이를 해결하기 위해, 민감한 정보는 Vault에 저장하고, VSO(Vault Secrets Operator)를 사용하여 Vault에서 Secret 값을 가져와 Kubernetes Secrets를 자동으로 생성하는 방식으로 민감정보를 공개하지않고 사용하고자한다.
Vault란? (간단하게)
Vault는 Kubernetes Secrets를 안전하게 관리하기 위해 사용된다.
Secrets 정보를 포함한 Manifest 파일이 GitHub 같은 레포지토리에 실수로 업로드될 경우, 중요한 정보가 유출될 위험이 있다.
이를 방지하기 위해 Vault에 Secrets를 미리 저장하고, Kubernetes에서는 VSO(Vault Secrets Operator)를 사용해 Vault에 저장된 Secrets를 Kubernetes Secrets로 동기화하여, Secrets에 대한 정보가 유출되는 것을 최소화한다.
VSO(Vault Secrets Operator)란?
간단하게 Vault Secrets에 생성된 Secrets을 Kubernetes Secrets으로 사용할 수 Operator이다.
Vault에서 가져온 Secret 정보를 Kubernetes Secrets으로 생성하며, Vault에서 발생하는 모든 변경사항이 Kubernetes의 Secrets에도 사용자가 설정한 주기만큼 체크하여 거의 실시간으로 반영된다.

1. Vault Secrets Operator 설치
우선 helm을 사용하여 Vault Secrets Operator를 설치해준다.
$ helm install -n vault vault-secrets-operator hashicorp/vault-secrets-operator \
--set "defaultVaultConnection.enabled=true"
※ Vault Endpoint의 경우 추후 관리 용이성을 위해 VaultConnection을 사용해서 Endpoint를 지정하여 Vault를 지정할 것이지만, 만약 VaultConnection 없이 사용하고 싶으면 아래와 같은 옵션을 추가하면 된다.
--set "defaultVaultConnection.address=http://vault-internal.vault.svc.cluster.local:8200"
2. Vault Secrets 생성
Vault에서는 표준 kv v2엔진을 사용하는 kvv2라는 이름을 가진 Secrets Engine을 생성해준다.
$ vault secrets enable -path=kvv2 kv-v2
그 후 아래와 같이 username, password라는 K/V 형태의 데이터를 넣어준다.
$ vault kv put kvv2/home-iot username="root" password="password"
Vault Role을 생성 후 Vault Role에 권한을 줘야하기에 Policy를 미리 생성 해주자.
$ vault policy write home-iot - <<EOF
path "kvv2/data/home-iot" {
capabilities = ["read"]
}
path "kvv2/metadata/home-iot" {
capabilities = ["read"]
}
EOF
※ path의 경로는 Vault Secrets이 저장된 API Path를 적어주면 된다.
kubernetes로 이동 후 아래와 같이 Vault에서 Secrets을 가져올 namespace에서 Service Account와 Secret를 생성해준다.
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: vault-auth
---
apiVersion: v1
kind: Secret
metadata:
name: vault-auth
annotations:
kubernetes.io/service-account.name: vault-auth
type: kubernetes.io/service-account-token
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: role-tokenreview-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:auth-delegator
subjects:
- kind: ServiceAccount
name: vault-auth
namespace: default
생성이 잘 되었다면 아래와 같이 TOKEN과 CA, HOST를 미리 가져와준다.
가져오는 이유는, 추후 생성할 VaultAuth에서 Kubernetes 방식으로 인증할꺼기 때문이다.
TOKEN_REVIEW_JWT=$(kubectl get secret vault-auth --output='go-template={{ .data.token }}' | base64 --decode)
KUBE_CA_CERT=$(kubectl config view --raw --minify --flatten --output='jsonpath={.clusters[].cluster.certificate-authority-data}' | base64 --decode)
KUBE_HOST=$(kubectl config view --raw --minify --flatten --output='jsonpath={.clusters[].cluster.server}')
vault에서 Kubernetes 인증을 활성화 해준다.
vault auth enable -path=vso kubernetesvault write auth/vso/config \
token_reviewer_jwt="$TOKEN_REVIEW_JWT" \
kubernetes_host="$KUBE_HOST" \
kubernetes_ca_cert="$KUBE_CA_CERT" \
disable_issuer_verification=truevault write auth/vso/role/vso-role \
bound_service_account_names=default \
bound_service_account_namespaces=default \
policies=home-iot \
ttl=24h
3. VaultConnection, VaultAuth, VaultStaticSecret 생성
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultConnection
metadata:
name: vault-connection
spec:
# address to the Vault server.
address: http://vault.vault.svc.cluster.local:8200
skipTLSVerify: trueapiVersion: secrets.hashicorp.com/v1beta1
kind: VaultAuth
metadata:
name: vault-auth
spec:
vaultConnectionRef: vault-connection
method: kubernetes
mount: vso
kubernetes:
role: vso-role
serviceAccount: defaultapiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
name: home-iot-secret
spec:
type: kv-v2 # 시크릿 type 입력
mount: kvv2 # 마운트 할 Secret Engine name 입력
path: home-iot # 시크릿을 생성할 때 설정한 secret name을 설정한다.
destination:
name: home-iot-secret # kubernetes secret으로 생성할 kubernetes secret name 입력
create: true # kubernetes secret으로 생성여부
refreshAfter: 60s # vault secret과 kuberentes secret 동기화 시간 입력
vaultAuthRef: vault-auth
VaultStaticSecret에서 지정한 VaultAuth에 작성되어있는 인증 방식을 참고하여, VaultConnection에 작성된 Vault Address로 접근하여 인증 후 정상적으로 인증이 완료되면, home-iot-secret이라는 Kubernetes Secrets을 생성하며, 해당 Secrets에 Vault Secret Engine에서 생성한 Secrets 데이터가 들어간 것을 볼 수 있다.
4. Using Secrets
아래와 같은 형태로 일반적은 Kubernetes Secrets을 불러오는 방식으로 사용할 수 있다.
spec:
containers:
- name: home-iot
image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
imagePullPolicy: Always
env:
- name: USERNAME
valueFrom:
secretKeyRef:
name: home-iot-secret
key: USERNAME
- name: PASSWORD
valueFrom:
secretKeyRef:
name: home-iot-secret
key: PASSWORD