Ssoon

HashiCorp Vault - 암호화(Encryption)와 Vault Transit 엔진 본문

CICD Study [1기]

HashiCorp Vault - 암호화(Encryption)와 Vault Transit 엔진

구구달스 2025. 11. 27. 20:39

🧩 Transit Secrets Engine이란

  • Vault는 여러 종류의 Secrets Engine을 제공하며 그 중 하나가 Transit입니다.
  • Transit Secrets Engine은 “저장(persistent storage)”을 목적으로 하는 것이 아니라, 암호화(encrypt), 복호화(decrypt), 서명(sign), 검증(verify), 키 파생(key derivation) 같은 암호화 관련 작업을 수행하기 위한 서비스입니다. 
  • Transit 엔진을 사용하면 Vault 내부에 저장된 키(named key)를 이용해 클라이언트가 보낸 데이터를 암호화하거나, 암호화된 데이터를 복호화할 수 있습니다. 이때 Vault는 키를 직접 노출하지 않고, 암호화 연산만 수행합니다. 
  • 애플리케이션은 민감 데이터를 Vault에 보내 “암호화된 결과”만 돌려받고, 실제 키는 Vault 내부에 안전하게 보관되는 구조입니다. 

"Transit Secrets Engine은 Vault 내부 키를 이용해 암호화·복호화 같은 암호화 연산을 수행하는 서비스이며,
실제 키는 결코 외부에 노출되지 않는다." 


🔄 암호화 방식: 두 가지 주요 모드

Transit Secrets Engine은 크게 두 가지 방식으로 동작합니다. 사용 목적에 따라 적절한 모드를 선택할 수 있습니다. 

• Encryption-as-a-Service (직접 암호화/복호화)

  • 애플리케이션이 문(plaintext) 데이터를 Vault에 보내고, Vault는 이를 지정된 named key로 암호화하여 암호문(ciphertext)을 반환합니다.
  • 반대로 암호문을 보내면, Vault는 같은 key로 복호화를 수행해 평문을 반환할 수 있습니다.
  • 이 방식은 클라이언트가 소규모 데이터(예: 사용자 정보, API 키, 작은 JSON 문서 등)를 암호화해야 할 때 유용합니다.
  • 이 방식의 장점은 단순성 — 개발자가 로컬 암호화 로직을 구현할 필요 없이 Vault API로 암호화/복호화가 가능하다는 점입니다.

"Encryption-as-a-Service 모드는 애플리케이션이 Vault에 데이터를 보내 암호화 또는 복호화를 요청하고,
결과만 받아오는 가장 직접적인 암호화 방식이다." 

• Datakey 생성 모드 (Envelope Encryption 패턴)

  • 대용량 데이터(예: 파일, 블록 스토리지, 로그, 사용자 업로드 파일 등)를 암호화할 때는 Vault 내부에서 직접 암호화하기보다는 Envelope Encryption 패턴을 사용하는 것이 효율적입니다.
  • 이 패턴에서는 Vault가 임의의 데이터 암호화 키(Data Encryption Key, DEK) 를 생성하고, 그 DEK를 named key로 암호화(즉, 키를 보호)한 후, 암호화된 DEK와 평문 DEK를 클라이언트에게 반환합니다. 
  • 클라이언트는 평문 DEK를 사용해 로컬에서 데이터를 암호화하고, 암호화된 데이터와 암호화된 DEK를 함께 저장합니다. 나중에 복호화할 때는 암호화된 DEK를 Vault로 보내 복호화된 DEK를 얻고, 이를 사용해서 데이터를 복호화합니다. 
  • 이 방식의 장점:
    • Vault에 대용량 데이터를 보내지 않아 네트워크/성능 부담이 적음
    • DEK는 일회성 또는 단명성으로 사용 가능 — DEK가 유출되어도 노출된 데이터에만 영향을 줌
    • 키의 수명 관리(key rotation)와 관리를 중앙에서 Vault가 담당

"Datakey 생성 모드는 Envelope Encryption 패턴으로, Vault는 DEK를 생성·암호화해 주고,
클라이언트는 로컬에서 대용량 데이터를 암호화함으로써 효율성과 보안을 동시에 확보한다." 


