
이 글은 kubernetes 클러스터, Helm 설정 및 설치되어 있다고 가정하고, Vault 설정 및 설치를 다룹니다.
Vault는 클라이언트(사용자, 시스템, 앱)에게 비밀 또는 저장된 민감한 데이터에 대한 액세스 권한을 제공하기 전에 유효성을 검사하고 승인합니다.

출처 : https://developer.hashicorp.com/vault/docs/what-is-vault
차트를 Pull 받아서 value를 override하여 배포하는 방식으로 설명합니다.
# Helm Repo 등록
$ helm repo add hashicorp https://helm.releases.hashicorp.com
$ helm repo update
# vault helm chart pull
$ helm pull hashicorp/vault
# 차트 압축해제
$ tar -xvf vault-0.27.0.tgz
server:
enabled: true
ha:
enabled: true
replicas: 3
raft:
enabled: true
image:
repository: "hashicorp/vault"
tag: "1.15.2"
pullPolicy: IfNotPresent
resources:
requests:
memory: 256Mi
cpu: 250m
limits:
memory: 256Mi
cpu: 250m
injector:
hostNetwork: true #calico cni를 사용하는 경우 true 설정
enabled: true
image:
repository: "hashicorp/vault-k8s"
tag: "1.3.1"
pullPolicy: IfNotPresent
resources:
requests:
memory: 256Mi
cpu: 250m
limits:
memory: 256Mi
cpu: 250m
ui:
enabled: true
serviceType: NodePort
serviceNodePort: 8200
vault 데이터를 저장할 backend storage는 raft 방식을 통해서 HA로 구성하고, 3개의 Vault 서버를 사용한다.
Vault UI는 nodeport 8200번을 사용한다.
만약 Calico CNI를 사용하고 있다면 injector.hostNetwork 옵션을 true로 선택해야 한다. (default 옵션은 false이며, Calico 환경에서 실행 시 Vault Injector가 제대로 동작하지 않음)
Vault Agent Injector Annotations are not creating : External EKS #731
Raft란?
Raft는 분산 시스템에서 일관성을 유지하기 위한 합의 알고리즘 중 하나로, 클러스터 내의 서버간에 데이터 일관성을 보장하고 다수결 합의를 기반으로 리더 서버를 선택하여 동작한다. Raft는 다운되거나 장애가 발생한 서버를 자동으로 복구할 수 있으며, 높은 가용성과 안정적인 운영을 보장한다.
# vault 네임스페이스 생성
$ kubectl crate ns vault
# helm install
$ cd vault
$ helm install vault . -f override-values.yaml
Vault Helm 차트를 설치한 후 Vault 서버 중 하나를 초기화 해야 합니다 . 초기화하면 모든 Vault 서버의 봉인을 해제하는 데 필요한 자격 증명이 생성된다.
$ kubectl get pods --selector='app.kubernetes.io/name=vault' -n vault
NAME READY STATUS RESTARTS AGE
vault-0 0/1 Running 0 45s
vault-1 0/1 Running 0 45s
vault-2 0/1 Running 0 45s
초기에 READY 0/1 상태로 나오는 것이 정상이다. 여기서 초기화 및 봉인해제를 해야 1/1 로 변경된다.
$ kubectl exec -it vault-0 -n vault -- /bin/sh
#기본값 key-share=5 key-threshold=3
$ vault operator init
Unseal Key 1: q7hVrE9DLGZTTUwI248ep/Yv1551w/3NkAN33fyGSTm7
Unseal Key 2: i/mtNO0j6ACYNw8BtD4MAlHuOef4w0kkxhQaaSUS22iy
Unseal Key 3: gsGhzx09Sc6vdVzvraDTEk4nCEYUmGhJHRy5dbYzzm3k
Unseal Key 4: JJfoCquehit/FjrPz0/x8usFIckD38McNT6aa3Jw3ZUI
Unseal Key 5: 7QEK7gBGAfoJx+lk18jY1eW+TzruyKL7Eq866kOOAzpB
#Vault login시 필요한 인증토큰
Initial Root Token: s.1n5HUMIrUz3wzzHxF2ZEquS4
기본 키 공유 수와 기본 키 임계값을 사용하여 하나의 Vault 서버를 초기화합니다.
초기화 후 key 값과 토큰은 꼭 저장해야 한다.
다음과 같이 생성될 키와 인증할 키의 개수를 지정하는 방식으로도 초기화가 가능하다.
vault operator init -key-shares=3 -key-threshold=2
$ vault status
Key Value
--- -----
Seal Type shamir
Initialized true
**Sealed true**
Total Shares 5
Threshold 3
Unseal Progress 0/3
Unseal Nonce n/a
Version 1.15.2
Build Date 2023-11-06T11:33:28Z
Storage Type raft
HA Enabled true
기본적으로 봉인해제를 하지 않으면 위와 같이 Sealed 의 상태가 true로 나오는데 봉인 해제를 해야 false로 변경되고 사용이 가능해진다.
$ vault operator unseal
Unseal Key (will be hidden): {INPUT_YOUR_KEY_LIKE_BELOW}
Key Value
--- -----
Seal Type shamir
Initialized true
**Sealed false**
Total Shares 5
Threshold 3
Version 1.15.2
Build Date 2023-11-06T11:33:28Z
Storage Type raft
Cluster Name vault-cluster-3b4976f8
Cluster ID 0a66788f-05cb-43ab-bf3a-be39e27c6dea
HA Enabled true
vault operator unseal 명령어를 입력 후 초기화 과정에서 생성한 unseal key를 입력해준다.
기본 설정값은 3개의 키를 입력해야 하도록 설정되어 있어, 이 과정을 3번 반복하면 Sealed 의 상태가 false로 변경된다.
$ kubectl exec -it vault-1 -- /bin/sh
$ vault operator raft join http://vault-0.vault-internal:8200
Key Value
--- -----
Joined true
$ kubectl exec -it vault-2 -- /bin/sh
$ vault operator raft join http://vault-0.vault-internal:8200
Key Value
--- -----
Joined true
Join을 진행한 후 위에서 진행했던 unseal 과정을 반복
$ kubectl exec -it vault-0 -- /bin/sh
$ vault login # root token 입력
$ vault operator raft list-peers
Node Address State Voter
---- ------- ----- -----
1dc07f10-68e5-5a6c-f643-b1e28e65668c vault-0.vault-internal:8201 leader true
9920923e-8739-6fde-2ab0-3bc14fe00d9c vault-1.vault-internal:8201 follower true
9db399e2-33aa-e5a4-091a-92801bdab300 vault-2.vault-internal:8201 follower true
처음 봉인해제를 진행한 vault-0 이 leader로 선출되고, 나머지도 피어 리스트 확인
$ kubectl exec -it vault-0 -n vault -- /bin/sh
# Vault init 때 발급받은 root token 입력
$ vault login
# 이름이 secrets인 K/V 엔진 생성
$ vault secrets enable -path=secret kv-v2
Success! Enabled the kv-v2 secrets engine at: secret/
# secret에 데이터 저장
$ vault kv put secret/test ID="test" password="passwd"
== Secret Path ==
secret/data/test
======= Metadata =======
Key Value
--- -----
created_time 2024-01-16T05:59:05.063883379Z
custom_metadata <nil>
deletion_time n/a
destroyed false
version 1
# 저장된 데이터 확인
$ vault kv get secret/test
== Secret Path ==
secret/data/test
======= Metadata =======
Key Value
--- -----
created_time 2024-01-16T05:59:05.063883379Z
custom_metadata <nil>
deletion_time n/a
destroyed false
version 1
====== Data ======
Key Value
--- -----
ID test
password passwd
# kubernetes 인증 활성화
$ vault auth enable kubernetes
Success! Enabled kubernetes auth method at: kubernetes/
# kubernetes 인증 설정
$ vault write auth/kubernetes/config \
token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443" \
kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
Success! Data written to: auth/kubernetes/config
# secret 하위는 read만 가능하도록 설정
$ vault policy write secret-read - <<EOF
path "secret/*" {
capabilities = ["read"]
}
EOF
$ k create serviceaccount test
# scret-read policy와 kubernetes test role 연결
# vault 네임스페이스에 secret-read 서비스어카운트에게 권한부여
# 만료시간 24시간 설정
$ vault write auth/kubernetes/role/test \
bound_service_account_names=test \
bound_service_account_namespaces=vault \
policies=secret-read \
ttl=24h
Vault Secret 사용 방법
Sidecar 형태로 Vault agent가 주입된다. 그렇기에 사용되는 Deployment 특정 annotation 값을 설정해야 한다.주입할 수 있다.vault.hashicorp.com/agent-inject = vault sidecar 주입 여부
vault.hashicorp.com/role = Vault 에이전트 자동 인증 방법에 사용되는 Vault 역할을 구성
vault.hashicorp.com/agent-inject-template-config = Vault Agent가 secret 렌더링에 사용해야 하는 템플릿을 구성
apiVersion: apps/v1
kind: Deployment
metadata:
name: vault-test
namespace: vault
labels:
app: vault-test
spec:
selector:
matchLabels:
app: vault-test
replicas: 1
template:
metadata:
annotations:
vault.hashicorp.com/agent-inject: 'true'
vault.hashicorp.com/role: 'test'
vault.hashicorp.com/agent-inject-template-config: |
{{- with secret "secret/data/test" -}}
export ID="{{ .Data.data.ID }}"
export password="{{ .Data.data.password }}"
{{- end -}}
labels:
app: vault-test
spec:
serviceAccountName: test
containers:
- name: nginx
image: nginx
args: ["sh", "-c", ". /vault/secrets/config && nginx -g 'daemon off;'"]
$ kubectl apply -f deployment.yaml
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
vault-test-77d888f9ff-lvh92 2/2 Running 0 14m
컨테이너는 총 3개이며, init container, vault-agent, nginx 컨테이너가 배포된 걸 확인할 수 있다.
init container는 Vault에서 민감 데이터를 가져오는 역할을 한다.

vault-agent인 sidecar container는 Init container에서 가져온 정보를 주기적으로 동기화 하는 역할을 한다.

vault-agent가 가져온 시크릿이 vault/secret 경로에 매핑되어 있고, vault.hashicorp.com/agent-inject-template-config 설정에서 시크릿을 export하여 args에서 해당 환경변수를 주입해준다.
# nginx 컨테이너 진입
$ kubectl exec -it -c nginx vault-test-77d888f9ff-lvh92 -n vault -- /bin/sh
# 생성된 시크릿 확인
$ cat /vault/secrets/config
export ID="test"
export password="passwd"
다음 글은 Vault-Secrets-Operator를 사용하여 더 간편하게 Vault Secret을 사용하는 방법을 가이드합니다.