Infra/AWS

[AEWS] #10μ£Όμ°¨ K8s Vault μ‹€μŠ΅

μ˜€λ¬˜ν•œ 루피 2025. 4. 13. 03:04
25년도 AWS EKS Hands-on Study μŠ€ν„°λ”” 정리 λ‚΄μš©μž…λ‹ˆλ‹€.
λ³Έ μ‹€μŠ΅ μ°Έκ³  νŽ˜μ΄μ§€ : https://aws-ia.github.io/terraform-aws-eks-blueprints/patterns/blue-green-upgrade/ 

 

μ‹€μŠ΅ ν™˜κ²½ ꡬ성

μ  ν‚¨μŠ€

cat <<EOT > docker-compose.yaml
services:

  jenkins:
    container_name: jenkins
    image: jenkins/jenkins
    restart: unless-stopped
    networks:
      - cicd-network
    ports:
      - "8080:8080"
      - "50000:50000"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - jenkins_home:/var/jenkins_home

volumes:
  jenkins_home:

networks:
  cicd-network:
    driver: bridge
EOT
docker compose up -d
docker compose ps

 

# Jenkins 초기 μ•”ν˜Έ 확인
docker compose exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
09a21116f3ce4f27a0ede79372febfb1

# Jenkins μ›Ή 접속
open "http://127.0.0.1:8080"

docker compose logs jenkins -f

 

μ  ν‚¨μŠ€μ— μ ‘μ†ν•˜μ—¬ 초기 μ„ΈνŒ…μ„ ν•΄μ€€ ν›„ 초기 ν”ŒλŸ¬κ·ΈμΈμ„ μ„€μΉ˜ν•œλ‹€.

이후 μ •μƒμ μœΌλ‘œ μ™„λ£Œλ˜μ—ˆμœΌλ©΄ 계정 μ„ΈνŒ…μ„ μ§„ν–‰ν•΄μ£Όκ³  Jenkins 관리 > ν”ŒλŸ¬κ·ΈμΈ > available pluginsμ—μ„œ hashicorp vaultλ₯Ό λ‹€μš΄λ°›λŠ”λ‹€.

kind  ν΄λŸ¬μŠ€ν„° ꡬ성

cat > kind-3node.yaml <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
  apiServerAddress: "127.0.0.1" # $MyIP둜 μ„€μ •ν•˜μ…”λ„ λ©λ‹ˆλ‹€.
nodes:
- role: control-plane
  extraPortMappings:
  - containerPort: 30000
    hostPort: 30000
  - containerPort: 30001
    hostPort: 30001
  - containerPort: 30002
    hostPort: 30002
  - containerPort: 30003
    hostPort: 30003
  - containerPort: 30004
    hostPort: 30004
  - containerPort: 30005
    hostPort: 30005
  - containerPort: 30006
    hostPort: 30006
- role: worker
- role: worker
EOF

kind create cluster --config kind-3node.yaml --name myk8s --image kindest/node:v1.32.2

argocd

kubectl create ns argocd
cat <<EOF > argocd-values.yaml
dex:
  enabled: false

server:
  service:
    type: NodePort
    nodePortHttps: 30002
  extraArgs:
    - --insecure
EOF

# μ„€μΉ˜
helm repo add argo https://argoproj.github.io/argo-helm
helm install argocd argo/argo-cd --version 7.8.13 -f argocd-values.yaml --namespace argocd

 

 

Vault

인증과 인가

인증 (Authentication) : "μ‚¬μš©μžκ°€ λˆ„κ΅¬μΈμ§€ 증λͺ…ν•˜λŠ” 절차"

  • μ‚¬μš©μžκ°€ μ£Όμž₯ν•˜λŠ” 신원을 ν™•μΈν•˜λŠ” 과정이닀.
  • 일반적으둜 ID와 λΉ„λ°€λ²ˆν˜Έ, μΈμ¦μ„œ, OTP, 생체 정보 등을 μ‚¬μš©ν•œλ‹€.
  • μ˜ˆμ‹œ
    • λ‘œκ·ΈμΈν•  λ•Œ ID/PWλ₯Ό μž…λ ₯ν•΄μ„œ λ‚΄κ°€ μ§„μ§œ νŠΉμ • μ‚¬λžŒμ΄λΌλŠ” κ±Έ ν™•μΈλ°›λŠ” 것
    • OAuthμ—μ„œ ꡬ글 κ³„μ •μœΌλ‘œ λ‘œκ·ΈμΈν•˜κΈ°

