Vault HA
kind k8s : worker 3node + ingress-nginx
#
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
hostPort: 30000
- containerPort: 30001
hostPort: 30001
- role: worker
- role: worker
- role: worker
EOF
# NGINX ingress 배포
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
# nodeSelector 지정
kubectl patch deployment ingress-nginx-controller -n ingress-nginx \
--type='merge' \
-p='{
"spec": {
"template": {
"spec": {
"nodeSelector": {
"ingress-ready": "true"
}
}
}
}
}'
# SSL Passthrough flag 활성화 설정 https://kubernetes.github.io/ingress-nginx/user-guide/tls/#ssl-passthrough
kubectl get deployment ingress-nginx-controller -n ingress-nginx -o yaml \
| sed '/- --publish-status-address=localhost/a\
- --enable-ssl-passthrough' | kubectl apply -f -
Vault 소개
HA mode : 1대의 Active 와 나머지 Standby

Workflow : Secrets , Policy , Authentication

- Secrets
- 볼트는 여러 가지 유형의 비밀을 관리할 수 있으며, 각 비밀 유형은 비밀 엔진이라는 독립된 구성 요소에 의해 볼트에서 관리됩니다.
- 비밀 엔진은 데이터를 저장, 생성 또는 암호화하는 구성 요소입니다.
- 비밀 엔진은 데이터를 제공하고, 해당 데이터에 대해 조치를 취하며, 결과를 반환합니다.
- 일부 비밀 엔진은 암호화된 Redis/Memcached와 같이 데이터를 저장하고 읽습니다.
- 다른 비밀 엔진은 다른 서비스에 연결하여 주문형 동적 자격 증명을 생성하고, 서비스로서의 암호화, 시간 기반 일회용 비밀번호(TOTP) 생성, 인증서 등을 제공합니다.
- Policy
- 두 번째 핵심 개념은 정책입니다.
- 볼트의 모든 작업은 비밀에 접근하거나 관리 기능을 수행하라는 요청이 적어도 하나의 정책에 의해 처리된다는 것입니다.
- 정책은 "기본적으로 거부"되며, 빈 정책은 시스템에서 비밀에 접근하거나 사용자나 애플리케이션이 관리 기능을 수행할 수 있는 권한을 부여하지 않습니다.
- 앞서 볼트의 모든 비밀과 관리 작업은 경로 기반이라는 점을 언급했습니다.
- 경로는 정책을 작성할 때 핵심 개념 중 하나입니다.
- 경로는 정책이 적용되는 기능을 정의하며, 여기에는 매우 대략적인 정책에 대한 전역 기반 유닉스 스타일 경로명 표현식이 포함될 수 있습니다.
- 또한 매우 세분화된 정책을 만들기 위한 명시적인 경로도 포함될 수 있습니다.
- Authentication
- 볼트를 사용하면 모든 작업이 토큰으로 보호됩니다.
- 토큰은 볼트 정책에 의해 할당되어 볼트의 비밀 엔진 및 다양한 관리 기능에 대한 액세스 권한을 부여하거나 거부합니다.
- 토큰을 얻으려면 사용자는 다양한 볼트 방법을 사용하여 볼트로 인증해야 합니다.
- 여러 인증 방법을 사용하면 사용 사례와 조직에 맞는 인증 방법을 사용할 수 있습니다.
- 예를 들어, Kubernetes에서 실행 중인 애플리케이션은 Kubernetes 인증을 사용하여 Pod's Service 계정을 사용하여 볼트로 인증할 수 있습니다.
- 동작 설명
- 볼트는 다른 웹 애플리케이션과 마찬가지로 사용자가 애플리케이션에 로그인하려면 인증을 받아야 합니다.
- 또한, 비밀을 저장하거나 검색하거나 볼트를 구성하기 위해서는 볼트 사용자 또는 애플리케이션이 볼트로 인증하고 정책을 할당받아야 합니다.
- 첫 번째 단계에서는 사용자가 자신의 LDAP 자격 증명을 사용하여 볼트로 인증합니다.
- 구성된 LDAP 인증 방법을 사용하여 볼트는 구성된 백엔드를 사용하여 이러한 자격 증명을 검증합니다.
- 자격 증명이 유효한 경우, 볼트는 토큰을 생성하고 사용자 또는 사용자와 관련된 정책을 첨부합니다
- 그런 다음 이 토큰을 사용자에게 반환하여 이후 요청에 사용합니다.
- 사용자가 비밀을 요청하면 요청의 일부로 이 토큰을 전달합니다.
- 볼트는 토큰에 첨부된 정책을 확인하여 요청된 작업이 허용되는지 확인합니다.
- 정책이 유효한 경우에만 작업이 허용됩니다.
- 사용자가 인간 운영자이든 애플리케이션이든 상관없이 이 워크플로우는 동일하게 유지됩니다.
vault HA : init → join - Helm-Chart
#
kubectl create ns vault
#
cat << EOF > values-ha.yaml
server:
replicas: 3
# Vault HA mode
ha:
enabled: true
replicas: 3
raft:
enabled: true
config: |
ui = true
listener "tcp" {
tls_disable = 1 # TLS 바활성화
address = "[::]:8200" # 모든 IPv6 주소에서 8200 포트 수신
cluster_address = "[::]:8201" # 클러스터 통신 포트
}
service_registration "kubernetes" {} # Kubernetes 서비스 등록 활성화
readinessProbe:
enabled: true
# PVC for Raft storage
dataStorage:
enabled: true
size: 10Gi
service:
enabled: true
type: ClusterIP
port: 8200
targetPort: 8200
ui:
enabled: true
serviceType: "NodePort"
externalPort: 8200
serviceNodePort: 30000
injector:
enabled: false
EOF
#
helm install vault hashicorp/vault -n vault -f values-ha.yaml --version 0.31.0
#
kubectl get pods --selector='app.kubernetes.io/name=vault' -n vault
NAME READY STATUS RESTARTS AGE
vault-0 0/1 Running 0 100s
vault-1 0/1 Running 0 100s
vault-2 0/1 Running 0 100s
k get pvc,pv -n vault
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
persistentvolumeclaim/data-vault-0 Bound pvc-3df4e80d-b692-429a-aee9-368bd1297aa9 10Gi RWO standard <unset> 19m
persistentvolumeclaim/data-vault-1 Bound pvc-7a199dd9-53c5-4b63-9970-f8888e2c4e8c 10Gi RWO standard <unset> 19m
persistentvolumeclaim/data-vault-2 Bound pvc-6066c850-c7c0-46c5-9ea3-9ffce755c19a 10Gi RWO standard <unset> 19m
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
persistentvolume/pvc-3df4e80d-b692-429a-aee9-368bd1297aa9 10Gi RWO Delete Bound vault/data-vault-0 standard <unset> 19m
persistentvolume/pvc-6066c850-c7c0-46c5-9ea3-9ffce755c19a 10Gi RWO Delete Bound vault/data-vault-2 standard <unset> 19m
persistentvolume/pvc-7a199dd9-53c5-4b63-9970-f8888e2c4e8c 10Gi RWO Delete Bound vault/data-vault-1 standard <unset> 19m
#
kubectl stern -n vault vault-0
vault-0 vault 2025-11-23T09:16:53.928Z [INFO] core: security barrier not initialized
vault-0 vault 2025-11-23T09:16:53.928Z [INFO] core: seal configuration missing, not initialize
...
# 초기화 Initialize a new Vault cluster:
kubectl exec -it vault-0 -n vault -- sh
---------------------------------------
ps -ef
vault operator -h
vault operator init
Unseal Key 1: kzXYpeB5VV/yjKtDAibL/p80lKqRQo19P4xqykFU4xfL
Unseal Key 2: M+M+I2QzJ+fnYaCtkqGP8I+PQzRXx7vTQWG0Zg9jrve2
Unseal Key 3: vjFgw9jYDrtEgKSl7bCeDwGvz0nY/ZlxFyGo1kzowEru
Unseal Key 4: cgnXMYm0O3GgwN3RITmk8OoukKsrdjAFvkl/pJK+/xkE
Unseal Key 5: u7yRCM1fNOqHOzT1d0X6+NB39v3lEa/uWSjStCybdt76
Initial Root Token: hvs.tzUPjoMilb4ma9j1i0SWIsxx
#
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.20.4
Build Date 2025-09-23T13:22:38Z
Storage Type raft
Removed From Cluster false
HA Enabled true
#
vault operator unseal
Unseal Key (will be hidden): kzXYpeB5VV/yjKtDAibL/p80lKqRQo19P4xqykFU4xfL
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed true
Total Shares 5
Threshold 3
Unseal Progress 1/3
Unseal Nonce 5ceab019-b703-8579-69f1-464b34908f9c
Version 1.20.4
Build Date 2025-09-23T13:22:38Z
Storage Type raft
Removed From Cluster false
HA Enabled true
vault operator unseal
Unseal Key (will be hidden): M+M+I2QzJ+fnYaCtkqGP8I+PQzRXx7vTQWG0Zg9jrve2
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed true
Total Shares 5
Threshold 3
Unseal Progress 2/3
Unseal Nonce 5ceab019-b703-8579-69f1-464b34908f9c
Version 1.20.4
Build Date 2025-09-23T13:22:38Z
Storage Type raft
Removed From Cluster false
HA Enabled true
vault operator unseal
Unseal Key (will be hidden): vjFgw9jYDrtEgKSl7bCeDwGvz0nY/ZlxFyGo1kzowEru
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed false
Total Shares 5
Threshold 3
Version 1.20.4
Build Date 2025-09-23T13:22:38Z
Storage Type raft
Cluster Name vault-cluster-11468486
Cluster ID afd833ca-07cc-e6ea-3a29-0b531c181c4c
Removed From Cluster false
HA Enabled true
HA Cluster https://vault-0.vault-internal:8201
HA Mode active
Active Since 2025-11-23T09:22:22.262545206Z
Raft Committed Index 37
Raft Applied Index 37
vault status
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed false
Total Shares 5
Threshold 3
Version 1.20.4
Build Date 2025-09-23T13:22:38Z
Storage Type raft
Cluster Name vault-cluster-11468486
Cluster ID afd833ca-07cc-e6ea-3a29-0b531c181c4c
Removed From Cluster false
HA Enabled true
HA Cluster https://vault-0.vault-internal:8201
HA Mode active
Active Since 2025-11-23T09:22:22.262545206Z
Raft Committed Index 37
Raft Applied Index 37
exit
---------------------------------------
#
kubectl get pods --selector='app.kubernetes.io/name=vault' -n vault
NAME READY STATUS RESTARTS AGE
vault-0 1/1 Running 0 6m54s
vault-1 0/1 Running 0 6m54s
vault-2 0/1 Running 0 6m54s
k stern -n vault vault-1
vault-1 vault 2025-11-23T09:24:01.861Z [INFO] core: security barrier not initialized
vault-1 vault 2025-11-23T09:24:01.861Z [INFO] core: seal configuration missing, not initialized
# vault join
kubectl exec -n vault -it vault-1 -- vault operator raft join http://vault-0.vault-internal:8200
kubectl exec -n vault -it vault-2 -- vault operator raft join http://vault-0.vault-internal:8200
Key Value
--- -----
Joined true
#
kubectl exec -it vault-0 -n vault -- sh
---------------------------------------
vault login
Token (will be hidden): hvs.tzUPjoMilb4ma9j1i0SWIsxx
...
vault operator raft list-peers
Node Address State Voter
---- ------- ----- -----
f8cd3129-35e4-7b5a-6aef-1f755266d1e7 vault-0.vault-internal:8201 leader true
exit
---------------------------------------
#
kubectl get pods --selector='app.kubernetes.io/name=vault' -n vault
kubectl exec -it vault-1 -n vault -- sh
---------------------------------------
vault status
vault operator unseal
vault operator unseal
vault operator unseal
vault status
exit
---------------------------------------
#
kubectl exec -it vault-2 -n vault -- sh
---------------------------------------
vault status
vault operator unseal
vault operator unseal
vault operator unseal
vault status
exit
---------------------------------------
#
kubectl get pods --selector='app.kubernetes.io/name=vault' -n vault
NAME READY STATUS RESTARTS AGE
vault-0 1/1 Running 0 13m
vault-1 1/1 Running 0 13m
vault-2 1/1 Running 0 13m
# hvs.nEMhDcK5ry90YR0sMXwD14bA
export VAULT_ROOT_TOKEN=hvs.uHKVZKlswZ0wKsp1tMA3DZOs
export VAULT_ADDR='http://localhost:30000'
vault login
Token (will be hidden): hvs.uHKVZKlswZ0wKsp1tMA3DZOs
#
vault operator raft list-peers
Node Address State Voter
---- ------- ----- -----
47b855a5-4475-a8a2-d43d-60d12f194284 vault-0.vault-internal:8201 leader true
385df71f-fabe-9668-e0ed-a58829113cc9 vault-1.vault-internal:8201 follower true
a406c034-1948-acfc-637b-77a3ca85712d vault-2.vault-internal:8201 follower true





