본문 바로가기
devops/cicd

[CI/CD] 8주차 - Hashicorp Vault/VSO on K8S

by gnobaaaar 2025. 12. 6.
어느덧 스터디 마지막 주차 
주말마다 쉽지는 않지만 할때마다 많이 성장하는 느낌
부족한 부분은 복습 간 조금씩 채워넣을 예정입니다.

 

설치

실습을 위해서 kind로 클러스터와 vault를 설치해주겠습니다.

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: 30000  # Vault Web UI
    hostPort: 30000
  - containerPort: 30001  # Sample application
    hostPort: 30001
EOF

 
설치확인 후 노드에 기본툴을 설치해줍니다.

# 설치 확인
docker ps
kubectl get node
NAME                  STATUS   ROLES           AGE     VERSION
myk8s-control-plane   Ready    control-plane   4h12m   v1.32.2


# 노드에 기본 툴 설치
docker exec -it myk8s-control-plane sh -c 'apt update && apt install tree psmisc lsof wget net-tools dnsutils tcpdump ngrep iputils-ping git vim -y'

 

Vault 설치

Helm 설치를 진행합니다.

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

# Create a Kubernetes namespace.
kubectl create namespace vault

cat <<EOF > vault-values.yaml
global:
  enabled: true
  tlsDisable: true

server:
  standalone:
    enabled: true
    config: |
      ui = true

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

      storage "file" {
        path = "/vault/data"
      }

  dataStorage:
    enabled: true
    size: "10Gi"
    mountPath: "/vault/data"

  auditStorage:
    enabled: true
    size: "10Gi"
    mountPath: "/vault/logs"

  service:
    enabled: true
    type: NodePort
    nodePort: 30000
  
ui:
  enabled: true

injector:
  enabled: false
EOF


# helm 설치
helm upgrade vault hashicorp/vault -n vault -f vault-values.yaml --install --dry-run=client
helm upgrade vault hashicorp/vault -n vault -f vault-values.yaml --install --version 0.31.0

 
unseal 상태로 파드가 Readiness Probe 체크 실패 상태입니다.

 

# Vault Status 명령으로 Sealed 상태확인
kubectl exec -ti vault-0 -n vault -- vault status
...
Key                Value
---                -----
Seal Type          shamir
Initialized        false
Sealed             true
...

# vault 로그 확인
kubectl stern -n vault -l app.kubernetes.io/name=vault
...
vault-0 vault 2025-04-16T05:35:09.225Z [INFO]  core: seal configuration missing, not initialized
...

 
 

Vault Unseal

unseal을 진행하겠습니다.

 

-key-shares=N전체 Unseal Key 조각을 몇 개로 쪼갤 것인가?
-key-threshold=M그 조각 중 몇 개 이상 모이면 Unseal이 되는가?

 

# Initialize vault-0 with one key share and one key threshold.
kubectl exec vault-0 -n vault -- vault operator init \
    -key-shares=1 \
    -key-threshold=1 \
    -format=json > cluster-keys.json

# cluster-keys.json 파일 확인
cat cluster-keys.json| jq
{
  "unseal_keys_b64": [
    "VthosBSlObBJ1DSpenVby4wmxt+Dx2dWeldBK725ies="
  ],
  ...
  "root_token": "hvs.XEPc44aa6q5CFA8puMfSIwlo"
}

# Display the unseal key found in cluster-keys.json.
jq -r ".unseal_keys_b64[]" cluster-keys.json
VthosBSlObBJ1DSpenVby4wmxt+Dx2dWeldBK725ies=

# Create a variable named VAULT_UNSEAL_KEY to capture the Vault unseal key.
VAULT_UNSEAL_KEY=$(jq -r ".unseal_keys_b64[]" cluster-keys.json)

# Unseal Vault running on the vault-0 pod : The Vault server is initialized and unsealed.
kubectl exec vault-0 -n vault -- vault operator unseal $VAULT_UNSEAL_KEY
Key             Value
---             -----
Seal Type       shamir
Initialized     true
Sealed          false
...