인가 (Authorization) : "μ‚¬μš©μžκ°€ 무엇을 ν•  수 μžˆλŠ”μ§€ κ²°μ •ν•˜λŠ” 절차"

  • 인증이 λλ‚œ μ‚¬μš©μžμ—κ²Œ μ–΄λ–€ κΆŒν•œμ΄ μžˆλŠ”μ§€ νŒλ‹¨ν•˜λŠ” 과정이닀.
  • μ–΄λ–€ λ¦¬μ†ŒμŠ€μ— μ ‘κ·Ό κ°€λŠ₯ν•œμ§€, μ–΄λ–€ μž‘μ—…μ„ μˆ˜ν–‰ν•  수 μžˆλŠ”μ§€λ₯Ό κ²°μ •ν•œλ‹€.
  • μ˜ˆμ‹œ
    • λ‘œκ·ΈμΈν•œ μ‚¬μš©μžκ°€ κ΄€λ¦¬μž νŽ˜μ΄μ§€μ— μ ‘κ·Όν•  수 μžˆλŠ”μ§€ 확인
    • 파일 μ‹œμŠ€ν…œμ—μ„œ 읽기/μ“°κΈ° κΆŒν•œμ΄ μžˆλŠ”μ§€ 체크

 

vault μ‹€μŠ΅ ν™˜κ²½ ꡬ성

helm으둜 νŒ¨ν‚€μ§€ μ„€μΉ˜

helm repo add hashicorp https://helm.releases.hashicorp.com

helm search repo hashicorp/vault
NAME                                    CHART VERSION   APP VERSION     DESCRIPTION                          
hashicorp/vault                         0.30.0          1.19.0          Official HashiCorp Vault Chart       
hashicorp/vault-secrets-gateway         0.0.2           0.1.0           A Helm chart for Kubernetes          
hashicorp/vault-secrets-operator        0.10.0          0.10.0          Official Vault Secrets Operator Chart
# Create a Kubernetes namespace.
kubectl create namespace vault

# View all resources in a namespace.
kubectl get all --namespace vault

# Setup Helm repo
helm repo add hashicorp https://helm.releases.hashicorp.com

# Check that you have access to the chart.
helm search repo hashicorp/vault
# NAME                                    CHART VERSION   APP VERSION     DESCRIPTION                          
# hashicorp/vault                         0.30.0          1.19.0          Official HashiCorp Vault Chart       
# hashicorp/vault-secrets-gateway         0.0.2           0.1.0           A Helm chart for Kubernetes          
# hashicorp/vault-secrets-operator        0.10.0          0.10.0          Official Vault Secrets Operator Chart

 

cat <<EOF > override-values.yaml
global:
  enabled: true
  tlsDisable: true  # Disable TLS for demo purposes

server:
  image:
    repository: "hashicorp/vault"
    tag: "1.19.0"

  standalone:
    enabled: true
    replicas: 1  # 단일 λ…Έλ“œ

    config: |
      ui = true
      disable_mlock = true
      cluster_name = "vault-local"

      listener "tcp" {
        address = "[::]:8200"
        cluster_address = "[::]:8201"
        tls_disable = 1
      }

      storage "raft" {
        path = "/vault/data"
        node_id = "vault-dev-node-1"
      }
  service:
    enabled: true
    type: NodePort
    port: 8200
    targetPort: 8200
    nodePort: 30000

injector:
  enabled: true

ui:
  enabled: true
  serviceType: "NodePort"
EOF

 