🔐 지원하는 암호화 알고리즘과 유연성

  • Transit 엔진은 여러 암호화 및 암호화 관련 알고리즘을 지원합니다. 예: AES-GCM, ChaCha20-Poly1305, RSA (비대칭 암호), ECDSA, Ed25519 등. 
  • 또한 signing(서명)과 verification(검증) 기능도 제공하므로, 단순 암호화/복호화 뿐 아니라 데이터 무결성 보장, 인증, 서명 기반 워크플로우에도 활용할 수 있습니다. 
  • Transit은 key versioning(키 버전 관리)도 지원합니다. 키를 갱신하면 새 버전이 생성되며, 이전 버전으로 암호화된 데이터는 여전히 복호화할 수 있도록 관리됩니다. 

"Transit 엔진은 AES-GCM, RSA, ChaCha20-Poly1305 같은 다양한 암호화 알고리즘과 signing 기능을 제공하며,
key versioning도 지원한다." 


✅ Vault + Transit 사용의 장점

  • 개발자 편의성 — 암호화 구현, 키 관리, 버전 관리, 키 회전 등을 Vault가 대신 처리하므로 애플리케이션 코드는 단순해집니다.
  • 보안 중심 설계 — 키가 절대 외부에 노출되지 않으며, 암호화 연산은 검증된 구현을 통해 수행됩니다.
  • 키 수명 및 회전 관리 — key versioning, key rotation, access control, audit logging 등의 기능을 통해 키의 전체 라이프사이클을 체계적으로 관리할 수 있습니다.
  • 유연성 — 단순한 암호화/복호화부터 envelope encryption, signing, verification, 대용량 데이터 암호화까지 다양한 워크플로우를 지원합니다.
  • 감사 가능성(auditability) — Vault의 로깅 및 감사 기능을 통해 누가 언제 어떤 암호화/복호화 요청을 했는지 기록할 수 있어, 규제 준수 및 보안 사고 대응이 용이합니다.

"Vault + Transit을 사용하면 암호화의 복잡성과 위험을 Vault로 위임하고,
안전하고 유연한 암호화 서비스를 애플리케이션에 제공할 수 있다." 


⚠️ 주의사항 및 한계

  • Transit은 데이터를 저장하지 않습니다 — 암호화/복호화 또는 key wrapping/unwrapping만 제공하는 서비스입니다. 따라서 저장된 암호문(ciphertext)은 별도의 저장소에 보관해야 합니다. 
  • 암호화된 데이터를 복호화하거나 DEK를 얻기 위해서는 Vault에 접근 가능한 권한(policy)이 필요합니다. 잘못된 권한 설정은 보안 취약점이 될 수 있습니다.
  • Transit key 자체를 관리하는 Vault 서버의 보안이 매우 중요합니다. 만약 Vault가 compromised 된다면, 암호화된 데이터의 보안이 위협받을 수 있습니다.
  • 암호화/복호화에 네트워크 호출이 필요하므로, 암호화 요청이 매우 빈번하거나 대용량 데이터가 많다면 latency 또는 성능 이슈가 발생할 수 있습니다 — 이런 경우 envelope encryption 패턴을 쓰는 것이 일반적입니다.

"Transit은 데이터 저장소가 아니며, 암호화 연산만 제공하므로 저장과 권한,
키 관리에 대한 책임은 여전히 중요하다."


사전준비: 환경변수 설정

  • 환경변수 설정
(⎈|kind-myk8s:vault) ssoon@DESKTOP-72C919S:~$ export NS=vault-demo
(⎈|kind-myk8s:vault) ssoon@DESKTOP-72C919S:~$ export IMAGE=hyungwookhub/vault-transit-demo:v1
(⎈|kind-myk8s:vault) ssoon@DESKTOP-72C919S:~$ export VAULT_ADDR=http://localhost:30000
(⎈|kind-myk8s:vault) ssoon@DESKTOP-72C919S:~$ export VAULT_TOKEN=root

Vault Transit 활성화

  • Vault 서버 현재 상태 확인
