Cloud

HashiCorp Vault 맛보기

Hanhorang31 2025. 11. 29. 22:01

 

Vault 학습 내용을 정리 합니다.

학습 내용은 CloudNet@ 가시다님이 진행하는 CI/CD스터디를 참고하였습니다.

 

사전 보안 지식

액세스 제어 3단계(AAA)

  • 인증(Who are you?)
  • 인가(What can you do?)
  • 감사(what did you do?)

 

Vault가 필요한 이유

  1. IT아키텍처 진화에 따른 보안 복잡성
  2. 제로 트러스트 보안 모델의 대두
  • 기존: "저 서비스는 내부망에 있으니깐 DB에 접근해도 돼." (위치 기반 신뢰)
  • 제로 트러스트: "저 서비스가 정말 '주문 서비스'가 맞는지 신원(Identity)을 확인하고, DB의 '주문 테이블'에만 SELECT할 수 있는 최소한의 권한5분 동안만 부여하겠어." (신원 기반 + 최소 권한 + 동적 접근)

 

Vault ?

Hashcorp에서 개발한 시크릿 중앙 관리 솔루션으로, 신원 기반(Identity-based)의 시크릿 및 암호화 관리 시스템입니다.

  • 1. 시크릿 스프롤(분산) 문제 해결 → 시크릿 중앙 저장소
  • 2. 동적 인프라 문제 해결 → 동적 시크릿 (Dynamic Secrets)
  • 3. 제로 트러스트(신원 확인) 문제 해결 → 신원 기반 접근 (Identity-Based Access)
  • 4. 감사 및 통제 문제 해결 → 감사 로그 (Audit Logs)

 

Vault 기본 구조 및 동작 방식 이해

  • 인증/인가를 통해 필요한 자격 증명을 동적으로 발급

모든 인증의 목적은 토큰을 얻기 위한 행위입니다.

Vault에서는 인증/인가를 위해 외부 IDP를 연동하고, 엔티티를 기준으로 인증 방법/정책/엔진 접근을 관리합니다.

그림: HashiCorp Korea 내부자료

Vault On Kuberentes 설치

Kind 환경 구성

# 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
  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

Dev Mode 설치(학습용)

  • In-Memory Storage: 데이터를 메모리에 저장하므로 재시작 시 데이터가 초기화됩니다.
  • Auto Unseal: 자동으로 봉인이 해제된 상태로 시작됩니다.
  • Root Token: 초기 루트 토큰이 지정된 값(여기서는 root)으로 고정됩니다.

HELM 설정

# Create a Kubernetes namespace.
kubectl create namespace vault

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

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

# Check that you have access to the chart.
helm search repo hashicorp/vault
NAME                                    CHART VERSION   APP VERSION     DESCRIPTION                          
hashicorp/vault                         0.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

Valut value 구성

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

injector:
  enabled: true
  # Sidecar Injection을 위해 필요한 설정

server:
  dev: # Dev Mode 허용
    enabled: true
    devRootToken: "root" # 학습 편의를 위해 Root Token을 'root'로 고정
  
  # 데이터 영구 저장이 필요 없으므로 비활성화 (Dev모드는 메모리 사용)
  dataStorage:
    enabled: false

  # UI 활성화 및 NodePort 노출
  service:
    type: "NodePort"
    nodePort: 30000
  # 외부 접근을 위해 허용  
  ui:
    enabled: true
EOF

Vault 배포

# Helm Install 실행
helm upgrade vault hashicorp/vault -n vault -f vault-values-dev.yaml --install

# 자원 확인
kubectl get pods,svc,pvc -n vault
NAME                                        READY   STATUS    RESTARTS   AGE
pod/vault-0                                 1/1     Running   0          72s
pod/vault-agent-injector-556c5dd8fb-pk6qw   1/1     Running   0          72s

NAME                               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                         AGE
service/vault                      NodePort    10.96.23.194    <none>        8200:30000/TCP,8201:32590/TCP   72s
service/vault-agent-injector-svc   ClusterIP   10.96.150.240   <none>        443/TCP                         72s
service/vault-internal             ClusterIP   None            <none>        8200/TCP,8201/TCP               72s

(참고) Shamir 방식의 봉인 해제 : 볼트 초기화시, 초기 UnSeal 필요하나 dev모드로 봉인 해제되어 있음

 kubectl exec -ti vault-0 -n vault -- 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-41f08771
Cluster ID      f97ecd4e-85f3-adf5-5c6d-9bcc719f53b7
HA Enabled      false

