스터디(Study)/CI·CD Study

HashiCorp Vault로 시작하는 현대 시크릿 관리

Chann._.y 2025. 11. 29.
728x90

 

가시다님께서 진행하신 CI/CD 스터디 추가 실습 내용을 기반으로 작성했다.
CI/CD 스터디에서 HashiCorp Vault를 이용해 시크릿 관리와 Kubernetes 연동을 실습했다.

이 글에서는 그때 정리한 내용을 바탕으로 다음 내용을 한 번에 훑어본다.
  • 왜 이제는 “시크릿 관리 도구”가 필수인지
  • HashiCorp Vault가 무엇을 해결해 주는지
  • Kind 기반 Kubernetes에 Vault를 설치하는 방법
  • KV 시크릿 엔진으로 정적 시크릿을 저장하는 기본 흐름
  • Vault Agent + Sidecar 패턴으로 애플리케이션과 Vault를 자연스럽게 붙이는 방법

1. 시크릿과 현대 인프라: 왜 이제는 그냥 .env로는 안 되는가

1-1. 시크릿(Secret)이란?

시크릿은 “노출되면 안 되는 모든 정보”라고 보면 된다. 대표적인 것만 정리해도 이 정도다.

  • 사용자/시스템 자격 증명
    • 비밀번호, SSH Key, DB 계정(ID/PW)
  • 서비스 연동 및 자동화 키
    • AWS/GCP/Azure Credential, GitHub Token, Slack/Webhook Token, OpenAI API Key 등
  • 보안 통신 및 암호화 자산
    • TLS 인증서, 개인 키, 데이터 암호화 키

과거에는 이런 시크릿이 많지 않았고, 변경 주기도 길었다.
하지만 클라우드, Kubernetes, MSA로 넘어오면서 상황이 완전히 바뀌었다.

1-2. 아키텍처 변화가 만든 시크릿 지옥

아키텍처를 아주 거칠게 요약하면 이렇게 진화해 왔다.

  1. 메인프레임 / 모놀리식
    • 한두 개의 큰 시스템 안에서 모든 게 돌아감
    • 관리해야 할 시크릿이 적고, 위치도 명확함
  2. 3-Tier / 웹 + 앱 + DB
    • Web / WAS / DB로 나뉘지만, 여전히 인프라는 “정적”
  3. 클라우드 / Kubernetes / MSA / DevOps 시대
    • 수십~수백 개의 마이크로서비스
    • 컨테이너가 계속 생성/삭제되는 동적 환경
    • AWS, GCP, 외부 SaaS, 각종 API까지 전부 자격 증명이 필요

결과적으로:

  • 시크릿 개수가 폭발적으로 증가
  • 컨테이너/VM이 수시로 생겼다가 사라지니, 시크릿도 동적으로 관리해야 함
  • Git, 설정 파일, CI/CD 도구, 개발자 PC 등 여기저기 흩어짐

이 현상을 HashiCorp는 “Secret Sprawl(시크릿 스프롤)”이라고 부른다.
한마디로, 여기저기 퍼져 있는 시크릿 때문에 누가 뭘 쓰는지 아무도 모르는 상태다.

1-3. Zero Trust와 시크릿

요즘 보안 키워드 중 하나가 Zero Trust(제로 트러스트)다.

  • 과거: “내부망이면 어느 정도는 믿자” (성·해자 모델)
  • 지금: “내부/외부 구분 의미 없다. 누구든, 무엇이든 항상 검증”

시크릿 관점에서 보면:

  • “내부망에 있는 서비스니까 DB 접근해도 되겠지” 같은 발상은 이제 위험하다.
  • 대신
    • 저 서비스가 정말 누구인지(Identity)
    • 어떤 리소스에(Permission)
    • 얼마 동안(TTL)
      만 접근할지, 명시적으로 통제해야 한다.

이걸 제대로 하기 위해서는, 시크릿을 한 곳에서 통합 관리하고
“누가, 무엇을, 얼마나, 어떻게” 썼는지 추적 가능한 시스템이 필요하다.
그게 바로 HashiCorp Vault 같은 도구다.


2. HashiCorp Vault 한눈에 보기

Vault는 HashiCorp에서 만든 신원 기반(Identity-based) 시크릿 및 암호화 관리 시스템이다.