(⎈|kind-myk8s:vault) ssoon@DESKTOP-72C919S:~$ 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-b3ed10e3
Cluster ID      115af5f7-be8f-9a15-444c-bd004c076a9c
HA Enabled      false
  • Transit 엔진 활성화
(⎈|kind-myk8s:vault) ssoon@DESKTOP-72C919S:~$ vault secrets enable transit
Success! Enabled the transit secrets engine at: transit/
  • 활성화된 모든 Secrets Engine 목록
(⎈|kind-myk8s:vault) ssoon@DESKTOP-72C919S:~$ vault secrets list
Path          Type         Accessor              Description
----          ----         --------              -----------
cubbyhole/    cubbyhole    cubbyhole_649880ef    per-token private secret storage
database/     database     database_a1043ab0     n/a
identity/     identity     identity_6f0e93c1     identity store
secret/       kv           kv_cc436ec6           key/value secret storage
sys/          system       system_4a77190f       system endpoints used for control, policy and debugging
transit/      transit      transit_8921c3fb      n/a
  • ds-poc 이름의 암호화 키 생성
(⎈|kind-myk8s:vault) ssoon@DESKTOP-72C919S:~$ 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:1764430630]
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
  • Transit 엔진 내에 생성된 키 목록
(⎈|kind-myk8s:vault) ssoon@DESKTOP-72C919S:~$ vault list transit/keys
Keys
----
ds-poc
  • 평문을 Base64 → Vault Encrypt API 호출 → ciphertext 출력값 저장    암호문(Ciphertext) 값 확인
(⎈|kind-myk8s:vault) ssoon@DESKTOP-72C919S:~$ PLAINTEXT="My Data"
(⎈|kind-myk8s:vault) ssoon@DESKTOP-72C919S:~$ CIPHERTEXT=$(vault write -field=ciphertext transit/encrypt/ds-poc \
  plaintext=$(echo -n "$PLAINTEXT" | base64))
(⎈|kind-myk8s:vault) ssoon@DESKTOP-72C919S:~$ echo "ciphertext: $CIPHERTEXT"
ciphertext: vault:v1:sOznUKV/6U80TPyoMDa/n8M0mZW8f7FCKgY7OadV9YzK1AM=
  • 암호문을 Vault Decrypt API에 전달 → base64-decoding → 원문 출력
(⎈|kind-myk8s:vault) ssoon@DESKTOP-72C919S:~$ vault write -field=plaintext transit/decrypt/ds-poc \
  ciphertext="$CIPHERTEXT" | base64 -d && echo
My Data

MySQL 배포 (NodePort 30002)

(⎈|kind-myk8s:vault) ssoon@DESKTOP-72C919S:~$ cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
  name: ${NS}
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
  namespace: ${NS}
spec:
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.0.31
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: "rootpassword"
        ports:
        - containerPort: 3306
---
apiVersion: v1
kind: Service
metadata:
  name: mysql
  namespace: ${NS}
spec:
  type: NodePort
  selector:
    app: mysql
  ports:
  - name: mysql
    port: 3306
    targetPort: 3306
    nodePort: 30002
EOF
namespace/vault-demo created
deployment.apps/mysql created
service/mysql created
  • MySQL 배포확인 및 샘플 DB 생성
    • Vault는 데이터베이스 시크릿 엔진을 통해 MySQL 사용자 계정을 동적으로 생성/관리할 수 있습니다.
    • 이를 위해 MySQL이 준비되어야 하고, Vault가 접근할 수 있는 DB가 있어야 합니다.
    • 지금 만든 VaultData는 Vault가 테스트용으로 접근할 DB입니다.
(⎈|kind-myk8s:vault) ssoon@DESKTOP-72C919S:~$ kubectl -n ${NS} rollout status deploy/mysql
deployment "mysql" successfully rolled out
(⎈|kind-myk8s:vault) ssoon@DESKTOP-72C919S:~$ 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) ssoon@DESKTOP-72C919S:~$ 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            |
+----------------------+

 


Transit Demo 앱 배포

