Vault 들어가기 전
정보보안의 3요소
- 기밀성 : 허가된 사람, 열쇠를 가진 사람만 접근
- 무결성 : 정보가 허가없이 변조되지 않아야함 = 편지봉투 Sealing (+ 변경된 경우 추적 필수)
- 가용성 : 언제든지 24시간 서비스 이용 가능해야함
액세스 제어 3단계 [AAA]
(sk하이닉스 출근)
- 인증 : 신원 확인 (사원증 찍기) - Vault에서 인증된 사용자에게 토큰 발급
- 인가 : 인증된 사용자에게 권한 부여 (출입 구역 제한) - IAM, RBAC
- 감사,계정관리 : 사용자가 수행한 모든 행동 기록,추적 (cctv 녹화, 출입 기록) - 일이 생겼을때 누가 무엇을 했나 역추적
시크릿이란?
- 사용처 1. 사용자 및 시스템 접근 자격 증명. Password, SSH key, DB credentials
- 사용처 2. 서비스 연동 및 자동화 키. Cloud Credentials(클라우드 환경 리소스 제어를 위한 접근 키), Token(Github, Docker Hub), API Key(OpenAI) → Vibe Coding이 유행하면서 API Key 관리 중요성 강화
- 사용처 3. 보안 통신 및 암호화 자산. TLS 인증서, 데이터베이스 데이터 암호화/복호화 키(KMS)
Vault를 활용한 시크릿 중앙 관리가 중요한 이유
시크릿 중앙 관리, Vault 필요성 향상
→ IT인프라, 애플리케이션 아키텍처가 복잡해지면서 기존 방식으로는 시크릿 안전하게 관리 불가능. 한계가 있음
IT 아키텍터 진화와 시크릿 관리 복잡성
→ 예전엔 모놀리식.
→ 그 다음은 3-Tier/Client-Server. 웹 앱 DB로 역할 분리로 관리 컴포넌트가 늘어나면서 시크릿도 분산
→ 현재는 클라우드/MSA/DevOps 시대. 수백개의 MSA가 동적으로 생성되고 사라짐.
시크릿 관리 위기
→ 각 서비스, 컨테이너, API 연동마다 고유한 자격증명 필요. 시크릿 기하급수적으로 증가
→ 동적 환경에서 어떻게 안전하게 전달하고 사용후 즉시 폐기할지. 생성/소멸 주기가 인프라 생명 주기와 같아져야 함(초,분 단위)
→ 시크릿 분산(Sprawl 스프롤) : 시크릿이 git, config파일, 환경변수, 개발자 PC 등 여러곳에 하드코딩되기나 방치되기 시작함. 시크릿 노출 사고 늘어남.
제로 트러스트(Zero Trust)
→ 과거 보안 모델 vs 현대 보안 모델
→ 과거엔 Castle and Moat (성과 해자) 방식. 내부에 들어오면 안에는 안전하다! 무조건 신뢰
→ 지금은 제로 트러스트 모델. 클라우드, MSA 환경에서는 내부-외부 경계가 모호해짐.
내부에 해커가 들어오면 S-N 통신이 잦은 요즘 너무 쉽게 접근할수 있게 되어서 위험함. 내부망에서도 무조건 인증,인가,추적 필수.
<Never Trust, Always Verify!>
사용자 뿐만 아니라 애플리케이션, 시스템에도 동일하게 적용 (신원확인+최소권한+동적접근)
최소 권한의 원칙 (Principle of Least Privilege) : 인증된 신원이라도 최소 권한, 최소 시간만 부여
그래서, Vault가 필요한 이유?
- 시크릿 스프롤(분산) 문제 해결을 위한 시크릿 중앙 저장소 ⇒ 안전한 저장소에서 암호화하여 중앙 관리
- 동작 인프라 해결을 위한 Dynamic Secrets ⇒ 실시간으로 시크릿을 관리하며 생명 주기 관리
- 제로 트러스트 문제 해결을 위한 Identity-Based Access ⇒ 사람+머신 모두 신원 인증 가능. 인증된 사람,머신만 정책에 따라 시크릿 접근 권한 부여
- 감사 및 통제 문제 해결을 위한 Audit logs ⇒ 감사 로그로 추적 구성
**운영 환경에서 시크릿 관리 어떻게 할지 보안문제 어떻게 해결할지 고민해보는 것이 좋음
Vault 란?
신원 기반 시크릿 및 암호화 관리 시스템
오픈소스, 상용버전 둘다 있음. 오픈소스 사용해도 됨.
먼저, 접근 대상이 신뢰할수 있는 주체인지 확인
ID/PW, Github토큰, K8S SA, AWS IAM 등 다양한 방식을 통해 신원 검증 후 인증 완료되면 토큰 발급
그다음, 인증된 사용자가 어떤 시스템에 접근 해야하는지 명확히 하기
그리고, 접근 시간 제어. TTL(Time To Live) 유효기한을 두고 시간이 지나면 자동으로 만료되는 임시자격 증명(Lease) 발급
마지막으로, 키 생성-갱신-폐기 라이프 사이클 자동화.
동적 시크릿으로 관리하여 사용자가 요청할때 즉시 생성하고 끝나면 즉시 삭제 Just In Time(JIT).
미리 만들어진게 아니고 요청할때 바로 발급.
관리자 개입 없이 시스템이 알아서 키의 수명주기를 관리하여 운영 부담을 줄이고 보안 위험을 줄임.
Vault 기본 구조와 동작 방식 이해
(Vault 동작 = 호텔 체크인)
리셉션 방문
→ 신분증 및 호텔 사이트 예약 정보 확인
→ 3일동안 208호에서 지낼 수 있습니다. (3일 동안 호텔키로 2층만 출입 가능, 식당/휘트니스센터만 출입 가능, 수영장은 불가)
- Vault에서 보면 클라우드에 접근할 수 있는 임시 접근키 발급
- 사용자랑 클라우드 중간에서 키를 대신 발급 및 전달
사람, 애플리케이션(시스템)마다 최적화된 인증 방식
→ 사람 : LDAP, Okta, ...
모든 인증의 목적은 “토 큰 획 득”