# vault-0 파드 확인 : Readiness Probe 체크 성공!
## (참고) Readiness:      exec [/bin/sh -ec vault status -tls-skip-verify]
kubectl get pod -n vault
NAME      READY   STATUS    RESTARTS   AGE
vault-0   1/1     Running   0          9m3s

# Display the root token found in cluster-keys.json.
jq -r ".root_token" cluster-keys.json
hvs.XEPc44aa6q5CFA8puMfSIwlo

 
 

Vault Login with CLI

# 설치 (macOS)
brew tap hashicorp/tap
brew install hashicorp/tap/vault
vault --version  # 설치 확인

# NodePort로 공개한 30000 NodePort로 설정
export VAULT_ADDR='http://localhost:30000'

# vault 상태확인
vault status

# Root Token으로 로그인
vault login
Token (will be hidden): hvs.XEPc44aa6q5CFA8puMfSIwlo
...

 

vault login

 

Vault Audit log

Vault Audit devices는 최소 2개 이을 활성화 하는 것을 권장 : 예) File 과 Syslog , File 과 Socket 등등
https://developer.hashicorp.com/vault/docs/audit/best-practices#enable-at-least-two-audit-devices

Audit logging best practices | Vault | HashiCorp Developer

Recommendations for setting up audit logging in HashiCorp Vault.

developer.hashicorp.com

 
PVC 디스크 풀 차면 Vault Audit 로그만 동작하지 않는게 아니라 Vault 자체 동작 수행을 막습니다. (일반적인 Log와는 다르다는 점!)

# audit 용 pvc 확인 : /vault/logs 마운트 설정되어 있음
kubectl get pvc -n vault
NAME            STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
audit-vault-0   Bound    pvc-51681977-020b-4838-9510-2d4a31748839   10Gi       RWO            standard       <unset>                 2m55s
data-vault-0    Bound    pvc-ed68083a-e725-4243-b93e-3475a88bc1e6   10Gi       RWO            standard       <unset>                 2m55s

# audit 용 pv(pvc) 에 저장될 수 있게 file audit log 설정
vault audit enable file file_path=/vault/logs/audit.log
vault audit list -detailed

# 확인
kubectl exec -it vault-0 -n vault -- tail -f /vault/logs/audit.log
...

 

vault audit list

 
 
 

Vault on K8s

k8s Auth 방식(Service Account + JWT)를 통한 Vault 인증 및 시크릿 조회 전체흐름을 확인해보겠습니다.
 
큰 흐름은 아래와 같습니다.
Pod -> SA JWT -> Vault Login -> Vault 가 k8s API 검증 -> Vault Token 발급 -> Secret 조회

(1) Vault 에 Secret 를 요청 처리를 위해 사전에 Role(Policy) 설정
(2) 파드 생성 시, 서비스 어카운트 토큰(JWT) 생성
(3) 파드의 애플리케이션이 Vault 에 로그인 과정
     3-1) 애플리케이션은 JWT를 전달하여 Vault 로그인 요청
     3-2) Vault 는 정보 확인을 위해 K8S API 서버에 TokenReview API 호출(JWT확인)
     3-3) K8S API 서버는 서비스 어카운트의 이름과 네임스페이스를 반환
     3-4) Vault 는 ‘서비스 어카운트 이름, 네임스페이스’를 Vault 해당 시크릿에 정책과 매칭 확인
     3-5) 확인 후 Vault 는 Auth Token 을 애플리케이션에게 반환
(4) 파드의 애플리케이션이 Vault 에 Secret 요청 과정
     4-1) 애플리케이션은 (3)에서 받은 Auth Token 으로 Vault 해당 시크릿 정보를 요청
     4-2) Vault 는 Auth Token 확인 및 매칭 정책 확인
     4-3) 확인 후 Vault 는 최종적으로 해당 시크릿 정보를 반환
 

Vault의 k8s-auth 인증은 AWS EKS에서도 유사한 구조로 활용됩니다.
Kubernetes 기반 인증 메커니즘의 기본 원리는 모두 동일하고,
AWS EKS의 aws-auth / Pod Identity 또한
외부 시스템(EKS Control Plane)이 신뢰하는 토큰 기반 주체 인증 + RBAC 매핑 구조로 동작합니다.

 
 