암호학자 아디 샤미르(Adi Shamir)의 이름에서 따온 이 시크릿 공유 알고리즘은 개인이 특정 시크릿 정보를 보유하지 않고 결합 될 때에야 암호화 된 루트 키를 해제할 수 있는 단일 키로 조합되는 방식이다.

아래 그림처럼 관리자에게 초기 비밀번호를 5개로 분류하고 3개이상의 관리자 키를 통해서만 봉인 해제가 가능함

[그림] Shamir 방식의 봉인 해제

  • Vault CLI 설정 및 콘솔 확인
brew tap hashicorp/tap
brew install hashicorp/tap/vault
vault --version  # 설치 확인

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

# vault 상태확인
vault status

# Root Token으로 로그인
vault login

# 아래, root 입력 
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       JieAzXpXlILfJxTsgBe3cIZC
token_duration       ∞
token_renewable      false
token_policies       ["root"]
identity_policies    []
policies             ["root"]


open http://localhost:30000

→ root 토큰은 초기화 이후 없애는 것을 권고

  • KV 시크릿 엔진 활성화(정적 시크릿)

Version1 : KV 버전관리 불가 / Version2 : KV 버전관리 가능 [Docs]

→ Version에 버전 관리 메타데이터가 추가되어 속도는 Version 1이 더 빠르나 차이는 없음(다량의 트랜젝션이 필요한 경우 Version 1 고려)

→ 실습에서는 Version 2로 실습

# 활성화된 시크렛 엔진 목록 조회
vault secrets list

Path          Type         Accessor              Description
----          ----         --------              -----------
cubbyhole/    cubbyhole    cubbyhole_92a571f7    per-token private secret storage
identity/     identity     identity_464bc39a     identity store
secret/       kv           kv_737df813           key/value secret storage
sys/          system       system_699be8c4       system endpoints used for control, policy and debugging

# KV v2 형태로 엔진 활성화하기 위한 명령은 다음과 같지만 Dev 모드에서 활성화 되어있음.
# vault secrets enable -path=secret kv-v2

# 샘플 시크릿 저장 (경로: secret/sampleapp/config)
vault kv put secret/sampleapp/config \
  username="demo" \
  password="p@ssw0rd"
  
# 입력된 데이터 확인
vault kv get secret/sampleapp/config

Vault Agent

애플리케이션에서 Vault 연동이 어려움(언어마다 다르며, 보안 인프라 로직 필요)

→ Vault Agent는 이 문제를 사이드카(Sidecar) 기반 표준 패턴으로 깔끔하게 해결해준다

  • Vault Agent가 Vault 인증을 대신 수행하기에 Application에서는 수정할 것이 최소화됨(TTL, 인증)

Agent 구성을 위해 Vault AppRole 인증 구성을 먼저합니다.

→ Approle이란 애플리케이션이 Vault에 접근할 때 쓸 수 있도록 만든 인증 메커니즘으로 쿠버네티스에서 Vault에 접근하기위해 구성합니다.

# 정책 생성
vault auth enable approle || echo "AppRole already enabled"
vault auth list

---
Success! Enabled approle auth method at: approle/
Path        Type       Accessor                 Description                Version
----        ----       --------                 -----------                -------
approle/    approle    auth_approle_700b3381    n/a                        n/a
token/      token      auth_token_98fb68f1      token based credentials    n/a

# 위 생성한 Role 에 정책 연결
vault write auth/approle/role/sampleapp-role \
  token_policies="sampleapp-policy" \
  secret_id_ttl="1h" \
  token_ttl="1h" \
  token_max_ttl="4h"
  
# Role ID 및 Secret ID 추출 및 저장
ROLE_ID=$(vault read -field=role_id auth/approle/role/sampleapp-role/role-id)
SECRET_ID=$(vault write -f -field=secret_id auth/approle/role/sampleapp-role/secret-id)

echo "ROLE_ID: $ROLE_ID"
echo "SECRET_ID: $SECRET_ID"

# 접근 키 저장
mkdir -p approle-creds
echo "$ROLE_ID" > approle-creds/role_id.txt
echo "$SECRET_ID" > approle-creds/secret_id.txt

# 쿠버네티스 시크릿으로 저장(Agent 인증시 Approle 사용) 
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 -
  

Vault Agent SideCar 연동

Vault Agent 구성은 Configmap 설정을 통해 연결할 정보(Vault의 정보와, Template 구성, 렌더링 주기, 참조할 Vault KV 위치정보)들을 정의합니다.


cat <

