Hashicorp Vault/VSO on K8S

 

Vault 설치 ON k8s

실습 환경 구성 ( k8s cluster m Vault 설치 )

# Cluster 생성
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  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

Creating cluster "myk8s" ...
 ✓ Ensuring node image (kindest/node:v1.32.8) 🖼
 ✓ Preparing nodes 📦  
 ✓ Writing configuration 📜 
 ✓ Starting control-plane 🕹️ 
 ✓ Installing CNI 🔌 
 ✓ Installing StorageClass 💾 
Set kubectl context to "kind-myk8s"
You can now use your cluster with:

kubectl cluster-info --context kind-myk8s

# 설치 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  docker ps
CONTAINER ID   IMAGE                  COMMAND                  CREATED          STATUS          PORTS                                                             NAMES
65069c3aeac9   kindest/node:v1.32.8   "/usr/local/bin/entr…"   15 seconds ago   Up 14 seconds   0.0.0.0:30000-30001->30000-30001/tcp, 127.0.0.1:60972->6443/tcp   myk8s-control-plane

 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl get nodes
NAME                  STATUS   ROLES           AGE   VERSION
myk8s-control-plane   Ready    control-plane   83s   v1.32.8

# node에 기본 tool 설치
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'
																				.
																				.
Setting up bind9-host (1:9.18.41-1~deb12u1) ...
Setting up libperl5.36:arm64 (5.36.0-7+deb12u3) ...
Setting up tcpdump (4.99.3-1) ...
Setting up ngrep (1.47+ds1-5+b1) ...
Setting up perl (5.36.0-7+deb12u3) ...
Setting up bind9-dnsutils (1:9.18.41-1~deb12u1) ...
Setting up dnsutils (1:9.18.41-1~deb12u1) ...
Setting up liberror-perl (0.17029-2) ...
Setting up git (1:2.39.5-0+deb12u2) ...
Processing triggers for libc-bin (2.36-9+deb12u10) ...

# Vault 설치
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  helm repo add hashicorp https://helm.releases.hashicorp.com
"hashicorp" already exists with the same configuration, skipping
 
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "hashicorp" chart repository
...Successfully got an update from the "argo" chart repository
...Successfully got an update from the "grgo" chart repository
...Successfully got an update from the "agrgo" chart repository
...Successfully got an update from the "prometheus-community" chart repository
...Successfully got an update from the "geek-cookbook" chart repository
...Successfully got an update from the "bitnami" chart repository
Update Complete. ⎈Happy Helming!⎈
 
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  helm search repo hashicorp/vault
NAME                                    CHART VERSION   APP VERSION     DESCRIPTION                          
hashicorp/vault                         0.31.0          1.20.4          Official HashiCorp Vault Chart       
hashicorp/vault-secrets-gateway         0.0.2           0.1.0           A Helm chart for Kubernetes          
hashicorp/vault-secrets-operator        1.0.1           1.0.1           Official Vault Secrets Operator Chart

# Create a k8s namespace
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

# 배포 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl get-all -n vault
NAME                                                 NAMESPACE  AGE
configmap/kube-root-ca.crt                           vault      27m  
configmap/vault-config                               vault      25m  
endpoints/vault                                      vault      25m  
endpoints/vault-internal                             vault      25m  
endpoints/vault-ui                                   vault      25m  
persistentvolumeclaim/audit-vault-0                  vault      25m  
persistentvolumeclaim/data-vault-0                   vault      25m  
pod/vault-0                                          vault      25m  
secret/sh.helm.release.v1.vault.v1                   vault      25m  
serviceaccount/default                               vault      27m  
serviceaccount/vault                                 vault      25m  
service/vault                                        vault      25m  
service/vault-internal                               vault      25m  
service/vault-ui                                     vault      25m  
controllerrevision.apps/vault-7594f49575             vault      25m  
statefulset.apps/vault                               vault      25m  
endpointslice.discovery.k8s.io/vault-d8xjg           vault      25m  
endpointslice.discovery.k8s.io/vault-internal-phm5l  vault      25m  
endpointslice.discovery.k8s.io/vault-ui-r64sd        vault      25m

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl get sts,pods,svc,ep,pvc,cm -n vault
NAME                     READY   AGE
statefulset.apps/vault   0/1     26m
																				.
																				.
																				.
# Vault Status 명령으로 Sealed 상태 확인
kubectl exec -ti vault-0 -n vault -- vault status
Key                Value
---                -----
Seal Type          shamir
Initialized        false
Sealed             true
Total Shares       0
Threshold          0
																				.
																				.
																				.
# Vault 로그 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  stern -n vault -l app.kubernetes.io/name=vault 
vault-0 vault 2025-12-01T02:02:43.807Z [INFO]  core: security barrier not initialized
vault-0 vault 2025-12-01T02:02:43.807Z [INFO]  core: seal configuration missing, not initialized
vault-0 vault 2025-12-01T02:02:48.805Z [INFO]  core: security barrier not initialized
vault-0 vault 2025-12-01T02:02:48.805Z [INFO]  core: seal configuration missing, not initialized
vault-0 vault 2025-12-01T02:02:53.826Z [INFO]  core: security barrier not initialized
vault-0 vault 2025-12-01T02:02:53.827Z [INFO]  core: seal configuration missing, not initialized
vault-0 vault 2025-12-01T02:02:58.811Z [INFO]  core: security barrier not initialized
vault-0 vault 2025-12-01T02:02:58.811Z [INFO]  core: seal configuration missing, not initialized

 

Vault Unseal

  • 쉬운 설명
    • Unseal key를 여러 조각으로 나눠서 여러 사람에게 나눠준다
    • Ex : 총 5조각 중 3조각이 모이면 열 수 있게 설정
    • 이렇게 하면 한 사람이 key를 다 갖고 있지 않기 때문에 혼자서는 아무것도 할 수 없음
    • 이러한 과정을 가능하게 해주는것이 vault unseal
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl exec vault-0 -n vault -- vault operator init \
    -key-shares=1 \
    -key-threshold=1 \
    -format=json > cluster-keys.json
    
# Cluster-keys.json 파일 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  cat cluster-keys.json | jq
{
  "unseal_keys_b64": [
    "kMhfCMstHhzYVnBobb51ztbkKdiML+beVGjdFshXYNs="
  ],
  "unseal_keys_hex": [
    "90c85f08cb2d1e1cd85670686dbe75ced6e429d88c2fe6de5468dd16c85760db"
  ],
  "unseal_shares": 1,
  "unseal_threshold": 1,
  "recovery_keys_b64": [],
  "recovery_keys_hex": [],
  "recovery_keys_shares": 0,
  "recovery_keys_threshold": 0,
  "root_token": "hvs.9UqYwzkOWQr09tXxr6daJtSj"
  
  # unseal key found in cluster-keys.json
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  jq -r ".unseal_keys_b64[]" cluster-keys.json
kMhfCMstHhzYVnBobb51ztbkKdiML+beVGjdFshXYNs=

# 환경 변수 지정
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  VAULT_UNSEAL_KEY=$(jq -r ".unseal_keys_b64[]" cluster-keys.json)

# Unseal Vault running on the vault-0 pod
oo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl exec vault-0 -n vault -- vault operator unseal $VAULT_UNSEAL_
KEY
Key             Value
---             -----
Seal Type       shamir
Initialized     true
Sealed          false
Total Shares    1
Threshold       1
Version         1.20.4
Build Date      2025-09-23T13:22:38Z
Storage Type    file
Cluster Name    vault-cluster-0b4e84bc
Cluster ID      644f1035-8de4-b952-38a8-2291929de215
HA Enabled      false

# vault-0 pod 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl get pod -n vault
NAME      READY   STATUS    RESTARTS   AGE
vault-0   1/1     Running   0          91m

# root token found in cluster-keys.json
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  jq -r ".root_token" cluster-keys.json
hvs.9UqYwzkOWQr09tXxr6daJtSj

 

Vault login with CLI

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  brew tap hashicorp/tap
✔︎ JSON API cask.jws.json                                                                                                             [Downloaded   15.0MB/ 15.0MB]
✔︎ JSON API formula.jws.json                                                                                                          [Downloaded   31.7MB/ 31.7MB]

 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  brew install hashicorp/tap/vault
==> Auto-updating Homebrew...
Adjust how often this is run with `$HOMEBREW_AUTO_UPDATE_SECS` or disable with
`$HOMEBREW_NO_AUTO_UPDATE=1`. Hide these hints with `$HOMEBREW_NO_ENV_HINTS=1` (see `man brew`).
Warning: hashicorp/tap/vault 1.21.1 is already installed and up-to-date.
To reinstall 1.21.1, run:
  brew reinstall vault

 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault --version
Vault v1.21.1 (2453aac2638a6ae243341b4e0657fd8aea1cbf18), built 2025-11-18T13:04:32Z

# vault status 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  export VAULT_ADDR='http://localhost:30000'
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault status
Key             Value
---             -----
Seal Type       shamir
Initialized     true
Sealed          false
Total Shares    1
Threshold       1
Version         1.20.4
Build Date      2025-09-23T13:22:38Z
Storage Type    file
Cluster Name    vault-cluster-0b4e84bc
Cluster ID      644f1035-8de4-b952-38a8-2291929de215
HA Enabled      false

# Root token으로 로그인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault login
Token (will be hidden): 
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.

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl get svc,ep -n vault vault
NAME            TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)                         AGE
service/vault   NodePort   10.96.100.179   <none>        8200:30000/TCP,8201:32355/TCP   107m

NAME              ENDPOINTS                         AGE
endpoints/vault   10.244.0.7:8201,10.244.0.7:8200   107m

 

Vault UI 접속

# audit용 pvc 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl get pvc -n vault
NAME            STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
audit-vault-0   Bound    pvc-2e799b30-961c-47f8-8190-6e2674136d79   10Gi       RWO            standard       <unset>                 110m
data-vault-0    Bound    pvc-ac3a224e-da7c-4fa7-bed9-0c30d3e81479   10Gi       RWO            standard       <unset>                 110m

# audit pv에 저장될 수 있게 file audit log 설정
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault audit enable file file_path=/vault/logs/audit.log
Success! Enabled the file audit device at: file/

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault audit list -detailed
Path     Type    Description    Replication    Options
----     ----    -----------    -----------    -------
file/    file    n/a            replicated     file_path=/vault/logs/audit.log

# log 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl exec -it vault-0 -n vault -- tail -f /vault/logs/audit.log
{"request":{"id":"c1088d68-0c5c-2b02-9b01-fb923ed81388","namespace":{"id":"root"},"operation":"update","path":"sys/audit/test"},"time":"2025-12-01T03:19:23.069745045Z","type":"request"}
{"auth":{"accessor":"hmac-sha256:1784dd5122a5b64cd6c80637f1e74b830a895a707065ba99ce5a9a52c46a518a","client_token":"hmac-sha256:f836428ff71412d9825c112538204f1911b56f264944655fecaf45ebc8fe2c3c","display_name":"root","policies":["root"],"policy_results":{"allowed":true,"granting_policies":[{"type":""},{"name":"root","namespace_id":"root","type":"acl"}]},"token_policies":["root"],"token_issue_time":"2025-12-01T02:55:08Z","token_type":"service"},"request":{"client_id":"0DHqvq2D77kL2/JTPSZkTMJbkFVmUu0TzMi0jiXcFy8=","client_token":"hmac-sha256:f836428ff71412d9825c112538204f1911b56f264944655fecaf45ebc8fe2c3c","client_token_accessor":"hmac-sha256:1784dd5122a5b64cd6c80637f1e74b830a895a707065ba99ce5a9a52c46a518a","data":{"description":"hmac-sha256:d20db63032eb13c56df927a27eeef285db1462578802011bc35422fed0d8d963","local":false,"options":{"file_path":"hmac-sha256:b69bafa34b8f5255e95c3f07dc1b8911e69746870f31fa641091455b6a95cfe1"},"type":"hmac-sha256:36e93a388d112b164efef4f4410471a476bc3e99d456e929dfec10d71ee7267f"},"headers":{"user-agent":["Go-http-client/1.1"]},"id":"e7cc8645-fde1-e434-e33b-7de18eaf928c","mount_accessor":"system_a863f780","mount_class":"secret","mount_point":"sys/","mount_running_version":"v1.20.4+builtin.vault","mount_type":"system","namespace":{"id":"root"},"operation":"update","path":"sys/audit/file","remote_address":"10.244.0.1","remote_port":64206},"time":"2025-12-01T03:19:23.072529043Z","type":"response"}
{"auth":{"accessor":"hmac-sha256:1784dd5122a5b64cd6c80637f1e74b830a895a707065ba99ce5a9a52c46a518a","client_token":"hmac-sha256:f836428ff71412d9825c112538204f1911b56f264944655fecaf45ebc8fe2c3c","display_name":"root","policies":["root"],"policy_results":{"allowed":true,"granting_policies":[{"type":""},{"name":"root","namespace_id":"root","type":"acl"}]},"token_policies":["root"],"token_issue_time":"2025-12-01T02:55:08Z","token_type":"service"},"request":{"client_id":"0DHqvq2D77kL2/JTPSZkTMJbkFVmUu0TzMi0jiXcFy8=","client_token":"hmac-sha256:f836428ff71412d9825c112538204f1911b56f264944655fecaf45ebc8fe2c3c","client_token_accessor":"hmac-sha256:1784dd5122a5b64cd6c80637f1e74b830a895a707065ba99ce5a9a52c46a518a","headers":{"user-agent":["Go-http-client/1.1"]},"id":"ec68e283-8154-6180-3a5b-29d287032b0f","mount_class":"secret","mount_point":"sys/","mount_running_version":"v1.20.4+builtin.vault","mount_type":"system","namespace":{"id":"root"},"operation":"read","path":"sys/audit","remote_address":"10.244.0.1","remote_port":38407},"time":"2025-12-01T03:19:31.867071892Z","type":"request"}
{"auth":{"accessor":"hmac-sha256:1784dd5122a5b64cd6c80637f1e74b830a895a707065ba99ce5a9a52c46a518a","client_token":"hmac-sha256:f836428ff71412d9825c112538204f1911b56f264944655fecaf45ebc8fe2c3c","display_name":"root","policies":["root"],"policy_results":{"allowed":true,"granting_policies":[{"type":""},{"name":"root","namespace_id":"root","type":"acl"}]},"token_policies":["root"],"token_issue_time":"2025-12-01T02:55:08Z","token_type":"service"},"request":{"client_id":"0DHqvq2D77kL2/JTPSZkTMJbkFVmUu0TzMi0jiXcFy8=","client_token":"hmac-sha256:f836428ff71412d9825c112538204f1911b56f264944655fecaf45ebc8fe2c3c","client_token_accessor":"hmac-sha256:1784dd5122a5b64cd6c80637f1e74b830a895a707065ba99ce5a9a52c46a518a","headers":{"user-agent":["Go-http-client/1.1"]},"id":"ec68e283-8154-6180-3a5b-29d287032b0f","mount_accessor":"system_a863f780","mount_class":"secret","mount_point":"sys/","mount_running_version":"v1.20.4+builtin.vault","mount_type":"system","namespace":{"id":"root"},"operation":"read","path":"sys/audit","remote_address":"10.244.0.1","remote_port":38407},"response":{"data":{"file/":{"description":"hmac-sha256:d20db63032eb13c56df927a27eeef285db1462578802011bc35422fed0d8d963","local":false,"options":{"file_path":"hmac-sha256:b69bafa34b8f5255e95c3f07dc1b8911e69746870f31fa641091455b6a95cfe1"},"path":"hmac-sha256:1505a2a94a41e8b3b410ad70a293bbbfc4541bcd1fb0d69a6b4f5be0fd6a8b2b","type":"hmac-sha256:36e93a388d112b164efef4f4410471a476bc3e99d456e929dfec10d71ee7267f"}},"mount_accessor":"system_a863f780","mount_class":"secret","mount_point":"sys/","mount_running_plugin_version":"v1.20.4+builtin.vault","mount_type":"system"},"time":"2025-12-01T03:19:31.867452678Z","type":"response"}

 

Set a secret in vault

# Enable an instance of the kv-v2 secrets engine at the path secret
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault secrets enable -path=secret kv-v2
Success! Enabled the kv-v2 secrets engine at: secret/

# 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault secrets list -detailed
Path          Plugin       Accessor              Default TTL    Max TTL    Force No Cache    Replication    Seal Wrap    External Entropy Access    Options           Description                                                UUID                                    Version    Running Version          Running SHA256    Deprecation Status
----          ------       --------              -----------    -------    --------------    -----------    ---------    -----------------------    -------           -----------                                                ----                                    -------    ---------------          --------------    ------------------
cubbyhole/    cubbyhole    cubbyhole_45e49c9d    n/a            n/a        false             local          false        false                      map[]             per-token private secret storage                           0bae9dfc-23b5-7601-fe21-2c23256497cc    n/a        v1.20.4+builtin.vault    n/a               n/a
identity/     identity     identity_c6002e44     system         system     false             replicated     false        false                      map[]             identity store                                             c954c63a-8dfe-bf7d-a0e9-0eee5b9293bc    n/a        v1.20.4+builtin.vault    n/a               n/a
secret/       kv           kv_59d38a94           system         system     false             replicated     false        false                      map[version:2]    n/a                                                        b281e40c-5ae1-f509-8945-dd58862ee310    n/a        v0.24.0+builtin          n/a               supported
sys/          system       system_a863f780       n/a            n/a        false             replicated     true         false                      map[]             system endpoints used for control, policy and debugging    7cf3d12d-ca06-0873-194f-4f787c994c8f    n/a        v1.20.4+builtin.vault    n/a               n/a


# Create a secret at path with a username and password
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault kv put secret/webapp/config username="static-user" password="
static-password"
====== Secret Path ======
secret/data/webapp/config

======= Metadata =======
Key                Value
---                -----
created_time       2025-12-01T04:41:04.095047282Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1
 
# verift that the secret 
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault kv get secret/webapp/config
====== Secret Path ======
secret/data/webapp/config

======= Metadata =======
Key                Value
---                -----
created_time       2025-12-01T04:41:04.095047282Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1

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

# Verify that the secret
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  curl -s --header "X-Vault-Token: $VAULT_ROOT_TOKEN" --request GET \

> http://127.0.0.1:30000/v1/secret/data/webapp/config | jq
{
  "request_id": "81c2010e-2dca-9499-45b2-f62684d95473",
  "lease_id": "",
  "renewable": false,
  "lease_duration": 0,
  "data": {
    "data": {
      "password": "static-password",
      "username": "static-user"
    },
    "metadata": {
      "created_time": "2025-12-01T04:41:04.095047282Z",
      "custom_metadata": null,
      "deletion_time": "",
      "destroyed": false,
      "version": 1
    }
  },
  "wrap_info": null,
  "warnings": null,
  "auth": null,
  "mount_type": "kv"
}

 