핵심 키워드만 정리하면 다음 네 가지다.

  1. 시크릿 중앙 저장소
    • 토큰, API Key, 비밀번호, 인증서, 암호화 키 등 모든 시크릿을 중앙에서 암호화 저장
    • 애플리케이션 코드나 설정 파일에 하드코딩할 필요가 없어짐
  2. 동적 시크릿(Dynamic Secrets)
    • 요청이 올 때마다 DB 계정이나 클라우드 Credential을 즉시 생성
    • TTL이 지나면 자동 폐기 → 유출돼도 피해를 줄일 수 있음
  3. 신원 기반 접근 제어(Identity-based Access)
    • 사람(개발자), 애플리케이션, 시스템 모두 “신원(Identity)”로 구분
    • LDAP, OIDC, Kubernetes, AWS IAM 등으로 인증 후 Policy로 권한 제어
  4. 감사 로그(Audit)
    • 누가, 언제, 어떤 시크릿을 조회/수정했는지 상세 기록
    • 컴플라이언스/보안 감사 대응에 유리

Vault의 동작 흐름을 단순히 요약하면:

  1. 클라이언트(사람/앱)가 인증(Auth Method)으로 로그인
  2. Vault가 외부 IdP/플랫폼을 통해 신원 검증
  3. 해당 신원에 매핑된 Policy를 확인해 권한을 판별
  4. 토큰(Token)을 발급하고, 이 토큰으로 시크릿/암호화 기능을 사용

3. 실습 환경: Kind 기반 Kubernetes에 Vault 설치

여기서는 로컬에서 쉽게 테스트할 수 있도록 Kind + Helm으로 Vault를 올리는 과정을 정리한다.

3-1. Kind 클러스터 준비

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
    hostPort: 30002
  - containerPort: 30003 # Sample App
    hostPort: 30003
EOF

3-2. Dev Mode로 Vault 설치 (Helm)

학습용으로는 Dev Mode가 편하다.

특징:

  • In-memory storage → 재시작 시 데이터 초기화
  • Auto Unseal → 바로 Unseal 상태로 올라옴
  • Root token을 고정 값으로 지정 가능

네임스페이스와 Helm Repo 설정:

kubectl create namespace vault
helm repo add hashicorp https://helm.releases.hashicorp.com
helm search repo hashicorp/vault

Dev 용 values 파일:

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

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

server:
  dev:
    enabled: true
    devRootToken: "root"  # 학습용 고정 Root Token

  dataStorage:
    enabled: false  # Dev 모드는 in-memory

  service:
    type: "NodePort"
    nodePort: 30000

  ui:
    enabled: true
EOF

설치:

helm upgrade vault hashicorp/vault -n vault -f vault-values-dev.yaml --install
kubens vault
kubectl get pods,svc

Vault 상태 확인:

kubectl exec -ti vault-0 -- vault status

Dev 모드라 Initialized=true, Sealed=false로 바로 뜬다.

3-3. CLI 설정 및 로그인

Mac 기준:

brew tap hashicorp/tap
brew install hashicorp/tap/vault
vault --version

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

vault status
vault login   # Token: root

Vault UI는 http://localhost:30000 으로 접속해서 Root Token으로 로그인하면 된다.


4. KV 시크릿 엔진으로 정적 시크릿 관리하기

Vault의 가장 기본적인 사용법은 KV(키/값) 시크릿 엔진에 시크릿을 저장하는 것이다.
Dev 모드에서는 기본적으로 secret/ 경로에 KV 엔진이 하나 올라가 있다.

4-1. 시크릿 저장

현재 활성화된 시크릿 엔진:

vault secrets list

Path          Type
----          ----
cubbyhole/    cubbyhole
identity/     identity
secret/       kv
sys/          system

샘플 시크릿 저장:

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

조회:

vault kv get secret/sampleapp/config

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

UI에서도 Secrets → secret → sampleapp/config 로 들어가면 같은 값을 확인할 수 있다.

4-2. HTTP API로 읽어보기

동일 데이터를 API로도 가져올 수 있다.

curl -s \
  --header "X-Vault-Token: root" \
  --request GET \
  http://127.0.0.1:30000/v1/secret/data/sampleapp/config | jq

응답 구조를 보면 data.data 아래에 우리가 넣은 username/password가 들어 있다.
이 경로(/v1/secret/data/...)와 구조를 이해하면, 애플리케이션에서 직접 API를 호출해 시크릿을 가져오는 것도 가능하다.