username: {{ with secret "secret/data/sampleapp/config" }}{{ .Data.data.username }}{{ end }}

password: {{ with secret "secret/data/sampleapp/config" }}{{ .Data.data.password }}{{ end }}

  
  
EOH
}
EOF

샘플 애플리케이션 배포

  • Vault Agent 컨테이너를 별도 구성하여 주입(수동)

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

# SVC 생성
kubectl apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  type: NodePort
  selector:
    app: nginx-vault-demo
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
      nodePort: 30001 # Kind에서 설정한 Port
EOF

SideCar 확인

 

kubectl describe pod -l app=nginx-vault-demo
...
Containers:
  nginx:
    Container ID:   containerd://c160c2268ce6d7602b718336b9036ae89646b5fdb7ebf310bed3dcf497b8675e
    Image:          nginx:latest
  ...
  vault-agent-sidecar:
    Container ID:  containerd://6d7e37e8925e681d163b06ae7d18f5d3a236164a60ebf422abd4229a2e337e4e
    Image:         hashicorp/vault:latest
    Image ID:      docker.io/hashicorp/vault@sha256:ee674e47dcf85849aadf255b5341f76c0e1a474bc5fa9be9cdfff2a2edf9a628
    Port:          
    Host Port:     
    Args:
      agent
      -config=/etc/vault/agent-config.hcl
...

# Agent 내 주입 정보 확인
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 = <

username: {{ with secret "secret/data/sampleapp/config" }}{{ .Data.data.username }}{{ end }}

password: {{ with secret "secret/data/sampleapp/config" }}{{ .Data.data.password }}{{ end }}

  
  
EOH
      
  • Annotation을 통한 자동 주입
# Kubernetes Auth Method 활성화
vault auth enable kubernetes
vault auth list

# Kubernetes Auth Config 설정 (최신 방식)
# Service Account관련 Secrets을 자동으로 생성하려면? - Helm Chart Values
TOKEN=$(kubectl create token vault -n vault)
CA_CERT=$(kubectl config view --raw --minify --flatten -o jsonpath='{.clusters[0].cluster.certificate-authority-data}' | base64 --decode)

vault write auth/kubernetes/config \
  token_reviewer_jwt="$TOKEN" \
  kubernetes_host="https://kubernetes.default.svc.cluster.local" \
  kubernetes_ca_cert="$CA_CERT" \
  issuer="https://kubernetes.default.svc.cluster.local" \
  disable_iss_validation=false
  
# (앞선 과정에서 만들었으므로 생략가능) 필요한 Policy 작성 
vault policy write sampleapp-policy - <

 

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  name: vault-ui-sa
  namespace: vault
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: vault-injected-ui
  namespace: vault
spec:
  replicas: 1
  selector:
    matchLabels:
      app: vault-injected-ui
  template:
    metadata:
      labels:
        app: vault-injected-ui
      annotations:
        vault.hashicorp.com/agent-inject: "true"
        vault.hashicorp.com/role: "sampleapp-role"
        vault.hashicorp.com/agent-inject-secret-config.json: "secret/data/sampleapp/config"
        vault.hashicorp.com/agent-inject-template-config.json: |
          {{- with secret "secret/data/sampleapp/config" -}}
          {
            "username": "{{ .Data.data.username }}",
            "password": "{{ .Data.data.password }}"
          }
          {{- end }}
        vault.hashicorp.com/agent-inject-output-path: "/vault/secrets"
    spec:
      serviceAccountName: vault-ui-sa
      containers:
      - name: app
        image: python:3.10
        ports:
        - containerPort: 5000
        command: ["sh", "-c"]
        args:
          - |
            pip install flask && cat <<PYEOF > /app.py
            import json, time
            from flask import Flask, render_template_string
            app = Flask(__name__)
            while True:
                try:
                    with open("/vault/secrets/config.json") as f:
                        secret = json.load(f)
                    break
                except:
                    time.sleep(1)
            @app.route("/")
            def index():
                return render_template_string("<h2>🔐 Vault Injected UI</h2><p>👤 사용자: {{username}}</p><p>🔑 비밀번호: {{password}}</p>", **secret)
            app.run(host="0.0.0.0", port=5000)
            PYEOF
            python /app.py
---
apiVersion: v1
kind: Service
metadata:
  name: vault-injected-ui
  namespace: vault
spec:
  type: NodePort
  ports:
    - port: 5000
      targetPort: 5000
      nodePort: 30002
  selector:
    app: vault-injected-ui
EOF