Configure k8s authentication in Vault

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl rolesum vault -n vault
ServiceAccount: vault/vault
Secrets:

Policies:

• [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 k8s authentication method
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault auth enable kubernetes
Success! Enabled kubernetes auth method at: kubernetes/

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault auth list -detailed
Path           Plugin        Accessor                    Default TTL    Max TTL    Token Type         Replication    Seal Wrap    External Entropy Access    Options    Description                UUID                                    Version    Running Version          Running SHA256    Deprecation Status
----           ------        --------                    -----------    -------    ----------         -----------    ---------    -----------------------    -------    -----------                ----                                    -------    ---------------          --------------    ------------------
kubernetes/    kubernetes    auth_kubernetes_73c97610    system         system     default-service    replicated     false        false                      map[]      n/a                        34d8f9a9-55f9-ad16-ada8-4517347db31d    n/a        v0.22.2+builtin          n/a               supported
token/         token         auth_token_6d5ee9da         system         system     default-service    replicated     false        false                      map[]      token based credentials    6f024d78-cd9a-5af0-f3ff-63c12216d1ed    n/a        v1.20.4+builtin.vault    n/a               n/a

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault auth list
Path           Type          Accessor                    Description                Version
----           ----          --------                    -----------                -------
kubernetes/    kubernetes    auth_kubernetes_73c97610    n/a                        n/a
token/         token         auth_token_6d5ee9da         token based credentials    n/a  

# 설정 정보 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  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

# Policy write
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault policy write webapp - <<EOF
path "secret/data/webapp/config" {
  capabilities = ["read"]
}
EOF
Success! Uploaded policy: webapp

# Create a kubernetes authentication role 
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  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

 

사전 지식

  • ServiceAccount
    • 파드에서 실행되는 애플리케이션 프로세스에 대한 식별자를 제공, 파드 내부의 애플리케이션 프로세스는 자신에게 부여된 서비스 어카운트의 식별자를 사용하여 클러스터의 API 서버에 인증 가능
  • ServiceAccountToken
    • kubele이 kube-apiserver로부터 취득한 토큰을 포함한다
    • kubelet은 TokenRequest API를 통해 일정 시간 동안 사용할 수 있는 토큰을 발급
    • 이렇게 취득한 토큰은 파드가 삭제되거나 지정된 수명 주기 이후에 만료 ( defults 1 hour )
    • 이 토큰은 특정한 파드에 바인딩되며 kube-apiserver를 대상으로 한다
  • Token Contoller
    • Kube-contoller-manager의 일부로써 실행되고 비동기적으로 동작
    • ServiceAccount에 대한 삭제를 감시
    • ServiceAccountToken Secret에 대한 추가를 감시
    • 참조된 서비스어카운트가 존재하는지 확인
    • 필요한 경우 secret에 token을 추가
  • Service Account Adminission Contolloer
    • pod에 .spce.serviceAccountName 항목이 지정되지 않았다면 어드미션 컨트롤러는 실행하려는 파드의 서비스어카운트 이름을 default로 설정한다
    • 어드미션 컨트롤러는 실행되는 파드가 참조하는 서비스어카운트가 존재하는 확인
      • 만약 해당하는 이름의 서비스어카운트가 존재하지 않는 경우 어드미션 컨트롤러는 파드를 실행시키지 않음
      • 이는 defult 서비스어카운트에 대해서도 동일하게 적용
  • TokenReQuest API
    • 서비스어카운트의 하위 리소스인 TokenRequest를 사용하여 ㅇ리정 시간 동안 해당 서비스어카운트에서 사용할 수 있는 토큰을 가져올 수 있다.
    • 컨테이너 내에서 사용하고 위한 API토큰을 얻기위해 이 요청을 직접 호출할 필요는 없는데 kubeclet 프로젝티드 볼륨을 사용하여 이를 설정하기 때문
  • 인증/인가 seriese
    • JWT ( JsonWebToken )
      • k8s에서는 Bearer 토큰을 전송할 떄 주로 JWT을 사용
      • JSON 형태로 토큰 형식을 정의한 스펙
    • OIDC ( OpenID Connect )
      • OpenID 인증 + OAuth2.0 인가
      • Json 포멧을 이용한 RESTful API 형식으로 인증
        • iss : 토큰 발행자
        • sub : 사용자를 구분하기 위한 유니크한 구분자
        • iat : 토큰이 발행되는 시간을 Unix time으로 표기한 것
        • exp : 토큰이 만료되는 시간을 Unix time으로 표기한 것
        • aud : ID token이 어떤 client를 위해 발급된것인지

Launch a web application

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  >....                                                              
          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
deployment.apps/webapp created
service/webapp created
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl gtet pod -l app-webapp
error: unknown command "gtet" for "kubectl"
Did you mean this?
        set
        get
 ✘ howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl get pod -l app=webapp
NAME                     READY   STATUS    RESTARTS   AGE
webapp-9484c6fd7-8rp9m   1/1     Running   0          33s
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl exec -it deploy/webapp -- cat /app/main.go
package main

import (
        "fmt"
        "log"
        "os"
        "time"
        "bytes"
        "net/http"
        "io/ioutil"
        "encoding/json"
)

func main() {
        vaultToken := "root"

        port := os.Getenv("SERVICE_PORT")
        if port == "" {
                port = "8080"
                log.Println("PORT environment variable not set, defaulting to", port)
        }

        vaultUrl := os.Getenv("VAULT_ADDR")
        if vaultUrl == "" {
                vaultUrl = "http://vault:8200"
        }

        http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
                log.Println("Received Request - Port forwarding is working.")

                // If the JWT path is setup then get the new token from Vault using the k8s Auth
                jwtPath := os.Getenv("JWT_PATH")
                if jwtPath != "" {
                        jwtFile, err := ioutil.ReadFile(jwtPath)
                        if err != nil {
                                fmt.Println("Error reading JWT file at", jwtPath, ": ", err)
                                return
                        }

                        jwt := string(jwtFile)
                        fmt.Println("Read JWT:", jwt)

                        authPath := "auth/kubernetes/login"

                        // Create the payload for Vault authentication
                        pl := VaultJWTPayload { Role: "webapp", JWT: jwt }
                        jwtPayload, err := json.Marshal(pl)
                        if err != nil {
                                fmt.Println("Error encoding Vault request JSON:", err)
                                return
                        }

                        // Send a request to Vault to retrieve a token
                        vaultLoginResponse := &VaultLoginResponse{}
                        err = SendRequest(vaultUrl + "/v1/" + authPath, "", "POST", jwtPayload, vaultLoginResponse)
                        if err != nil {
                                fmt.Println("Error getting response from Vault k8s login:", err)
                                return
                        }

                        vaultToken = vaultLoginResponse.Auth.ClientToken
                        fmt.Println("Retrieved token: ", vaultToken)
                }

                secretsPath := "secret/data/webapp/config"

                // Send a request to Vault using the token to retrieve the secret
                vaultSecretResponse := &VaultSecretResponse{}
                err := SendRequest(vaultUrl + "/v1/" + secretsPath, vaultToken, "GET", nil, &vaultSecretResponse)
                if err != nil {
                        fmt.Println("Error getting secret from Vault:", err)
                        return
                }

                secretResponseData, ok := vaultSecretResponse.Data.Data.(map[string]interface{})
                if ok {
                        for key, value := range secretResponseData {
                                fmt.Fprintf(w, "%s:%s ",  key, value)
                        }
                } else {
                        fmt.Println("Error getting the secret from Vault, cannot convert Data to map[string]interface{}")
                }
        })

        log.Println("Listening on port", port)
        if err := http.ListenAndServe(":" + port, nil); err != nil {
                log.Fatalf("Failed to start server:", err)
        }
}

func SendRequest(url string, token string, requestType string, payload []byte, target interface{}) error {
        req, err := http.NewRequest(requestType, url, bytes.NewBuffer(payload))
        if err != nil {
                fmt.Println("Error creating request:", err)
                return err
        }

        req.Header.Set("Content-Type", "application/json")
        if token != "" {
                req.Header.Set("X-Vault-Token", token)
        }

        client := &http.Client{Timeout: 10 * time.Second}
        res, err := client.Do(req)
        if err != nil {
                fmt.Println("Error sending request to Vault:", err)
                return err
        }
        defer res.Body.Close()
        return json.NewDecoder(res.Body).Decode(target)
}%                                                                                                                                                                
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  curl 127.0.0.1:30001
password:static-password username:static-user %                                                                                                                   
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Ga

 

Vault 에서 secret 업데이트 후 변경 반영 확인

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault kv put secret/webapp/config username="changed-user" password="c
hanged-password"
====== Secret Path ======
secret/data/webapp/config

======= Metadata =======
Key                Value
---                -----
created_time       2025-12-02T03:14:59.597295438Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            2
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault kv get secret/webapp/config
====== Secret Path ======
secret/data/webapp/config

======= Metadata =======
Key                Value
---                -----
created_time       2025-12-02T03:14:59.597295438Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            2

====== Data ======
Key         Value
---         -----
password    changed-password
username    changed-user
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  curl 127.0.0.1:30001
password:changed-password username:changed-user %

 

Vault Secrets Operaor ( VSO )

실습 환경

# Cluster 생성 및 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  docker ps
CONTAINER ID   IMAGE                  COMMAND                  CREATED         STATUS         PORTS                                                                                                       NAMES
bd636784c73a   kindest/node:v1.32.8   "/usr/local/bin/entr…"   2 minutes ago   Up 2 minutes   0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp, 0.0.0.0:30000-30001->30000-30001/tcp, 127.0.0.1:50797->6443/tcp   myk8s-control-plane

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl get node
NAME                  STATUS   ROLES           AGE     VERSION
myk8s-control-plane   Ready    control-plane   2m56s   v1.32.8

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 설치 : dev 모드 활성화 설치
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  git clone https://github.com/hashicorp-education/learn-vault-secrets-operator
Cloning into 'learn-vault-secrets-operator'...
remote: Enumerating objects: 208, done.
remote: Counting objects: 100% (93/93), done.
remote: Compressing objects: 100% (47/47), done.
remote: Total 208 (delta 49), reused 62 (delta 46), pack-reused 115 (from 1)
Receiving objects: 100% (208/208), 42.14 KiB | 770.00 KiB/s, done.
Resolving deltas: 100% (113/113), done.

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator   main  cat <<EOF > vault-values.yaml
server:
  image:
    repository: "hashicorp/vault"
    tag: "1.19.0"

  dev:
    enabled: true
    devRootToken: "root"
																	    .
																	    .
																	    .

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator   main  helm install vault hashicorp/vault -n vault --create-namespace --values vault-values.yaml --version 0.30.0
NAME: vault
LAST DEPLOYED: Tue Dec  2 13:42:32 2025
NAMESPACE: vault
STATUS: deployed
REVISION: 1
NOTES:
Thank you for installing HashiCorp Vault!

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator   main  kubectl get pods -n vault
NAME      READY   STATUS    RESTARTS   AGE
vault-0   1/1     Running   0          2m55s

 

Vault 설정

# Vault 로그인 : token ( root )
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator   main  vault login
Token (will be hidden): 
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                root
token_accessor       3uoZA3u6nYTe4Tcohlbo9KGE
token_duration       ∞
token_renewable      false
token_policies       ["root"]
identity_policies    []
policies             ["root"]

# kubernetes 인증 활성화
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator   main  vault auth enable -path demo-auth-mount ku
bernetes
Success! Enabled kubernetes auth method at: demo-auth-mount/

 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator   main  vault write auth/demo-auth-mount/config ku
bernetes_host="https://https://kubernetes.default.svc"
Success! Data written to: auth/demo-auth-mount/config

# secret ( engine_v2 ) 활성화
 ✘ howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator   main  vault secrets enable -path=kvv2 kv-v2
Success! Enabled the kv-v2 secrets engine at: kvv2/

# Create a JSON file with a vault policy
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator   main  tee webapp.json <<EOF
path "kvv2/data/webapp/config" {
   capabilities = ["read", "list"]
}
EOF

path "kvv2/data/webapp/config" {
   capabilities = ["read", "list"]
}

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator   main  vault policy write webapp webapp.json
Success! Uploaded policy: webapp

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator   main  vault write auth/demo-auth-mount/role/role1 \
   bound_service_account_names=demo-static-app \
   bound_service_account_namespaces=app \
   policies=webapp \
   audience=vault \
   ttl=24h

Success! Data written to: auth/demo-auth-mount/role/role1
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator   main  vault kv put kvv2/webapp/config username="
static-user" password="static-password"
===== Secret Path =====
kvv2/data/webapp/config

======= Metadata =======
Key                Value
---                -----
created_time       2025-12-02T04:55:57.772444472Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1

 

Vault Secrets Operator 설치

# Helm version 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator   main  helm version
version.BuildInfo{Version:"v3.19.0", GitCommit:"3d8990f0836691f0229297773f3524598f46bda6", GitTreeState:"clean", GoVersion:"go1.25.1"}
 
# VSO 설치
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator   main  helm install vault-secrets-operator hashicorp/vault-secrets-operator -n vault-secrets-operator-system --create-namespace --values vault/vault-operator-values.yaml --version 0.7.1
NAME: vault-secrets-operator
LAST DEPLOYED: Tue Dec  2 14:48:25 2025
NAMESPACE: vault-secrets-operator-system
STATUS: deployed
REVISION: 1

# 설치 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator   main  kubectl get-all -n vaul
t-secrets-operator-system
NAME                                                                                      NAMESPACE                      AGE
configmap/kube-root-ca.crt                                                                vault-secrets-operator-system  2m45s  
configmap/vault-secrets-operator-manager-config                                           vault-secrets-operator-system  2m45s  
endpoints/vault-secrets-operator-metrics-service                                          vault-secrets-operator-system  2m45s  
pod/vault-secrets-operator-controller-manager-5b646c7766-66b87                            vault-secrets-operator-system  2m45s  
secret/sh.helm.release.v1.vault-secrets-operator.v1                                       vault-secrets-operator-system  2m45s  
secret/vso-cc-storage-hmac-key                                                            vault-secrets-operator-system  2m25s  
serviceaccount/default                                                                    vault-secrets-operator-system  2m45s  
serviceaccount/vault-secrets-operator-controller-manager                                  vault-secrets-operator-system  2m45s  
service/vault-secrets-operator-metrics-service                                            vault-secrets-operator-system  2m45s  
deployment.apps/vault-secrets-operator-controller-manager                                 vault-secrets-operator-system  2m45s  
replicaset.apps/vault-secrets-operator-controller-manager-5b646c7766                      vault-secrets-operator-system  2m45s  
lease.coordination.k8s.io/b0d477c0.hashicorp.com                                          vault-secrets-operator-system  2m25s  
endpointslice.discovery.k8s.io/vault-secrets-operator-metrics-service-7lk4j               vault-secrets-operator-system  2m45s  
rolebinding.rbac.authorization.k8s.io/vault-secrets-operator-leader-election-rolebinding  vault-secrets-operator-system  2m45s  
role.rbac.authorization.k8s.io/vault-secrets-operator-leader-election-role                vault-secrets-operator-system  2m45s  
vaultauth.secrets.hashicorp.com/vault-secrets-operator-default-transit-auth               vault-secrets-operator-system  2m43s  
vaultconnection.secrets.hashicorp.com/default                                             vault-secrets-operator-system  2m43s  

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator   main  kubectl get crd | grep se
crets.hashicorp.com
hcpauths.secrets.hashicorp.com                2025-12-02T05:48:25Z
hcpvaultsecretsapps.secrets.hashicorp.com     2025-12-02T05:48:25Z
secrettransformations.secrets.hashicorp.com   2025-12-02T05:48:25Z
vaultauths.secrets.hashicorp.com              2025-12-02T05:48:25Z
vaultconnections.secrets.hashicorp.com        2025-12-02T05:48:25Z
vaultdynamicsecrets.secrets.hashicorp.com     2025-12-02T05:48:25Z
vaultpkisecrets.secrets.hashicorp.com         2025-12-02T05:48:25Z
vaultstaticsecrets.secrets.hashicorp.com      2025-12-02T05:48:25Z

# vso pod 상세 정보 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator   main  kubectl describe pod -n v
ault-secrets-operator-system
Name:             vault-secrets-operator-controller-manager-5b646c7766-66b87
Namespace:        vault-secrets-operator-system
																					.
																					.
																					.
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age    From               Message
  ----    ------     ----   ----               -------
  Normal  Scheduled  4m51s  default-scheduler  Successfully assigned vault-secrets-operator-system/vault-secrets-operator-controller-manager-5b646c7766-66b87 to myk8s-control-plane
  Normal  Pulling    4m51s  kubelet            Pulling image "gcr.io/kubebuilder/kube-rbac-proxy:v0.15.0"
  Normal  Pulled     4m41s  kubelet            Successfully pulled image "gcr.io/kubebuilder/kube-rbac-proxy:v0.15.0" in 9.842s (9.842s including waiting). Image size: 23450421 bytes.
  Normal  Created    4m41s  kubelet            Created container: kube-rbac-proxy
  Normal  Started    4m41s  kubelet            Started container kube-rbac-proxy
  Normal  Pulling    4m41s  kubelet            Pulling image "hashicorp/vault-secrets-operator:0.7.1"
  Normal  Pulled     4m32s  kubelet            Successfully pulled image "hashicorp/vault-secrets-operator:0.7.1" in 9.451s (9.451s including waiting). Image size: 31493165 bytes.
  Normal  Created    4m32s  kubelet            Created container: manager
  Normal  Started    4m32s  kubelet            Started container manager
  
  # CRD 확인
  howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator   main  kubectl get vaultconnecti
ons,vaultauths -n vault-secrets-operator-system
NAME                                            AGE
vaultconnection.secrets.hashicorp.com/default   5m58s

NAME                                                                          AGE
vaultauth.secrets.hashicorp.com/vault-secrets-operator-default-transit-auth   5m58s

# vaultauth CRD 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator   main          kubectl get vaultauth -n vault-secrets-operator-system vault-secrets-operator-default-transit-auth -o jsonpath='{.spec}' | jq
{
  "kubernetes": {
    "audiences": [
      "vault"
    ],
    "role": "auth-role-operator",
    "serviceAccount": "vault-secrets-operator-controller-manager",
    "tokenExpirationSeconds": 600
  },
  "method": "kubernetes",
  "mount": "demo-auth-mount",
  "storageEncryption": {
    "keyName": "vso-client-cache",
    "mount": "demo-transit"
  },
  "vaultConnectionRef": "default"
}