Set a secret in Vault

# Enable an instance of the kv-v2 secrets engine at the path secret.
vault secrets enable -path=secret kv-v2
Success! Enabled the kv-v2 secrets engine at: secret/

# 확인
vault secrets list -detailed
vault secrets list

# Create a secret at path secret/webapp/config with a username and password.
vault kv put secret/webapp/config username="static-user" password="static-password"
====== Secret Path ======
secret/data/webapp/config

======= Metadata =======
Key                Value
---                -----
created_time       2025-04-15T12:50:53.565975884Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1

# Verify that the secret is defined at the path secret/webapp/config.
vault kv get secret/webapp/config
====== Secret Path ======
secret/data/webapp/config

======= Metadata =======
Key                Value
---                -----
created_time       2025-04-15T12:50:53.565975884Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1

====== Data ======
Key         Value
---         -----
password    static-password
username    static-user

# Verify that the secret is defined at the path secret/webapp/config.
export VAULT_ROOT_TOKEN=hvs.nEMhDcK5ry90YR0sMXwD14bA

curl -s --header "X-Vault-Token: $VAULT_ROOT_TOKEN" --request GET \
  http://127.0.0.1:30000/v1/secret/data/webapp/config | jq
...

 

 
 

k8s auth in vault

[Auth Method: Kubernetes]  →  [Role: webapp]  →  [Policy: webapp-policy]  →  [Secret: secret/data/webapp/config]

 
Kubernetes 서비스 계정(SA) 으로 Vault에 로그인하면
→ Role(webapp) 이 매칭되고
→ Role에 연결된 Policy(webapp-policy) 가 적용되어
→ Policy가 허용한 Secret 경로(secret/data/webapp/config) 를 읽을 수 있습니다.
 

  • 볼트는 고객이 Kubernetes 서비스 계정 토큰으로 인증할 수 있는 방법을 제공합니다.
    • Vault provides a Kubernetes authentication method that enables clients to authenticate with a Kubernetes Service Account Token.
  • 볼트는 Kubernetes 클러스터 내의 모든 클라이언트로부터 이 서비스 토큰을 받습니다.
    • Vault accepts this service token from any client within the Kubernetes cluster.
  • 인증 중에 볼트는 구성된 Kubernetes 엔드포인트를 조회하여 서비스 계정 토큰이 유효한지 확인합니다.
    • During authentication, Vault verifies that the service account token is valid by querying a configured Kubernetes endpoint.

 
RBAC -> Auth Method 활성화 -> K8s API 서버 정보설정

# vault 서버가 가지고 있는 Role 확인 : 이를 통해 K8S Service Account Token 유효 여부 확인
## subjectaccessreviews : 쿠버네티스 환경에서 사용자 또는 그룹의 액션 수행 가능 여부 확인
## tokenreviews : 쿠버네티스 API 서버가 제시된 토큰의 유효성을 확인하고, 그 토큰과 관련된 사용자 정보를 얻기 위해 사용
kubectl rbac-tool lookup vault
  SUBJECT | SUBJECT TYPE   | SCOPE       | NAMESPACE | ROLE                  | BINDING               
----------+----------------+-------------+-----------+-----------------------+-----------------------
  vault   | ServiceAccount | ClusterRole |           | system:auth-delegator | vault-server-binding  

kubectl rolesum vault -n vault
...
• [CRB] */vault-server-binding ⟶  [CR] */system:auth-delegator
  Resource                                   Name  Exclude  Verbs  G L W C U P D DC  
  subjectaccessreviews.authorization.k8s.io  [*]     [-]     [-]   ✖ ✖ ✖ ✔ ✖ ✖ ✖ ✖   
  tokenreviews.authentication.k8s.io         [*]     [-]     [-]   ✖ ✖ ✖ ✔ ✖ ✖ ✖ ✖   


# Enable the Kubernetes authentication method.
vault auth enable kubernetes
Success! Enabled kubernetes auth method at: kubernetes/