Unseal Key 입력 시 카운트가 하나 올라감. key 를 3개 이상 unseal 해줘야 함




Unseal 과정 다른 노드에서도 반복 해준다
모든 Unseal 과정 커치면 파드가 Running 상태가 됨을 알수 있음

vault operator raft list-peers 명령어로 leader 와 follower 확인
Writing/Reading secrets into Vault
- 볼트에 비밀 기록하기 비밀을 볼트에 저장하는 가장 간단한 방법은 키-값 비밀 엔진을 사용하는 것입니다.
- 비밀 엔진은 데이터를 저장, 생성 또는 암호화할 수 있는 볼트의 구성 요소입니다. 간단히 말해, 데이터를 제공하면 결과가 반환됩니다.
- 특히 키-값 비밀 엔진은 비밀을 저장하고 암호화할 수 있게 해주며, 비밀은 하나 이상의 키/값 쌍을 포함하는 문서로 저장됩니다.
- 일반적으로 비밀 엔진은 볼트에 독립적으로 추가하고 구성해야 하는데, 이를 마운트라고 합니다.
- 볼트의 비밀 엔진은 특정 "경로"에서 활성화됩니다.
- 볼트의 경로는 파일 시스템의 디렉토리 구조와 유사합니다.
- 예를 들어, /etc/vault는 기본적으로 키-값 비밀 엔진은 경로 비밀에 마운트됩니다.
- 기본적으로 키-값 비밀 엔진은 볼트 서버를 새로 시작할 때 이미 활성화되어 있습니다.
- 이제 HelloCorp의 웹 개발 팀 구성원들이 볼트의 키-값 비밀 엔진에 사용하는 타사 제공자의 사용자 이름과 비밀번호를 어떻게 저장할 수 있는지 살펴보겠습니다.
- 타사 서비스 제공자와 같은 로그인의 경우, 제공자 이름이 Datadog이기 때문에 경로 로그인/<제공자 이름>에 이를 저장하기로 결정했으며, 이 경우 전체 경로는 비밀/로그인/데이터독이 됩니다.
- 이 비밀 문서에는 사용자 이름과 비밀번호에 대한 두 개의 키-값 쌍이 포함됩니다.
- 볼트는 경로가 URI 경로를 준수하는 한 비밀에 조직적인 제약을 가하지 않습니다.
- 키를 찾을 수 없었던 경험을 통해 규칙을 정의하고 따르는 것이 올바른 접근 방식이라는 것을 알 수 있습니다.
- 키-값 비밀 엔진에 비밀을 작성하려면 kv 명령을 사용합니다.
- kv 명령에는 문서를 작성하거나 업데이트하기 위해 여러 하위 명령어가 있습니다.
- 볼트 kv 풋 명령을 사용하여 키-값 비밀 엔진에 비밀을 쓸 수 있습니다.
- 가장 간단하게 말하자면, 풋 명령은 비밀을 쓰는 경로와 해당 경로에 저장할 키 및 값 쌍이라는 두 가지 매개변수가 필요합니다.
- 첫 번째 비밀을 저장하려면 터미널에서 다음 명령을 실행하세요.
#
vault secrets enable -path=mysecret kv-v2
# 샘플 시크릿 저장 : 경로 확인
vault kv put mysecret/logins/study \
username="demo" \
password="p@ssw0rd"
# 입력된 데이터 확인
vault kv get mysecret/logins/study
#
vault kv list mysecret
vault kv list mysecret/logins