# vaultconnection CRD 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator   main  kubectl get vaultconnecti
on -n vault-secrets-operator-system default -o jsonpath='{.spec}' | jq
{
  "address": "http://vault.vault.svc.cluster.local:8200",
  "skipTLSVerify": false
}

 

Deploy and sync a secret 실습

# namespace 생성
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator   main  kubectl create ns app
namespace/app created

# CRD 확인 : vaultauths
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator   main  kubectl explain vaultauths
GROUP:      secrets.hashicorp.com
KIND:       VaultAuth
VERSION:    v1beta1
																						.
																						.
																						.
# service account, vaultauth 생성
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator   main  kubectl apply -f vault/va
ult-auth-static.yaml 
serviceaccount/demo-operator created
serviceaccount/demo-static-app created
vaultauth.secrets.hashicorp.com/static-auth created

# 생성 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator   main  kubectl get sa,vaultauth -n app 
NAME                             SECRETS   AGE
serviceaccount/default           0         20m
serviceaccount/demo-static-app   0         52s

NAME                                          AGE
vaultauth.secrets.hashicorp.com/static-auth   52s

# CRD 생성
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator   main  kubectl apply -f vault/st
atic-secret.yaml 
vaultstaticsecret.secrets.hashicorp.com/vault-kv-app created

# CRD 생성 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator   main  kubectl get vaultstaticse
cret -n app
NAME           AGE
vault-kv-app   50s

 

rotate the static secret

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/vault   main  kubectl get-all -
n app
NAME                                                  NAMESPACE  AGE
configmap/kube-root-ca.crt                            app        32m    
serviceaccount/default                                app        32m    
serviceaccount/demo-static-app                        app        11m    
vaultauth.secrets.hashicorp.com/static-auth           app        11m    
vaultstaticsecret.secrets.hashicorp.com/vault-kv-app  app        8m49s  
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/vault   main  kubectl krew instal
l view-secret
Updated the local copy of plugin index.
  New plugins available:
    * commatrix
    * kfilt
Installing plugin: view-secret
Installed plugin: view-secret
\
 | Use this plugin:
 |      kubectl view-secret
 | Documentation:
 |      https://github.com/elsesiy/kubectl-view-secret
/
WARNING: You installed plugin "view-secret" from the krew-index plugin repository.
   These plugins are not audited for security by the Krew maintainers.
   Run them at your own risk.
   
 # 실패 next mission

 

Dynamic secret

PostgreSQL pod 배포 및 Vault Database Secret Engine 설정

# namespace 생성
kubectl create ns postgres

# Add the Bitnami
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  helm repo add bitnami https://charts.bitnami.com/bitnami
"bitnami" already exists with the same configuration, skipping

# PostgreSQL 설치
helm upgrade --install postgres bitnami/postgresql --namespace postgres --set auth.audit.logConnections=true  --set auth.postgresPassword=secret-pass
Release "postgres" does not exist. Installing it now.
NAME: postgres
LAST DEPLOYED: Tue Dec  2 16:11:18 2025
NAMESPACE: postgres
																	.
																	.
																	.

# 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl get sts,pod,svc,ep,pvc,secret -n postgres
NAME                                   READY   AGE
statefulset.apps/postgres-postgresql   1/1     78s

NAME                        READY   STATUS    RESTARTS   AGE
pod/postgres-postgresql-0   1/1     Running   0          78s

NAME                             TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
service/postgres-postgresql      ClusterIP   10.96.82.91   <none>        5432/TCP   78s
service/postgres-postgresql-hl   ClusterIP   None          <none>        5432/TCP   78s

NAME                               ENDPOINTS         AGE
endpoints/postgres-postgresql      10.244.0.8:5432   78s
endpoints/postgres-postgresql-hl   10.244.0.8:5432   78s

NAME                                               STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
persistentvolumeclaim/data-postgres-postgresql-0   Bound    pvc-915b058b-2b82-4933-b51e-46db3810b0c3   8Gi        RWO            standard       <unset>                 78s

NAME                                    TYPE                 DATA   AGE
secret/postgres-postgresql              Opaque               1      78s
secret/sh.helm.release.v1.postgres.v1   helm.sh/release.v1   1      78s

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl view-secret -n postgres postgres-postgresql --all
secret-pass

# postgreSQL 로그인 확인
kubectl exec -it -n postgres postgres-postgresql-0 -- sh -c "PGPASSWORD=secret-pass psql -U postgres -h localhost -c '\l'"
                                                     List of databases
   Name    |  Owner   | Encoding | Locale Provider |   Collate   |    Ctype    | Locale | ICU Rules |   Access privileges   
-----------+----------+----------+-----------------+-------------+-------------+--------+-----------+-----------------------
 postgres  | postgres | UTF8     | libc            | en_US.UTF-8 | en_US.UTF-8 |        |           | 
 template0 | postgres | UTF8     | libc            | en_US.UTF-8 | en_US.UTF-8 |        |           | =c/postgres          +
           |          |          |                 |             |             |        |           | postgres=CTc/postgres
 template1 | postgres | UTF8     | libc            | en_US.UTF-8 | en_US.UTF-8 |        |           | =c/postgres          +
           |          |          |                 |             |             |        |           | postgres=CTc/postgres
(3 rows)

 

Vault 설정

# Enable an instance of the DB secrets Engine
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault secrets enable -path=demo-db database
Success! Enabled the database secrets engine at: demo-db/

# configure the DB secrets Engine
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  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"

Success! Data written to: demo-db/config/demo-db