# 확인
vault auth list -detailed
vault auth list

# K8S API 서버 정보 설정 : 현재 vault 가 k8s 에 설치되어 있으므로, 아래처럼 서비스명 주소 입력 가능
vault write auth/kubernetes/config \
    kubernetes_host="https://kubernetes.default.svc"
Success! Data written to: auth/kubernetes/config

 
 

# 설정 정보 확인
vault read auth/kubernetes/config
Key                                  Value
---                                  -----
disable_iss_validation               true
disable_local_ca_jwt                 false
issuer                               n/a
kubernetes_ca_cert                   n/a
kubernetes_host                      https://kubernetes.default.svc
pem_keys                             []
token_reviewer_jwt_set               false
use_annotations_as_alias_metadata    false

 

  • 클라이언트가 secret/webapp/config 에서 정의된 비밀 데이터에 접근하려면, path secret/data/webapp/config에 대한 읽기 기능이 부여되어야 합니다.
    • For a client to access the secret data defined, at secret/webapp/config, requires that the read capability be granted for the path secret/data/webapp/config.

KV v2 API Path 구조

실제 논리 경로API 요청 경로
secret/webapp/configsecret/data/webapp/config
secret/webapp/config 메타정보secret/metadata/webapp/config

 

  • 이것은 정책의 한 예입니다. 정책은 일련의 기능을 정의합니다. This is an example of a policy. A policy defines a set of capabilities.
# Write out the policy named webapp that enables the read capability for secrets at path secret/data/webapp/config.
vault policy write webapp - <<EOF
path "secret/data/webapp/config" {
  capabilities = ["read"]
}
EOF
Success! Uploaded policy: webapp
  • webapp policy 을 사용하는 인증 메서드 역할을 정의합니다. Define an auth method role that uses the webapp policy.
  • 역할정책과 환경 매개변수를 결합하여 웹 애플리케이션의 로그인을 생성합니다.
    • A role binds policies and environment parameters together to create a login for the web application.

 
SA <> Policy 연결

# Kubernetes 서비스 계정 이름과 웹앱 정책을 연결하는 웹앱이라는 이름의 Kubernetes 인증 역할을 만듭니다.
# Create a Kubernetes authentication role, named webapp, that connects the Kubernetes service account name and webapp policy.
vault write auth/kubernetes/role/webapp \
        bound_service_account_names=vault \
        bound_service_account_namespaces=default \
        policies=webapp \
        ttl=24h \
        audience="https://kubernetes.default.svc.cluster.local"
Success! Data written to: auth/kubernetes/role/webapp
  • 이 역할은 기본적으로 Kubernetes 서비스 계정, 볼트, 네임스페이스를 볼트 정책인 웹앱과 연결합니다. 인증 후 반환되는 토큰은 24시간 동안 유효합니다.
    • The role connects the Kubernetes service account, vault, and namespace, default, with the Vault policy, webapp. The tokens returned after authentication are valid for 24 hours.

 
 
 

Launch a web application

  • 웹 애플리케이션을 생성하여 DockerHub에 게시하고 기존 클러스터에서 애플리케이션을 실행할 Kubernetes 배포를 만들었습니다.
    • You have created a web application, published it to DockerHub, and created a Kubernetes deployment that will run the application in your existing cluster.
  • 예시 웹 애플리케이션은 HTTP 요청을 청취하는 단일 기능을 수행합니다.
    • The example web application performs the single function of listening for HTTP requests.
  • 요청 시 Kubernetes 서비스 토큰을 읽고 볼트로그인한 다음 비밀을 요청합니다.
    • During a request it reads the Kubernetes service token, logs into Vault, and then requests the secret.

 
서비스 어카운트 생성 후 
웹 애플리케이션 디플로이먼트와 서비스를 배포합니다
 
웹파드는 아래의 역할을 합니다
Kubernetes ServiceAccount JWT를 읽어서 Vault에 로그인하고,
Vault에서 Secret을 가져와서 사용자가 HTTP로 요청하면 화면에 출력해주는 역할.

# vault 서비스 어카운트 생성
kubectl create sa vault

