이 글은 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을 사용하는 방법을 가이드합니다.