이 ν™˜κ²½μ€ μ‹€μŠ΅μš© κ΅¬μ„±μ΄λ―€λ‘œ μŠ€νƒ λ“œμ–Όλ‘ μ˜ 단일 λ…Έλ“œ ν˜•νƒœλ‘œ ꡬ성이 μ§„ν–‰λœλ‹€. μ‹€μ œ 운영 ν™˜κ²½μ—μ„œλŠ” ꢌμž₯λ˜μ§€ μ•ŠλŠ” ν™˜κ²½ ꡬ성인 점에 μ£Όμ˜ν•œλ‹€.

k get pods,svc,pvc
NAME                                        READY   STATUS              RESTARTS   AGE
pod/vault-0                                 0/1     ContainerCreating   0          14s
pod/vault-agent-injector-56459c7545-bqgk2   0/1     Running             0          14s

NAME                               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                         AGE
service/vault                      NodePort    10.96.228.133   <none>        8200:30000/TCP,8201:32730/TCP   14s
service/vault-agent-injector-svc   ClusterIP   10.96.147.40    <none>        443/TCP                         14s
service/vault-internal             ClusterIP   None            <none>        8200/TCP,8201/TCP               14s
service/vault-ui                   NodePort    10.96.51.20     <none>        8200:31005/TCP                  14s

NAME                                 STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
persistentvolumeclaim/data-vault-0   Bound    pvc-6df6d70b-9815-4f4c-b8fc-8c05efe570a5   10Gi       RWO            standard       <unset>                 14s

 

kubectl exec -ti vault-0 -- vault status
Key                     Value
---                     -----
Seal Type               shamir
Initialized             false
Sealed                  true
Total Shares            0
Threshold               0
Unseal Progress         0/0
Unseal Nonce            n/a
Version                 1.19.0
Build Date              2025-03-04T12:36:40Z
Storage Type            raft
Removed From Cluster    false
HA Enabled              true
command terminated with exit code 2

 

vault unseal

VaultλŠ” '금고'λΌλŠ” λœ»μ„ κ°€μ§€κ³  μžˆλŠ”λ§ŒνΌ κΈ°λ³Έ μƒνƒœμ—μ„œλŠ” '잠긴 채'둜 μ‹œμž‘ν•œλ‹€.

  • Initialize: Vaultκ°€ μƒνƒœλ₯Ό μ €μž₯ν•  backend(예: Consul, file λ“±)에 졜초 데이터λ₯Ό κ΅¬μ„±ν•˜κ³ , λ§ˆμŠ€ν„° 킀와 ν‚€ 쉐어λ₯Ό μƒμ„±ν•œλ‹€.
  • Unseal: VaultλŠ” μ΄ˆκΈ°ν™” 이후에도 μ‹€μ œ λΉ„λ°€ μ €μž₯μ†Œλ₯Ό μ‚¬μš©ν•  수 없도둝 봉인(seal)λ˜μ–΄ μžˆλ‹€. μ΅œμ†Œ 3개 쀑 2개 μ΄μƒμ˜ ν‚€ 쉐어λ₯Ό μž…λ ₯ν•΄μ•Ό 봉인을 ν•΄μ œν•  수 μžˆλ‹€.

즉, VaultλŠ” μ„€μΉ˜ν•˜λ©΄ μ¦‰μ‹œ μ‚¬μš© κ°€λŠ₯ μƒνƒœκ°€ μ•„λ‹ˆλ‹€. μˆ˜λ™μœΌλ‘œ μ΄ˆκΈ°ν™”ν•˜κ³  봉인을 ν•΄μ œν•΄μ•Ό 본격적인 λ™μž‘μ„ μ‹œμž‘ν•œλ‹€.

 

cat <<EOF > init-unseal.sh
#!/bin/bash

# Vault Pod 이름
VAULT_POD="vault-0"

# Vault λͺ…λ Ή μ‹€ν–‰
VAULT_CMD="kubectl exec -ti \$VAULT_POD -- vault"

# 좜λ ₯ μ €μž₯ 파일
VAULT_KEYS_FILE="./vault-keys.txt"
UNSEAL_KEY_FILE="./vault-unseal-key.txt"
ROOT_TOKEN_FILE="./vault-root-token.txt"