# 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault read demo-db/config/demo-db
Key                                   Value
---                                   -----
allowed_roles                         [dev-postgres]
connection_details                    map[connection_url:postgresql://{{username}}:{{password}}@postgres-postgresql.postgres.svc.cluster.local:5432/postgres?sslmode=disable username:postgres]
disable_automated_rotation            false
password_policy                       n/a
plugin_name                           postgresql-database-plugin
plugin_version                        n/a
root_credentials_rotate_statements    []
rotation_period                       0s
rotation_schedule                     n/a
rotation_window                       0
skip_static_role_import_rotation      false
verify_connection                     true

# DB user 동적 생성 Role 등록
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  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="10m" \
   max_ttl="20m"

Success! Data written to: demo-db/roles/dev-postgres

# 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault read demo-db/roles/dev-postgres
Key                      Value
---                      -----
creation_statements      [CREATE ROLE "{{name}}" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';       GRANT ALL PRIVILEGES ON DATABASE postgres TO "{{name}}";]
credential_type          password
db_name                  demo-db
default_ttl              10m
max_ttl                  20m
renew_statements         []
revocation_statements    [REVOKE ALL ON DATABASE postgres FROM  "{{name}}";]
rollback_statements      []

# Create the demo-auth-policy-db policy
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault policy write demo-auth-policy-db - <<EOF
path "demo-db/creds/dev-postgres" {
   capabilities = ["read"]
}
EOF

Success! Uploaded policy: demo-auth-policy-db

# postgreSQL 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl exec -it -n postgres postgres-postgresql-0 -- sh -c "PGPASSWORD=secret-pass psql -U postgres -h localhost -c '\du'"
                             List of roles
 Role name |                         Attributes                         
-----------+------------------------------------------------------------
 postgres  | Superuser, Create role, Create DB, Replication, Bypass RLS

 

Setup dynamic secrets

# Create a new role for the dynamic secret
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±     vault write auth/demo-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

Success! Data written to: auth/demo-auth-mount/role/auth-role

# 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault read auth/demo-auth-mount/role/auth-role
Key                                         Value
---                                         -----
alias_name_source                           serviceaccount_uid
audience                                    vault
bound_service_account_names                 [demo-dynamic-app]
bound_service_account_namespace_selector    n/a
bound_service_account_namespaces            [demo-ns]
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-db]
token_ttl                                   0s
token_type                                  default

 

Create the application : demo-ns , namespace에 vso-db-demo pod가 동적 암호를 사용할 수 있게 해보기

# namespace 생성
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl create ns demo-ns
namespace/demo-ns created

# deployment, secret, serviceaccount, crd 생성
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator   main  kubectl apply -f dynamic-secrets/.
deployment.apps/vso-db-demo created
secret/vso-db-demo created
serviceaccount/demo-dynamic-app created
vaultauth.secrets.hashicorp.com/dynamic-auth created
vaultdynamicsecret.secrets.hashicorp.com/vso-db-demo-create created
vaultdynamicsecret.secrets.hashicorp.com/vso-db-demo created
serviceaccount/demo-operator unchanged

# 실습 실패 
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/dynamic-secrets   main ±  kubectl get pods -n demo-ns                                  
NAME                           READY   STATUS                       RESTARTS   AGE
vso-db-demo-66d65747cc-hgv69   0/1     CreateContainerConfigError   0          5m33s
vso-db-demo-66d65747cc-phjnf   0/1     CreateContainerConfigError   0          5m33s
vso-db-demo-67f877bbc5-jtrlb   0/1     CreateContainerConfigError   0          13s
vso-db-demo-67f877bbc5-zvdvp   0/1     CreateContainerConfigError   0          13s

# default vault url은 아래가 맞음
http://vault.vault.svc.cluster.local:8200
  
# pod내부에서는 로그인 되지만 pod 외부에서는 로그인이 안됨
export VAULT_ADDR='http://vault.vault.svc.cluster.local:8200'
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/dynamic-secrets   main ±  vault login
Token (will be hidden): 
Error authenticating: error looking up token: Get "http://vault.vault.svc.cluster.local:8200/v1/auth/token/lookup-self": dial tcp: lookup vault.vault.svc.cluster.local: no such host

# yaml 수정 후 재배포
kubectl -n <vault-operator-namespace> get vaultconnection vault-connection -o yaml

# 다시 확인해보니 내 local에 listening 돼 있는 vault TCP는 30000
# vaultconnection.yaml file 수정 후 재배포
 meta.helm.sh/release-name: vault-secrets-operator
      meta.helm.sh/release-namespace: vault-secrets-operator-system
    creationTimestamp: "2025-12-02T05:48:28Z"
    finalizers:
    - vaultconnection.secrets.hashicorp.com/finalizer
    generation: 3
    labels:
      app.kubernetes.io/component: controller-manager
      app.kubernetes.io/instance: vault-secrets-operator
      app.kubernetes.io/managed-by: Helm
      app.kubernetes.io/name: vault-secrets-operator
      app.kubernetes.io/version: 0.7.1
      component: controller-manager
      control-plane: controller-manager
      helm.sh/chart: vault-secrets-operator-0.7.1
    name: default
    namespace: vault-secrets-operator-system
    resourceVersion: "19623"
    uid: 3f176bb3-a5f3-4388-9081-a85699cdddb9
  spec:
    address: http://http://vault.vault.svc.cluster.local:30000
    skipTLSVerify: false
  status:
    valid: false
kind: List
metadata:
  resourceVersion: ""
  
# vault login 확인

# pod 재실행
kubectl -n demo-ns rollout restart deploy/vso-db-demo                

# 실패 , 제자리

 

PKI secret

# Enable the PKI secrets
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/dynamic-secrets   main ±  vault secrets enable pki
Success! Enabled the pki secrets engine at: pki/

# configure the max lease time-to-live to 8769h
vault secrets tune -max-lease-ttl=8760h pki
Success! Tuned the secrets engine at: pki/

# Generate a self-signed certificate valid for 8760h
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/dynamic-secrets   main ±  vault w
rite pki/root/generate/internal \
> common_name=example.com \
> ttl=8760h 
WARNING! The following warnings were returned from Vault:

  * This mount hasn't configured any authority information access (AIA)
  fields; this may make it harder for systems to find missing certificates
  in the chain or to validate revocation status of certificates. Consider
  updating /config/urls or the newly generated issuer with this information.

Key              Value
---              -----
certificate      -----BEGIN CERTIFICATE-----
MIIDNTCCAh2gAwIBAgIURBx2IgGhlHuoC5IbIfoO1RzG+rgwDQYJKoZIhvcNAQEL
BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjUxMjAzMDEyNDA4WhcNMjYx
MjAzMDEyNDM4WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAL5E3iVGXnnbtYINtUP2h3qL2BYWngEialK2vvMb
2Zn23IqM+8ZmRFxJ4MWZVJEEPqf1G6if7sBLxaXFiAKFyrhWbmNGtyV1PmuPzA/c
WzdlnXaL4sqFknh3forhj4yLW1JpBk4yFBOpoheM0xf4KSHmiTEuV47taRGkD2IB
wj3R22wDSK93vPFKjzYqL4XrzaBF3hvqQdIs4zxvJ/Yu9gRUY0HOrWPsnsSmyk8T
1EHaaQkcRlczNAFS8hxrPxs5ZSH347XSps4DCIJ4iua/1XkvEkmZjeCyi5k9IKNA
+cfMuE2oL93Zy+s44VIjMLBvqSgPk7mXt6JpNBGXgLZWm1sCAwEAAaN7MHkwDgYD
VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFPiWE3AlTpWd
t+PXFBEzEtWxTc2UMB8GA1UdIwQYMBaAFPiWE3AlTpWdt+PXFBEzEtWxTc2UMBYG
A1UdEQQPMA2CC2V4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQBvRjFL9Nhp
1ZJ9U7D5UFcFJeAi5GXyu/T3yX8+xV52f+FTdscTkcl323pgHryIYDzz6Uu7Y18m
5xJI7l1C6lPopVea+V8OsxAJyRplm8Y97ze6+tp06jYrlOn+kz9v4xXj2g19JAbG
glxVZ0vOcF3JjC/TRHWkW4aKAp9hiGVDvKgq3bVHcoyFror8BwGZVwwrAmnEIb5b
Z6rtkVkgN3eATHU9+bRTOEzzaXYYBAqRrxJP2sRXtqpmRBPHjtaPDPC2r8ZW7KnA
nOyukp9NJtQa+IbFqZ+54IIFADxq3uEa9axn5Fk8LPYd2p2kp1M62uBWdMGE4/+8
Vy0HhIY7Cclx
-----END CERTIFICATE-----
expiration       1796261078
issuer_id        136fda50-440f-6f4d-6cff-c88979ae6839
issuer_name      n/a
issuing_ca       -----BEGIN CERTIFICATE-----
MIIDNTCCAh2gAwIBAgIURBx2IgGhlHuoC5IbIfoO1RzG+rgwDQYJKoZIhvcNAQEL
BQAwFjEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMjUxMjAzMDEyNDA4WhcNMjYx
MjAzMDEyNDM4WjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBAL5E3iVGXnnbtYINtUP2h3qL2BYWngEialK2vvMb
2Zn23IqM+8ZmRFxJ4MWZVJEEPqf1G6if7sBLxaXFiAKFyrhWbmNGtyV1PmuPzA/c
WzdlnXaL4sqFknh3forhj4yLW1JpBk4yFBOpoheM0xf4KSHmiTEuV47taRGkD2IB
wj3R22wDSK93vPFKjzYqL4XrzaBF3hvqQdIs4zxvJ/Yu9gRUY0HOrWPsnsSmyk8T
1EHaaQkcRlczNAFS8hxrPxs5ZSH347XSps4DCIJ4iua/1XkvEkmZjeCyi5k9IKNA
+cfMuE2oL93Zy+s44VIjMLBvqSgPk7mXt6JpNBGXgLZWm1sCAwEAAaN7MHkwDgYD
VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFPiWE3AlTpWd
t+PXFBEzEtWxTc2UMB8GA1UdIwQYMBaAFPiWE3AlTpWdt+PXFBEzEtWxTc2UMBYG
A1UdEQQPMA2CC2V4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQBvRjFL9Nhp
1ZJ9U7D5UFcFJeAi5GXyu/T3yX8+xV52f+FTdscTkcl323pgHryIYDzz6Uu7Y18m
5xJI7l1C6lPopVea+V8OsxAJyRplm8Y97ze6+tp06jYrlOn+kz9v4xXj2g19JAbG
glxVZ0vOcF3JjC/TRHWkW4aKAp9hiGVDvKgq3bVHcoyFror8BwGZVwwrAmnEIb5b
Z6rtkVkgN3eATHU9+bRTOEzzaXYYBAqRrxJP2sRXtqpmRBPHjtaPDPC2r8ZW7KnA
nOyukp9NJtQa+IbFqZ+54IIFADxq3uEa9axn5Fk8LPYd2p2kp1M62uBWdMGE4/+8
Vy0HhIY7Cclx
-----END CERTIFICATE-----
key_id           967b02fe-8fbe-f2e6-1963-548dd88285be
key_name         n/a
serial_number    44:1c:76:22:01:a1:94:7b:a8:0b:92:1b:21:fa:0e:d5:1c:c6:fa:b8

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/dynamic-secrets   main ±  vault write pki/config/urls \
    issuing_certificates="http://vault.vault.svc:8200/v1/pki/ca" \
    crl_distribution_points="http://vault.vault.svc:8200/v1/pki/crl"

Key                        Value
---                        -----
crl_distribution_points    [http://vault.vault.svc:8200/v1/pki/crl]
enable_templating          false
issuing_certificates       [http://vault.vault.svc:8200/v1/pki/ca]
ocsp_servers               []

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/dynamic-secrets   main ±  vault r
ead pki/config/urls
Key                        Value
---                        -----
crl_distribution_points    [http://vault.vault.svc:8200/v1/pki/crl]
enable_templating          false
issuing_certificates       [http://vault.vault.svc:8200/v1/pki/ca]
ocsp_servers               []


howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/dynamic-secrets   main ±  vault write pki/roles/example-dot-com \
    allowed_domains=example.com \
    allow_subdomains=true \
    max_ttl=72h
WARNING! The following warnings were returned from Vault:

  * Issuing Certificate was set to default, but no default issuing certificate
  (configurable at /config/issuers) is currently set

Key                                   Value
---                                   -----
allow_any_name                        false
allow_bare_domains                    false
allow_glob_domains                    false
allow_ip_sans                         true
allow_localhost                       true
allow_subdomains                      true
allow_token_displayname               false
allow_wildcard_certificates           true
allowed_domains                       [example.com]
allowed_domains_template              false
allowed_other_sans                    []
allowed_serial_numbers                []
allowed_uri_sans                      []
allowed_uri_sans_template             false
allowed_user_ids                      []
basic_constraints_valid_for_non_ca    false
client_flag                           true
cn_validations                        [email hostname]
code_signing_flag                     false
country                               []
email_protection_flag                 false
enforce_hostnames                     true
ext_key_usage                         []
ext_key_usage_oids                    []
generate_lease                        false
issuer_ref                            default
key_bits                              2048
key_type                              rsa
key_usage                             [DigitalSignature KeyAgreement KeyEncipherment]
locality                              []
max_ttl                               72h
no_store                              false
not_after                             n/a
not_before_duration                   30s
organization                          []
ou                                    []
policy_identifiers                    []
postal_code                           []
province                              []
require_cn                            true
serial_number_source                  json-csr
server_flag                           true
signature_bits                        256
street_address                        []
ttl                                   0s
use_csr_common_name                   true
use_csr_sans                          true
use_pss                               false

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/dynamic-secrets   main ±  vault policy write pki - <<EOF
path "pki*"                        { capabilities = ["read", "list"] }
path "pki/sign/example-dot-com"    { capabilities = ["create", "update"] }
path "pki/issue/example-dot-com"   { capabilities = ["create"] }
EOF

 

동적 설정 및 확인 : Cert Manager 활용

# kubernetes 인증 활성화

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/dynamic-secrets   main ±  vault
 auth enable kubernetes
Success! Enabled kubernetes auth method at: kubernetes/

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/dynamic-secrets   main ±  vault
 write auth/kubernetes/config kubernetes_host="https://kubernetes.default.svc"
Success! Data written to: auth/kubernetes/config

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/dynamic-secrets   main ±  vault write auth/kubernetes/role/issuer \
    bound_service_account_names=issuer \
    bound_service_account_namespaces=default \
    policies=pki \
    ttl=20m
Success! Data written to: auth/kubernetes/role/issuer

# Deploy Cert Manager
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/dynamic-secrets   main ±  kubec
tl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v1.12.3/cert-manager.crds.yaml
customresourcedefinition.apiextensions.k8s.io/certificaterequests.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/certificates.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/challenges.acme.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/clusterissuers.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/issuers.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/orders.acme.cert-manager.io created


howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/dynamic-secrets   main ±  kubectl
 get crd | grep cert-manager
certificaterequests.cert-manager.io           2025-12-04T00:21:59Z
certificates.cert-manager.io                  2025-12-04T00:21:59Z
challenges.acme.cert-manager.io               2025-12-04T00:21:59Z
clusterissuers.cert-manager.io                2025-12-04T00:21:59Z
issuers.cert-manager.io                       2025-12-04T00:21:59Z
orders.acme.cert-manager.io                   2025-12-04T00:21:59Z

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/dynamic-secrets   main ±  helm re
po add jetstack https://charts.jetstack.io
"jetstack" has been added to your repositories
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/dynamic-secrets   main ±  helm re
po update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "jetstack" chart repository
...Successfully got an update from the "hashicorp" chart repository
...Successfully got an update from the "grgo" chart repository
...Successfully got an update from the "argo" chart repository
...Successfully got an update from the "agrgo" chart repository
...Successfully got an update from the "geek-cookbook" chart repository
...Successfully got an update from the "prometheus-community" chart repository
...Successfully got an update from the "bitnami" chart repository
Update Complete. ⎈Happy Helming!⎈

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/dynamic-secrets   main ±  helm install cert-manager --namespace cert-manager --version v1.12.3 jetstack/cert-manager
NAME: cert-manager
LAST DEPLOYED: Thu Dec  4 09:26:02 2025
NAMESPACE: cert-manager
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
cert-manager v1.12.3 has been deployed successfully!

In order to begin issuing certificates, you will need to set up a ClusterIssuer
or Issuer resource (for example, by creating a 'letsencrypt-staging' issuer).

More information on the different types of issuers and how to configure them
can be found in our documentation:

https://cert-manager.io/docs/configuration/

For information on how to configure cert-manager to automatically provision
Certificates for Ingress resources, take a look at the `ingress-shim`
documentation:

https://cert-manager.io/docs/usage/ingress/

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/dynamic-secrets   main ±  kubec
tl get pods --namespace cert-manager
NAME                                       READY   STATUS    RESTARTS   AGE
cert-manager-cainjector-74bf899bd6-npblv   1/1     Running   0          86s
cert-manager-fb6f6945f-prpq6               1/1     Running   0          86s
cert-manager-webhook-8fc69bc68-vk7k4       1/1     Running   0          86s

# serviceaccount 생성
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/dynamic-secrets   main ±  kubectl
 create serviceaccount issuer
serviceaccount/issuer created

# secrets 생성
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/dynamic-secrets   main ±  kubectl apply -f issuer-secret.yaml
secret/issuer-token-lmzpj created
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/dynamic-secrets   main ±  kubectl
 get secrets
NAME                 TYPE                                  DATA   AGE
issuer-token-lmzpj   kubernetes.io/service-account-token   3      8s

# 환경 변수 설정
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/dynamic-secrets   main ±  ISSUER_SECRET_REF=$(kubectl get secrets --output=json | jq -r '.items[].metadata | select(.name|startswith("issuer-token-")).name')

# 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/dynamic-secrets   main ±  kubectl view-secret $ISSUER_SECRET_REF --all 
ca.crt='-----BEGIN CERTIFICATE-----
MIIDBTCCAe2gAwIBAgIIBGBCa4ElSZYwDQYJKoZIhvcNAQELBQAwFTETMBEGA1UE
AxMKa3ViZXJuZXRlczAeFw0yNTEyMDIwMzIxMTJaFw0zNTExMzAwMzI2MTJaMBUx
EzARBgNVBAMTCmt1YmVybmV0ZXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQCt8P/TRwcjx5BAGh6zBufgnwb0CGFe8RlygUoxlUAa31LrpvRUMhevVHOT
dEhqzruyOModh9X6eirot7TnIhNgf6pkPyt72pfphTFH3BJ5N2j8wxznGQTnMvrd
jJNS+ZhmIofCyhCUynGcKXWI1OaIWXSmJ4m5cz9FyibD+aNOIcCXsoDl/FgPHgGh
1h1aapcAcg/jXo3/mzrO3jBq/LZ21Gr5I2gqrKDUSG+HbbiXDeN7mZdTf2J4H/5Z
e4M+Lmcd6Qn8nOtMudaGraUKqHBfAsEyEYr/EoiHlbWHXEcF9MAtfo+yJ/gnQOHc
m1vft6wAeqZ4NO0z+GS2n/IMl1xPAgMBAAGjWTBXMA4GA1UdDwEB/wQEAwICpDAP
BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQN5cEWls/UUFNslTImIhSVDtnbfTAV
BgNVHREEDjAMggprdWJlcm5ldGVzMA0GCSqGSIb3DQEBCwUAA4IBAQBcPXY27VhX
wyxgan4VHrcJIlJApiWdB+Z6RofhFHdKPpWQ/WluL1pVUeRm3GAhdGMeyxtQqU0p
alGEjMtI7EqBeN7zmjhMyuu4WfCvpztt6vhqKszAhz0+NEafh0Qo1R99K8lu6Exf
+IDeN8ugnTAn1LQzc68Sw2n2DOLZbRYdd+CjUZWSw/1TM7BL7fwLxIco2KYLE3wW
DsMmbi9UqRnPYdSSXt6qJeVaAzi1crokPlpUDhM2WPv5dNOYPNsWKx3alHYbo+ws
U5i60Qj0/lRtmPl/2te+iwWJKfiISIk1RtZe1lK4Zw4+FsVHJKhRZVzlocrFN163
CAGB9KrujgFG
-----END CERTIFICATE-----
'
namespace='default'
token='eyJhbGciOiJSUzI1NiIsImtpZCI6IjI2N3V1SUNja3ZYWG55X0lsdG9wLVc2dHc2OHI4WVFMTjQ1ZzVFUklrT1kifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6Imlzc3Vlci10b2tlbi1sbXpwaiIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJpc3N1ZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiI4NDQ1YTAyZC05ZDllLTRhMDItODU2Ni0yYjJmNDY5YTVjYTciLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDppc3N1ZXIifQ.gdtv9Ug-LxNTi2hX0Pi3BSY1qdecDXR2RwoZYZyPUXJNm-9rGEH5CUTYSLpzbqp7sUCuuQIMxZOK5KsHIfj8G2Ru7ySeCDAFWZJuH4DsR-ftX1_TZxrMxQ4k6ujPP8tyn9DR5Ea-BO2cpxYt8mZVe1RCk_u2mpXFD7ncB3NmdYgIFZI6p06DdvUAFZPH89a5lxFYrn6bJFXDbHdIXEk05phDK1lLfO40HAQYwhHuO-pSqONFs4fVJytKuffYMGRTu6hp3qaMH-h20hQiQwye6lGteO6bBzlQrOkyYajkWY1fBYJsBhbYHQ0dAH37BxvS4XAAvx018vLTBYrASlk61w'

# vault-issuer 생성
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/dynamic-secrets   main ±  kubectl
 apply --filename vault-issuer.yaml 
issuer.cert-manager.io/vault-issuer created
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/dynamic-secrets   main ±  kubectl
 get issuer.cert-manager.io/vault-issuer
NAME           READY   AGE
vault-issuer   True    18s

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/dynamic-secrets   main ±  kubectl apply --filename example-com-cert.yaml # Create the example-com certificate.
certificate.cert-manager.io/example-com created
 
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs/learn-vault-secrets-operator/dynamic-secrets   main ±  kubectl get certificate.cert-manager.io/example-com -owide
NAME          READY   SECRET               ISSUER         STATUS                                                         AGE
example-com   False   issuer-token-lmzpj   vault-issuer   Issuing certificate as Secret does not contain a private key   38s

 

 

Vault Production

Vault HA

실습 환경 구성

# Cluster 생성
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  docker ps
CONTAINER ID   IMAGE                  COMMAND                  CREATED          STATUS          PORTS                                                                                                       NAMES
805e26bd6471   kindest/node:v1.32.8   "/usr/local/bin/entr…"   26 seconds ago   Up 24 seconds                                                                                                               myk8s-worker3
8cd60227b4a5   kindest/node:v1.32.8   "/usr/local/bin/entr…"   26 seconds ago   Up 24 seconds   0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp, 0.0.0.0:30000-30001->30000-30001/tcp, 127.0.0.1:56436->6443/tcp   myk8s-control-plane
845663e6f68c   kindest/node:v1.32.8   "/usr/local/bin/entr…"   26 seconds ago   Up 24 seconds                                                                                                               myk8s-worker
9d0af534219f   kindest/node:v1.32.8   "/usr/local/bin/entr…"   26 seconds ago   Up 24 seconds                                                                                                               myk8s-worker2

# Nginx ingress 생성
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
namespace/ingress-nginx created
serviceaccount/ingress-nginx created
serviceaccount/ingress-nginx-admission created
role.rbac.authorization.k8s.io/ingress-nginx created
role.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrole.rbac.authorization.k8s.io/ingress-nginx created
clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created
rolebinding.rbac.authorization.k8s.io/ingress-nginx created
rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
configmap/ingress-nginx-controller created
service/ingress-nginx-controller created
service/ingress-nginx-controller-admission created
deployment.apps/ingress-nginx-controller created
job.batch/ingress-nginx-admission-create created
job.batch/ingress-nginx-admission-patch created
ingressclass.networking.k8s.io/nginx created
validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created
 
# node selector 지정 
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl patch deployment ingress-nginx-controller -n ingress-nginx \
  --type='merge' \
  -p='{
    "spec": {
      "template": {
        "spec": {
          "nodeSelector": {
            "ingress-ready": "true"
          }
        }
      }
    }
  }'

deployment.apps/ingress-nginx-controller patched

# SSL passthrough flag 활성화
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl get deployment ingress-nginx-controller -n ingress-nginx -o yaml \
| sed '/- --publish-status-address=localhost/a\
        - --enable-ssl-passthrough' | kubectl apply -f -

deployment.apps/ingress-nginx-controller configured

 

vault HA

# nsmaspace 생성
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl create ns vault
namespace/vault created

# helm 을 이용하여 vault 설치
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  helm install vault hashicorp/vault -n vault -f values-ha.yaml --version 0.31.0
NAME: vault
LAST DEPLOYED: Thu Dec  4 17:43:56 2025
NAMESPACE: vault
STATUS: deployed
REVISION: 1
NOTES:
Thank you for installing HashiCorp Vault!

Now that you have deployed Vault, you should look over the docs on using
Vault with Kubernetes available here:

https://developer.hashicorp.com/vault/docs


Your release is named vault. To learn more about the release, try:

  $ helm status vault
  $ helm get manifest vault
  
# 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl get pods --selector='app.kubernetes.io/name=
vault' -n vault
NAME      READY   STATUS    RESTARTS   AGE
vault-0   0/1     Running   0          53s
vault-1   0/1     Running   0          53s
vault-2   0/1     Running   0          53s

# 초기화 ( initialize a new vault cluster )
/ $ vault operator init
Unseal Key 1: In/6fO1SgFfi1eTZ4LO5HmfRzb2nnYM30RWPE6BKmCrL
Unseal Key 2: 3yaQ2a1/EkCLcD5ZXaNZFAvOa3/jv2DwdkqrDtsWTZxF
Unseal Key 3: gXah7g2w53BaixTV4ZVKY3cDtBrNFQJ96uifjlZ70kgX
Unseal Key 4: bwrlkRwBUwzOg9fJvdq6iFLK6H70C0u4FwAq2kLiM32a
Unseal Key 5: aRAQ9uwDb8pqUSXMeN3ZyxH9WRxtqnoZVqsNtIWiiwOI

Initial Root Token: hvs.WjGmkhWFqalXi8IMEultvlf6

Vault initialized with 5 key shares and a key threshold of 3. Please securely
distribute the key shares printed above. When the Vault is re-sealed,
restarted, or stopped, you must supply at least 3 of these keys to unseal it
before it can start servicing requests.

Vault does not store the generated root key. Without at least 3 keys to
reconstruct the root key, Vault will remain permanently sealed!

It is possible to generate new unseal keys, provided you have a quorum of
existing unseal keys shares. See "vault operator rekey" for more information.

$ vault operator init
Unseal Key 1: In/6fO1SgFfi1eTZ4LO5HmfRzb2nnYM30RWPE6BKmCrL
Unseal Key 2: 3yaQ2a1/EkCLcD5ZXaNZFAvOa3/jv2DwdkqrDtsWTZxF
Unseal Key 3: gXah7g2w53BaixTV4ZVKY3cDtBrNFQJ96uifjlZ70kgX
Unseal Key 4: bwrlkRwBUwzOg9fJvdq6iFLK6H70C0u4FwAq2kLiM32a
Unseal Key 5: aRAQ9uwDb8pqUSXMeN3ZyxH9WRxtqnoZVqsNtIWiiwOI
Initial Root Token: hvs.WjGmkhWFqalXi8IMEultvlf6

# 특정 label pods 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl get pods --selector='app.kubernetes.io/nam
e=vault' -n vault
NAME      READY   STATUS    RESTARTS   AGE
vault-0   1/1     Running   0          15h
vault-1   0/1     Running   0          15h
vault-2   0/1     Running   0          15h

# vault join
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl exec -n vault -it vault-1 -- vault operator raft join http://vault-0.vault-internal:8200
Key       Value
---       -----
Joined    true
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl exec -n vault -it vault-2 -- vault operator raft join http://vault-0.vault-internal:8200
Key       Value
---       -----
Joined    true


/ $ vault login
Token (will be hidden): 
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.WjGmkhWFqalXi8IMEultvlf6
token_accessor       1jDdg9olwzSLWoiIbl9O2trV
token_duration       ∞
token_renewable      false
token_policies       ["root"]
identity_policies    []
policies             ["root"]


/ $ 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-acc7d8a4
Cluster ID              c013bc9c-163b-344b-e21b-b7a7dbabcb35
Removed From Cluster    false
HA Enabled              true
HA Cluster              https://vault-0.vault-internal:8201
HA Mode                 active
Active Since            2025-12-04T08:50:00.94034933Z
Raft Committed Index    39
Raft Applied Index      39

# vault operator unseal
/ $ vault operator unseal
Unseal Key (will be hidden): 
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-acc7d8a4
Cluster ID              c013bc9c-163b-344b-e21b-b7a7dbabcb35
Removed From Cluster    false
HA Enabled              true
HA Cluster              https://vault-0.vault-internal:8201
HA Mode                 active
Active Since            2025-12-04T08:50:00.94034933Z
Raft Committed Index    39
Raft Applied Index      39
/ $ vault operator unseal
Unseal Key (will be hidden): 
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-acc7d8a4
Cluster ID              c013bc9c-163b-344b-e21b-b7a7dbabcb35
Removed From Cluster    false
HA Enabled              true
HA Cluster              https://vault-0.vault-internal:8201
HA Mode                 active
Active Since            2025-12-04T08:50:00.94034933Z
Raft Committed Index    39
Raft Applied Index      39
/ $ vault operator unseal
Unseal Key (will be hidden): 
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-acc7d8a4
Cluster ID              c013bc9c-163b-344b-e21b-b7a7dbabcb35
Removed From Cluster    false
HA Enabled              true
HA Cluster              https://vault-0.vault-internal:8201
HA Mode                 active
Active Since            2025-12-04T08:50:00.94034933Z
Raft Committed Index    39
Raft Applied Index      39
/ $ vault operator unseal
Unseal Key (will be hidden): 
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-acc7d8a4
Cluster ID              c013bc9c-163b-344b-e21b-b7a7dbabcb35
Removed From Cluster    false
HA Enabled              true
HA Cluster              https://vault-0.vault-internal:8201
HA Mode                 active
Active Since            2025-12-04T08:50:00.94034933Z
Raft Committed Index    39
Raft Applied Index      39
/ $ vault operator unseal
Unseal Key (will be hidden): 
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-acc7d8a4
Cluster ID              c013bc9c-163b-344b-e21b-b7a7dbabcb35
Removed From Cluster    false
HA Enabled              true
HA Cluster              https://vault-0.vault-internal:8201
HA Mode                 active
Active Since            2025-12-04T08:50:00.94034933Z
Raft Committed Index    39
Raft Applied Index      39
/ $ 

/ $ vault operator raft list-peers
Node                                    Address                        State     Voter
----                                    -------                        -----     -----
d6de4d70-9b76-80d8-51b7-e45e49b28800    vault-0.vault-internal:8201    leader    true

# "vault-0" ~ "vault-2" operator unseal 후
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl get pods --selector='app.kubernetes.io/name=
vault' -n vault
NAME      READY   STATUS    RESTARTS   AGE
vault-0   1/1     Running   0          15h
vault-1   1/1     Running   0          15h
vault-2   1/1     Running   0          15h

# 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  export VAULT_ROOT_TOKEN=hvs.WjGmkhWFqalXi8IMEultvlf6 
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  export VAULT_ADDR='http://localhost:30000'
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault login
Token (will be hidden): 
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.WjGmkhWFqalXi8IMEultvlf6
token_accessor       1jDdg9olwzSLWoiIbl9O2trV
token_duration       ∞
token_renewable      false
token_policies       ["root"]
identity_policies    []
policies             ["root"]
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault operator raft list-peers
Node                                    Address                        State       Voter
----                                    -------                        -----       -----
d6de4d70-9b76-80d8-51b7-e45e49b28800    vault-0.vault-internal:8201    leader      true
e90ba614-399d-5ee4-bdb7-836f86546829    vault-1.vault-internal:8201    follower    true
b627e941-14c7-0f63-d21c-a753809ef043    vault-2.vault-internal:8201    follower    true

 

secret into Vault

# 비밀 입력
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault secrets enable -path=mysecret kv-v2
Success! Enabled the kv-v2 secrets engine at: mysecret/
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault kv put mysecret/logins/studey \
> username="demo" \
> password="p@ssw0rd"
======= Secret Path =======
mysecret/data/logins/studey

======= Metadata =======
Key                Value
---                -----
created_time       2025-12-05T02:43:56.70141103Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1

# 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault kv get mysecret/logins/studey
======= Secret Path =======
mysecret/data/logins/studey

======= Metadata =======
Key                Value
---                -----
created_time       2025-12-05T02:43:56.70141103Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1

====== Data ======
Key         Value
---         -----
password    p@ssw0rd
username    demo
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault kv list mysecret
Keys
----
logins/
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault kv list mysecret/logins
Keys
----
studey

# 정책 생성

 

vault API

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  export VAULT_ROOT_TOKEN=hvs.WjGmkhWFqalXi8IMEultvlf6 
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  curl -s --header "X-Vault-Token: $VAULT_ROOT_TOKEN" http://localhost:30000/v1/mysecret/data/logins/study | jq
{
  "request_id": "b4b27a89-426b-bb29-6900-6359011b78ac",
  "lease_id": "",
  "renewable": false,
  "lease_duration": 0,
  "data": {
    "data": {
      "password": "p@ssw0rd",
      "username": "demo"
    },
    "metadata": {
      "created_time": "2025-12-05T02:48:08.155329918Z",
      "custom_metadata": null,
      "deletion_time": "",
      "destroyed": false,
      "version": 1
    }
  },
  "wrap_info": null,
  "warnings": null,
  "auth": null,
  "mount_type": "kv"
  
  howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  curl -s --header "X-Vault-Token: $VAULT_ROOT_TOKEN" http://localhost:30000/v1/mysecret/data/logins/study \
  | jq -r .data.data.username
demo

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  curl -s --header "X-Vault-Token: $VAULT_ROOT_TOKEN" http://localhost:30000/v1/mysecret/data/logins/study \
  | jq -r .data.data.password
p@ssw0rd

 

 

Vault Auth with LDAP

실습 환경

# OpenLDAP server 배포
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  >....                                               
              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
namespace/openldap created
deployment.apps/openldap created
service/openldap created

# 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl get deploy,pod,svc,ep -n openldap
NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/openldap   1/1     1            1           31s

NAME                            READY   STATUS    RESTARTS   AGE
pod/openldap-54857b746c-wv78f   2/2     Running   0          31s

NAME               TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)                                    AGE
service/openldap   NodePort   10.96.139.107   <none>        80:30001/TCP,389:30573/TCP,636:31817/TCP   31s

NAME                 ENDPOINTS                                     AGE
endpoints/openldap   10.244.3.5:80,10.244.3.5:389,10.244.3.5:636   30s

 

 

OepnLDAP 조직 구성 : 사용, 그룹

# 
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl -n openldap exec -it deploy/openldap -c op
enldap -- bash
root@openldap-54857b746c-wv78f:/# 

# LDAPadd로 OU 추가
root@openldap-54857b746c-wv78f:/# 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
adding new entry "ou=people,dc=example,dc=org"

# LDAPadd로 user추가
root@openldap-54857b746c-wv78f:/# 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
adding new entry "uid=alice,ou=people,dc=example,dc=org"

adding new entry "uid=bob,ou=people,dc=example,dc=org"

# LDAPadd로 groups추가
root@openldap-54857b746c-wv78f:/# 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
adding new entry "cn=devs,ou=groups,dc=example,dc=org"

adding new entry "cn=admins,ou=groups,dc=example,dc=org"

 

LDAP 인증 활성화

# ldap 활성화
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault auth enable ldap
Success! Enabled ldap auth method at: ldap/

# 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault auth list
Path      Type     Accessor               Description                Version
----      ----     --------               -----------                -------
ldap/     ldap     auth_ldap_33d9400c     n/a                        n/a
token/    token    auth_token_7bb86c6a    token based credentials    n/a
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault auth list -detailed
Path      Plugin    Accessor               Default TTL    Max TTL    Token Type         Replication    Seal Wrap    External Entropy Access    Options    Description                UUID                                    Version    Running Version          Running SHA256    Deprecation Status
----      ------    --------               -----------    -------    ----------         -----------    ---------    -----------------------    -------    -----------                ----                                    -------    ---------------          --------------    ------------------
ldap/     ldap      auth_ldap_33d9400c     system         system     default-service    replicated     false        false                      map[]      n/a                        cf38555c-0cc9-723e-2bfc-400913c568eb    n/a        v1.20.4+builtin.vault    n/a               supported
token/    token     auth_token_7bb86c6a    system         system     default-service    replicated     false        false                      map[]      token based credentials    7f5a23d9-3594-9088-66a4-c346d9611be4    n/a        v1.20.4+builtin.vault    n/a               n/a

# ldap config
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  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 인증 테스트
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault login -method=ldap username=alice
Password (will be hidden): 
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.CAESIFCHvfqwEJPxk8HLTnJl-6kav2t0hdoaDpRXfVmDdZKIGh4KHGh2cy5uRGpkSlhTNzExbElyeHVVWUs1M1dDbGg
token_accessor         qc74mkxhLQRBMmsO8YiplvCd
token_duration         768h
token_renewable        true
token_policies         ["default"]
identity_policies      []
policies               ["default"]
token_meta_username    alice

# token 정보 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault token lookup -format=json | jq .data.policies
[
  "default"
]

# bob 계정도 LDAP 인증 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault login -method=ldap username=bob password=bob12
3
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.CAESIN04-9HYzd_mCWrlVfsElNmnUGcCCX-Zx5Xz69J2pMdCGh4KHGh2cy5nYmpnSUhXY1lmV28zTVVJSTVIQUVCSm0
token_accessor         fxIgPrDrcN9IXJ8hjQI19LEK
token_duration         768h
token_renewable        true
token_policies         ["default"]
identity_policies      []
policies               ["default"]
token_meta_username    bob

 

Mapping LDAP group to Vault policy

# 정책 생성
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault policy write admin - <<EOF
path "*" {
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}
EOF
Success! Uploaded policy: admin

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault token lookup
Key                 Value
---                 -----
accessor            1jDdg9olwzSLWoiIbl9O2trV
creation_time       1764838082
creation_ttl        0s
display_name        root
entity_id           n/a
expire_time         <nil>
explicit_max_ttl    0s
id                  hvs.WjGmkhWFqalXi8IMEultvlf6
meta                <nil>
num_uses            0
orphan              true
path                auth/token/root
policies            [root]
ttl                 0s
type                service

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault policy read admin
path "*" {
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}

# LDAP admins 그룹에 admin 정책 지정
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault write auth/ldap/groups/admins policies=admin
Success! Data written to: auth/ldap/groups/admins

# alice 로그인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault login -method=ldap username=alice     
Password (will be hidden): 
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.CAESIFdYHNzc0G4Ifo3uyid8m0zjrpQ7nUzEk97SzBobdzYrGh4KHGh2cy51dWE2dUlFVGluNzRIYXFwVHZzTEVLcW8
token_accessor         uYkO8wNLwiBnq7yFwxSrfcIV
token_duration         768h
token_renewable        true
token_policies         ["admin" "default"]
identity_policies      []
policies               ["admin" "default"]
token_meta_username    alice
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault policy read admin
path "*" {
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}

 

Vault Server TLS

실습 환경 설정

# clsuter 생성
Creating cluster "myk8s" ...
 ✓ Ensuring node image (kindest/node:v1.32.8) 🖼
 ✓ Preparing nodes 📦  
 ✓ Writing configuration 📜 
 ✓ Starting control-plane 🕹️ 
 ✓ Installing CNI 🔌 
 ✓ Installing StorageClass 💾 
Set kubectl context to "kind-myk8s"
You can now use your cluster with:

kubectl cluster-info --context kind-myk8s

Thanks for using kind! 😊
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  docker ps
CONTAINER ID   IMAGE                  COMMAND                  CREATED          STATUS          PORTS                                                                                           NAMES
69bc6dc55907   kindest/node:v1.32.8   "/usr/local/bin/entr…"   18 seconds ago   Up 16 seconds   0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp, 0.0.0.0:30000->30000/tcp, 127.0.0.1:64215->6443/tcp   myk8s-control-plane

# Nginx ingress 배포
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
namespace/ingress-nginx created
serviceaccount/ingress-nginx created
serviceaccount/ingress-nginx-admission created
role.rbac.authorization.k8s.io/ingress-nginx created
role.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrole.rbac.authorization.k8s.io/ingress-nginx created
clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created
rolebinding.rbac.authorization.k8s.io/ingress-nginx created
rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
configmap/ingress-nginx-controller created
service/ingress-nginx-controller created
service/ingress-nginx-controller-admission created
deployment.apps/ingress-nginx-controller created
job.batch/ingress-nginx-admission-create created
job.batch/ingress-nginx-admission-patch created
ingressclass.networking.k8s.io/nginx created
validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created

# SSL Passthrough flag 활성화
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl get deployment ingress-nginx-controller -n i
ngress-nginx -o yaml \
| sed '/- --publish-status-address=localhost/a\
        - --enable-ssl-passthrough' | kubectl apply -f -
deployment.apps/ingress-nginx-controller configured

# nodeselector 지정
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl patch deployment ingress-nginx-controller 
-n ingress-nginx \
> --type='merge' \
  -p='{
    "spec": {
      "template": {
        "spec": {
          "nodeSelector": {
            "ingress-ready": "true"
          }
        }
      }
    }
  }'

 

Vault tls 설치

# Helm v3 으로 설치
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  docker exec -it myk8s-control-plane bash
root@myk8s-control-plane:/# curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 11929  100 11929    0     0  60920      0 --:--:-- --:--:-- --:--:-- 61174
[WARNING] Could not find git. It is required for plugin installation.
Downloading https://get.helm.sh/helm-v3.19.2-linux-arm64.tar.gz
Verifying checksum... Done.
Preparing to install helm into /usr/local/bin
helm installed into /usr/local/bin/helm
root@myk8s-control-plane:/# helm version
version.BuildInfo{Version:"v3.19.2", GitCommit:"8766e718a0119851f10ddbe4577593a45fadf544", GitTreeState:"clean", GoVersion:"go1.24.9"}

root@myk8s-control-plane:/# helm repo add hashicorp https://helm.releases.hashicorp.com
"hashicorp" has been added to your repositories
root@myk8s-control-plane:/# helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "hashicorp" chart repository
Update Complete. ⎈Happy Helming!⎈

# CA 생성
root@myk8s-control-plane:/tls# openssl genrsa -out ca.key 2048
root@myk8s-control-plane:/tls# openssl req -x509 -new -nodes \
> -key ca.key \
  -subj "/CN=Vault-CA" \
  -days 3650 \
  -out ca.crt
root@myk8s-control-plane:/tls# ls 
ca.crt  ca.key

# vault server 인증서 생성
root@myk8s-control-plane:/tls# openssl genrsa -out vault.key 2048
root@myk8s-control-plane:/tls# openssl req -new -key vault.key \
  -subj "/CN=vault.example.com" \
  -out vault.csr
root@myk8s-control-plane:/tls# ls
ca.crt  ca.key  vault.csr  vault.key

# 인증서 서명
root@myk8s-control-plane:/tls# 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 등록
root@myk8s-control-plane:/tls# kubectl -n vault create secret tls vault-tls \
  --cert=vault.crt \
  --key=vault.key
secret/vault-tls created

# CA도 저장
root@myk8s-control-plane:/tls# kubectl -n vault create secret generic vault-ca \
  --from-file=ca.crt=ca.crt
secret/vault-ca created

# 
root@myk8s-control-plane:/tls# helm install vault hashicorp/vault -n vault -f values-tls.yaml --version 0.29.0
NAME: vault
LAST DEPLOYED: Fri Dec  5 06:46:27 2025
NAMESPACE: vault
STATUS: deployed
REVISION: 1
NOTES:
Thank you for installing HashiCorp Vault!

Now that you have deployed Vault, you should look over the docs on using
Vault with Kubernetes available here:

https://developer.hashicorp.com/vault/docs


Your release is named vault. To learn more about the release, try:



  $ helm status vault
  $ helm get manifest vault
root@myk8s-control-plane:/tls# # 
root@myk8s-control-plane:/tls# ^C
root@myk8s-control-plane:/tls# helm list -A
NAME    NAMESPACE       REVISION        UPDATED                                 STATUS          CHART           APP VERSION
vault   vault           1               2025-12-05 06:46:27.792999203 +0000 UTC deployed        vault-0.29.0    1.18.1   

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl exec -ti vault-0 -n vault -- vault status -tls-skip-verify
Key                Value
---                -----
Seal Type          shamir
Initialized        false
Sealed             true
Total Shares       0
Threshold          0
Unseal Progress    0/0
Unseal Nonce       n/a
Version            1.18.1
Build Date         2024-10-29T14:21:31Z
Storage Type       file
HA Enabled         false

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl exec vault-0 -n vault -- vault operator init -tls-skip-verify \
    -key-shares=1 \
    -key-threshold=1 \
    -format=json > cluster-keys.json
    
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  cat cluster-keys.json| jq
{
  "unseal_keys_b64": [
    "xhTxieC+SIQi3AGBqdAWz9rf+EuI0DxqkSwY1z6AF/4="
  ],
  "unseal_keys_hex": [
    "c614f189e0be488422dc0181a9d016cfdadff84b88d03c6a912c18d73e8017fe"
  ],
  "unseal_shares": 1,
  "unseal_threshold": 1,
  "recovery_keys_b64": [],
  "recovery_keys_hex": [],
  "recovery_keys_shares": 0,
  "recovery_keys_threshold": 0,
  "root_token": "hvs.4BpnLGCXWikDPS1rftcePBHy"
}

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  VAULT_UNSEAL_KEY=$(jq -r ".unseal_keys_b64[]" cluster-keys.json)

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl exec vault-0 -n vault -- vault operator unseal -tls-skip-verify $VAULT_UNSEAL_KEY
Key             Value
---             -----
Seal Type       shamir
Initialized     true
Sealed          false
Total Shares    1
Threshold       1
Version         1.18.1
Build Date      2024-10-29T14:21:31Z
Storage Type    file
Cluster Name    vault-cluster-e579bdfe
Cluster ID      2d66d3f6-6de6-1c4b-bd5a-247335e1b9f1
HA Enabled      false

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl get pod -n vault
NAME      READY   STATUS    RESTARTS   AGE
vault-0   1/1     Running   0          43m

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  jq -r ".root_token" cluster-keys.json
hvs.4BpnLGCXWikDPS1rftcePBHy

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl patch svc -n vault vault -p '{"spec":{"type":"NodePort","ports":[{"port":8200,"targetPort":8200,"nodePort":30000}]}}'
service/vault patched

howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  export VAULT_ADDR=https://127.0.0.1:30000
 howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault status -tls-skip-verify            
Key             Value
---             -----
Seal Type       shamir
Initialized     true
Sealed          false
Total Shares    1
Threshold       1
Version         1.18.1
Build Date      2024-10-29T14:21:31Z
Storage Type    file
Cluster Name    vault-cluster-e579bdfe
Cluster ID      2d66d3f6-6de6-1c4b-bd5a-247335e1b9f1
HA Enabled      false

# vault 상태 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault status -tls-skip-verify            
Key             Value
---             -----
Seal Type       shamir
Initialized     true
Sealed          false
Total Shares    1
Threshold       1
Version         1.18.1
Build Date      2024-10-29T14:21:31Z
Storage Type    file
Cluster Name    vault-cluster-e579bdfe
Cluster ID      2d66d3f6-6de6-1c4b-bd5a-247335e1b9f1
HA Enabled      false

# vault root token 로그인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  vault login -tls-skip-verify
Token (will be hidden): 
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.4BpnLGCXWikDPS1rftcePBHy
token_accessor       lnb41LP3m1hFCuXmSmkSX1P6
token_duration       ∞
token_renewable      false
token_policies       ["root"]
identity_policies    []
policies             ["root"]

 

ingress-nginx ssl-passthrough 설정

# host file 설정
echo "127.0.01 vault.example.com" | sudo tee -a /etc/hosts

# pod 확인
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  kubectl describe pod -n vault vault-0 | grep -i port
s
    Ports:         8200/TCP, 8201/TCP, 8202/TCP
    Host Ports:    0/TCP, 0/TCP, 0/TCP
    
# Ingress 생성
howoo@ttokkang-ui-MacBookAir  ~/Desktop/work/Gasida_series/practice/cicd-labs   main ±  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
ingress.networking.k8s.io/vault-https created

0. 실습 환경 구성

Kind K8s 배포 (WSL기반)

(⎈|N/A:N/A) zosys@4:~$ 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
  extraPortMappings:
  - containerPort: 30000 # Vault UI
    hostPort: 30000
  - containerPort: 30001 # Jenkins UI
    hostPort: 30001
  - containerPort: 30002 # DB 배포(PostgreSQL 또는 MySQL)
    hostPort: 30002
  - containerPort: 30003 # # Sample App
    hostPort: 30003
EOF
Creating cluster "myk8s" ...
 ✓ Ensuring node image (kindest/node:v1.32.8) 🖼
 ✓ Preparing nodes 📦
 ✓ Writing configuration 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
Set kubectl context to "kind-myk8s"
You can now use your cluster with:

kubectl cluster-info --context kind-myk8s

Thanks for using kind! 😊

 

1. Vault 개요

사전 지식

1. 정보 보안의 3요소 (CIA Triad)

  • 기밀성(Confidentiality)
  • 허가된 사람(시스템)만 정보에 접근해야한다.
  • 무결성(Integrity)
  • 정보가 허가 없이 변조되거나 삭제되지 않아야 한다. 변경 될 경우 식별이 되어야한다.
  • 가용성(Availability)
  • 사용자가 원할 때는 언제든지 서비스를 이용가능해야한다.

2.액세스 제어의 3단계(AAA)

  • 인증(Authentication)
  • 신원을 확인하는 절차(출입 게이트에 사원증)
  • 인가(Authorization)
  • 인증된 사용자에게 허용된 권한을 부여하는 절차.(사원증에 따른 출입구역제한)
  • 감사/계정관리(Accounting/Auditing)
  • 사용자가 수행한 모든활동을 기록하고 추적하는것(CCTV 녹화,출입기록로그)

시크릿이란?

A. 사용자 및 시스템 접근 자격 증명 ex) 비밀번호,SSH KEY,DB Credential

B. 서비스 연동 및 자동화 키 ex) Cloud Credentials,Token,API Key

C. 보안 통신 및 암호화 자산 ex) PKI, TLS인증서,암호화키