Vault 동작 매커니즘

Vault on Kubernetes (KinD, Helm) 배포 실습
- 테스트니까 Dev Mode로 설치
- 기본 개념이나 어떻게 사용하면 되는지 알아보기 위함
- In-memory Storage : 메모리에 올라가있어서 재시작 시 데이터 초기화됨
- 초기 루트 토큰을 “root”로 고정
Vault 배포
cat <<EOF > vault-values-dev.yaml
global:
enabled: true
tlsDisable: true
injector:
enabled: true
# Sidecar Injection을 위해 필요한 설정
server:
dev:
enabled: true
devRootToken: "root" # 학습 편의를 위해 Root Token을 'root'로 고정
# 데이터 영구 저장이 필요 없으므로 비활성화 (Dev모드는 메모리 사용)
dataStorage:
enabled: false
# UI 활성화 및 NodePort 노출
service:
type: "NodePort"
nodePort: 30003
ui:
enabled: true
EOF
(⎈|kind-myk8s:N/A) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# helm upgrade vault hashicorp/vault -n vault -f vault-values-dev.yaml --install
Release "vault" has been upgraded. Happy Helming!
NAME: vault
LAST DEPLOYED: Sat Nov 29 18:47:47 2025
NAMESPACE: vault
STATUS: deployed
REVISION: 3
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
Vault 배포 확인
(⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# kubectl exec -ti vault-0 -- vault status
Key Value
--- -----
Seal Type shamir //샤미르 방식 사용, 다른 도구 사용하면 변경
Initialized true
Sealed false //개발모드라서 false
Total Shares 1
Threshold 1
Version 1.20.4
Build Date 2025-09-23T13:22:38Z
Storage Type inmem
Cluster Name vault-cluster-0c5bbf87
Cluster ID 311acb4e-9146-75fc-2690-55aa30d04fd6
HA Enabled false
** SSS(Shamir Secret Sharing) 방식이란?
암호학자 '아디 샤미르'의 이름에서 따온 이 시크릿 공유 알고리즘은 개인이 특정 시크릿 정보를 보유하지 않고
결합될 때 암호화 된 루트 키를 해제할 수 있는 단일 키로 조합되는 방식이다.
이 알고리즘은 N개의 Unseal Key를 생성(Key Shares)하고,
그 중 M개의 Key를 제공(Key Threshold)하면 이를 토대로 Root Key 획득하여 Unseal 한다.
→ Key Shares: 5, Threshold: 3 인 경우, 관리자가 4명이 키를 제공하면 과반수 이상 참여했으므로 Root Key 제공
실제 관리자 5명한테 직접 분배하는건 legacy 방식이고 번거로움.
클라우드 환경에서는 Cloud에서 제공하는 서비스나, HSM 로 Auto Unseal 구현 가능
Vault CLI 설정
# 1) 필수 패키지 설치
sudo apt-get update
sudo apt-get install -y gpg curl lsb-release
# 2) HashiCorp 공식 APT Repository 등록
# HashiCorp GPG Key 등록
curl -fsSL https://apt.releases.hashicorp.com/gpg \
| sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
# HashiCorp Repo 추가
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
# 3) Vault 설치
sudo apt-get update
sudo apt-get install -y vault
# 4) 설치 확인
vault --version
# 5) Vault 주소 설정 (NodePort 30000 사용)
export VAULT_ADDR='http://172.18.0.2:30003'
# 6) Vault 상태 확인
vault status
# 7) Root Token으로 로그인
vault login
Vault 로그인 (CLI & Web)
(⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# 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 iAlyK7vX9zklmsHLWtlUnlr1
token_duration ∞
token_renewable false
token_policies ["root"]
identity_policies []
policies ["root"]
(⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go# kubectl -n vault port-forward svc/vault 8200:8200

** 원래 root는 초기 셋팅만 한 후 파기시키고 admin 따로 만들어서 관리해야함
[Static Secret] KV 엔진 활성화 (key/value secret storage)
- 유저들이 제일 많이씀
- v2 사용 (v1은 버전 관리 불가, 버전 메타데이터가 없어서 성능이 조~금 더 좋음)
- KV v2 형태로 엔진 활성화하는 명령은 다음과 같지만 Dev 모드에서 활성화 되어서 따로 설정해줄 필요는 없음
- # vault secrets enable -path=secret kv-v2
(⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# vault secrets list
Path Type Accessor Description
---- ---- -------- -----------
cubbyhole/ cubbyhole cubbyhole_999746f9 per-token private secret storage
identity/ identity identity_33fdb9fe identity store
secret/ kv kv_5a48abd5 key/value secret storage
sys/ system system_bfd3a636 system endpoints used for control, policy and debugging
Vault Secret 설정 및 확인
(⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# vault kv put secret/sampleapp/config \
> username="demo" \
> password="p@ssw0rd"
======== Secret Path ========
secret/data/sampleapp/config
======= Metadata =======
Key Value
--- -----
created_time 2025-11-29T10:24:18.087438488Z
custom_metadata <nil>
deletion_time n/a
destroyed false
version 1



Vault Agent 란?
- k8s 환경이 아니면 secret을 어떻게 만들까? ⇒ Vault Agent
- Vault Agent를 sidecar 패턴으로 통합. Pod가 Secret을 Volume으로 mount하여 사용
Vault Agent가 필요한 이유
개발자들이 많이 고민하는 문제 : APP에서 Vault를 어떻게 연동할 것인가?
⇒ DB 접속 비밀번호 같은 보안 정보를 APP 안에서 Vault로 받아와야함.
⇒ Legacy 시스템에서 도입하기 어려울때 사용 (CI/CD 같은 도구들에 연동하여 플랫폼 레벨에서 보안성을 높임)
현재 Vault SDK가 잘되었어서 쉽게 적용할 수 있지만 (APP에서 직접 호출), 동작중인 어플리케이션에는 들이기 어려움
Vault 연동 방법
1.애플리케이션에서 Vault를 직접 호출하는 Direct Integration
⇒ 개발자가 비즈니스 로직 외에도 인증/보안/TTL/파일 관리까지 전부 구현해야 함
⇒ 언어별로 구현 편차가 발생하고, 협업 시 표준화 어려움
⇒ Vault 변경 시(정책·토큰 TTL 등) 개발팀 코드도 수정해야 하는 비효율 발생
2.Vault Agent (sidecar 패턴)
⇒ Agent가 대신 인증 → 토큰 발급/갱신 → 시크릿 렌더링 → 파일 생성(+명령 실행)까지 자동 처리
⇒ 자동 로그인, 토큰 자동 갱신, 템플린 렌더링 다 해주고 APP은 파일만 읽으면 끝
즉, Vault Agent를 사용하면 개발자는 Vault API를 호출할 필요가 없어지고, 보안·토큰·시크릿 갱신 문제는 전부 플랫폼 표준으로 해결할 수 있다.
Vault Agent vs Vault Proxy
- Vault Agent - Client Side 초점. 애플리케이션에 필요한 파일(Config) 생성 및 토큰 관리.
- Vault Proxy - Network Side 초점. Vault API 호출 시 중계(Gateway) 역할 및 캐싱 수행.
- 앱이 HTTP 요청을 보내면, 이를 받아서 Vault 서버로 전달하는 기능. (Agent의 Proxy 기능은 Proxy 모드로 이관됨)
- host server 외부, 내부(socket) 모두 구성 가능
Vault Agent Injector
- Kubernetes Pod 내부에 Vault Agent 자동으로 주입 (특정 annotation이 있는 Pod만)
- Kubernetes Auth Method를 활성화
- 특정 서비스 어카운트에 Vault 접근 권한 부여 (Vault에 접근할 수 있도록 적절한 Policy와 Role 설정 필수)
Vault Agent 실습
Vault AppRole 방식 사용
- LDAP, Keycloak 같은 도구들 쓸 역량이 안되거나 도입이 어려울때 주로 사용
- username/password 쓰는 것과 유사
- 애플리케이션 친화적, Vault 내부에 임베딩된 체계
- AppRole 활성화 → 정책 부여 → Role ID, Secret ID 확인 → Secret 생성
AppRole 인증 방식 활성화
(⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# vault auth list
Path Type Accessor Description Version
---- ---- -------- ----------- -------
token/ token auth_token_91205d52 token based credentials n/a
(⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# vault auth enable approle || echo "AppRole already enabled"
Success! Enabled approle auth method at: approle/
(⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# vault auth list
Path Type Accessor Description Version
---- ---- -------- ----------- -------
approle/ approle auth_approle_b98f2e41 n/a n/a
token/ token auth_token_91205d52 token based credentials n/a
정책 생성
- 모든 Key-Value Secret을 다 읽을 수 있는 권한 부여
vault policy write sampleapp-policy - <<EOF
path "secret/data/sampleapp/*" {
capabilities = ["read"]
}
EOF
AppRole에 Role 부여 및 Role ID(=user), Secret ID(=password) 확인
(⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# vault write auth/approle/role/sampleapp-role \
> token_policies="sampleapp-policy" \
> secret_id_ttl="1h" \
> token_ttl="1h" \
> token_max_ttl="4h"
Success! Data written to: auth/approle/role/sampleapp-role
(⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# ROLE_ID=$(vault read -field=role_id auth/approle/role/sampleapp-role/role-id)
(⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# SECRET_ID=$(vault write -f -field=secret_id auth/approle/role/sampleapp-role/secret-id)
(⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# echo "ROLE_ID: $ROLE_ID"
ROLE_ID: 33df0592-6771-3773-8e8b-db15c73ae45b
(⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# echo "SECRET_ID: $SECRET_ID"
SECRET_ID: 004911bf-1bbb-2718-d604-2e78f88763b2
Role ID(=user), Secret ID(=password) 기반 파일 저장 및 Secret 생성
mkdir -p approle-creds
echo "$ROLE_ID" > approle-creds/role_id.txt
echo "$SECRET_ID" > approle-creds/secret_id.txt
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 연동 실습 - sidecar 수동 생성
- Vault Agent는 vault-agent-config.hcl 설정을 통해, Vault의 정보, Template 구성, 렌더링 주기, 참조할 Vault KV 위치정보 등을 정의
- vault-agent-config.hcl 파일 작성 (hcl = HashiCorp Configuration Language)
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 - nginx 샘플 APP에 value-agent-sidecar 같이 띄움 - sidecar를 원래 annotation으로 자동 생성하는데 지금은 수동 생성
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 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 - 배포 확인
[wsl에서 image pull 안될때 수동으로 넣는 법]
docker pull nginx:1.25
docker save nginx:1.25 -o nginx.tar
docker cp nginx.tar myk8s-control-plane:/nginx.tar
docker exec -it myk8s-control-plane ctr --namespace=k8s.io images import /nginx.tar
(⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# kubectl get mutatingwebhookconfigurations.admissionregistration.k8s.io NAME WEBHOOKS AGE vault-agent-injector-cfg 1 141m (⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# 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 For a full list of examples, please see the documentation. (⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# kubectl exec -it -n vault deploy/nginx-vault-demo -c vault-agent-sidecar -- cat /etc/vault/approle/secret_id ca1db9ed-0f2c-44f5-4e98-5244bf694eca (⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# 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> - KV 값 변경 후 확인
(⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# 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: newp@ssw0rd</p> </body> </html>


Vault Agent Sidecar 연동 실습 - sidecar annotation으로 자동 생성
- Kubernetes Auth Method 활성화
- Kubernetes Auth Config 설정
- Role 생성 (Injector 로그인)
(⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# vault auth enable kubernetes Success! Enabled kubernetes auth method at: kubernetes/ (⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# vault auth list Path Type Accessor Description Version ---- ---- -------- ----------- ------- approle/ approle auth_approle_47aa6709 n/a n/a kubernetes/ kubernetes auth_kubernetes_0411d7e4 n/a n/a token/ token auth_token_83e86544 token based credentials n/a (⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# TOKEN=$(kubectl create token vault -n vault) _CERT=$(kubectl config view --raw --minify --flatten -o jsonpath='{.clusters[0].cluster.certificate-authority-data}' | base64 --decode)(⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# CA_CERT=$(kubectl config view --raw --minify --flatten -o jsonpath='{.clusters[0].cluster.certificate-authority-data}' | base64 --decode) (⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# 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 Success! Data written to: auth/kubernetes/config (⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# vault write auth/kubernetes/role/sampleapp-role \ > bound_service_account_names="vault-ui-sa" \ > bound_service_account_namespaces="vault" \ > policies="sampleapp-policy" \ > ttl="24h" \ https://kuberne> audience="https://kubernetes.default.svc.cluster.local" Success! Data written to: auth/kubernetes/role/sampleapp-role (⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# vault policy write sampleapp-policy - <<EOF > path "secret/data/sampleapp/*" { > capabilities = ["read"] > } > EOF Success! Uploaded policy: sampleapp-policy (⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# cat <<EOF | kubectl apply -f - > apiVersion: v1 eAccoun> kind: ServiceAccount > metadata: > name: vault-ui-sa > namespace: vault > --- > apiVersion: apps/v1 > kind: Deployment name: vault> 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" va> 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 }}", "pass> "password": "{{ .Data.data.password }}" > } > {{- end }} > vault.hashicorp.com/agent-inject-output-path: "/vault/secrets" > spec: viceAc> serviceAccountName: vault-ui-sa > containers: > - name: app > image: python:3.10 > ports: - conta> - 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> 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 dePort:> nodePort: 30002 > selector: > app: vault-injected-ui > EOF serviceaccount/vault-ui-sa created deployment.apps/vault-injected-ui created service/vault-injected-ui created - 생성된 Pod, Deploy 확인
- Deploy 생성 시 annotation 명시해주면 Init Container(vault agent) 자동 생성
(⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# k describe pod vault-injected-ui Name: vault-injected-ui-77fb865789-vhwjx Namespace: vault Priority: 0 Service Account: vault-ui-sa Node: myk8s-control-plane/172.18.0.2 Start Time: Sat, 29 Nov 2025 22:04:00 +0900 Labels: app=vault-injected-ui pod-template-hash=77fb865789 Annotations: vault.hashicorp.com/agent-inject: true vault.hashicorp.com/agent-inject-output-path: /vault/secrets vault.hashicorp.com/agent-inject-secret-config.json: secret/data/sampleapp/config vault.hashicorp.com/agent-inject-status: injected 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/role: sampleapp-role Status: Running IP: 10.244.0.22 IPs: IP: 10.244.0.22 Controlled By: ReplicaSet/vault-injected-ui-77fb865789 Init Containers: vault-agent-init: Container ID: containerd://97786b1146759873ab4c1bbda14a67953c48bc90864f800cda12602ff98831a7 Image: hashicorp/vault:1.20.4 Image ID: sha256:3a2f0470834d7d8ebfcbe58f3962053a7aaf59066ab9a0b9fe7dd8e6fa822207 Port: <none> Host Port: <none> Command: /bin/sh -ec Args: echo ${VAULT_CONFIG?} | base64 -d > /home/vault/config.json && vault agent -config=/home/vault/config.json State: Terminated Reason: Completed Exit Code: 0 Started: Sat, 29 Nov 2025 22:04:01 +0900 Finished: Sat, 29 Nov 2025 22:04:01 +0900 Ready: True Restart Count: 0 Limits: cpu: 500m memory: 128Mi Requests: cpu: 250m memory: 64Mi Environment: NAMESPACE: vault (v1:metadata.namespace) HOST_IP: (v1:status.hostIP) POD_IP: (v1:status.podIP) VAULT_LOG_LEVEL: info VAULT_LOG_FORMAT: standard VAULT_CONFIG: eyJhdXRvX2F1dGgiOnsibWV0aG9kIjp7InR5cGUiOiJrdWJlcm5ldGVzIiwibW91bnRfcGF0aCI6ImF1dGgva3ViZXJuZXRlcyIsImNvbmZpZyI6eyJyb2xlIjoic2FtcGxlYXBwLXJvbGUiLCJ0b2tlbl9wYXRoIjoiL3Zhci9ydW4vc2VjcmV0cy9rdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3Rva2VuIn19LCJzaW5rIjpbeyJ0eXBlIjoiZmlsZSIsImNvbmZpZyI6eyJwYXRoIjoiL2hvbWUvdmF1bHQvLnZhdWx0LXRva2VuIn19XX0sImV4aXRfYWZ0ZXJfYXV0aCI6dHJ1ZSwicGlkX2ZpbGUiOiIvaG9tZS92YXVsdC8ucGlkIiwidmF1bHQiOnsiYWRkcmVzcyI6Imh0dHA6Ly92YXVsdC52YXVsdC5zdmM6ODIwMCJ9LCJ0ZW1wbGF0ZSI6W3siZGVzdGluYXRpb24iOiIvdmF1bHQvc2VjcmV0cy9jb25maWcuanNvbiIsImNvbnRlbnRzIjoie3stIHdpdGggc2VjcmV0IFwic2VjcmV0L2RhdGEvc2FtcGxlYXBwL2NvbmZpZ1wiIC19fVxue1xuICBcInVzZXJuYW1lXCI6IFwie3sgLkRhdGEuZGF0YS51c2VybmFtZSB9fVwiLFxuICBcInBhc3N3b3JkXCI6IFwie3sgLkRhdGEuZGF0YS5wYXNzd29yZCB9fVwiXG59XG57ey0gZW5kIH19XG4iLCJsZWZ0X2RlbGltaXRlciI6Int7IiwicmlnaHRfZGVsaW1pdGVyIjoifX0ifV0sInRlbXBsYXRlX2NvbmZpZyI6eyJleGl0X29uX3JldHJ5X2ZhaWx1cmUiOnRydWV9fQ== Mounts: /home/vault from home-init (rw) /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-d9n87 (ro) /vault/secrets from vault-secrets (rw) Containers: app: Container ID: containerd://f5155d060adced0ba488fd3d9f3a53843b7b1d1d268e3bb2a63b2e8897c6872e Image: python:3.10 Image ID: sha256:19b9e42cee4403dabd9def165e11e169a0d4a0470cd726bd01f2270cf792f997 Port: 5000/TCP Host Port: 0/TCP 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 State: Waiting Reason: CrashLoopBackOff Last State: Terminated Reason: Error Exit Code: 2 Started: Sat, 29 Nov 2025 22:12:25 +0900 Finished: Sat, 29 Nov 2025 22:13:02 +0900 Ready: False Restart Count: 3 Environment: <none> Mounts: /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-d9n87 (ro) /vault/secrets from vault-secrets (rw) vault-agent: Container ID: containerd://94af832d355095913c7f579b97dbf379992f5f2347aa675c439469f96df4204c Image: hashicorp/vault:1.20.4 Image ID: sha256:3a2f0470834d7d8ebfcbe58f3962053a7aaf59066ab9a0b9fe7dd8e6fa822207 Port: <none> Host Port: <none> Command: /bin/sh -ec Args: echo ${VAULT_CONFIG?} | base64 -d > /home/vault/config.json && vault agent -config=/home/vault/config.json State: Running Started: Sat, 29 Nov 2025 22:04:09 +0900 Ready: True Restart Count: 0 Limits: cpu: 500m memory: 128Mi Requests: cpu: 250m memory: 64Mi Environment: NAMESPACE: vault (v1:metadata.namespace) HOST_IP: (v1:status.hostIP) POD_IP: (v1:status.podIP) VAULT_LOG_LEVEL: info VAULT_LOG_FORMAT: standard VAULT_CONFIG: eyJhdXRvX2F1dGgiOnsibWV0aG9kIjp7InR5cGUiOiJrdWJlcm5ldGVzIiwibW91bnRfcGF0aCI6ImF1dGgva3ViZXJuZXRlcyIsImNvbmZpZyI6eyJyb2xlIjoic2FtcGxlYXBwLXJvbGUiLCJ0b2tlbl9wYXRoIjoiL3Zhci9ydW4vc2VjcmV0cy9rdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3Rva2VuIn19LCJzaW5rIjpbeyJ0eXBlIjoiZmlsZSIsImNvbmZpZyI6eyJwYXRoIjoiL2hvbWUvdmF1bHQvLnZhdWx0LXRva2VuIn19XX0sImV4aXRfYWZ0ZXJfYXV0aCI6ZmFsc2UsInBpZF9maWxlIjoiL2hvbWUvdmF1bHQvLnBpZCIsInZhdWx0Ijp7ImFkZHJlc3MiOiJodHRwOi8vdmF1bHQudmF1bHQuc3ZjOjgyMDAifSwidGVtcGxhdGUiOlt7ImRlc3RpbmF0aW9uIjoiL3ZhdWx0L3NlY3JldHMvY29uZmlnLmpzb24iLCJjb250ZW50cyI6Int7LSB3aXRoIHNlY3JldCBcInNlY3JldC9kYXRhL3NhbXBsZWFwcC9jb25maWdcIiAtfX1cbntcbiAgXCJ1c2VybmFtZVwiOiBcInt7IC5EYXRhLmRhdGEudXNlcm5hbWUgfX1cIixcbiAgXCJwYXNzd29yZFwiOiBcInt7IC5EYXRhLmRhdGEucGFzc3dvcmQgfX1cIlxufVxue3stIGVuZCB9fVxuIiwibGVmdF9kZWxpbWl0ZXIiOiJ7eyIsInJpZ2h0X2RlbGltaXRlciI6In19In1dLCJ0ZW1wbGF0ZV9jb25maWciOnsiZXhpdF9vbl9yZXRyeV9mYWlsdXJlIjp0cnVlfX0= Mounts: /home/vault from home-sidecar (rw) /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-d9n87 (ro) /vault/secrets from vault-secrets (rw) Conditions: Type Status PodReadyToStartContainers True Initialized True Ready False ContainersReady False PodScheduled True Volumes: kube-api-access-d9n87: Type: Projected (a volume that contains injected data from multiple sources) TokenExpirationSeconds: 3607 ConfigMapName: kube-root-ca.crt Optional: false DownwardAPI: true home-init: Type: EmptyDir (a temporary directory that shares a pod's lifetime) Medium: Memory SizeLimit: <unset> home-sidecar: Type: EmptyDir (a temporary directory that shares a pod's lifetime) Medium: Memory SizeLimit: <unset> vault-secrets: Type: EmptyDir (a temporary directory that shares a pod's lifetime) Medium: Memory SizeLimit: <unset> QoS Class: Burstable Node-Selectors: <none> Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s node.kubernetes.io/unreachable:NoExecute op=Exists for 300s Events:
(⎈|kind-myk8s:vault) root@DESKTOP-HUU6SC7:/mnt/e/vscode_go/vault# k describe deploy vault-injected-ui Name: vault-injected-ui Namespace: vault CreationTimestamp: Sat, 29 Nov 2025 22:04:00 +0900 Labels: <none> Annotations: deployment.kubernetes.io/revision: 1 Selector: app=vault-injected-ui Replicas: 1 desired | 1 updated | 1 total | 0 available | 1 unavailable StrategyType: RollingUpdate MinReadySeconds: 0 RollingUpdateStrategy: 25% max unavailable, 25% max surge Pod Template: Labels: app=vault-injected-ui Annotations: vault.hashicorp.com/agent-inject: true vault.hashicorp.com/agent-inject-output-path: /vault/secrets 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/role: sampleapp-role Service Account: vault-ui-sa Containers: app: Image: python:3.10 Port: 5000/TCP Host Port: 0/TCP 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 Environment: <none> Mounts: <none> Volumes: <none> Node-Selectors: <none> Tolerations: <none> Conditions: Type Status Reason ---- ------ ------ Progressing True NewReplicaSetAvailable Available False MinimumReplicasUnavailable OldReplicaSets: <none> NewReplicaSet: vault-injected-ui-77fb865789 (1/1 replicas created) Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal ScalingReplicaSet 10m deployment-controller Scaled up replica set vault-injected-ui-77fb865789 from 0 to
'CI&CD' 카테고리의 다른 글
| [Vault] VSO(Vault Secrets Operator)로 Kubernetes Secret 안전하게 관리하기 (0) | 2025.12.13 |
|---|---|
| [CI/CD] OpenLDAP + KeyCloak + Argo CD + Jenkins 구축 과정 (1) | 2025.11.23 |
| [ArgoCD] Vault 플러그인으로 인증 관리하기 (0) | 2025.11.11 |
| [ArgoCD] HA 구성으로 안정적인 배포 자동화 구현 (0) | 2025.11.06 |
| [CI/CD] Tekton + ArgoCD로 만드는 CI/CD 파이프라인 (1) | 2025.11.01 |