# Vault μ΄ˆκΈ°ν™” (Unseal Key 1개만 μƒμ„±λ˜λ„λ‘ μ„€μ •)
\$VAULT_CMD operator init -key-shares=1 -key-threshold=1 | sed \$'s/\\x1b\\[[0-9;]*m//g' | tr -d '\r' > "\$VAULT_KEYS_FILE"

# Unseal Key / Root Token μΆ”μΆœ
grep 'Unseal Key 1:' "\$VAULT_KEYS_FILE" | awk -F': ' '{print \$2}' > "\$UNSEAL_KEY_FILE"
grep 'Initial Root Token:' "\$VAULT_KEYS_FILE" | awk -F': ' '{print \$2}' > "\$ROOT_TOKEN_FILE"

# Unseal μˆ˜ν–‰
UNSEAL_KEY=\$(cat "\$UNSEAL_KEY_FILE")
\$VAULT_CMD operator unseal "\$UNSEAL_KEY"

# κ²°κ³Ό 좜λ ₯
echo "[πŸ”“] Vault Unsealed!"
echo "[πŸ”] Root Token: \$(cat \$ROOT_TOKEN_FILE)"
EOF


chmod +x init-unseal.sh
./init-unseal.sh
kubectl exec -ti vault-0 -- vault status
Key                     Value
---                     -----
Seal Type               shamir
Initialized             true

 

unseal 이후 vault-root-token.txtλ₯Ό μ‚¬μš©ν•˜μ—¬ 접속할 수 μžˆλ‹€.

 

vault cli

brew tap hashicorp/tap
brew install hashicorp/tap/vault

export VAULT_ADDR='http://localhost:30000'

vault status
vault login

 

vault μ‹œν¬λ¦Ώμ—”μ§„ ν™œμ„±ν™”

vault secrets enable -path=secret kv-v2
Success! Enabled the kv-v2 secrets engine at: secret/

vault kv put secret/sampleapp/config \
  username="demo" \
  password="p@ssw0rd"
======== Secret Path ========
secret/data/sampleapp/config

======= Metadata =======
Key                Value
---                -----
created_time       2025-04-12T16:10:07.147938837Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1

vault kv get secret/sampleapp/config
======== Secret Path ========
secret/data/sampleapp/config

======= Metadata =======
Key                Value
---                -----
created_time       2025-04-12T16:10:07.147938837Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1

====== Data ======
Key         Value
---         -----
password    p@ssw0rd
username    demo

 

 

vault sidecar μ‹€μŠ΅

AppRole 방식 인증 ꡬ성

AppRole 인증 ν™œμ„±ν™”

vault auth enable approle || echo "AppRole already enabled"
vault auth list
Success! Enabled approle auth method at: approle/
Path        Type       Accessor                 Description                Version
----        ----       --------                 -----------                -------
approle/    approle    auth_approle_e9721a41    n/a                        n/a
token/      token      auth_token_363206bd      token based credentials    n/a

μ ‘κ·Ό μ •μ±…(Policy) μž‘μ„±

vault policy write sampleapp-policy - <<EOF
path "secret/data/sampleapp/*" {
  capabilities = ["read"]
}
EOF
Success! Uploaded policy: sampleapp-policy

 

 

sampleapp-policyλΌλŠ” μ΄λ¦„μ˜ 정책을 μž‘μ„±ν•œλ‹€.