Vault를 필요한 시크릿 중앙관리가 필요한 이유

기존 모놀리식구조나 3Tier구조와 달리 MSA아키텍쳐가 도입되어 인프라와 아키텍쳐가 상당히 복잡해지며 모든 “시크릿”을 안전하게 관리하기가 불가능하기때문이다.

Vault란?

  • HashiCorp Vault는 신원 기반(identity-based)의 시크릿 및 암호화 관리 시스템입니다.
  • 인증 및 인가방법을 통해 암호화서비스를 제공하여 비밀에 대한 안전하고 감사가능하며, 제한된 접근을 보장
  • Vault의 4가지 핵심요소
  1. 누가(Who) : 인증(Authentication)
  2. 무엇에(What) : 대상 지정(Target System)
  3. 얼마동안(How Long) : 접근시간제어(TTL)
  4. 라이프사이클(Lifecycle) : 자동화(Automation)

 

2. Vault 동작 방식

Vault의 동작 방식 이해

개별 ID인증/인가를 통해 필요한 자격 증명 동적발급

 

Authentication을 위한 절차

3. Kubenetes에 Vault설치

Vault on kubernetes 설치

실습 환경 구성(개발모드로 진행)

네임스페이스 생성 및 Helm Repo추가

(⎈|kind-myk8s:N/A) zosys@4:~$ kubectl create namespace vault
namespace/vault created