# 웹 애플리케이션 디플로이먼트 + 서비스(NodePort) 배포
# JWT_PATH sets the path of the JSON web token (JWT) issued by Kubernetes. This token is used by the web application to authenticate with Vault.
# VAULT_ADDR sets the address of the Vault service. The Helm chart defined a Kubernetes service named vault that forwards requests to its endpoints (i.e. The pods named vault-0, vault-1, and vault-2).
# SERVICE_PORT sets the port that the service listens for incoming HTTP requests.
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp
  labels:
    app: webapp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      serviceAccountName: vault
      containers:
        - name: app
          image: hashieducation/simple-vault-client:latest
          imagePullPolicy: Always
          env:
            - name: VAULT_ADDR
              value: 'http://vault.vault.svc:8200'
            - name: JWT_PATH
              value: '/var/run/secrets/kubernetes.io/serviceaccount/token'
            - name: SERVICE_PORT
              value: '8080'
          volumeMounts:
          - name: sa-token
            mountPath: /var/run/secrets/kubernetes.io/serviceaccount
            readOnly: true
      volumes:
      - name: sa-token
        projected:
          sources:
          - serviceAccountToken:
              path: token
              expirationSeconds: 600 # 10분 만료 , It defaults to 1 hour and must be at least 10 minutes (600 seconds)
---
apiVersion: v1
kind: Service
metadata:
  name: webapp
spec:
  selector:
    app: webapp
  type: NodePort
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
    nodePort: 30001
EOF

# 배포 확인
kubectl get pod -l app=webapp
NAME                    READY   STATUS    RESTARTS   AGE
webapp-8d4859ff-9rvqt   1/1     Running   0          88s
준비완료

main.go : Vault와 상호작용하는 HTTP 서버를 구현
types.go : Vault와 상호작용하기 위한 데이터 구조체를 정의

# (참고) 코드 정보 확인
kubectl exec -it deploy/webapp -- cat /app/main.go
kubectl exec -it deploy/webapp -- cat /app/types.go

# 서비스 어카운트 토큰 확인 : 600초(10분)마다 갱신됨
kubectl exec -it deploy/webapp -- cat /var/run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCI6IlZIVlVTc21yMTBDZlhoNzEwb3dNVk5PYms1S0o1OHVoTlh5R0M1YjdiMjgifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNzc2MjU5MTg5LCJpYXQiOjE3NDQ3MjMxODksImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwianRpIjoiOTg1ODc0MTUtZmJiNi00NmNmLThlN2YtZTIzZjk0NGQyNTU1Iiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0Iiwibm9kZSI6eyJuYW1lIjoibXlrOHMtY29udHJvbC1wbGFuZSIsInVpZCI6ImEzYTIzOWUyLTNkMzctNDU5ZS04OTg1LWUyMzcwZTliNWI3YSJ9LCJwb2QiOnsibmFtZSI6IndlYmFwcC04ZDQ4NTlmZi05cnZxdCIsInVpZCI6IjczNTlhZTdiLTY2MjYtNGYwOS1hNGFiLTY1MDc4YjY0OWYwNCJ9LCJzZXJ2aWNlYWNjb3VudCI6eyJuYW1lIjoidmF1bHQiLCJ1aWQiOiJjOWNjYTA0ZS0yMDAwLTQ1YWQtODkxYi1kZDRhZGRiOTdmYWUifSwid2FybmFmdGVyIjoxNzQ0NzI2Nzk2fSwibmJmIjoxNzQ0NzIzMTg5LCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDp2YXVsdCJ9.Nxf0ka_gwzPzo6ISNnpY4u73LIjpyvmSLWWY2jj3EDiBtUFZyada6XTfSCwQSg8py3dikhtQAgaNG_feQsfycS0Xc3MJG88e_7R5I7fBVp9HXjhoFqyPl31isp-Gkbs8E6v4eD9caRzEoqrDeOpQcnNXKzfHx8O1LFUmtNGcNcJNv57WDFwyrLUwbLDBIZIfwtlsbX97nCl3LjcFfPwkrvV3rJw0gVInpblAmZQ8uZA6oRfkuA6B39sAPErxtnupdZMcnD4eKIxL2Ap3nE4O1VZ_2kJAMwwZkNGKV7TXvT1cjWqIH4Fn3WB--ZV_qvFNIH6EAc_LTO9rfQJpPvWXnA