5. Vault Agent와 Sidecar 패턴

Vault를 도입하면 거의 바로 부딪히는 문제가 하나 있다.

“앱에서 Vault를 어떻게 붙일까?”

5-1. 직접 연동 방식의 문제

애플리케이션이 Vault SDK를 직접 사용하면 다음을 전부 구현해야 한다.

  • Kubernetes, AWS, AppRole 등 Auth Method 별 로그인 로직
  • 발급받은 토큰의 TTL 관리, 자동 갱신(Renew), 재로그인 처리
  • Vault 장애/네트워크 에러 시 재시도 및 백오프
  • Vault에서 가져온 JSON을 파싱해서 파일/환경변수에 반영
  • 시크릿 rotation 시 재반영 및 앱 재기동/재로드 처리

※ 운영팀 관점에서 보면, 이 로직을 각 언어/각 서비스마다 개발팀에 맡기는 건 현실적으로 위험하다는 의견이 많다.

5-2. Vault Agent가 해결해 주는 것

Vault Agent는 클라이언트 옆(Sidecar)에서 돌면서 다음을 대신 해준다.

  • Auto-Auth
    • Kubernetes Service Account, AWS IAM, AppRole 등으로 자동 로그인
  • Token Lifecycle 관리
    • 토큰 TTL 체크 → 자동 갱신 → 실패 시 재로그인까지 처리
  • 템플릿 렌더링(Consul Template 내장)
    • Vault에서 가져온 시크릿을 .env, config.yaml, tls.crt 같은 파일로 렌더링
    • 시크릿이 갱신되면 파일도 자동 업데이트

애플리케이션은 단순히:

  • “/config/app.env 파일을 읽는다”
  • “/etc/tls/tls.crt, tls.key를 읽어서 TLS 설정한다”

정도만 하면 된다. Vault API를 직접 몰라도 된다.

5-3. Kubernetes에서의 Sidecar 패턴

Kubernetes에서는 Vault Agent를 Pod 안에 Sidecar 컨테이너로 함께 배포한다.

  • Vault Agent Injector(웹훅)가 Pod 정의를 가로채서
    • Sidecar 컨테이너 추가
    • 시크릿이 마운트될 공유 Volume 추가
    • Auto-Auth 설정 추가
  • 앱 컨테이너는 마운트된 파일만 읽어서 애플리케이션 설정에 사용

이 패턴을 쓰면:

  • 개발자는 “시크릿이 어디서 왔는지” 몰라도 되고
  • 플랫폼 팀은 Vault Policy, TTL, Rotation 전략을 중앙에서 통제할 수 있고
  • 언어에 따라 구현이 제각각 갈라지지 않는다.

6. 마치며: Vault를 도입할 때 생각해 볼 포인트

※ 개인적으로 Vault는 “크고 복잡한 환경에서 반드시 필요한 인프라 보안 레이어”라고 본다.
단, 처음부터 너무 많은 것을 한 번에 하려 하면 팀이 지치기 때문에, 단계적으로 가져가는 게 현실적이다.

예를 들면 이런 식의 단계적 접근을 생각해 볼 수 있다.

  1. Git/설정 파일에 하드코딩된 시크릿을 Vault(KV 엔진)로 옮기기
  2. 사람이 쓰는 정적 자격 증명부터 Vault로 통합
  3. Kubernetes, CI/CD(Jenkins, GitHub Actions 등)와 Vault 연동
  4. DB 계정, 클라우드 Credential을 동적 시크릿으로 전환
  5. Audit Log, Policy 고도화, Zero Trust 관점의 접근 제어 정교화

이 글에서 다룬 내용은 그 중에서도:

  • Vault가 왜 필요한지에 대한 개념
  • 로컬 Kubernetes에 Vault를 올리는 기본 실습
  • KV 시크릿 엔진으로 시크릿을 중앙 관리하는 맛보기
  • Vault Agent + Sidecar 패턴으로 애플리케이션 연동의 방향성

까지를 정리한 것이다.
추가로 Dynamic Secrets, PKI 엔진, Transit 엔진(암호화 서비스) 등으로 확장하면
실제 프로덕션 환경에서도 꽤 강력한 시크릿/암호화 플랫폼을 만들 수 있다.

728x90

댓글