(⎈|kind-myk8s:N/A) zosys@4:~$ helm repo add hashicorp https://helm.releases.hashicorp.com
"hashicorp" has been added to your repositories

(⎈|kind-myk8s:N/A) zosys@4:~$ helm search repo hashicorp/vault
NAME                                    CHART VERSION   APP VERSION     DESCRIPTION
hashicorp/vault                         0.31.0          1.20.4          Official HashiCorp Vault Chart
hashicorp/vault-secrets-gateway         0.0.2           0.1.0           A Helm chart for Kubernetes
hashicorp/vault-secrets-operator        1.0.1           1.0.1           Official Vault Secrets Operator Chart

 

Helm Chart 설정 Values설정 및 배포


(⎈|kind-myk8s:N/A) zosys@4:~$ cat vault-values-dev.yaml
global:
  enabled: true
  tlsDisable: true

injector:
  enabled: true

server:
  dev:
    enabled: true
    devRootToken: "root"
  dataStorage:
    enabled: false
  service:
    type: "NodePort"
    nodePort: 30000

  ui:
    enabled: true
(⎈|kind-myk8s:N/A) zosys@4:~$ cat vault-values-dev.yaml
global:
  enabled: true
  tlsDisable: true

injector:
  enabled: true

server:
  dev:
    enabled: true
    devRootToken: "root"
  dataStorage:
    enabled: false
  service:
    type: "NodePort"
    nodePort: 30000

  ui:
    enabled: true
(⎈|kind-myk8s:vault) zosys@4:~$ kubectl exec -it vault-0 -- vault status
Key             Value
---             -----
Seal Type       shamir
Initialized     true
Sealed          false
Total Shares    1
Threshold       1
Version         1.20.4
Build Date      2025-09-23T13:22:38Z
Storage Type    inmem
Cluster Name    vault-cluster-5d53df9b
Cluster ID      195a8699-aff7-c091-925e-8fb0d437777b
HA Enabled      false

 

wsl2환경에서 실습환경 구성

(⎈|kind-myk8s:vault) zosys@4:~$ sudo apt-get update
[sudo] password for zosys:
Get:1 https://download.docker.com/linux/ubuntu noble InRelease [48.5 kB]
Hit:2 http://security.ubuntu.com/ubuntu noble-security InRelease
Hit:3 http://archive.ubuntu.com/ubuntu noble InRelease
Hit:4 http://archive.ubuntu.com/ubuntu noble-updates InRelease
Hit:5 http://archive.ubuntu.com/ubuntu noble-backports InRelease
Hit:6 https://ppa.launchpadcontent.net/cncf-buildpacks/pack-cli/ubuntu noble InRelease
Fetched 48.5 kB in 1s (41.7 kB/s)
Reading package lists... Done

(⎈|kind-myk8s:vault) zosys@4:~$ sudo apt-get install -y gpg curl lsb-release
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
gpg is already the newest version (2.4.4-2ubuntu17.3).
gpg set to manually installed.
curl is already the newest version (8.5.0-2ubuntu10.6).
lsb-release is already the newest version (12.0-2).
lsb-release set to manually installed.
0 upgraded, 0 newly installed, 0 to remove and 48 not upgraded.


(⎈|kind-myk8s:vault) zosys@4:~$ echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com noble main
(⎈|kind-myk8s:vault) zosys@4:~$ sudo apt-get update
Hit:1 https://download.docker.com/linux/ubuntu noble InRelease
Get:2 https://apt.releases.hashicorp.com noble InRelease [12.9 kB]
Get:3 https://apt.releases.hashicorp.com noble/main amd64 Packages [210 kB]
Hit:4 http://archive.ubuntu.com/ubuntu noble InRelease
Hit:5 http://security.ubuntu.com/ubuntu noble-security InRelease
Hit:6 http://archive.ubuntu.com/ubuntu noble-updates InRelease
Hit:7 http://archive.ubuntu.com/ubuntu noble-backports InRelease
Hit:8 https://ppa.launchpadcontent.net/cncf-buildpacks/pack-cli/ubuntu noble InRelease
Fetched 223 kB in 1s (196 kB/s)
Reading package lists... Done

(⎈|kind-myk8s:vault) zosys@4:~$ sudo apt-get install -y vault
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following NEW packages will be installed:
  vault
0 upgraded, 1 newly installed, 0 to remove and 48 not upgraded.
Need to get 169 MB of archives.
After this operation, 512 MB of additional disk space will be used.
Get:1 https://apt.releases.hashicorp.com noble/main amd64 vault amd64 1.21.1-1 [169 MB]
Fetched 169 MB in 14s (11.9 MB/s)
Selecting previously unselected package vault.
(Reading database ... 93628 files and directories currently installed.)
Preparing to unpack .../vault_1.21.1-1_amd64.deb ...
Unpacking vault (1.21.1-1) ...
Setting up vault (1.21.1-1) ...
Generating Vault TLS key and self-signed certificate...
..+...+....+...+.....+.......+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*..+....+.....+......+.+..+............+...+...+...+......+......+.......+...+.........+...............+......+.....+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*........+.+...+..+..........+..............+..........+...+........+..........+..............+...+.+..+.......+...............+...............+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
......+.+.............................+..........+..+.........+...+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*.....+......+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*.........+.+.....+.+..+..........+........+..........+...+..+...................+...+...+..+......+..........+...........+.+.....+.......+.................+....+.................+...............+....+....................................+........+....+...........+......+.+..............+...+...+............+.+.....+.........+................+...........+................+..+...+...+......+.............+......+.........+..+...+.+.....+.......+........+.........+......+.........+.......+..............+.+.....+.............+..+....+..............+..........+......+.....+.........+................+..+.......+..+......+...+......+.+........+....+..+............................+...+...+......+..............+.......+...+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
-----
Vault TLS key and self-signed certificate have been generated in '/opt/vault/tls'.

(⎈|kind-myk8s:vault) zosys@4:~$ vault --version
Vault v1.21.1 (2453aac2638a6ae243341b4e0657fd8aea1cbf18), built 2025-11-18T13:04:32Z

(⎈|kind-myk8s:vault) zosys@4:~$ export VAULT_ADDR='http://localhost:30000'
(⎈|kind-myk8s:vault) zosys@4:~$ vault status
Key             Value
---             -----
Seal Type       shamir
Initialized     true
Sealed          false
Total Shares    1
Threshold       1
Version         1.20.4
Build Date      2025-09-23T13:22:38Z
Storage Type    inmem
Cluster Name    vault-cluster-5d53df9b
Cluster ID      195a8699-aff7-c091-925e-8fb0d437777b
HA Enabled      false
(⎈|kind-myk8s:vault) zosys@4:~$ vatuls login
vatuls: command not found
(⎈|kind-myk8s:vault) zosys@4:~$ vault login
Token (will be hidden):
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                root
token_accessor       Z79KiTYlABr4QrIXgYNfLsyn
token_duration       ∞
token_renewable      false
token_policies       ["root"]
identity_policies    []
policies             ["root"]

 

 

 

실습

KV시크릿 엔진 활성화 및 샘플구성 → 정적시크릿(Static Secret)

(⎈|kind-myk8s:vault) zosys@4:~$ vault secrets list
Path          Type         Accessor              Description
----          ----         --------              -----------
cubbyhole/    cubbyhole    cubbyhole_668bf8fc    per-token private secret storage
identity/     identity     identity_827e2b1e     identity store
secret/       kv           kv_eb39bee4           key/value secret storage
sys/          system       system_07d18235       system endpoints used for control, policy and debugging


(⎈|kind-myk8s:vault) zosys@4:~$ vault kv put secret/sampleapp/config \
  username="demo" \
  password="p@ssw0rd"
======== Secret Path ========
secret/data/sampleapp/config

======= Metadata =======
Key                Value
---                -----
created_time       2025-11-24T15:27:21.004785439Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1

(⎈|kind-myk8s:vault) zosys@4:~$ vault kv get secret/sampleapp/config
======== Secret Path ========
secret/data/sampleapp/config

======= Metadata =======
Key                Value
---                -----
created_time       2025-11-24T15:27:21.004785439Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1

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

 

 

Vault Agent와 Sidecar 패턴

실습

1. Vault AppRole 방식 인증 구성

  • 인증 구성 및 정책 적용
(⎈|kind-myk8s:vault) zosys@4:~$ vault auth list
Path      Type     Accessor               Description                Version
----      ----     --------               -----------                -------
token/    token    auth_token_6a22d402    token based credentials    n/a

(⎈|kind-myk8s:vault) zosys@4:~$ vault policy write sampleapp-policy - <<EOF
> path "secret/data/sampleapp/*" {
>   capabilities = ["read"]
> }
> EOF
Success! Uploaded policy: sampleapp-policy

(⎈|kind-myk8s:vault) zosys@4:~$ vault write auth/approle/role/sampleapp-role token_policies="sampleapp-policy" secret_id_ttl="100h" token_ttl="100h" token_max_ttl="200h"
Success! Data written to: auth/approle/role/sampleapp-role

(⎈|kind-myk8s:vault) zosys@4:~$ SECRET_ID=$(vault write -f -field=secret_id auth/approle/role/sampleapp-role/secret-id)
(⎈|kind-myk8s:vault) zosys@4:~$ echo $ROLE_ID
b3712987-c900-3f0f-d381-e86c19c0ec72
(⎈|kind-myk8s:vault) zosys@4:~$ echo $SECRET_ID
098b8b14-dc3a-fe6f-d777-efe805f7a48c

(⎈|kind-myk8s:vault) zosys@4:~$ 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

 

2. Vault Agent Sidecar연동

(⎈|kind-myk8s:vault) zosys@4:~$ 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
configmap/vault-agent-config created

(⎈|kind-myk8s:vault) zosys@4:~$ kubectl apply -n vault -f test.yaml
deployment.apps/nginx-vault-demo created
(⎈|kind-myk8s:vault) zosys@4:~$ cat test.yaml
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: {}

 

3. SVC생성

(⎈|kind-myk8s:vault) zosys@4:~$ 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 
EOF
service/nginx-service created

 

4. 컨테이너 확인

(⎈|kind-myk8s:vault) zosys@4:~$ kubectl get pod -l app=nginx-vault-demo
NAME                                READY   STATUS    RESTARTS   AGE
nginx-vault-demo-7776649597-2jk4f   2/2     Running   0          10s

(⎈|kind-myk8s:vault) zosys@4:~$ kubectl describe pod -l app=nginx-vault-demo
Name:             nginx-vault-demo-7776649597-2jk4f
Namespace:        vault
Priority:         0
Service Account:  default
Node:             myk8s-control-plane/172.18.0.2
Start Time:       Thu, 27 Nov 2025 01:27:58 +0900
Labels:           app=nginx-vault-demo
                  pod-template-hash=7776649597
Annotations:      <none>
Status:           Running
IP:               10.244.0.8
IPs:
  IP:           10.244.0.8
Controlled By:  ReplicaSet/nginx-vault-demo-7776649597
-------------------------중략 -------------------------------

(⎈|kind-myk8s:vault) zosys@4:~$ kubectl get mutatingwebhookconfigurations.admissionregistration.k8s.io
NAME                       WEBHOOKS   AGE
vault-agent-injector-cfg   1          2d1h

(⎈|kind-myk8s:vault) zosys@4:~$ kubectl  exec -it -n vault deploy/nginx-vault-demo -c vault-agent-sidecar -- vault agent -h
Usage: vault agent [options]

  This command starts a Vault Agent that can perform automatic authentication
  in certain environments.

  Start an agent with a configuration file:

      $ vault agent -config=/etc/vault/config.hcl

(⎈|kind-myk8s:vault) zosys@4:~$ kubectl  exec -it -n vault deploy/nginx-vault-demo -c vault-agent-sidecar -- cat /etc/vault/approle/secret_id
87fb5719-5b31-dd97-e701-08ba809d3121(⎈|kind-myk8s:vault) zosys@4:~$ kubectl  exec -it -n vault deploy/nginx-vault-demo -c vault-agent-sidecar -- cat /etc/vault/approle/role_id
b3712987-c900-3f0f-d381-e86c19c0ec72(⎈|kind-myk8s:vault) zosys@4:~$ kubectl  exec -it -n vault deploy/nginx-vault-demo -c vault-agent-sidecar -- cat /etc/vault/approle/role_id
(⎈|kind-myk8s:vault) zosys@4:~$ ec72(⎈|kind-myk8s:vault) zosys@4:~$ kubectl  exec -it -n vault deploy/nginx-vls -l /etc/vault/approlear -- cat /etc/vault/approle/role_id
total 0
lrwxrwxrwx    1 root     root            14 Nov 26 16:27 role_id -> ..data/role_id
lrwxrwxrwx    1 root     root            16 Nov 26 16:27 secret_id -> ..data/secret_id
(⎈|kind-myk8s:vault) zosys@4:~$ kubectl  exec -it -n vault deploy/nginx-vault-demo -c vault-agent-sidecar -- cat /etc/vault/agent-config.hcl
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
}

(⎈|kind-myk8s:vault) zosys@4:~$ kubectl exec -it -n vault deploy/nginx-vault-demo -c nginx -- ls -l /usr/share/nginx/html
total 4
-rw-r--r-- 1 100 1000 94 Nov 26 16:28 index.html
(⎈|kind-myk8s:vault) zosys@4:~$ kubectl exec -it -n vault deploy/nginx-vault-demo -c nginx -- cat /usr/share/nginx/html/index.html
  <html>
  <body>
    <p>username: demo</p>
    <p>password: p@ssw0rd</p>
  </body>
  </html>

 

5. 실제 배포된 화면 확인

 

6. 변경후 재배포 확인

 

4. Jenkins + Vault (AppRole) - CI

실습1. Jenkins + KV 시크릿

1. 젠킨스는 두가지 방법 중 Pod배포 방식으로 진행

(⎈|kind-myk8s:vault) zosys@4:~$ kubectl create ns jenkins
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
-------중략-----------
namespace/jenkins created
persistentvolumeclaim/jenkins-pvc created
deployment.apps/jenkins created
service/jenkins-svc created

(⎈|kind-myk8s:vault) zosys@4:~$ kubectl get deploy,svc,ep,pvc -n jenkins
NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/jenkins   1/1     1            1           2m21s

NAME                  TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)                          AGE
service/jenkins-svc   NodePort   10.96.150.6   <none>        8080:30001/TCP,50000:31211/TCP   2m21s

NAME                    ENDPOINTS                            AGE
endpoints/jenkins-svc   10.244.0.10:50000,10.244.0.10:8080   2m21s

NAME                                STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
persistentvolumeclaim/jenkins-pvc   Bound    pvc-ea0d13e8-ea9d-49cf-879c-578d1532fbc3   10Gi       RWO            standard       <unset>                 2m21s


(⎈|kind-myk8s:vault) zosys@4:~$ kubectl exec -it -n jenkins deploy/jenkins -- cat /var/jenkins_home/secrets/initialAdminPassword
0db0da89485340638ecf03a53599de62

*배포 및 어드민설정/Vault플러그인 설치

 

 

 

 

2. Vault Approle정보 확인

(⎈|kind-myk8s:vault) zosys@4:~/approle-creds$ cat role_id.txt
b3712987-c900-3f0f-d381-e86c19c0ec72
(⎈|kind-myk8s:vault) zosys@4:~/approle-creds$ cat secret_id.txt
87fb5719-5b31-dd97-e701-08ba809d3121

 

3. vault plugin 설치 및 credential 설정

 

4. 파이프 라인 작성 및 구동. pod배포시에 시스템 설정 및 젠킨스파이프라인내에 http://vault.vault.svc:8200로 설정할것.

 

 

실습2. Jenkins + 동적(Dynamic) DB 시크릿

1. 인프라 구성

(⎈|kind-myk8s:vault) zosys@4:~$ cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres
  namespace: default
spec:
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres:13
          env:
            - name: POSTGRES_PASSWORD
              value: "rootpassword"
            - name: POSTGRES_DB
              value: "mydb"
          ports:
            - containerPort: 5432
---
apiVersion: v1
kind: Service
metadata:
  name: postgres
  namespace: default
spec:
  type: NodePort
  ports:
    - port: 5432
      targetPort: 5432
      nodePort: 30002  # [External] Jenkins 접속용
  selector:
    app: postgres
EOF
deployment.apps/postgres created
service/postgres created

 

2. Vault DB Engine 설정