→ HELM 차트로 관리함에 있어 SideCar 주입이 어려움

 

 

Jenkins Vault 연동(AppRole)

Jenkins를 설치하고 Vault를 통해 시크릿 관리 연동 진행


#
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: NodePort
  selector:
    app: jenkins
  ports:
    - nodePort: 30001
      port: 8080
      targetPort: http
      protocol: TCP
      name: http
    - port: 50000
      targetPort: agent
      protocol: TCP
      name: agent
EOF


# 확인
kubectl get deploy,svc,ep,pvc -n jenkins

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

# 웹 접속 : 기본 설정 진행
open "http://127.0.0.1:30001"
  • Customize Jenkins : Install suggested plugins 클릭 후 설치
  • 관리자 계정 설정 : admin / qwe123
  • Jenkins에서 Vault Plugin 설치

Jenkins 내 Approle 설정

# 이전 생성한 크리덴셜 확인 
vault read auth/approle/role/sampleapp-role/role-id
vault write -f auth/approle/role/sampleapp-role/secret-id

Key        Value
---        -----
role_id    dc9207f1-e57e-5629-b34a-38964e7b3a18
Key                   Value
---                   -----
secret_id             e6b5cab4-6b9e-f105-72a4-780236481264
secret_id_accessor    2c6feb2e-324b-5200-99db-ed4060264700
secret_id_num_uses    0
secret_id_ttl         1h

Jenkins 내 Vault 등록

  • 종류: Vault AppRole Credential
  • Role ID & Secret ID 입력 → 생성해놓은 변수 또는 파일참고
  • ID는 기억하기 쉬운 이름으로 지정 (vault-approle-creds 등)

Jenkins Vault 테스트

Jenkins PipeLine 생성 (이름 : jenkins-vault-kv)

 

pipeline {
  agent any

  environment {
    VAULT_ADDR = 'http://vault.vault.svc:820'  }

  stages {
    stage('Read Vault Secret') {
      steps {
        withVault([
          vaultSecrets: [
            [
              path: 'secret/sampleapp/config',
              engineVersion: 2,
              secretValues: [
                [envVar: 'USERNAME', vaultKey: 'username'],
                [envVar: 'PASSWORD', vaultKey: 'password']
              ]
            ]
          ],
          configuration: [
            vaultUrl: "${VAULT_ADDR}",
            vaultCredentialId: 'vault-approle-creds'
          ]
        ]) {
          sh '''
            echo "Username from Vault: $USERNAME"
            echo "Password from Vault: $PASSWORD"
          '''
          script {
            echo "Username (env): ${env.USERNAME}"
            echo "Password (env): ${env.PASSWORD}"
          }
        }
      }
    }
  }
}
  • KV Version1은 경로에 data을 넣고 Version2는 경로에 data을 넣지 않는다.

 

Vault Dynamic Secret 구성

요청 시 즉시 발급되고, TTL이 지나면 자동으로 사라지는 단기(Short-lived) 인증 정보

→ 하지만 현실적으로 실제 동적 시크릿 로직을 구현하기 위해서 고려해야 할 사항이 많지만, Vault에서 지원 가능

인프라 구성(PostgreSQL)

# postgres-deploy.yaml
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

Vault DB 엔진 구성 & AppRole 권한 확장

  • 관리자 권한을 가지고 요청시 임시 유저 설정

 

# (로컬 터미널에서 수행)
export VAULT_ADDR=http://127.0.0.1:30000
export VAULT_TOKEN=root

# 1. Database Secret Engine 활성화
vault secrets enable database
vault secrets list

# 2. Vault -> Postgres 연결 설정
# (Vault와 DB는 같은 K8s 안에 있으므로 내부 DNS 사용)
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"

# 3. "1시간짜리 임시 계정" 생성 규칙 정의 (DB Role)
# 주의: Vault 내부의 Role이 아니라, DB 엔진용 Role입니다.
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"
    
  # 기존 KV 권한 + 새로운 DB 권한(database/creds/jenkins-role) 병합
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

# 업데이트된 Policy 확인 (위 내용과 동일한지 확인)
vault policy read sampleapp-policy


# 관리자 로그인 상태에서 실행. token polices에 default도 함께추가
vault write auth/approle/role/sampleapp-role \
  token_policies="default,sampleapp-policy" \
  secret_id_ttl="0" \
  token_ttl="1h" \
  token_max_ttl="4h"

Jenkin 테스트

  • KV 엔진을 1로 명시해야함
