CI/CD Study 8주차 Vault (2/2)
가시다님이 운영하시는 CI/CD Study 8주차 내용(Vault Production)을 정리한 게시글 입니다.
1. Vault HA & OpenLDAP 연동
Vault를 Production에서 운영 시 고가용성(High Availability)을 확보하면서 운영하는 방법을 알아보겠습니다.
Vault HA 설명
Vault 에서 HA Mode를 사용하는 방식은 1개의 Active 서버(Leader)와 여러대의 Hot StandBy 서버(Follower)를 구성하여 고 가용성을 보장합니다.

Figure 1.1 Vault HA 설명
실습 환경 구성
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
# 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"
}
}
}
}
}'
kubectl create ns vault
# Helm Values 정의
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:7
enabled: true
serviceType: "NodePort"
externalPort: 8200
serviceNodePort: 30000
injector:
enabled: false
EOF
# Vault Install
helm install vault hashicorp/vault -n vault -f values-ha.yaml --version 0.31.0
# Vault Initialize
kubectl exec -it vault-0 -n vault -- sh
vault operator init
# Unseal Key 1: Rm0SQsQDijTKgRSDLjqxmLuEZry0mfwAgJxLYQ1bq+TQ
# Unseal Key 2: TEBxBDvdQilHEKSeIJG63n8uskJS+J+42B9Q65bJc0eT
# Unseal Key 3: EnMvZTFY9qD9lgAIQQToz1HeVXVINMfV6NH22Z4kly//
# Unseal Key 4: gbGrI8gbeVomosaEx3NjgMWrSg0lNybdolWlni5EmNVY
# Unseal Key 5: GSTTXLyIpXF+Mqt/LFj2N4FKRog3+4Gh+kwhoQ9M1y7i
# Initial Root Token: hvs.JfmYEzcN8HNSnuGuPP0LMIwh
vault operator unseal
# Unseal Key (will be hidden): Rm0SQsQDijTKgRSDLjqxmLuEZry0mfwAgJxLYQ1bq+TQ
# Key Value
# --- -----
# Seal Type shamir
# Initialized true
# Sealed true
# Total Shares 5
# Threshold 3
# Unseal Progress 1/3
# Unseal Nonce aa1e1bc8-6c0e-e325-dc89-6c0b695d7119
# Version 1.20.4
# Build Date 2025-09-23T13:22:38Z
# Storage Type raft
# Removed From Cluster false
# HA Enabled true
# Unseal을 위해 다른 Key를 이용하여 2번 더 반복
# 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-099c826b
# Cluster ID 8afdbc97-adc3-6e85-36b9-19245343956b
# Removed From Cluster false
# HA Enabled true
# HA Cluster https://vault-0.vault-internal:8201
# HA Mode active
# Active Since 2025-12-13T06:03:01.90998077Z
# Raft Committed Index 37
# Raft Applied Index 37
# 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
# Vault 로그인
kubectl exec -it vault-0 -n vault -- sh
vault login
# Token (will be hidden): hvs.JfmYEzcN8HNSnuGuPP0LMIwh
# vault-1, vault-2 Pod에서도 Unseal 작업 진행
kubectl exec -it vault-1 -n vault -- sh
kubectl exec -it vault-2 -n vault -- sh
# Vault 접근 정보 지정 후 로그인
export VAULT_ADDR='http://localhost:30000'
export VAULT_ROOT_TOKEN=hvs.JfmYEzcN8HNSnuGuPP0LMIwh
vault login
vault operator raft list-peers
# Node Address State Voter
# ---- ------- ----- -----
# 8e1245a8-8561-09e1-09dd-8e383bec57a0 vault-0.vault-internal:8201 leader true
# bdfeca8b-7e72-f7eb-8916-d4c099799fd6 vault-1.vault-internal:8201 follower true
# 139a1f3d-fdce-d5ac-3705-f5ecad8762ad vault-2.vault-internal:8201 follower true
# 샘플 엔진 생성
vault secrets enable -path=mysecret kv-v2
# Success! Enabled the kv-v2 secrets engine at: mysecret/
# 샘플 시크릿 저장 : 경로 확인
vault kv put mysecret/logins/study \
username="demo" \
password="p@ssw0rd"
# 샘플 정책 생성
vault policy write restrict_study - <<EOF
path "mysecret/logins/study*" {
capabilities = ["deny"]
}
EOF
# Success! Uploaded policy: restrict_study
# 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
# OpenLDAP 서버 접속
kubectl -n openldap exec -it deploy/openldap -c openldap -- bash
# 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
# users 추가 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
# groups 추가 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
OpenLDAP 연동
기존 실습 시에는 Root 계정을 이용해서 실습을 진행 했었지만, 이제는 LDAP 계정 정보를 이용하여 Vault에 접속하여 Secret에 접근을 해보겠습니다.


Figure 1.2 Vault LDAP 연동 설명
vault auth enable ldap
# Success! Enabled ldap auth method at: ldap/
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
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.
LDAP 과 Vault Policy 정책 추가
vault login
# Token (will be hidden): hvs.JfmYEzcN8HNSnuGuPP0LMIwh
# 정책 생성
vault policy write admin - <<EOF
path "*" {
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}
EOF
# Success! Uploaded policy: admin
# LDAP admins 그룹에 admin (vault) 정책 지정
vault write auth/ldap/groups/admins policies=admin
# Success! Data written to: auth/ldap/groups/admins
2. Vault Server TLS 적용
Vault를 Server 사용 시 https 를 사용하는 방법에 대해서 실습해보겠습니다.
실습 환경 구성
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"
}
}
}
}
}'
# 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
# Vault 서버 인증서 생성
openssl genrsa -out vault.key 2048
openssl req -new -key vault.key \
-subj "/CN=vault.example.com" \
-out vault.csr
# 인증서 서명
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
)
# Certificate request self-signature ok
# subject=CN = vault.example.com
# 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
# Root Key 확인
jq -r ".root_token" cluster-keys.json
# Unseal 작업
kubectl exec vault-0 -n vault -- vault operator init -tls-skip-verify \
-key-shares=1 \
-key-threshold=1 \
-format=json > cluster-keys.json
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
# 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'
# Root Token으로 로그인
vault login -tls-skip-verify
HTTPS 설정 확인
# Host 등록
echo "127.0.0.1 vault.example.com" | sudo tee -a /etc/hosts
# Ingress 설정
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'DevOps > Study' 카테고리의 다른 글
| k8s Deploy Study 1주차 Bootstrap Kubernetes the hard way (0) | 2026.01.10 |
|---|---|
| CI/CD Study 8주차 Vault (1/2) (0) | 2025.12.13 |
| CI/CD Study 7주차 Vault (2/2) (0) | 2025.11.29 |
| CI/CD Study 7주차 Vault (1/2) (0) | 2025.11.29 |
| CI/CD Study 6주차 ArgoCD 3/3 (1/2) (0) | 2025.11.23 |