kubectl exec -it deploy/webapp -- cat /var/run/secrets/kubernetes.io/serviceaccount/token | cut -d '.' -f2 | base64 -d ; echo "\"}"
{"aud":["https://kubernetes.default.svc.cluster.local"],"exp":1763822166,"iat":1763821566,"iss":"https://kubernetes.default.svc.cluster.local","jti":"c5333b9f-5470-4ff5-8375-c754665b81bd","kubernetes.io":{"namespace":"default","node":{"name":"myk8s-control-plane","uid":"8651b768-a9fa-40bf-9113-2d65db1e9477"},"pod":{"name":"webapp-9484c6fd7-x7kdl","uid":"b33edcaa-4ea8-4447-80e3-ff26cfce5ec1"},"serviceaccount":{"name":"vault","uid":"8b4051ca-ea3c-4db5-8499-3d7c97e560e4"}},"nbf":1763821566,"sub":"system:serviceaccount:default:vault"}


# 웹 애플리케이션 접속 동작 확인 : 접속 시 토큰 정보를 확인 후 vault 서버에 로그인 후 시크릿 정보 가져와서 http 출력
curl 127.0.0.1:30001
password:static-password username:static-user

# webapp 파드 로그 확인 : 서비스 어카운트 토큰(JWT) 정보 확인 후 vault 로그인 후 token 발급 확인
kubectl logs -l app=webapp -f
2025/04/15 13:26:42 Received Request - Port forwarding is working.
Read JWT: eyJhbGciOiJSUzI1NiIsImtpZCI6IlZIVlVTc21yMTBDZlhoNzEwb3dNVk5PYms1S0o1OHVoTlh5R0M1YjdiMjgifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNzc2MjU5MTg5LCJpYXQiOjE3NDQ3MjMxODksImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwianRpIjoiOTg1ODc0MTUtZmJiNi00NmNmLThlN2YtZTIzZjk0NGQyNTU1Iiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0Iiwibm9kZSI6eyJuYW1lIjoibXlrOHMtY29udHJvbC1wbGFuZSIsInVpZCI6ImEzYTIzOWUyLTNkMzctNDU5ZS04OTg1LWUyMzcwZTliNWI3YSJ9LCJwb2QiOnsibmFtZSI6IndlYmFwcC04ZDQ4NTlmZi05cnZxdCIsInVpZCI6IjczNTlhZTdiLTY2MjYtNGYwOS1hNGFiLTY1MDc4YjY0OWYwNCJ9LCJzZXJ2aWNlYWNjb3VudCI6eyJuYW1lIjoidmF1bHQiLCJ1aWQiOiJjOWNjYTA0ZS0yMDAwLTQ1YWQtODkxYi1kZDRhZGRiOTdmYWUifSwid2FybmFmdGVyIjoxNzQ0NzI2Nzk2fSwibmJmIjoxNzQ0NzIzMTg5LCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDp2YXVsdCJ9.Nxf0ka_gwzPzo6ISNnpY4u73LIjpyvmSLWWY2jj3EDiBtUFZyada6XTfSCwQSg8py3dikhtQAgaNG_feQsfycS0Xc3MJG88e_7R5I7fBVp9HXjhoFqyPl31isp-Gkbs8E6v4eD9caRzEoqrDeOpQcnNXKzfHx8O1LFUmtNGcNcJNv57WDFwyrLUwbLDBIZIfwtlsbX97nCl3LjcFfPwkrvV3rJw0gVInpblAmZQ8uZA6oRfkuA6B39sAPErxtnupdZMcnD4eKIxL2Ap3nE4O1VZ_2kJAMwwZkNGKV7TXvT1cjWqIH4Fn3WB--ZV_qvFNIH6EAc_LTO9rfQJpPvWXnA
Retrieved token:  hvs.CAESIFUPqjOKrzUM78X7QQKX1H_88At5uyewGe-UT-lC56ERGh4KHGh2cy5JSk8xWkREM2lZRW9wclByNlA0T2FNTWU
...

 
Vault에서 Secret 업데이트 후 반영 확인합니다.