(⎈|kind-myk8s:vault) ssoon@DESKTOP-72C919S:~$ cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: vault-transit-demo
  namespace: ${NS}
spec:
  replicas: 1
  selector:
    matchLabels:
      app: vault-transit-demo
  template:
    metadata:
      labels:
        app: vault-transit-demo
    spec:
      containers:
      - name: app
        image: ${IMAGE}
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080
        env:
        - name: MYSQL_HOST
          value: mysql.${NS}.svc.cluster.local
        - name: MYSQL_PORT
          value: "3306"
        - name: MYSQL_DB_NAME
          value: VaultData
        - name: MYSQL_USERNAME
          value: root
        - name: MYSQL_USERPW
          value: rootpassword
        - name: VAULT_HOST
          value: vault.vault.svc.cluster.local    # 필요 시 노드 IP/NodePort로 교체
        - name: VAULT_PORT
          value: "8200"                           # NodePort로 붙을 땐 30000 등으로 교체
        - name: VAULT_SCHEME
          value: http
        - name: VAULT_TOKEN
          value: root
        - name: VAULT_TRANSIT_KEY_NAME
          value: ds-poc
        - name: SERVER_PORT
          value: "8080"
        - name: AWS_REGION
          value: "ap-northeast-2"
---
apiVersion: v1
kind: Service
metadata:
  name: vault-transit-demo
  namespace: ${NS}
spec:
  type: NodePort
  selector:
    app: vault-transit-demo
  ports:
  - port: 8080
    targetPort: 8080
    nodePort: 30003
    name: http
EOF
deployment.apps/vault-transit-demo created
service/vault-transit-demo created

애플리케이션 레벨에서 컬럼 암호화 및 파일 암호화

  • 웹 접속


1️⃣ Data Encryption: 텍스트 암/복호화

  • Decrypt Data: 평문 데이터 확인

  • Raw Data: vault: 접두사로 시작하는 암호화된 데이터 확인

  • DB 직접 조회하여 확인
(⎈|kind-myk8s:vault) ssoon@DESKTOP-72C919S:~$ 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:p4s2b56XiC32D0V1XVHAe3PiXoTXdLImLCxJVw==     | 2025-11-30 01:05:44.973000 |
|  2 | vault:v1:dOV9IDHXfLMCjbTR0d9uXQjTrPZOrr2ZauWPGRI59VsR | 2025-11-30 01:05:55.649000 |
+----+-------------------------------------------------------+----------------------------+

2️⃣ Data Encryption: 파일 암/복호화

  • 테스트 파일 생성
(⎈|kind-myk8s:vault) ssoon@DESKTOP-72C919S:~$ echo "hello vault transit" > original.txt
(⎈|kind-myk8s:vault) ssoon@DESKTOP-72C919S:~$ md5sum original.txt
5b92abfe363ae7212a37a55f96bf5967  original.txt
  • [File Encryption] - [Choose File] 선택 후 original.txt 파일 업로드

  • 원본 파일과 해시 값 비교
    • 원본 파일 암호화된 파일의 MD5 해시 값이 서로 다릅니다.
    • 이는 암호화 과정에서 데이터가 안전하게 변형되었음을 의미합니다.
(⎈|kind-myk8s:vault) ssoon@DESKTOP-72C919S:~$ md5sum original.txt original.txt.enc
5b92abfe363ae7212a37a55f96bf5967  original.txt
8fe1abe31c85728211c0bb0a52605768  original.txt.enc
  • [File Decryption] - [Choose File] 선택 후 original.txt.enc 파일 업로드

  • 원본 - 암호화 - 복호화 해시 값 비교
    • 원본 파일(original.txt)과 복호화된 파일(original.txt.dec.txt)의 MD5 값이 동일합니다.
(⎈|kind-myk8s:vault) ssoon@DESKTOP-72C919S:~$ md5sum original.txt original.txt.enc original.txt.dec.txt
5b92abfe363ae7212a37a55f96bf5967  original.txt
8fe1abe31c85728211c0bb0a52605768  original.txt.enc
5b92abfe363ae7212a37a55f96bf5967  original.txt.dec.txt
Comments