pipeline {
  agent any

  environment {
    // Jenkins(Docker) -> Vault(K8s NodePort)
    // 주의: http:// 가 포함된 전체 주소
    // Jenkins 파드 사용 시 : http://vault.vault.svc:8200
    VAULT_ADDR = 'http://vault.vault.svc:8200' 
    
    // Jenkins(Docker) -> DB(K8s NodePort)
    // 주의: DB 접속에는 프로토콜(http://) 없이 IP만 필요합니다.
    // Jenkins 파드 사용 시 : DB_HOST = 'postgres.default.svc' , DB_PORT = '5432'
    DB_HOST = 'postgres.default.svc'
    DB_PORT = '5432'
  }

  stages {
    stage('Vault 통합 및 DB 접속 테스트') {
      steps {
        withVault([
          configuration: [
            vaultUrl: "${VAULT_ADDR}",
            vaultCredentialId: 'vault-approle-creds',
            // ⚠️ 중요: 여기서 전역 engineVersion 설정을 하지 않습니다.
            skipSslVerification: true
          ],
          vaultSecrets: [
            // 1. KV Secret (정적 시크릿)
            // KV v2 엔진을 사용하므로 engineVersion: 2를 명시합니다.
            [
              path: 'secret/sampleapp/config',
              engineVersion: 2,
              secretValues: [
                [envVar: 'STATIC_USER', vaultKey: 'username']
              ]
            ],
            // 2. Database Secret (동적 시크릿)
            // DB 엔진은 기본 방식(v1)으로 통신해야 경로 에러가 없습니다.
            [
              path: 'database/creds/jenkins-role',
              engineVersion: 1,
              secretValues: [
                [envVar: 'DB_USER', vaultKey: 'username'],
                [envVar: 'DB_PASS', vaultKey: 'password']
              ]
            ]
          ]
        ]) {
          script {
            echo "=================================================="
            echo "             Vault 연동 테스트 시작                "
            echo "=================================================="

            // 1. 정적 시크릿 확인
            // sed 명령어로 글자 사이에 공백을 넣어 마스킹(****)을 우회합니다.
            // 예: d e m o
            sh '''
              echo "[1] KV Secret (Static)"
              echo " - 원본 값은 보안상 **** 로 표시됩니다."
              echo " - 실제 값 확인: $(echo $STATIC_USER | sed "s/./& /g")"
            '''
            
            // 2. 동적 시크릿 확인 (핵심!)
            // Vault가 생성한 임시 DB 계정(v-token-...)을 확인합니다.
            sh '''
              echo "--------------------------------------------------"
              echo "[2] Database Secret (Dynamic)"
              echo " - Vault가 생성한 임시 계정 ID입니다."
              echo " - 실제 값 확인: $(echo $DB_USER | sed "s/./& /g")"
              echo "--------------------------------------------------"
            '''
            
            // 3. DB 접속 시뮬레이션
            // 실제 애플리케이션에서 DB 연결 문자열을 만드는 과정입니다.
            sh '''
              echo "[3] DB Connection Simulation"
              echo " - Connecting to: ${DB_HOST}:${DB_PORT}"
              echo " - User: ${DB_USER}"
              echo " - Password: (Hidden)"
              echo " >> ✅ DB 접속 테스트 성공! (가상)"
            '''
          }
        }
      }
    }
  }
  
  post {
    success {
      script {
        echo "🎉 Pipeline 성공!" 
        echo "   -> 확인된 DB 계정(${env.DB_USER})은 Vault의 TTL 설정에 따라 1시간 후 자동 삭제됩니다."
      }
    }
    failure {
      echo "💥 Pipeline 실패! Vault 로그나 네트워크 설정을 확인하세요."
    }
  }
}

 

kubectl exec -it -n default deploy/postgres -- psql -U postgres
\du 

                     Role name                      |                         Attributes                         | Member of 
----------------------------------------------------+------------------------------------------------------------+-----------
 postgres                                           | Superuser, Create role, Create DB, Replication, Bypass RLS | {}
 v-approle-jenkins--1zRpViME96NtQ4Xzqtjn-1764420933 | Password valid until 2025-11-29 13:55:38+00                | {}

'Cloud' 카테고리의 다른 글

Vault VSO 맛보기  (0) 2025.12.07
KeyCloak SSO 실습  (0) 2025.11.23
ArgoCD로 멀티클러스터 관리하기  (0) 2025.11.23
ArgoCD 접근제어 설정 방법  (0) 2025.11.16
ArgoCD 정리 (1)  (0) 2025.11.08