(⎈|kind-myk8s:vault) zosys@4:~$ export VAULT_ADDR=http://127.0.0.1:30000
(⎈|kind-myk8s:vault) zosys@4:~$ export VAULT_TOKEN=root
(⎈|kind-myk8s:vault) zosys@4:~$ vault secrets enable database
Success! Enabled the database secrets engine at: database/
(⎈|kind-myk8s:vault) zosys@4:~$ vault secrets list
Path          Type         Accessor              Description
----          ----         --------              -----------
cubbyhole/    cubbyhole    cubbyhole_668bf8fc    per-token private secret storage
database/     database     database_43519aa6     n/a
identity/     identity     identity_827e2b1e     identity store
secret/       kv           kv_eb39bee4           key/value secret storage
sys/          system       system_07d18235       system endpoints used for control, policy and debugging

(⎈|kind-myk8s:vault) zosys@4:~$ vault write database/config/my-postgresql-database \
    plugin_name=postgresql-database-plugin \
    allowed_roles="jenkins-role" \
    connection_url="postgresql://{{username}}:{{password}}@postgres.default.svc.cluster.local:5432/mydb?sslmode=disable" \
    username="postgres" \
    password="rootpassword"
Success! Data written to: database/config/my-postgresql-database
(⎈|kind-myk8s:vault) zosys@4:~$ vault write database/roles/jenkins-role \
    db_name=my-postgresql-database \
    creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
    default_ttl="1h" \
    max_ttl="24h"
Success! Data written to: database/roles/jenkins-role

 

3. 기존 AppRole 권한 확장 진행

(⎈|kind-myk8s:vault) zosys@4:~$ vault policy write sampleapp-policy - <<EOF
# 1. KV v2 데이터 읽기
path "secret/data/sampleapp/*" {
  capabilities = ["read"]
}
# 2. KV v2 목록 조회 (플러그인 에러 방지용 필수!)
path "secret/metadata/sampleapp/*" {
  capabilities = ["list", "read"]
}
# 3. DB Creds 발급
path "database/creds/jenkins-role" {
  capabilities = ["read"]
}
EOF
Success! Uploaded policy: sampleapp-policy
(⎈|kind-myk8s:vault) zosys@4:~$ vault policy read sampleapp-policy
# 1. KV v2 데이터 읽기
path "secret/data/sampleapp/*" {
  capabilities = ["read"]
}
# 2. KV v2 목록 조회 (플러그인 에러 방지용 필수!)
path "secret/metadata/sampleapp/*" {
  capabilities = ["list", "read"]
}
# 3. DB Creds 발급
path "database/creds/jenkins-role" {
  capabilities = ["read"]
}
(⎈|kind-myk8s:vault) zosys@4:~$ vault write auth/approle/role/sampleapp-role \
  token_policies="default,sampleapp-policy" \
  secret_id_ttl="0" \
  token_ttl="1h" \
  token_max_ttl="4h"
Success! Data written to: auth/approle/role/sampleapp-role

 

4. 젠킨스 파이프라인 작성

 

(⎈|kind-myk8s:vault) zosys@4:~$ kubectl exec -it -n default deploy/postgres -- psql -U postgres
psql (13.23 (Debian 13.23-1.pgdg13+1))
Type "help" for help.

postgres=# \du
                                                       
                                                        List of roles
                     Role name                      |                         Attributes                         | Member of
----------------------------------------------------+------------------------------------------------------------+-----------
 postgres                                           | Superuser, Create role, Create DB, Replication, Bypass RLS | {}
 v-approle-jenkins--sKg8NpSTXnFzluA9jTvf-1764314075 | Password valid until 2025-11-28 08:14:40+00                | {}

 

5. 암호화(Encryption)와 Vault Transit엔진

이번 실습에서 주로 다룰 내용은 계층별 암호화 방안 중 애플리케이션 계층에 대한 암호화를 진행한다.

실습

0. 사전준비

(⎈|kind-myk8s:vault) zosys@4:~$ export NS=vault-demo
export IMAGE=hyungwookhub/vault-transit-demo:v1   # 이번 실습에서는 제 개인 저장소에 업로드한 이미지를 사용
# export IMAGE=DOCKER_HUB_USER/vault-transit-demo:latest   # 본인 계정으로 교체
export VAULT_ADDR=http://localhost:30000                 # NodePort 엔드포인트
export VAULT_TOKEN=root                                  # 실습용 토큰

 

1. Vault Transit활성화

(⎈|kind-myk8s:vault) zosys@4:~$ vault status
Key             Value
---             -----
Seal Type       shamir
Initialized     true
Sealed          false
Total Shares    1
Threshold       1
Version         1.20.4
Build Date      2025-09-23T13:22:38Z
Storage Type    inmem
Cluster Name    vault-cluster-5d53df9b
Cluster ID      195a8699-aff7-c091-925e-8fb0d437777b
HA Enabled      false
(⎈|kind-myk8s:vault) zosys@4:~$ vault secrets enable transit
Success! Enabled the transit secrets engine at: transit/
(⎈|kind-myk8s:vault) zosys@4:~$ vault secrets list
Path          Type         Accessor              Description
----          ----         --------              -----------
cubbyhole/    cubbyhole    cubbyhole_668bf8fc    per-token private secret storage
database/     database     database_43519aa6     n/a
identity/     identity     identity_827e2b1e     identity store
secret/       kv           kv_eb39bee4           key/value secret storage
sys/          system       system_07d18235       system endpoints used for control, policy and debugging
transit/      transit      transit_692f884a      n/a
(⎈|kind-myk8s:vault) zosys@4:~$ vault write -f transit/keys/ds-poc type=aes256-gcm96
Key                       Value
---                       -----
allow_plaintext_backup    false
auto_rotate_period        0s
deletion_allowed          false
derived                   false
exportable                false
imported_key              false
keys                      map[1:1764317025]
latest_version            1
min_available_version     0
min_decryption_version    1
min_encryption_version    0
name                      ds-poc
supports_decryption       true
supports_derivation       true
supports_encryption       true
supports_signing          false
type                      aes256-gcm96

(⎈|kind-myk8s:vault) zosys@4:~$ vault list transit/keys
Keys
----
ds-poc
(⎈|kind-myk8s:vault) zosys@4:~$ PLAINTEXT="My Data"
(⎈|kind-myk8s:vault) zosys@4:~$ CIPHERTEXT=$(vault write -field=ciphertext transit/encrypt/ds-poc \
  plaintext=$(echo -n "$PLAINTEXT" | base64))
(⎈|kind-myk8s:vault) zosys@4:~$ echo "ciphertext: $CIPHERTEXT"
ciphertext: vault:v1:aR/otS5ymKQcqqKN/jQ8RBVIQDf+4voBtzKXbKbwnUaosy4=
(⎈|kind-myk8s:vault) zosys@4:~$ vault write -field=plaintext transit/decrypt/ds-poc \
  ciphertext="$CIPHERTEXT" | base64 -d && echo
My Data

 

 

2. MYSQL 배포진행 (NodePort 30002)

(⎈|kind-myk8s:vault) zosys@4:~$ cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
  name: ${NS}
---
---------중략-------------
namespace/vault-demo created
deployment.apps/mysql created
service/mysql created

(⎈|kind-myk8s:vault) zosys@4:~$ kubectl -n ${NS} rollout status deploy/mysql
deployment "mysql" successfully rolled out
(⎈|kind-myk8s:vault) zosys@4:~$ kubectl -n ${NS} exec -it deploy/mysql -- \
  mysql -uroot -prootpassword -e "CREATE DATABASE IF NOT EXISTS VaultData;"
mysql: [Warning] Using a password on the command line interface can be insecure.
(⎈|kind-myk8s:vault) zosys@4:~$ kubectl -n ${NS} exec -it deploy/mysql -- \
  mysql -uroot -prootpassword -e "SHOW DATABASES LIKE 'VaultData';"
mysql: [Warning] Using a password on the command line interface can be insecure.
+----------------------+
| Database (VaultData) |
+----------------------+
| VaultData            |
+----------------------+

 

3. Transit Demo APP 배포

(⎈|kind-myk8s:vault) zosys@4:~$ cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: vault-transit-demo
  namespace: ${NS}
-----중략----------
deployment.apps/vault-transit-demo created
service/vault-transit-demo created

 

 

(⎈|kind-myk8s:vault) zosys@4:~$ kubectl -n ${NS} exec -it deploy/mysql -- \
mysql -uroot -prootpassword -e "USE VaultData; SHOW TABLES; SELECT id,data,date_created FROM vault_data;"
mysql: [Warning] Using a password on the command line interface can be insecure.
+---------------------+
| Tables_in_VaultData |
+---------------------+
| vault_data          |
+---------------------+
+----+-----------------------------------------------------------+----------------------------+
| id | data                                                      | date_created               |
+----+-----------------------------------------------------------+----------------------------+
|  1 | vault:v1:jAuhJlZtaoX20eeh87hXjQ1toUpyYjleuCK9roZWVi5VyF3w | 2025-11-28 17:11:09.457000 |
+----+-----------------------------------------------------------+----------------------------+
(⎈|kind-myk8s:vault) zosys@4:~$ echo "hello vault transit" > original.txt
(⎈|kind-myk8s:vault) zosys@4:~$ md5sum original.txt
5b92abfe363ae7212a37a55f96bf5967  original.txt

 

4.원본파일과 복호화파일 해쉬값비교

(⎈|kind-myk8s:vault) zosys@4:~$ md5sum original.txt
5b92abfe363ae7212a37a55f96bf5967  original.txt

(⎈|kind-myk8s:vault) zosys@4:~$ md5sum original.txt.enc
63b7c4829152bfb93fc8c8a7101f7cab  original.txt.enc

(⎈|kind-myk8s:vault) zosys@4:~$ md5sum original.dec.txt
5b92abfe363ae7212a37a55f96bf5967  original.dec.tx

실습 환경 배포

kind k8s 배포

# kind k8s 배포
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

# 확인
kubectl get node
kubectl get pod -A

 

 

Ingress-Nginx 배포 - Github

# 노드 라벨 확인
kubectl get nodes myk8s-control-plane -o jsonpath={.metadata.labels} | jq
...
  "ingress-ready": "true",
...

# 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
KUBE_EDITOR="nano" kubectl edit -n ingress-nginx deployments/ingress-nginx-controller
...
  - --enable-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 -

# ingress 배포 확인
kubectl get deploy,svc,ep ingress-nginx-controller -n ingress-nginx
kubectl describe -n ingress-nginx deployments/ingress-nginx-controller

" ingress-ready ": " true "

 

 

kubectl get deployment ingress-nginx-controller -n ingress-nginx -o yaml \
| sed '/- --publish-status-address=localhost/a\
        - --enable-ssl-passthrough' | kubectl apply -f - 

위 단계에서 다음과같이 json 파싱에러가 난다. (가시다님 실습에서는 안나던데...)

 

kubectl patch deployment ingress-nginx-controller -n ingress-nginx —type='json' -p='[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "—enable-ssl-passthrough"}]'

그래서 위 명령어로 패치했다.

 

 

 

 

Jenkins 배포 + Ingress

#
kubectl create ns jenkins
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: jenkins-pvc
  namespace: jenkins
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: jenkins
  namespace: jenkins
spec:
  replicas: 1
  selector:
    matchLabels:
      app: jenkins
  template:
    metadata:
      labels:
        app: jenkins
    spec:
      securityContext:
        fsGroup: 1000
      containers:
        - name: jenkins
          image: jenkins/jenkins:lts
          ports:
            - name: http
              containerPort: 8080
            - name: agent
              containerPort: 50000
          livenessProbe:
            httpGet:
              path: "/login"
              port: 8080
            initialDelaySeconds: 90
            periodSeconds: 10
            timeoutSeconds: 5
            failureThreshold: 5
          readinessProbe:
            httpGet:
              path: "/login"
              port: 8080
            initialDelaySeconds: 60
            periodSeconds: 10
            timeoutSeconds: 5
            failureThreshold: 3
          volumeMounts:
            - name: jenkins-home
              mountPath: /var/jenkins_home
      volumes:
        - name: jenkins-home
          persistentVolumeClaim:
            claimName: jenkins-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: jenkins-svc
  namespace: jenkins
spec:
  type: ClusterIP
  selector:
    app: jenkins
  ports:
    - port: 8080
      targetPort: http
      protocol: TCP
      name: http
    - port: 50000
      targetPort: agent
      protocol: TCP
      name: agent
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: jenkins-ingress
  namespace: jenkins
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: "0"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
spec:
  ingressClassName: nginx
  rules:
    - host: jenkins.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: jenkins-svc
                port:
                  number: 8080
EOF


# 확인
kubectl get deploy,svc,ep,pvc -n jenkins
kubectl get ingress -n jenkins jenkins-ingress
NAME              CLASS   HOSTS                 ADDRESS     PORTS   AGE
jenkins-ingress   nginx   jenkins.example.com   localhost   80      60s

# 도메인 설정
## macOS의 /etc/hosts 파일 수정
echo "127.0.0.1 jenkins.example.com" | sudo tee -a /etc/hosts
cat /etc/hosts

## C:\Windows\System32\drivers\etc\hosts 관리자모드에서 메모장에 내용 추가
127.0.0.1 jenkins.example.com


# 초기 암호 확인
kubectl exec -it -n jenkins deploy/jenkins -- cat /var/jenkins_home/secrets/initialAdminPassword
7521bc306a4049a6a0fd75391ae4b235

# 웹 접속 : 기본 설정 진행
curl -s http://jenkins.example.com -I
open "http://jenkins.example.com"

 

웹 접속 후 초기 설정 진행

  • 초기 암호 입력 : 초기 암호 확인후 입력
  • Customize Jenkins : Install suggested plugins 클릭 후 설치
  • 관리자 계정 설정 : admin / qwe123

 

 

Argo CD 배포 by Helm + Ingress - Ingress , Docs

[Argo CD TLS 동작 요약]

  • OpenSSL로 self-signed 인증서 생성 및 Secrets 생성
  • Argo CD 서버는 TLS 인증서를 argocd-server-tls Secret 에서 직접 로드
    • server.ingress.tls=true + nginx.ingress.kubernetes.io/ssl-passthrough=true
  • 설정으로 Ingress는 TLS를 종료하지 않고 그대로 Pod에게 전달
    • 따라서 브라우저 → Ingress → Argo CD Server 까지 end-to-end HTTPS가 유지됨

 

sequenceDiagram
    participant C as Client (Browser)
    participant I as Ingress (Nginx)
    participant S as Argo CD Server
    participant T as Secret(argocd-server-tls)

    C->>I: HTTPS Request (argocd.example.com)
    Note over I: ssl-passthrough=true<br/>TLS 그대로 전달
    I->>S: Forward HTTPS (Passthrough)
    S->>T: Load TLS certificate + key
    S->>C: HTTPS Response (Self-Signed Cert)

 

# TLS 키·인증서 생성 : 공통 이름이 argocd.example.com 이어야 합니다
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout argocd.example.com.key \
  -out argocd.example.com.crt \
  -subj "/CN=argocd.example.com/O=argocd"

ls -l argocd.example.com.*
-rw-r--r--@ 1 gasida  staff  1184 Nov  8 23:10 argocd.example.com.crt
-rw-------@ 1 gasida  staff  1704 Nov  8 23:10 argocd.example.com.key

# Argo CD가 자신의 TLS를 쓰도록 argocd 네임스페이스에 tls 타입 시크릿 생성
kubectl create ns argocd

# tls 시크릿 생성 : key/crt 파일이 로컬에 있어야 함
kubectl -n argocd create secret tls argocd-server-tls \
  --cert=argocd.example.com.crt \
  --key=argocd.example.com.key

# 
cat <<EOF > argocd-values.yaml
global:
  domain: argocd.example.com

server:
  ingress:
    enabled: true
    ingressClassName: nginx
    annotations:
      nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
      nginx.ingress.kubernetes.io/ssl-passthrough: "true"
    tls: true
EOF

# 설치 : Argo CD v3.1.9
helm repo add argo https://argoproj.github.io/argo-helm
helm install argocd argo/argo-cd --version 9.1.0 -f argocd-values.yaml --namespace argocd

# 각 구성요소 확인
kubectl get pod,ingress,svc,ep,secret,cm -n argocd
kubectl describe ingress -n argocd argocd-server
kubectl get ingress -n argocd argocd-server # 아래 address 에 localhost 는 배포 후 sync 까지 다소 시간 소요 될 수 있음
NAME            CLASS   HOSTS                ADDRESS     PORTS     AGE
argocd-server   nginx   argocd.example.com   localhost   80, 443   6m42s

kubectl get ingress -n argocd argocd-server -o yaml | kubectl neat |yq 
...
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    nginx.ingress.kubernetes.io/ssl-passthrough: "true"
  ...
  name: argocd-server
  namespace: argocd
spec:
  ingressClassName: nginx
  rules:
    - host: argocd.example.com
      http:
        paths:
          - backend:
              service:
                name: argocd-server
                port:
                  number: 443
            path: /
            pathType: Prefix
  tls:
    - hosts:
        - argocd.example.com
      secretName: argocd-server-tls

# 도메인 설정
## macOS의 /etc/hosts 파일 수정
echo "127.0.0.1 argocd.example.com" | sudo tee -a /etc/hosts
cat /etc/hosts

## C:\Windows\System32\drivers\etc\hosts 관리자모드에서 메모장에 내용 추가
127.0.0.1 argocd.example.com


# 접속 확인
curl -vk https://argocd.example.com/

# 최초 접속 암호 확인
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d ;echo
38L2ZmXne7jIaRSZ

ARGOPW=<최초 접속 암호>
ARGOPW=YYpjg453yyKi4HdK

# argocd 서버 cli 로그인 : argocd cli 설치 필요
argocd login argocd.example.com --insecure --username admin --password $ARGOPW

# 확인
argocd cluster list
argocd proj list
argocd account list

# admin 계정 암호 변경 : qwe12345
argocd account update-password --current-password $ARGOPW --new-password qwe12345


# Argo CD 웹 접속 주소 확인 : admin 계정 / qwe12345
open "http://argocd.example.com"
open "https://argocd.example.com"

 

 