#
vault kv put secret/webapp/config username="changed-user" password="changed-password"
====== Secret Path ======
secret/data/webapp/config

======= Metadata =======
Key                Value
---                -----
created_time       2025-04-15T15:14:59.37055476Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            2

# 확인
vault kv get secret/webapp/config

# 변경된 정보 확인
curl 127.0.0.1:30001
password:changed-password username:changed-user

 
위에서 수행된 구조는 아래와 같습니다.

[Webapp Pod]  --JWT-->  [Vault Auth (k8s)]  --TokenReview-->  [K8S API]
       |                      |                    |
       |                      |                  SA 검증
       |                      ▼
 JWT 읽기                Role(webapp)   ← SA=vault, NS=default 요구 
       ▼                      ▼
 Vault Login           Policy(webapp) 적용
       ▼                      ▼
 Vault Token 발급 → Secret 읽기 → HTTP 반환

 
 
 
 

참조 : 서비스 어카운트 토큰(JWT) 정보 확인

jwt.io

JSON Web Tokens - jwt.io

JSON Web Token (JWT) is a compact URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is digitally signed using JSON Web Signature (JWS).

www.jwt.io

 
 
 

Vault Secrets Operator (VSO)

https://developer.hashicorp.com/vault/tutorials/kubernetes-introduction/vault-secrets-operator

Manage Kubernetes native secrets with the Vault Secrets Operator | Vault | HashiCorp Developer

Set-up the Vault Secrets Operator to synchronise secrets between Vault and a Kubernetes Cluster. Retrieve native static and dynamic Kubernetes secrets.

developer.hashicorp.com

 
VSO는 K8S Native Secret을 업데이트 및 관리해줍니다.

 

  • 기존에 Vault 사용을 위해 Vault Login, Vault Secret Read 등에 대한 동작을 애플리케이션에서 구현할 필요 없이, VSO가 대신 수행합니다.
  • VSO는 Vault 의 Secret 를 k8S Native Secret 에 동기화.
    • Deployment, ReplicaSet, StatefulSet, Argo Rollout Kubernetes 리소스 유형에 대한 Rollout 으로 자동 시크릿 교체 적용 가능
      • 물론 Rollout 하지 않고, 애플리케이션에서 변경된 값을 반영하게 구성 가능함.
  • VSO는 ‘kv-v1, kv-2’, ‘TLS 인증서 in PKI’ - 고정/동적 Secret 지원

즉, VSO를 통해서 앱은 k8s secret만 보면됩니다. 
Vault와 통신하는 주체는 VSO 컨트롤러 입니다.
앱은 Configmap, Secret을 사용하는 것처럼 인지하지 않습니다.
 
TLS 인증서가 업데이트 되거나, DB 패스워드가 Vault에서 rotate되면,
기존 방식에서는 앱은 몰라서 이전값을 바라보지만
VSO -> 변경된 K8S Secret 감지후 deployment를 자동 재시작합니다.
 
https://github.com/michaelkosir/vault-secrets-operator-demo

GitHub - michaelkosir/vault-secrets-operator-demo: This repository provides a demo of the HashiCorp Vault Secrets Operator.

This repository provides a demo of the HashiCorp Vault Secrets Operator. - michaelkosir/vault-secrets-operator-demo

github.com

이미지 클릭

 
 
 
 
 
 
 
 
 
 
작성중

'devops > cicd' 카테고리의 다른 글

ArgoCD Image Updater  (0) 2026.01.19
[CI/CD] 7주차 - Vault (2)  (1) 2025.12.03
[CI/CD] 7주차 - Vault (1)  (0) 2025.11.29
[CI/CD] 6주차 - Argo CD 3/3  (0) 2025.11.23
keycloak - Oauth - OIDC  (0) 2025.11.16