Restricting secret access using policy
# 정책 생성 해보기
vault policy write restrict_study - <<EOF
path "mysecret/logins/study*" {
capabilities = ["deny"]
}
EOF

Vault API
- 볼트에는 CLI의 모든 기능을 갖춘 RESTful API가 있으며, 실제로 CLI는 커버 아래에 있는 API를 사용합니다.
- 이 API는 HTTP 요청만 있으면 볼트 서버에 네트워크로 액세스할 수 있기 때문에 볼트 비밀 및 운영에 대한 프로그래밍적 접근을 가능하게 합니다.
- 볼트 API를 사용하여 비밀에 액세스 볼트 API는 애플리케이션이 볼트와 상호 작용하는 데 사용할 수 있습니다.
- 코드를 작성하는 대신 컬과 같은 표준 리눅스 도구를 사용하여 간단한 bash 스크립트에서 이 작업을 수행할 수 있다고 결정합니다.
- API에서 비밀을 읽은 다음 애플리케이션이 시작할 때 사용할 수 있는 환경 변수에 삽입할 수 있습니다.
- CLI를 사용하면 볼트는 자동으로 $HOME/.vault-token 파일에 저장된 인증 토큰을 읽습니다.
- 하지만 API와 상호 작용할 때는 이 토큰을 HTTP 헤더 X-Vault-Token에 제공해야 합니다.
- 개발 환경(부록 A에 설정)을 사용하는 경우 이 토큰은 루트입니다.
- 비밀을 검색하는 데 사용할 경로에도 약간의 차이가 있습니다.
- CLI에서 사용하는 경로는 secret/logins/dataadog입니다.
- API로 동일한 비밀에 액세스하려면 /v1/secret/data/logins/dataadog 경로를 사용해야 합니다.
- 볼트 버전 0.9이므로 마운트된 키 값 비밀 엔진의 기본 버전은 비밀을 버전할 수 있는 추가 기능이 있는 버전 2입니다.
- API 호출 시 버전 1과 버전 2 엔진을 구분하려면 마운트 경로를 따라 하위 경로/data를 추가해야 합니다.
- 컬 명령을 사용하여 볼트 서버에 HTTP 요청을 하고 출력을 sed로 구문 분석할 수 있는 변수로 캡처하여 값을 추출하려고 합니다.
- 볼트가 요청을 인증할 수 있도록 헤더 X-Vault-Token에 유효한 토큰을 제공해야 하며, 비밀로 가는 경로도 제공해야 합니다.
- 기본 토큰 위치인 ~/.vault-Token에서 cat 명령을 사용하여 토큰을 읽을 수 있습니다.
- 스크립트가 프로덕션 환경에서 실행될 때는 서버에 유효한 토큰이 있는지 확인해야 합니다.
#
export VAULT_ROOT_TOKEN=hvs.uHKVZKlswZ0wKsp1tMA3DZOs
# http://127.0.0.1:30000/ui/vault/secrets/mysecret/kv/logins%2Fstudy/paths
curl -s --header "X-Vault-Token: $VAULT_ROOT_TOKEN" http://localhost:30000/v1/mysecret/data/logins/study | jq
curl -s --header "X-Vault-Token: $VAULT_ROOT_TOKEN" http://localhost:30000/v1/mysecret/data/logins/study \
| jq -r .data.data.username
curl -s --header "X-Vault-Token: $VAULT_ROOT_TOKEN" http://localhost:30000/v1/mysecret/data/logins/study \
| jq -r .data.data.password
#
export USER_NAME=$(curl -s --header "X-Vault-Token: $VAULT_ROOT_TOKEN" http://localhost:30000/v1/mysecret/data/logins/study | jq -r .data.data.username)
export PASSWORD=$(curl -s --header "X-Vault-Token: $VAULT_ROOT_TOKEN" http://localhost:30000/v1/mysecret/data/logins/study | jq -r .data.data.password)
echo $USER_NAME $PASSWORD


Vault Auth with LDAP
OpenLDAP 서버 배포 - OpenLDAP
#
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
name: openldap
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: openldap
namespace: openldap
spec:
replicas: 1
selector:
matchLabels:
app: openldap
template:
metadata:
labels:
app: openldap
spec:
containers:
- name: openldap
image: osixia/openldap:1.5.0
ports:
- containerPort: 389
name: ldap
- containerPort: 636
name: ldaps
env:
- name: LDAP_ORGANISATION # 기관명, LDAP 기본 정보 생성 시 사용
value: "Example Org"
- name: LDAP_DOMAIN # LDAP 기본 Base DN 을 자동 생성
value: "example.org"
- name: LDAP_ADMIN_PASSWORD # LDAP 관리자 패스워드
value: "admin"
- name: LDAP_CONFIG_PASSWORD
value: "admin"
- name: phpldapadmin
image: osixia/phpldapadmin:0.9.0
ports:
- containerPort: 80
name: phpldapadmin
env:
- name: PHPLDAPADMIN_HTTPS
value: "false"
- name: PHPLDAPADMIN_LDAP_HOSTS
value: "openldap" # LDAP hostname inside cluster
---
apiVersion: v1
kind: Service
metadata:
name: openldap
namespace: openldap
spec:
selector:
app: openldap
ports:
- name: phpldapadmin
port: 80
targetPort: 80
nodePort: 30001
- name: ldap
port: 389
targetPort: 389
- name: ldaps
port: 636
targetPort: 636
type: NodePort
EOF
# 확인
kubectl get deploy,pod,svc,ep -n openldap
# 기본 LDAP 정보 : 아래 Bind DN과 PW로 로그인
## Base DN: dc=example,dc=org
## Bind DN: cn=admin,dc=example,dc=org
## Password: admin
open http://127.0.0.1:30001
# 로그 확인
kubectl logs -n openldap -l app=openldap -c phpldapadmin -f # phpLDAPadmin
kubectl logs -n openldap -l app=openldap -c openldap -f # openldap

OpenLDAP 조직도 구성 : 사용자(alice/alice123, bob/bob123), 그룹(devs, admins)
#
kubectl -n openldap exec -it deploy/openldap -c openldap -- bash
----------------------------------------------------
# 실습 사용 최종 트리 구조
dc=example,dc=org
├── ou=people
│ ├── uid=alice
│ │ ├── cn: Alice
│ │ ├── sn: Kim
│ │ ├── uid: alice
│ │ └── mail: alice@example.org
│ └── uid=bob
│ ├── cn: Bob
│ ├── sn: Lee
│ ├── uid: bob
│ └── mail: bob@example.org
└── ou=groups
├── cn=devs
│ └── member: uid=bob,ou=people,dc=example,dc=org
└── cn=admins
└── member: uid=alice,ou=people,dc=example,dc=org
# ldapadd로 ou 추가 (organizationalUnit)
cat <<EOF | ldapadd -x -D "cn=admin,dc=example,dc=org" -w admin
dn: ou=people,dc=example,dc=org
objectClass: organizationalUnit
ou: people
dn: ou=groups,dc=example,dc=org
objectClass: organizationalUnit
ou: groups
EOF
# ldapadd로 users 추가 (inetOrgPerson) : alice , bob
cat <<EOF | ldapadd -x -D "cn=admin,dc=example,dc=org" -w admin
dn: uid=alice,ou=people,dc=example,dc=org
objectClass: inetOrgPerson
cn: Alice
sn: Kim
uid: alice
mail: alice@example.org
userPassword: alice123
dn: uid=bob,ou=people,dc=example,dc=org
objectClass: inetOrgPerson
cn: Bob
sn: Lee
uid: bob
mail: bob@example.org
userPassword: bob123
EOF
# ldapadd로 groups 추가 (groupOfNames) : devs, admins
cat <<EOF | ldapadd -x -D "cn=admin,dc=example,dc=org" -w admin
dn: cn=devs,ou=groups,dc=example,dc=org
objectClass: groupOfNames
cn: devs
member: uid=bob,ou=people,dc=example,dc=org
dn: cn=admins,ou=groups,dc=example,dc=org
objectClass: groupOfNames
cn: admins
member: uid=alice,ou=people,dc=example,dc=org
EOF
# 빠져나오기
exit
----------------------------------------------------

Authenticating with Vault
- 이제 사용자와 애플리케이션이 인증 방법을 사용하여 볼트로 인증하는 방법을 배우고 신원을 할당받을 차례입니다.
- 인증 방법을 사용하면 볼트는 기존 인증 시스템(예: LDAP, GitHub, X509 인증서 등)과 상호작용하여 사용자 또는 애플리케이션과 상호작용하려는 사용자 또는 애플리케이션을 검증할 수 있습니다.
- 또한, 사용자와 애플리케이션을 볼트로 인증하는 데 도움이 되는 LDAP 및 AppRole 인증 방법도 배우게 됩니다.
- 웹 개발 팀을 돕기 위해서는 먼저 사용자와 애플리케이션을 볼트로 인증할 수 있도록 필요에 맞는 인증 방법을 사용해야 합니다.
- 그림에 나와 있는 볼트를 사용하는 사용자와 관련된 기본 인증 워크플로우를 살펴보겠습니다.

- 사용자는 볼트에 LDAP 자격 증명과 같은 자격 증명을 제공합니다.
- 볼트는 이러한 LDAP 자격 증명을 사용하여 LDAP 서버와 상호작용하고 사용자의 신원을 검증합니다.
- 자격 증명과 사용자가 유효하다면 볼트는 사용자를 위한 볼트 토큰을 생성하여 사용자에게 반환합니다.
- 볼트 토큰은 사용자 또는 애플리케이션에 신원을 할당하는 데 사용되며, 이는 볼트와 상호작용하는 정책을 사용합니다.
- 볼트 정책은 토큰 접근을 통해 볼트에서 비밀을 구성하고 액세스할 수 있는 기능을 부여하거나 거부합니다.
- 정책을 생성하고 토큰에 할당하는 방법에 대해서는 이 장 뒷부분에서 자세히 설명하겠습니다.
- 볼트의 대부분의 작업에는 유효한 볼트 토큰이 필요합니다.
- 볼트 토큰은 최대 생존 기간과 갱신 간격을 갖습니다.
- 이 두 가지 기능은 토큰을 우발적인 손실로부터 보호하기 위해 설계되었으며, 토큰이 만료되면 더 이상 볼트 내에서 비밀에 액세스하고 관리 기능을 수행하는 데 사용할 수 없습니다.
Auth methods
- 그림에서 사용자는 볼트 토큰을 생성하기 위해 LDAP 자격 증명을 사용하는 것을 확인할 수 있었습니다.
- 사용자는 볼트의 LDAP 인증 방법을 사용하여 이를 수행했습니다.
- 인증 방법을 통해 볼트는 내부 및 외부 인증 시스템(예: LDAP, GitHub, X509 인증서 등)과 상호 작용하여 사용자 또는 상호 작용하려는 애플리케이션을 검증할 수 있습니다.
- 예를 들어, 대규모 조직에서 일하는 시스템의 인간 사용자는 기존의 LDAP 또는 Active Directory 계정을 사용하여 인증하는 것을 선호할 수 있습니다.
- 그러나 소규모 조직에는 LDAP 서버가 없을 수 있으며, 사용자는 GitHub 토큰으로 인증할 수 있습니다.
Vault Auth with LDAP 설정
Flow
sequenceDiagram
participant User as User (jdoe)
participant Vault as Vault Server
participant LDAP as OpenLDAP
participant KV as Vault Secret Engine
User->>Vault: vault login -method=ldap\nusername=jdoe,password=****
Vault->>LDAP: Bind/Search userdn (binddn used for search)\n(e.g. search uid=jdoe in ou=users,...)
LDAP-->>Vault: User found + verify credentials (bind or compare)
alt auth success
Vault->>LDAP: (optional) query groups (groupfilter/memberUid etc.)
LDAP-->>Vault: return groups (e.g. devs, ops)
Vault->>Vault: map groups -> policies (auth/ldap/groups mapping)
Vault-->>User: return Vault token (with policies)
User->>Vault: API request using token (e.g., kv read secret/data/dev/config)
Vault->>KV: policy check -> allow/deny
KV-->>User: secret data (if allowed)
else auth failed
LDAP-->>Vault: authentication failure
Vault-->>User: authentication error
end
1. 사용자 → Vault : LDAP 로그인 요청
사용자는 Vault CLI 또는 API를 통해 LDAP 인증 방식으로 로그인을 요청한다.
이 요청에는 사용자 이름과 비밀번호가 포함되며, Vault는 이를 LDAP 인증에 사용한다.
2. Vault → LDAP : 사용자 DN 검색
Vault는 LDAP에 직접 사용자를 인증하기 전에,
LDAP 디렉토리 내에서 해당 사용자의 DN(Distinguished Name)을 찾는다.
이 과정은 Vault에 설정된 binddn 계정을 사용하여 수행되며,
uid=jdoe와 같은 조건으로 LDAP 검색이 이루어진다.
3. LDAP → Vault : 사용자 인증 검증
LDAP 서버는 검색된 사용자 DN을 기준으로 비밀번호를 검증한다.
- 사용자가 존재하고
- 비밀번호가 일치하면
인증 성공 응답을 Vault로 반환한다.
일치하지 않으면 인증 실패로 처리된다.
4. 인증 성공 시: LDAP 그룹 조회
인증이 성공하면 Vault는 LDAP에 해당 사용자가 속한 그룹을 질의한다.
예를 들어:
- devs
- ops
와 같은 그룹 정보가 반환될 수 있다.
이 단계는 Vault의 LDAP 설정에 따라 선택적으로 수행된다.
5. Vault 내부 처리 : 그룹 → 정책 매핑
Vault는 LDAP에서 전달받은 그룹 정보를 기준으로
사전에 정의된 Vault 정책(Policy)을 매핑한다.
예:
- devs → dev-policy
- ops → ops-policy
이 매핑은 auth/ldap/groups/ 경로에 설정되어 있다.
6. Vault → 사용자 : Vault 토큰 발급
그룹 기반 정책이 적용된 Vault 토큰이 사용자에게 반환된다.
이 토큰에는 다음 정보가 포함된다.
- 적용된 정책 목록
- 토큰 TTL
- 갱신 가능 여부
이후 모든 Vault 요청은 해당 토큰을 기반으로 처리된다.
7. 사용자 → Vault : Secret 요청
사용자는 발급받은 토큰을 사용해 Vault Secret Engine(KV)에 접근한다.
8. Vault → KV : 정책 검증
Vault는 요청된 Secret 경로에 대해
토큰에 포함된 정책을 기준으로 접근 가능 여부를 판단한다.
정책이 허용된 경우에만 Secret Engine으로 요청이 전달된다.
9. KV → 사용자 : Secret 반환
정책 검증이 통과되면 KV Secret Engine은
요청된 Secret 데이터를 사용자에게 반환한다.
권한이 없는 경우에는 접근 거부 오류가 반환된다.
10. 인증 실패 시 흐름
LDAP 인증 단계에서 실패할 경우:
- Vault 토큰은 발급되지 않는다
- Secret 접근 또한 불가능하다
- 사용자에게 인증 오류 메시지가 반환된다
LDAP 인증 활성화
#
vault auth enable ldap
Success! Enabled ldap auth method at: ldap/
#
vault auth list
vault auth list -detailed
# ldap config
vault write auth/ldap/config \
url="ldap://openldap.openldap.svc:389" \
starttls=false \
insecure_tls=true \
binddn="cn=admin,dc=example,dc=org" \
bindpass="admin" \
userdn="ou=people,dc=example,dc=org" \
groupdn="ou=groups,dc=example,dc=org" \
groupfilter="(member=uid={{.Username}},ou=people,dc=example,dc=org)" \
groupattr="cn"
Success! Data written to: auth/ldap/config
# LDAP 인증 테스트 :
vault login -method=ldap username=alice
Password (will be hidden): alice123
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 hvs.CAESILgWZQ2UXd86lT-cNbT_-tWVmnG0eo7Iaa1naBRHIPnEGh4KHGh2cy44cnVQTGNiNlFFNUhRRmxJenVCdkc5c3I
token_accessor ak9uGKqU7AMuaUPiGVkEWi6X
token_duration 768h
token_renewable true
token_policies ["default"]
identity_policies []
policies ["default"]
token_meta_username alice
# token_policies : 토큰에 직접적으로 할당된 정책 목록. 토큰 생성 시 명시적으로 지정된 정책으로, 인증 방법(auth method)이나 토큰 생성 명령어에서 직접 정의합니다.
# identity_policies : Vault의 Identity 시스템(엔티티(entity)와 그룹(group))에서 동적으로 계산된 정책 목록
# policies : token_policies와 identity_policies의 합집합. 실제로 토큰이 사용하는 권한은 이 policies를 기반으로 평가됩니다.
# 토큰 정보 확인
vault token lookup -format=json | jq .data.policies # 토큰에 적용된 정책 확인
vault token lookup
Key Value
--- -----
accessor ak9uGKqU7AMuaUPiGVkEWi6X
creation_time 1764490175
creation_ttl 768h
display_name ldap-alice
entity_id 3273478d-fde9-e137-b693-42fdc3020303
expire_time 2026-01-01T08:09:35.527373025Z
explicit_max_ttl 0s
id hvs.CAESILgWZQ2UXd86lT-cNbT_-tWVmnG0eo7Iaa1naBRHIPnEGh4KHGh2cy44cnVQTGNiNlFFNUhRRmxJenVCdkc5c3I
issue_time 2025-11-30T08:09:35.527378609Z
meta map[username:alice]
num_uses 0
orphan true
path auth/ldap/login/alice
policies [default] # 토큰에 적용된 정책 확인
renewable true
ttl 767h57m58s
type service
#
vault login -method=ldap username=bob password=bob123
vault token lookup

alice 유저로 잘 로그인 됐음을 알 수 있음


Mapping LDAP groups to Vault policy
# 정책 생성
vault policy write admin - <<EOF
path "*" {
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}
EOF
# 현재 사용 중 토큰 확인 후 다시 로그인(root 계정)
vault token lookup
vault login
#
vault policy write admin - <<EOF
path "*" {
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}
EOF
Success! Uploaded policy: admin
vault policy read admin
# LDAP admins 그룹에 admin (vault) 정책 지정
vault write auth/ldap/groups/admins policies=admin
# alice 로그인 : 토큰, policy 확인
vault login -method=ldap username=alice password=alice123
...
token_policies ["admin" "default"]
identity_policies []
policies ["admin" "default"]
...
# 정책 적용 확인
vault policy read admin
...

어드민으로 로그인하기 위해 ROOT TOKEN 값 넣어주면 잘 로그인이 된다.


kind delete cluster --name myk8s
Vault Server TLS
kind k8s + ingress-nginx
#
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
hostPort: 30000
EOF
# NGINX ingress 배포
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
# SSL Passthrough flag 활성화 설정 https://kubernetes.github.io/ingress-nginx/user-guide/tls/#ssl-passthrough
kubectl get deployment ingress-nginx-controller -n ingress-nginx -o yaml \
| sed '/- --publish-status-address=localhost/a\
- --enable-ssl-passthrough' | kubectl apply -f -
# nodeSelector 지정
kubectl patch deployment ingress-nginx-controller -n ingress-nginx \
--type='merge' \
-p='{
"spec": {
"template": {
"spec": {
"nodeSelector": {
"ingress-ready": "true"
}
}
}
}
}'
Vault tls 설치
# Helm v3 으로 설치
docker exec -it myk8s-control-plane bash
------------------------------------
# Helm v3 설치
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm version
helm repo add hashicorp https://helm.releases.hashicorp.com
helm repo update
# ca 생성
mkdir /tls && cd /tls
openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes \
-key ca.key \
-subj "/CN=Vault-CA" \
-days 3650 \
-out ca.crt
ls
# Vault 서버 인증서 생성
openssl genrsa -out vault.key 2048
openssl req -new -key vault.key \
-subj "/CN=vault.example.com" \
-out vault.csr
ls
# 인증서 서명
openssl x509 -req \
-in vault.csr \
-CA ca.crt \
-CAkey ca.key \
-CAcreateserial \
-out vault.crt \
-days 3650 \
-extensions v3_req \
-extfile <(cat <<EOF
[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = vault.example.com
DNS.2 = vault
DNS.3 = localhost
DNS.4 = vault.vault.svc.cluster.local
DNS.5 = vault.vault-internal
DNS.6 = vault-0.vault-internal
DNS.7 = vault-1.vault-internal
DNS.8 = vault-2.vault-internal
DNS.9 = 127.0.0.1
EOF
)
# Kubernetes TLS Secret 등록
kubectl create namespace vault
kubectl -n vault create secret tls vault-tls \
--cert=vault.crt \
--key=vault.key
# CA도 저장 (init 과정에 사용)
kubectl -n vault create secret generic vault-ca \
--from-file=ca.crt=ca.crt
#
cat << EOF > values-tls.yaml
global:
tlsDisable: false
injector:
enabled: false
server:
volumes:
- name: vault-tls
secret:
secretName: vault-tls
- name: vault-ca
secret:
secretName: vault-ca
volumeMounts:
- name: vault-tls
mountPath: /vault/user-tls
readOnly: true
- name: vault-ca
mountPath: /vault/user-ca
readOnly: true
# Vault HTTPS listener 설정
standalone:
enabled: "true"
config: |-
ui = true
listener "tcp" {
tls_disable = 0
address = "0.0.0.0:8200"
cluster_address = "0.0.0.0:8201"
tls_cert_file = "/vault/user-tls/tls.crt"
tls_key_file = "/vault/user-tls/tls.key"
}
storage "file" {
path = "/vault/data"
}
api_addr = "https://vault.vault.svc.cluster.local:8200"
cluster_addr = "https://vault-0.vault-internal:8201"
EOF
helm install vault hashicorp/vault -n vault -f values-tls.yaml --version 0.29.0
helm list -A
exit
------------------------------------
#
kubectl exec -ti vault-0 -n vault -- vault status
kubectl exec -ti vault-0 -n vault -- vault status -tls-skip-verify
#
kubectl exec vault-0 -n vault -- vault operator init -tls-skip-verify \
-key-shares=1 \
-key-threshold=1 \
-format=json > cluster-keys.json
#
cat cluster-keys.json| jq
VAULT_UNSEAL_KEY=$(jq -r ".unseal_keys_b64[]" cluster-keys.json)
kubectl exec vault-0 -n vault -- vault operator unseal -tls-skip-verify $VAULT_UNSEAL_KEY
kubectl get pod -n vault
#
jq -r ".root_token" cluster-keys.json
hvs.9Q8bTapreqRHJpF4onrmRigl
# NodePort로 공개한 30000 NodePort로 설정
kubectl patch svc -n vault vault -p '{"spec":{"type":"NodePort","ports":[{"port":8200,"targetPort":8200,"nodePort":30000}]}}'
export VAULT_ADDR='https://localhost:30000'
# vault 상태확인
vault status -tls-skip-verify
# Root Token으로 로그인
vault login -tls-skip-verify
Token (will be hidden): hvs.9Q8bTapreqRHJpF4onrmRigl
...



-tls-skip-verify 설정이 없으면 접근이 안됨



ingress-nginx ssl-passthrough 설정
#
echo "127.0.0.1 vault.example.com" | sudo tee -a /etc/hosts
#
kubectl describe pod -n vault vault-0 | grep -i ports
Ports: 8200/TCP (https), 8201/TCP (https-internal), 8202/TCP (https-rep)
#
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: vault-https
namespace: vault
annotations:
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
ingressClassName: "nginx"
rules:
- host: vault.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: vault
port:
number: 8200
tls:
- hosts:
- vault.example.com
secretName: vault-tls
EOF
#
kubectl get ingress -n vault -w
NAME CLASS HOSTS ADDRESS PORTS AGE
vault-https nginx vault.example.com localhost 80, 443 107s
#
open "http://vault.example.com"






토큰 입력시 로그인 성공!

Vault + ArgoCD
vault + argocd 비권장
비권장하는 이유는??
https://github.com/argoproj-labs/argocd-vault-plugin/issues/698
Is this Repo Dead? + Next Steps · Issue #698 · argoproj-labs/argocd-vault-plugin
Last Release was June 2024. We need to decide as a community where we want to go moving forward. Do we want to create a fork? I created a fork and am willing to invite collaborators. I released a v...
github.com
대충 정리하자면 아래와 같음
- Argo CD의 매니페스트 생성 단계를 사용하여 비밀 정보를 주입합니다
- 이는 argocd-vault-plugin 과 같은 구성 관리 플러그인을 사용하여 수행할 수 있습니다 .
- 이러한 비밀 관리 방식에는 여러 가지 단점이 있으므로 강력히 경고합니다 .
- 1) 보안: Argo CD는 비밀 정보에 접근해야 하므로 정보 유출 위험이 높아집니다.
Argo CD는 생성된 매니페스트를 Redis 캐시에 평문으로 저장하기 때문에 매니페스트에 비밀 정보를 삽입하면 위험이 증가합니다. - 2) 사용자 경험: 비밀 정보 업데이트는 앱 동기화 작업과 연동되므로 관련 없는 릴리스 중에 의도치 않게 비밀 정보 업데이트가 적용될 위험이 있습니다
- 3) 렌더링된 매니페스트 패턴: 이 접근 방식은 GitOps의 모범 사례로 점점 더 중요해지고 있는 "렌더링된 매니페스트" 패턴과 호환되지 않습니다.
- 1) 보안: Argo CD는 비밀 정보에 접근해야 하므로 정보 유출 위험이 높아집니다.
- 이미 많은 사용자가 세대 기반 솔루션을 채택하고 있으며, 연산자 기반 솔루션으로 전환하는 데 상당한 노력이 필요하다는 점을 잘 알고 있습니다. Argo CD는 앞으로도 세대 기반 비밀 관리 방식을 지원할 예정이지만, 이러한 방식의 비밀 관리만을 지원하는 새로운 기능이나 개선 사항에는 우선순위를 두지 않을 것입니다.
'스터디 > CICD Study' 카테고리의 다른 글
| CI/CD Study [1기] - 8주차 - Hashicorp Vault/VSO on K8S (1/2) (0) | 2025.12.13 |
|---|---|
| CI/CD Study [1기] - 7주차 - HashiCorp Vault (2/2) (0) | 2025.11.29 |
| CI/CD Study [1기] - 7주차 - HashiCorp Vault (1/2) (1) | 2025.11.29 |
| CI/CD Study [1기] - 6주차 - Argo CD 3/3 (0) | 2025.11.23 |
| CI/CD Study [1기] - 5주차 - Argo CD 2/3 (0) | 2025.11.15 |