Keycloak 배포 + Ingress : miri 유저 (암호 : miri123) - Docs

  1. Keycloak은 사용자의 인증, 암호 복구, 약관 동의 등 다양한 로그인 기능을 별도 개발 없이 손쉽게 제공합니다.
  2. 다양한 인증 프로토콜(OAuth2, OpenID Connect, SAML 2.0)을 지원하여 표준 기반의 통합 인증 체계를 구축할 수 있습니다.
  3. 자체 사용자 DB 및 Active Directory, LDAP와의 연동이 모두 가능해 기존 인프라와 쉽게 통합됩니다.
  4. 소셜 로그인 및 외부 ID 공급자(구글, 페이스북 등)와 연동할 수 있는 Identity Brokering 기능을 내장합니다.
  5. SSO(싱글 사인온) 기능을 제공해 여러 애플리케이션에 한 번 인증으로 접근이 가능합니다.
  6. 애플리케이션이 직접 암호를 다루지 않고 보안 토큰 기반 인증을 사용하여 보안 수준을 높입니다.
  7. 세션 관리, 인증 위치 추적, 원격 세션 종료 등 관리 및 보안 유틸리티도 제공합니다.
  8. 사용자, 클라이언트, 역할, 정책 관리 등 관리자 도구와 편리한 UI를 제공합니다.
  9. 높은 확장성과 커스터마이징: 플러그인, 커스텀 인증 메커니즘, 자체 로그인 프로토콜 확장이 가능합니다.
  10. 클러스터링 및 고가용성 구성이 가능하여 대규모 서비스에서도 안정적으로 운영할 수 있습니다.

 

#
kubectl create ns keycloak
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: keycloak
  namespace: keycloak
  labels:
    app: keycloak
spec:
  replicas: 1
  selector:
    matchLabels:
      app: keycloak
  template:
    metadata:
      labels:
        app: keycloak
    spec:
      containers:
        - name: keycloak
          image: quay.io/keycloak/keycloak:26.4.0
          args: ["start-dev"]     # dev mode 실행
          env:
            - name: KEYCLOAK_ADMIN
              value: admin
            - name: KEYCLOAK_ADMIN_PASSWORD
              value: admin
          ports:
            - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: keycloak
  namespace: keycloak
spec:
  selector:
    app: keycloak
  ports:
    - name: http
      port: 80
      targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: keycloak
  namespace: keycloak
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
  ingressClassName: nginx
  rules:
    - host: keycloak.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: keycloak
                port:
                  number: 8080
EOF

# 확인
kubectl get deploy,svc,ep keycloak -n keycloak
kubectl get ingress keycloak -n keycloak
NAME       CLASS   HOSTS                  ADDRESS     PORTS   AGE
keycloak   nginx   keycloak.example.com   localhost   80      91s

# 도메인 설정
## macOS의 /etc/hosts 파일 수정
echo "127.0.0.1 keycloak.example.com" | sudo tee -a /etc/hosts
cat /etc/hosts

## C:\Windows\System32\drivers\etc\hosts 관리자모드에서 메모장에 내용 추가
127.0.0.1 keycloak.example.com


# keycloak 웹 접속 : admin / admin
curl -s http://keycloak.example.com -I
open "http://keycloak.example.com/admin"

 

 

realms 생성 : myrealm

 

users 생성 : miri - 암호 miri123

 

 

k8s 내부에서 실습을 위한 도메인 호출 설정

# 기본 통신 및 정보 확인 : ClusterIP 메모!
kubectl get svc -n jenkins
NAME          TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)              AGE
jenkins-svc   ClusterIP   10.96.56.98   <none>        8080/TCP,50000/TCP   19m

kubectl get svc -n argocd argocd-server
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
argocd-server   ClusterIP   10.96.249.176   <none>        80/TCP,443/TCP   27m

kubectl get svc -n keycloak
NAME       TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
keycloak   ClusterIP   10.96.199.16   <none>        80/TCP    5m2s

kubectl get svc -n jenkins
NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)              AGE
jenkins-svc   ClusterIP   10.96.230.164   <none>        8080/TCP,50000/TCP   30m
(⎈|kind-myk8s:N/A) gasida  ~/Downloads/cicd-study  kubectl get svc -n argocd argocd-server
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
argocd-server   ClusterIP   10.96.127.179   <none>        80/TCP,443/TCP   9m35s
(⎈|kind-myk8s:N/A) gasida  ~/Downloads/cicd-study  kubectl get svc -n keycloak
NAME       TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
keycloak   ClusterIP   10.96.144.94   <none>        80/TCP    3m51s


# 해결해보자 : coredns 에 hosts 플러그인 활용
KUBE_EDITOR="nano" kubectl edit cm -n kube-system coredns
.:53 {
       ...
        kubernetes cluster.local in-addr.arpa ip6.arpa {
           pods insecure
           fallthrough in-addr.arpa ip6.arpa
           ttl 30
        }
        hosts {
           <CLUSTER IP> jenkins.example.com
           <CLUSTER IP> argocd.example.com
           <CLUSTER IP> keycloak.example.com
           fallthrough
        }
        ...
        reload # cm 설정 변경 시 자동으로 reload 적용됨
...

# cm 설정 변경 시 자동으로 reload 적용 로그 확인
kubectl logs -n kube-system -l k8s-app=kube-dns --timestamps

 

 ✘ minji  ~/Study/cicd  kubectl get svc -n jenkins

NAME          TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)              AGE
jenkins-svc   ClusterIP   10.96.210.95   <none>        8080/TCP,50000/TCP   18m
 minji  ~/Study/cicd  kubectl get svc -n argocd argocd-server

NAME            TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)          AGE
argocd-server   ClusterIP   10.96.98.11   <none>        80/TCP,443/TCP   10m
 minji  ~/Study/cicd  kubectl get svc -n keycloak

NAME       TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
keycloak   ClusterIP   10.96.143.205   <none>        80/TCP    6m31s

 

 

 

SSO (OIDC) 설정

Keycloak Client 설정 : Jenkins - Plugin

 

Jenkins OIDC 설정 - Plugin

 

플러그인 설치 : OpenID Connect Authentication

 

Manage JenkinsSecurity : Security Realm 설정 → 하단 Save

 

Login with Openid Connect

  • Client id : jenkins
  • Client secret : <keycloak 에서 jenkins client 에서 credentials>
  • Configuration mode : Discovery…
  • Advanced configuration : [User fileds] 클릭 → User name field name : preferred_username
  • Security configuration 클릭
    • Disable ssl verification : Check

Jenkins 로그인 with SSO : jenkins 에서 admin logout 이후 로그인 - miri 유저 (암호 : miri123)

jenkins 로그인 시, 아래 처럼 302 Redirect 로 keycloak 으로 향하는 것 확인

 

키클락의 miri 계정으로 로그인 성공

 

Argo CD

생성된 client 에서 → Credentials : 메모 해두기

 

 

ArgoCD OIDC 설정 - Docs

- 클라이언트 시크릿 설정
    
    ```bash
    # 생성된 client 에서 → Credentials 값(메모)를 붙여넣어서 실행
    kubectl -n argocd patch secret argocd-secret --patch='{"stringData": { "oidc.keycloak.clientSecret": "<REPLACE_WITH_CLIENT_SECRET>" }}'
    kubectl -n argocd patch secret argocd-secret --patch='{"stringData": { "oidc.keycloak.clientSecret": "mV3IZO3nmHoZr3BBC37UpdrMSMkF9Umt" }}'
    
    # 확인
    kubectl get secret -n argocd argocd-secret -o jsonpath='{.data}' | jq
    ...
      "oidc.keycloak.clientSecret": "bVYzSVpPM25tSG9acjNCQkMzN1VwZHJNU01rRjlVbXQ=",
    ```
    
- Now we can configure the config map and add the oidc configuration to enable our keycloak authentication.
    
    ```bash
    # 설정 추가
    kubectl patch cm argocd-cm -n argocd --type merge -p '
    data:
      oidc.config: |
        name: Keycloak
        issuer: http://**keycloak.example.com**/realms/**myrealm**
        clientID: argocd
        clientSecret: mV3IZO3nmHoZr3BBC37UpdrMSMkF9Umt
        requestedScopes: ["openid", "profile", "email"]
    '
    
    # 확인
    kubectl get cm -n argocd argocd-cm -o yaml | grep oidc.config: -A5
    
    ```
    
- argocd-server 파드 재시작
    
    ```bash
    **kubectl rollout restart deploy argocd-server -n argocd**
    ```

 

 

 

OpenLDAP 설정

LDAP
네트워크상에서 조직 내 사용자, 그룹, 권한 정보를 관리하고 찾는 경량화된 표준 통신 규약임.​

디지털 회사 주소록 + 출입증 발급 시스템이라고 보면 됨. 회사 인사팀이 직원 명부 관리하고 보안팀이 출입 권한 주는 역할을 동시에 하는 셈.​

데이터 구조는 조직도랑 똑같음. 데이터가 마구잡이로 있는 게 아니라, 트리(Tree) 형태의 계층 구조로 저장됨. 회사 조직도(본사-부서-팀-직원) 생각하면 이해가 빠름.​

식별자(DN)는 각 항목(Entry)을 구분하는 고유 이름표를 DN(Distinguished Name)이라고 함. 파일 경로처럼 전체 위치를 나타냄.​
DN은 dc=example(회사), ou=people(부서), uid=alice(직원) 같이 작은 단위인 RDN들이 모여서 만들어짐. 주소 체계랑 비슷함.​

저장 단위 (Entry & Attribute)
정보 저장의 기본 단위는 Entry이고, 그 안에 이름, 이메일, 직급 같은 세부 정보들이 Attribute(속성)라는 이름으로 담겨 있음.​

인증 (Authentication): 사용자가 로그인할 때 아이디/비번을 LDAP 서버에 보내면, "어, 우리 직원 맞네" 하고 신분을 확인해줌.​

인가 (Authorization): "이 사람은 개발팀이니까 서버 접근 가능", "이 사람은 인사팀이니까 급여 정보 열람 가능" 처럼 그룹/직급 정보를 줘서 권한 설정의 근거를 마련해줌.​

왜 만들었나?
옛날에 쓰던 X.500이라는 프로토콜이 너무 무겁고 복잡해서, 네트워크 부담 줄이고 가볍게 쓰려고 만든 다이어트 버전임.​

 

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: 30000
    - 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:30000

# 로그 확인
kubectl logs -n openldap -l app=openldap -c phpldapadmin -f # phpLDAPadmin
kubectl logs -n openldap -l app=openldap -c openldap -f     # openldap

 

 

OpenLDAP 조직도 구성 : 사용자(alice, bob), 그룹(devs, admins)

#
kubectl -n openldap exec -it deploy/openldap -c openldap -- bash
----------------------------------------------------
# 프로세스 확인
pstree -aplpst
run,1 -u /container/tool/run
  └─slapd,433 -h ldap://openldap-54857b746c-ch9g4:389 ldaps://openldap-54857b746c-ch9g4:636 ldapi:/// -u openldap -g openldap -d 256
      ├─{slapd},436
      └─{slapd},437
      
# LDAP 관리자 인증 테스트 : 정상일 경우 LDAP 기본 엔트리 출력
ldapsearch -x -H ldap://localhost:389 -b dc=example,dc=org -D "cn=admin,dc=example,dc=org" -w admin

# 실습 사용 최종 트리 구조
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
adding new entry "ou=people,dc=example,dc=org"
adding new entry "ou=groups,dc=example,dc=org"

# 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
adding new entry "uid=alice,ou=people,dc=example,dc=org"
adding new entry "uid=bob,ou=people,dc=example,dc=org"

# 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
adding new entry "cn=devs,ou=groups,dc=example,dc=org"
adding new entry "cn=admins,ou=groups,dc=example,dc=org"

# ldapsearch 검색 : ou
ldapsearch -x -D "cn=admin,dc=example,dc=org" -w admin \
  -b "dc=example,dc=org" "(objectClass=organizationalUnit)" ou

# ldapsearch 검색 : 사용자
ldapsearch -x -D "cn=admin,dc=example,dc=org" -w admin \
  -b "ou=people,dc=example,dc=org" "(uid=*)" uid cn mail
  
# ldapsearch 검색 : 그룹/멤버 확인
ldapsearch -x -D "cn=admin,dc=example,dc=org" -w admin \
  -b "ou=groups,dc=example,dc=org" "(objectClass=groupOfNames)" cn member
  

# LDAP 사용자 인증 테스트 : 정상일 경우 LDAP 기본 엔트리 출력
ldapwhoami -x -D "uid=alice,ou=people,dc=example,dc=org" -w alice123
dn:uid=alice,ou=people,dc=example,dc=org

# 빠져나오기
exit
----------------------------------------------------

 

 

 

KeyClock 연동

  1. Keycloak은 ‘User Federation’ 메뉴를 통해 LDAP 서버와 사용자 정보를 연동하고 동기화합니다.
  2. General Options에서 Active Directory 같은 벤더 유형과 관리 콘솔에 표시될 이름을 설정합니다.
  3. Connection 설정 시 ldap:// URL과 StartTLS 보안 여부, 그리고 접속용 계정(Bind DN) 정보를 입력합니다.
  4. 제공되는 Test connection 및 Test authentication 버튼으로 연결 상태와 계정 권한을 즉시 검증할 수 있습니다.
  5. Searching 설정에서 사용자 검색의 루트(Users DN), 로그인 ID 속성, 검색 범위(Subtree 등)를 정의합니다.
  6. Edit mode를 통해 데이터를 읽기 전용(READ_ONLY)으로 할지, Keycloak 변경 사항을 LDAP에 쓸지(WRITABLE) 정합니다.
  7. Synchronization 설정으로 사용자 데이터 임포트 여부와 전체/변경 데이터의 자동 동기화 주기를 지정합니다.
  8. Kerberos 통합 옵션과 조회 성능 최적화를 위한 캐싱 정책(Cache settings)을 구성할 수 있습니다.
  9. Advanced 설정에서는 LDAPv3 암호 변경 기능 활성화 및 Keycloak의 비밀번호 정책 적용 여부를 다룹니다.
  10. 마지막으로 Trust email 옵션을 통해 LDAP에서 가져온 이메일 정보를 신뢰할지 여부를 확정합니다.

KeyCloak에서 LDAP 설정 실습

**admin Console**

[Realm] myrealm → User Federation → Add LDAP providers

**필드값 최소 설정**

- General
    - `UI display name`: ldap
    - `Vendor`: Other
    
- Connection and authentication
    - `Connection URL`:  `ldap://openldap.openldap.svc:389` ⇒ Test connection
    - `Bind DN`: (= Login DN) `cn=admin,dc=example,dc=org`
    - `Bind Credential`: **admin** ⇒ Test authentication
    
- LDAP searching and updating : 참고로 LDAP에 uid 로 alice, bob 존재
    - `Edit mode`: READ_ONLY
    - `Users DN`:  **`ou=people,dc=example,dc=org`**
    - `Username LDAP attribute`: **uid**
    - `RDN LDAP attribute`: **uid**
    - `User Object Classes`: **inetOrgPerson**
    - `Search scope`: Subtree (OU 하위 모두 탐색)
- Synchronization settings
    - `Import Users`: **On** (LDAP → KeyCloak : Sync OK)
    - `Sync Registrations`: **Off** (KeyCloak → LDAP : Sync OK)
    - `Periodic full sync`: **On**
    - `Periodic changed users sync`: **On**
- `Save`

**`[KeyCloak] LDAP Mapper 확인`**

**admin Console**

User Federation → LDAP Provider 선택 → **Mappers**: user에 대한 Mappers가 기본 존재

**`[KeyCloak] LDAP -> KeyCloak 동기화`**

**admin Console**

User Federation → LDAP Provider 선택 → Settings → Action : **Sync all users** ⇒ **실패 시, 다시 한번 더 시도!**

- 처음 LDAP 연동 후 사용자 전부 불러올 때 : LDAP의 **전체 사용자**를 Keycloak DB에 동기화(기존 데이터 덮어씀)

 

 

 

 

 

 

Argo CD 사용 시도 : LDAP devs 그룹에 bob 유저

#
cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: guestbook
  namespace: argocd
  finalizers:
  - resources-finalizer.argocd.argoproj.io
spec:
  project: default
  source:
    helm:
      valueFiles:
      - values.yaml
    path: helm-guestbook
    repoURL: https://github.com/gasida/cicd-study
    targetRevision: HEAD
  syncPolicy:
    automated:
      prune: true
    syncOptions:
    - CreateNamespace=true
  destination:
    namespace: guestbook
    server: https://kubernetes.default.svc
EOF

#
kubectl get applications -n argocd
kubectl get applications -n argocd guestbook -o yaml
kubectl get pod,svc,ep -n guestbook

 

 

RBAC with Groups

OpenLDAP 그룹 정보 → Keycloak 동기화 설정. keycloak 에 group 에 현재 아무 그룹도 없음

 

- **admin Console**
- User Federation → LDAP Provider 선택 → **Mappers → Add mapper → 아래 설정 후 Save**
    - name : **ldap-groups**
    - Mapper type: **group-ldap-mapper**
    - LDAP Groups DN : **ou=groups,dc=example,dc=org**
    - Group Name LDAP Attribute: **cn**
    - Group Object Classed: **groupOfNames**
    - Membership LDAP attribute: **member**
    - Membership attribute type: **DN**
    - Mode: **READ_ONLY**

 

 

  • admin Console

… → Mappers → ldap-groups 선택 → Action → Sync LDAP groups to Keycloak

 

 

Keycloak 에서 토큰에 Group 전달을 위한 설정 : ArgoCD Client 설정 - Docs

Client Scopes 생성 : Name (groups) , 나머지는 기본값 → 하단 Save

 

해당 Client scopes 메뉴 진입 상태에서 mappers 클릭 → [Configure a new mapper] 클릭

  • mapper 리스트가 나타나면 'Group Membership' 선택 후 Name, Token Claim Name 에 groups 입력 , Full group path 는 Off

 

 

argocd client 에서 groups 전달을 위한 설정 : Client 에서 argocd 클릭

  • [Client scopes] 탭 이동 후, Add client scope 클릭 후 생성한 groups를 선택합니다. 이때, [Add] 선택 후 드롭다운의 Default를 선택.

 

Argo CD에 scope 에 groups 추가 설정 → 로그인 시도 , 로그인 실패 시 반복 시도 해보자!

# 아래 추가 
KUBE_EDITOR="vi"  kubectl edit cm -n argocd argocd-cm
...
    requestedScopes: ["openid", "profile", "email" , "groups"]
-> 적용을 위해서 15초 정도 후에 아래 로그인 진행

argocd 로그인 시 scope 에 groups 추가 확인

 

Argo CD RBAC 할당

Keycloak 그룹 ArgoCDAdmins에 ArgoCD 권한을 매핑하기 위해 argocd-rbac-cm 컨피그맵을 업데이트

#
KUBE_EDITOR="vi" kubectl edit cm argocd-rbac-cm -n argocd
...
data:
  policy.csv: |
    g, devs, role:admin
...

 

bob계정에서도 애플리케이션확인

Jenkins 설정

Jenkins 에 scope 에 groups 추가 설정

  • Keycloak 설정 : jenkins client 에서 add client scope 클릭 후 groups 를 default 로 추가
  • Jenkins 설정 : Security → 아래 처럼 scops 에 groups 와 groups fileld name 에 groups 추가

 

+ Recent posts