ν•΄λ‹Ή 정책은 secret/data/sampleapp/* 경둜의 KV 데이터λ₯Ό 읽기(read) ν•  수 μžˆλŠ” κΆŒν•œμ„ λΆ€μ—¬ν•œλ‹€.

 

 

AppRole Role 생성

vault write auth/approle/role/sampleapp-role \
  token_policies="sampleapp-policy" \
  secret_id_ttl="1h" \
  token_ttl="1h" \
  token_max_ttl="4h"

Success! Data written to: auth/approle/role/sampleapp-role

 

vault policy writeλŠ” μ •μ±… 이름과 λ‚΄μš©μ„ Vault에 λ“±λ‘ν•œλ‹€.

이 Role을 μ‚¬μš©ν•˜λŠ” μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ€ sampleapp-policy에 μ •μ˜λœ λ¦¬μ†ŒμŠ€μ— μ ‘κ·Όν•  수 μžˆλŠ” 토큰을 얻을 수 μžˆλ‹€.

 

ROLE_ID 및 SECRET_ID 생성

ROLE_ID=$(vault read -field=role_id auth/approle/role/sampleapp-role/role-id)
SECRET_ID=$(vault write -f -field=secret_id auth/approle/role/sampleapp-role/secret-id)

mkdir -p approle-creds
echo "$ROLE_ID" > approle-creds/role_id.txt
echo "$SECRET_ID" > approle-creds/secret_id.txt

 

ROLE_IDλŠ” AppRole 자체의 μ‹λ³„μžλ‘œ 고정값이며 곡개적으둜 전달해도 μ•ˆμ „ν•˜λ‹€.

SECRET_IDλŠ” 일회용 μ‹œν¬λ¦Ώ ν‚€λ‘œ, μ‹€μ œ 인증 μ‹œ Role ID와 ν•¨κ»˜ μ‚¬μš©λ˜μ–΄ Vault 접근을 ν—ˆμš©ν•œλ‹€. 

이 두 값을 μ‘°ν•©ν•΄μ„œ AppRole 인증 토큰을 λ°œκΈ‰λ°›μ„ 수 μžˆλ‹€.

Kubernetes Secret 생성

kubectl create secret generic vault-approle -n vault \
  --from-literal=role_id="${ROLE_ID}" \
  --from-literal=secret_id="${SECRET_ID}" \
  --save-config \
  --dry-run=client -o yaml | kubectl apply -f -
secret/vault-approle created

 

νŒŒλ“œκ°€ Vault에 μ ‘κ·Όν•˜κΈ° μœ„ν•œ 자격증λͺ…μœΌλ‘œ μ‚¬μš©ν•  수 μžˆλ‹€.

 

vault agent sidecar 연동

vault-agent-config.hcl

cat <<EOF | kubectl create configmap vault-agent-config -n vault --from-file=agent-config.hcl=/dev/stdin --dry-run=client -o yaml | kubectl apply -f -
vault {
  address = "http://vault.vault.svc:8200"
}

auto_auth {
  method "approle" {
    config = {
      role_id_file_path = "/etc/vault/approle/role_id"
      secret_id_file_path = "/etc/vault/approle/secret_id"
      remove_secret_id_file_after_reading = false
    }
  }

  sink "file" {
    config = {
      path = "/etc/vault-agent-token/token"
    }
  }
}

template_config {
  static_secret_render_interval = "20s"
}

template {
  destination = "/etc/secrets/index.html"
  contents = <<EOH
  <html>
  <body>
    <p>username: {{ with secret "secret/data/sampleapp/config" }}{{ .Data.data.username }}{{ end }}</p>
    <p>password: {{ with secret "secret/data/sampleapp/config" }}{{ .Data.data.password }}{{ end }}</p>
  </body>
  </html>
EOH
}
EOF

 

Vault Agentλ₯Ό Kubernetes ν™˜κ²½μ—μ„œ μ‚¬μš©ν•˜κΈ° μœ„ν•œ ConfigMap 생성 과정을 λ‹΄κ³ μžˆμœΌλ©°, Vault AgentλŠ” Vaultμ™€μ˜ 인증, 토큰 관리, μ‹œν¬λ¦Ώ ν…œν”Œλ¦Ώ λ Œλ”λ§μ„ μžλ™μœΌλ‘œ μ²˜λ¦¬ν•΄μ£ΌλŠ” 도ꡬ이닀.

 

Nginx + Vault Agent 배포

kubectl apply -n vault -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-vault-demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-vault-demo
  template:
    metadata:
      labels:
        app: nginx-vault-demo
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80
        volumeMounts:
        - name: html-volume
          mountPath: /usr/share/nginx/html
      - name: vault-agent-sidecar
        image: hashicorp/vault:latest
        args:
          - "agent"
          - "-config=/etc/vault/agent-config.hcl"
        volumeMounts:
        - name: vault-agent-config
          mountPath: /etc/vault
        - name: vault-approle
          mountPath: /etc/vault/approle
        - name: vault-token
          mountPath: /etc/vault-agent-token
        - name: html-volume
          mountPath: /etc/secrets
      volumes:
      - name: vault-agent-config
        configMap:
          name: vault-agent-config
      - name: vault-approle
        secret:
          secretName: vault-approle
      - name: vault-token
        emptyDir: {}
      - name: html-volume
        emptyDir: {}
EOF

 

svc 생성

kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  type: NodePort
  selector:
    app: nginx-vault-demo
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
      nodePort: 30001 # Kindμ—μ„œ μ„€μ •ν•œ Port
EOF
service/nginx-service created

 

kubectl get pod -l app=nginx-vault-demo
NAME                                READY   STATUS    RESTARTS   AGE
nginx-vault-demo-7776649597-tcm4l   2/2     Running   0          3m14s

 

μ‹ κ·œ νŒ¨μŠ€μ›Œλ“œ

 

 

vault + jenkins

 

 

ArgoCD + vault plugin

kubectl apply -f - <<EOF
kind: Secret
apiVersion: v1
metadata:
  name: argocd-vault-plugin-credentials
  namespace: argocd
type: Opaque
stringData:
  VAULT_ADDR: "http://vault.vault:8200"
  AVP_TYPE: "vault"
  AVP_AUTH_TYPE: "approle"
  AVP_ROLE_ID: λ‘€
  AVP_SECRET_ID: μ‹œν¬λ¦Ώ
EOF
secret/argocd-vault-plugin-credentials created

ν”ŒλŸ¬κ·ΈμΈ ν™œμ„±ν™”

git clone https://github.com/hyungwook0221/argocd-vault-plugin.git
cd argocd-vault-plugin/manifests/cmp-sidecar

# kustomize edit fix

kubens argocd

kubectl kustomize .
kubectl apply -n argocd -k .

μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 배포

kubectl apply -n argocd -f - <<EOF
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: demo
  namespace: argocd
  finalizers:
  - resources-finalizer.argocd.argoproj.io
spec:
  destination:
    namespace: argocd
    server: https://kubernetes.default.svc
  project: default
  source:
    path: infra/helm
    repoURL: https://github.com/hyungwook0221/spring-boot-debug-app
    targetRevision: main
    plugin:
      name: argocd-vault-plugin-helm
      env:
        - name: HELM_ARGS
          value: -f new-values.yaml
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
EOF

 

 

 

λ””ν”Œλ‘œμ΄λ¨ΌνŠΈμ— 적용된 env값이 λ³€κ²½λœ νŒ¨μŠ€μ›Œλ“œλ‘œ μ μš©λΌμžˆλŠ” 것을 확인할 수 μžˆλ‹€.

 

Vault Secret Operator

# vault-operator-values.yaml
defaultVaultConnection:
  enabled: true
  address: "http://vault.vault.svc.cluster.local:8200"
  skipTLSVerify: false
controller:
  manager:
    clientCache:
      persistenceModel: direct-encrypted
      storageEncryption:
        enabled: true
        mount: k8s-auth-mount
        keyName: vso-client-cache
        transitMount: demo-transit
        kubernetes:
          role: auth-role-operator
          serviceAccount: vault-secrets-operator-controller-manager
          tokenAudiences: ["vault"]

 

κΆŒν•œ μ„€μ •

kubectl exec --stdin=true --tty=true vault-0 -n vault -- /bin/sh

vault login

# Kubernetes 인증 λ©”μ„œλ“œ ν™œμ„±ν™”
vault auth enable -path k8s-auth-mount kubernetes

vault write auth/k8s-auth-mount/config \
  kubernetes_host="https://kubernetes.default.svc:443" \
  kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
  token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"

# vault k8s auth role 
vault write auth/k8s-auth-mount/role/auth-role \
   bound_service_account_names=demo-dynamic-app \
   bound_service_account_namespaces=demo-ns \
   token_ttl=0 \
   token_period=120 \
   token_policies=demo-auth-policy-db \
   audience=vault

vault secrets enable -path=demo-db database

# PostgreSQL μ—°κ²° 정보 등둝
# ν•΄λ‹Ή 과정은 postgresκ°€ μ •μƒμ μœΌλ‘œ λ™μž‘ μ‹œ 적용 κ°€λŠ₯
# allowed_roles: 이후 μ„€μ •ν•  Role 이름 μ§€μ •
vault write demo-db/config/demo-db \
   plugin_name=postgresql-database-plugin \
   allowed_roles="dev-postgres" \
   connection_url="postgresql://{{username}}:{{password}}@postgres-postgresql.postgres.svc.cluster.local:5432/postgres?sslmode=disable" \
   username="postgres" \
   password="secret-pass"
  
# DB μ‚¬μš©μž 동적 생성 Role 등둝
# ν•΄λ‹Ή Role μ‚¬μš© μ‹œ Vaultκ°€ λ™μ μœΌλ‘œ μ‚¬μš©μž 계정과 λΉ„λ°€λ²ˆν˜Έλ₯Ό 생성 κ°€λŠ₯
# TTL은 μƒμ„±λœ 자격증λͺ…μ˜ 유효 μ‹œκ°„ (30초~10λΆ„)
vault write demo-db/roles/dev-postgres \
   db_name=demo-db \
   creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \
      GRANT ALL PRIVILEGES ON DATABASE postgres TO \"{{name}}\";" \
   revocation_statements="REVOKE ALL ON DATABASE postgres FROM  \"{{name}}\";" \
   backend=demo-db \
   name=dev-postgres \
   default_ttl="1m" \
   max_ttl="1m"
   
# demo-db/creds/dev-postgres κ²½λ‘œμ— λŒ€ν•œ read κΆŒν•œ λΆ€μ—¬
# μΆ”ν›„ Kubernetes μ„œλΉ„μŠ€ μ–΄μΉ΄μš΄νŠΈ(demo-dynamic-app)에 이 정책을 μ—°κ²°ν•΄μ„œ 자격증λͺ… μš”μ²­ κ°€λŠ₯
vault policy write demo-auth-policy-db - <<EOF
path "demo-db/creds/dev-postgres" {
   capabilities = ["read"]
}
EOF

vault secrets enable -path=demo-transit transit

# vso-client-cacheλΌλŠ” ν‚€λ₯Ό 생성
# 이 ν‚€λŠ” VSOκ°€ μ•”λ³΅ν˜Έν™” μ‹œ μ‚¬μš©ν•  μ•”ν˜Έν™” ν‚€ μ—­ν• 
vault write -force demo-transit/keys/vso-client-cache

# vso-client-cache 킀에 λŒ€ν•΄ μ•”ν˜Έν™”(encrypt), λ³΅ν˜Έν™”(decrypt)λ₯Ό ν—ˆμš©ν•˜λŠ” μ •μ±… 생성
vault policy write demo-auth-policy-operator - <<EOF
path "demo-transit/encrypt/vso-client-cache" {
   capabilities = ["create", "update"]
}
path "demo-transit/decrypt/vso-client-cache" {
   capabilities = ["create", "update"]
}
EOF

# Vault Secrets Operatorκ°€ μ‚¬μš©ν•˜λŠ” ServiceAccount에 μœ„ 정책을 바인딩
# vsoκ°€ Vault에 λ‘œκ·ΈμΈν•  λ•Œ μ‚¬μš©ν•  수 μžˆλŠ” JWT 기반 Role μ„€μ •
# ν•΄λ‹Ή Role을 톡해 OperatorλŠ” Transit 엔진을 μ΄μš©ν•œ μ•”λ³΅ν˜Έν™” API 호좜 κ°€λŠ₯
vault write auth/k8s-auth-mount/role/auth-role-operator \
   bound_service_account_names=vault-secrets-operator-controller-manager \
   bound_service_account_namespaces=vault-secrets-operator-system \
   token_ttl=0 \
   token_period=120 \
   token_policies=demo-auth-policy-operator \
   audience=vault
   
vault read auth/k8s-auth-mount/role/auth-role-operator

 

helm install vault-secrets-operator hashicorp/vault-secrets-operator \
  -n vault-secrets-operator-system \
  --create-namespace \
  --values vault-operator-values.yaml
NAME: vault-secrets-operator
LAST DEPLOYED: Sun Apr 13 02:35:06 2025
NAMESPACE: vault-secrets-operator-system
STATUS: deployed
REVISION: 1

 

vault read auth/k8s-auth-mount/role/auth-role-operator
Key                                         Value
---                                         -----
alias_name_source                           serviceaccount_uid
audience                                    vault
bound_service_account_names                 [vault-secrets-operator-controller-manager]
bound_service_account_namespace_selector    n/a
bound_service_account_namespaces            [vault-secrets-operator-system]
token_bound_cidrs                           []
token_explicit_max_ttl                      0s
token_max_ttl                               0s
token_no_default_policy                     false
token_num_uses                              0
token_period                                2m
token_policies                              [demo-auth-policy-operator]
token_ttl                                   0s
token_type                                  default

 

vault-auth-dynamic.yaml

---
# vault-auth-dynamic.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  namespace: demo-ns
  name: demo-dynamic-app
---
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultAuth
metadata:
  name: dynamic-auth
  namespace: demo-ns
spec:
  method: kubernetes
  mount: k8s-auth-mount
  kubernetes:
    role: auth-role
    serviceAccount: demo-dynamic-app
    audiences:
      - vault

 

vault-dynamic-secret.yaml

---
# vault-dynamic-secret.yaml
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultDynamicSecret
metadata:
  name: vso-db-demo
  namespace: demo-ns
spec:
  refreshAfter: 25s
  mount: demo-db
  path: creds/dev-postgres
  destination:
    name: vso-db-demo
    create: true
    overwrite: true
  vaultAuthRef: dynamic-auth
  rolloutRestartTargets:
  - kind: Deployment
    name: vaultdemo

app-secret.yaml

---
# app-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: vso-db-demo
  namespace: demo-ns

app-secret-deploy.yaml

---
# app-spring-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: vaultdemo
  namespace: demo-ns
  labels:
    app: vaultdemo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: vaultdemo
  template:
    metadata:
      labels:
        app: vaultdemo
    spec:
      volumes:
        - name: secrets
          secret:
            secretName: "vso-db-demo"
      containers:
        - name: vaultdemo
          image: hyungwookhub/vso-spring-demo:v5
          imagePullPolicy: IfNotPresent
          env:
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: "vso-db-demo"
                  key: password
            - name: DB_USERNAME
              valueFrom:
                secretKeyRef:
                  name: "vso-db-demo"
                  key: username
            - name: DB_HOST
              value: "postgres-postgresql.postgres.svc.cluster.local"
            - name: DB_PORT
              value: "5432"
            - name: DB_NAME
              value: "postgres"
          ports:
            - containerPort: 8088
          volumeMounts:
            - name: secrets
              mountPath: /etc/secrets
              readOnly: true
---
apiVersion: v1
kind: Service
metadata:
  name: vaultdemo
  namespace: demo-ns
spec:
  ports:
    - name: vaultdemo
      port: 8088         
      targetPort: 8088 
      nodePort: 30003
  selector:
    app: vaultdemo
  type: NodePort

 

k apply -f .
secret/vso-db-demo created
deployment.apps/vaultdemo created
service/vaultdemo created
serviceaccount/demo-dynamic-app created
vaultauth.secrets.hashicorp.com/dynamic-auth created
vaultdynamicsecret.secrets.hashicorp.com/vso-db-demo created

 

 

 

 

μ‹€μŠ΅ λ¦¬μ†ŒμŠ€ μ‚­μ œ

docker compose down --volumes --remove-orphans
[+] Running 3/3
 βœ” Container jenkins               Removed                                                     0.5s 
 βœ” Volume cicd-labs_jenkins_home   Removed                                                     0.2s 
 βœ” Network cicd-labs_cicd-network  Removed                                                     0.2s 

kind delete cluster --name myk8s
Deleting cluster "myk8s" ...
Deleted nodes: ["myk8s-worker2" "myk8s-worker" "myk8s-control-plane"]