Networking, Security, Protocols

Vault 설치와 사용법

Somaz 2024. 1. 24. 22:48
728x90
반응형

Overview

오늘은 HashiCorp VaultKubernetes(k8s) 환경에서 설치 및 운영하는 방법에 대해 알아보겠다.

 

Vault는 보안 및 인증 정보를 안전하게 저장하고 관리하는 솔루션으로, Kubernetes 환경에서의 동적 비밀 관리, 인증 설정 및 Secret 자동 주입 기능 등을 제공한다.

이번 과정에서는 kind(Kubernetes in Docker)를 이용한 클러스터 구성부터, Helm을 활용한 Vault 설치 및 초기화, Kubernetes 인증 설정 및 Secret 관리 방법을 다뤄볼 예정이다.

출처 : https://rafay.co/the-kubernetes-current/getting-started-with-hashicorp-vault/

 

 

 

 

주요 내용

  • Kind를 이용한 Kubernetes 클러스터 생성
  • Vault 초기화 및 HA 클러스터 설정
  • Vault Helm Chart를 이용한 배포 및 관리
  • Vault Secret 엔진 및 Kubernetes 인증 구성
  • 웹 인터페이스(NodePort 설정) 및 User Authentication
  • Kubernetes 애플리케이션과 Vault 연동하여 Secret 자동 주입

 

 

이번 글을 통해 Vault를 Kubernetes 환경에서 운영하는 실전 노하우를 익히고, Secret 관리 자동화 및 보안 강화를 위한 최적의 방법을 배우게 될 것이다.

 

 

 

📅 관련 글

2022.10.11 - [Networking, Security, Protocols] - Vault란?

2024.11.08 - [Open Source Software] - Vaultwarden(Bitwarden)이란?

2024.01.13 - [Container Orchestration/Kubernetes] - kind(Kubernetes in Docker)란?

 

 

 

 

 

 


 

 

 

 

 

 

Kind 설치

30000번 포트를 하나 더 포트포워딩 해준다. 그 이유는 외부에서 30000번을 타고 vault로 접속을 해야하기 때문이다.

 

# cluster yaml 파일 생성
cat <<EOF > cluster-3nodes.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  extraPortMappings:
  - containerPort: 30000
    hostPort: 30000
    listenAddress: "0.0.0.0"
    protocol: TCP
- role: worker
- role: worker
EOF

# kind 설치
kind create cluster --name somaz-3nodes-kubernetes --config ./cluster-3nodes.yaml

# 설치 확인
k get nodes
NAME                                    STATUS   ROLES           AGE   VERSION
somaz-3nodes-kubernetes-control-plane   Ready    control-plane   34s   v1.27.3
somaz-3nodes-kubernetes-worker          Ready    <none>          10s   v1.27.3
somaz-3nodes-kubernetes-worker2         Ready    <none>          9s    v1.27.3

 

 

 

 


 

 

 

Vault 초기화

설치에 들어가기 전에 Vault 초기화 개념에 대해서 알아야한다.

 

HA환경, 특히 Raft 스토리지 백엔드에서 Vault를 초기화할 때 몇 가지 중요한 정보가 생성된다.

Vault를 처음 설치하면, 모든 Pod가 잠겨있다. 따라서 해제키를 사용해 잠금을 해제하고 루트 토큰을 사용해서 HA클러스터에 연결해줘야 한다.

cat cluster-keys.json
{
  "unseal_keys_b64": [
    "i1twvuaHofgdjplSZUsPTu3aTNGxyZjz6W5bElc5pnI="
  ],
  "unseal_keys_hex": [
    "8b5b70bee687a1f81d8e9952654b0f4eedda4cd1b1c998f3e96e5b125739a672"
  ],
  "unseal_shares": 1,
  "unseal_threshold": 1,
  "recovery_keys_b64": [],
  "recovery_keys_hex": [],
  "recovery_keys_shares": 0,
  "recovery_keys_threshold": 0,
  "root_token": "hvs.AQtv1aABPFtf6s6xcsVjX3sR"
}

 

 

 

 

 


 

 

 

 

해제 키(UNSEAL_KEY)

해당 키는 Vault의 잠금을 해제하는데 사용된다. Sealing Vault는 Vault를 잠그고 올바른 키를 사용하여 봉인을 풀 때까지 Vault에 접근할 수 없도록 하는 보안 기능이다.

 

루트 토큰(CLUSTER_ROOT_TOKEN)

Vault에 액세스하기 위한 마스터 키이다. Vault의 모든 비밀 및 구성에 대한 전체 액세스 권한을 제공

한다. 아주 중요한 키이다.

 

 

 

 

 


 

 

 

 

 

Vault 초기화 예시

 

 

1. 해제키 1개이고 잠금을 해제하는 필요한 해제키 1개일 때

k exec vault-0 -n vault -- vault operator init -key-shares=1 -key-threshold=1 -format=json > cluster-keys.json
  • `-key-shares=1` : Vault에서 생성하는 해제 키의 총 개수를 결정한다. 1로 설정했기 때문에 하나만 생성한다.
  • `-key-threshold=1` : Vault 잠금을 해제하는데 필요한 해제키를 설정한다. 1로 설정했기 때문에 해제 키 중 하나만 있으면 설정이 가능하다.
  • `-format=json` : 변수는 출력(해제 키 및 초기 루트 토큰 포함)이 JSON 형식이어야 함을 지정한다.

 

 

초기화 후 클러스터 로그인 방법

# 해제키 확인
cat cluster-keys.json | jq -r ".unseal_keys_b64[]"

# 해제 키 변수 등록
VAULT_UNSEAL_KEY=$(cat cluster-keys.json | jq -r ".unseal_keys_b64[]")

# 해제키로 잠금해제
kubectl exec vault-0 -n vault -- vault operator unseal $VAULT_UNSEAL_KEY

# 루트 토큰 확인
cat cluster-keys.json | jq -r ".root_token"

# 루트 토큰 변수 등록
CLUSTER_ROOT_TOKEN=$(cat cluster-keys.json | jq -r ".root_token")

# 루트 토큰으로 클러스터 로그인
kubectl exec vault-0 -n vault -- vault login $CLUSTER_ROOT_TOKEN

# 나머지 Node들 해제 및 루트 토큰 확인 및 변수 등록 방법은 동일

# 루트 토큰으로 나머지 Node들 클러스터 로그인
kubectl exec vault-1 -n vault -- vault operator raft join <http://vault-0.vault-internal:8200>
kubectl exec vault-2 -n vault -- vault operator raft join <http://vault-0.vault-internal:8200>

# 클러스터 상태확인
kubectl exec vault-0 -n vault -- vault operator raft list-peers

Node                                    Address                        State       Voter
----                                    -------                        -----       -----
38ab1d7a-8f6b-93ee-ff54-c986492b73a6    vault-0.vault-internal:8201    leader      true
bdf0efc9-91a1-1984-ee3c-f2e20849c4bd    vault-1.vault-internal:8201    follower    true
7b498967-d383-affa-fb5a-95b1650b4113    vault-2.vault-internal:8201    follower    true

 

 

 

2. 해제키 2개이고 잠금을 해제하는 필요한 해제키 2개일 때

k exec vault-0 -n vault -- vault operator init -key-shares=2 -key-threshold=2 -format=json > cluster-keys.json
cat cluster-keys.json
{
  "unseal_keys_b64": [
    "FbPft3K5JmIoqOHlvg0RvDjkvxsEnmD4BlDja6x5Gk2E",
    "Lc+wBkFg/lYlQbN3EypoJFloaOcc+9N9yPZDdNNJqBmJ"
  ],
  "unseal_keys_hex": [
    "15b3dfb772b9266228a8e1e5be0d11bc38e4bf1b049e60f80650e36bac791a4d84",
    "2dcfb0064160fe562541b377132a6824596868e71cfbd37dc8f64374d349a81989"
  ],
  "unseal_shares": 2,
  "unseal_threshold": 2,
  "recovery_keys_b64": [],
  "recovery_keys_hex": [],
  "recovery_keys_shares": 0,
  "recovery_keys_threshold": 0,
  "root_token": "hvs.Iqzs15KWDnOHm2GoZNk7aTdV"
}

 

초기화 후 클러스터 로그인 방법

  • 나머지 방법은 동일하다. 다만 해제할때 해제키가 2개 필요하기 때문에 2번해제해 줘야 한다.
# 해제 키 확인
cat cluster-keys.json | jq -r ".unseal_keys_b64[]"
FbPft3K5JmIoqOHlvg0RvDjkvxsEnmD4BlDja6x5Gk2E
Lc+wBkFg/lYlQbN3EypoJFloaOcc+9N9yPZDdNNJqBmJ

# VAULT_UNSEAL_KEY를 캡처하기 위해 변수를 만든다.
FIRST_VAULT_UNSEAL_KEY=$(jq -r '.unseal_keys_b64[0]' cluster-keys.json)
SECOND_VAULT_UNSEAL_KEY=$(jq -r '.unseal_keys_b64[1]' cluster-keys.json)

# 확인
echo $FIRST_VAULT_UNSEAL_KEY
FbPft3K5JmIoqOHlvg0RvDjkvxsEnmD4BlDja6x5Gk2E

echo $SECOND_VAULT_UNSEAL_KEY
Lc+wBkFg/lYlQbN3EypoJFloaOcc+9N9yPZDdNNJqBmJ

# 해제
kubectl exec vault-0 -n vault -- vault operator unseal $FIRST_VAULT_UNSEAL_KEY
kubectl exec vault-0 -n vault -- vault operator unseal $SECOND_VAULT_UNSEAL_KEY

# 상태확인
kubectl exec vault-0 -n vault -- vault status

Key                     Value
---                     -----
Seal Type               shamir
Initialized             true
Sealed                  false
Total Shares            2
Threshold               2
Version                 1.15.2
Build Date              2023-11-06T11:33:28Z
Storage Type            raft
Cluster Name            vault-cluster-51dc2ed2
Cluster ID              3a2c9d24-09cc-418c-ae0e-6e3e174d8676
HA Enabled              true
HA Cluster              <https://vault-0.vault-internal:8201>
HA Mode                 active
Active Since            2024-01-11T05:29:23.992412628Z
Raft Committed Index    53
Raft Applied Index      53

 

 

 


 

 

 

Vault 설치

Vault Helm Chart를 사용해서 설치해보겠다.

 

 

 

 


 

 

 

 

helm 설치

curl -fsSL -o get_helm.sh <https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3>
chmod 700 get_helm.sh
./get_helm.sh

https://somaz.tistory.com/141

 

 

 

 


 

 

 

 

Vault 설치 전 사전준비

# namespace 생성
k create ns vault

# helm search
helm search repo hashicorp/vault

# helm repo 추가
helm repo add hashicorp <https://helm.releases.hashicorp.com>
helm repo update

# helm repo 추가 확인
helm repo list
NAME            URL
hashicorp       <https://helm.releases.hashicorp.com>

 

 

 

확인 방법(선택사항)

# helm 설치전 확인
helm install vault hashicorp/vault --namespace vault --dry-run

# 버전 확인
helm search repo hashicorp/vault --versions

# helm 설치(특정버전)
helm install vault hashicorp/vault --namespace vault --version 0.25.0

# 기본설정 재정의 확인
helm install vault hashicorp/vault \\
    --namespace vault \\
    --set "server.ha.enabled=true" \\
    --set "server.ha.replicas=5" \\
    --dry-run

# overide-value 확인
helm install vault hashicorp/vault \\
    --namespace vault \\
    -f override-values.yml \\
    --dry-run

 

 


 

 

Vault 설치 및 확인

helm install vault hashicorp/vault \\
    --set='server.ha.enabled=true' \\
    --set='server.ha.raft.enabled=true' \\
    -n vault

 

 

Pod 확인

k get po -n vault
NAME                                    READY   STATUS    RESTARTS   AGE
vault-0                                 0/1     Running   0          82s
vault-1                                 0/1     Running   0          82s
vault-2                                 0/1     Pending   0          82s
vault-agent-injector-7f7f68d457-kqlsh   1/1     Running   0          83s

kubectl exec vault-0 -n vault -- vault status
Key                Value
---                -----
Seal Type          shamir
Initialized        false
Sealed             true
Total Shares       0
Threshold          0
Unseal Progress    0/0
Unseal Nonce       n/a
Version            1.15.2
Build Date         2023-11-06T11:33:28Z
Storage Type       raft
HA Enabled         true
command terminated with exit code 2

 

 

Vault 초기화

k exec vault-0 -n vault -- vault operator init -key-shares=1 -key-threshold=1 -format=json > cluster-keys.json
  • `-key-shares=1` : Vault에서 생성하는 해제 키의 총 개수를 결정한다. 1로 설정했기 때문에 하나만 생성한다.
  • `-key-threshold=1` : Vault 잠금을 해제하는데 필요한 해제키를 설정한다. 1로 설정했기 때문에 해제 키 중 하나만 있으면 설정이 가능하다.
  • `-format=json` : 변수는 출력(해제 키 및 초기 루트 토큰 포함)이 JSON 형식이어야 함을 지정한다.

 

 

해제키 확인

cat cluster-keys.json | jq -r ".unseal_keys_b64[]"
Cx/RXjcjmJsRQAM57o9+sGbZTmODpOeQL097kbfMFQY=

 

VAULT_UNSEAL_KEY를 캡처하기 위해 변수를 만든다.

# jq 패키지 설치
sudo apt install jq

# 변수 설정
VAULT_UNSEAL_KEY=$(cat cluster-keys.json | jq -r ".unseal_keys_b64[]")

 

 

vault-0 잠금을 해제한다. 초기화가 완료되었다.

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.15.2
Build Date              2023-11-06T11:33:28Z
Storage Type            raft
Cluster Name            vault-cluster-a580d962
Cluster ID              25ccca2d-f62b-324b-8012-5c4535ddb760
HA Enabled              true
HA Cluster              <https://vault-0.vault-internal:8201>
HA Mode                 active
Active Since            2024-01-11T03:02:23.232069547Z
Raft Committed Index    52
Raft Applied Index      52

# 상태확인
kubectl exec vault-0 -n vault -- vault status

 

 

 

 

 


 

 

 

 

 

 

Vault HA 클러스터 연결

파드에서 실행되는 Vault 서버는 `vault-0` 단일 노드가 있는 Vault HA 클러스터이다.

노드 목록을 표시하려면 루트 토큰으로 로그인해야 한다.

 

 

루트토큰 확인

cat cluster-keys.json | jq -r ".root_token"
hvs.bOXica9b3vWqfBaiZWfASOC1

 

 

`CLUSTER_ROOT_TOKEN`를 캡처하기 위해 이름이 지정된 변수를 만든다.

CLUSTER_ROOT_TOKEN=$(cat cluster-keys.json | jq -r ".root_token")

 

 

`vault-0` 파드에 루트 토큰으로 로그인하여 Cluster에 로그인해준다.

kubectl exec vault-0 -n vault -- vault login $CLUSTER_ROOT_TOKEN

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.AQtv1aABPFtf6s6xcsVjX3sR
token_accessor       sMX1CoAiYLZQCVGKMBgZHRKg
token_duration       ∞
token_renewable      false
token_policies       ["root"]
identity_policies    []
policies             ["root"]

 

 

Vault 클러스터 내의 노드 확인

kubectl exec vault-0 -n vault -- vault operator raft list-peers
Node                                    Address                        State     Voter
----                                    -------                        -----     -----
38ab1d7a-8f6b-93ee-ff54-c986492b73a6    vault-0.vault-internal:8201    leader    true

 

 

동일하게 `vault-1, vault2` 파드도 진행해준다.

 

`vault-1` 파드를 `http://vault-0.vault-internal:8200` Cluster에 조인해준다.

kubectl exec vault-1 -n vault -- vault operator raft join <http://vault-0.vault-internal:8200>
Key       Value
---       -----
Joined    true

 

 

`vault-1` 파드에서 잠금을 해제한다. 초기화가 완료되었다.

kubectl exec vault-1 -n vault -- vault operator unseal $VAULT_UNSEAL_KEY

 

 

Vault 클러스터 상태를 확인해본다.

kubectl exec vault-0 -n vault -- vault operator raft list-peers
Node                                    Address                        State       Voter
----                                    -------                        -----       -----
38ab1d7a-8f6b-93ee-ff54-c986492b73a6    vault-0.vault-internal:8201    leader      true
bdf0efc9-91a1-1984-ee3c-f2e20849c4bd    vault-1.vault-internal:8201    follower    true

 

 

 

동일한 방법으로 `vault-2` 파드도 진행하면 된다. 다만 Kind로 진행시에, `2 node(s) didn't match pod anti-affinity rules` 해당 Message가 발생하며 `vault-2` 파드가 작동하지 않는다.

테스트를 위해 Control-Plane 에도 예약이 허용되도록 설정을 바꿔준다.

# vault-vaules.yaml
cat <<EOF > vault-values.yaml
server:
  ha:
    enabled: true
    raft:
      enabled: true
  tolerations:
    - key: "node-role.kubernetes.io/control-plane"
      operator: "Exists"
      effect: "NoSchedule"
EOF

# upgrade
helm upgrade vault hashicorp/vault -f vault-values.yaml -n vault

# upgrade2
helm upgrade vault hashicorp/vault \\
    --set='server.ha.enabled=true' \\
    --set='server.ha.raft.enabled=true' \\
    --set 'server.tolerations[0].key=node-role.kubernetes.io/control-plane' \\
    --set 'server.tolerations[0].operator=Exists' \\
    --set 'server.tolerations[0].effect=NoSchedule' \\
    -n vault

# vault-2 pod 삭제
k delete po -n vault vault-2

# 확인
k get po -n vault
NAME                                    READY   STATUS    RESTARTS   AGE
vault-0                                 1/1     Running   0          32m
vault-1                                 1/1     Running   0          32m
vault-2                                 0/1     Running   0          72s
vault-agent-injector-7f7f68d457-kqlsh   1/1     Running   0          32m

 

 

`vault-2` 파드를 `http://vault-0.vault-internal:8200` Cluster에 조인해준다.

kubectl exec vault-2 -n vault -- vault operator raft join <http://vault-0.vault-internal:8200>
Key       Value
---       -----
Joined    true

 

 

`vault-2` 파드에서 잠금을 해제한다. 초기화가 완료되었다.

kubectl exec vault-2 -n vault -- vault operator unseal $VAULT_UNSEAL_KEY

 

 

Vault 클러스터 상태를 확인한다.

kubectl exec vault-0 -n vault -- vault operator raft list-peers
Node                                    Address                        State       Voter
----                                    -------                        -----       -----
38ab1d7a-8f6b-93ee-ff54-c986492b73a6    vault-0.vault-internal:8201    leader      true
bdf0efc9-91a1-1984-ee3c-f2e20849c4bd    vault-1.vault-internal:8201    follower    true
7b498967-d383-affa-fb5a-95b1650b4113    vault-2.vault-internal:8201    follower    true

 

 

 

 


 

 

 

 

 

Vault Web 확인

 

NodePort로 오픈해준다.

k patch svc vault -n vault -p '{"spec": {"type": "NodePort", "ports": [{"nodePort": 30000, "port": 8200, "protocol": "TCP"}]}}'

 

 

웹 접속을 위해 Compute Engine 방화벽을 오픈한다.

## Firewall ##
resource "google_compute_firewall" "test_server_ssh" {
  name    = "allow-ssh-test-server"
  network = var.shared_vpc

  allow {
    protocol = "tcp"
    ports    = ["22", "30000"]
  }

  source_ranges = ["${var.public_ip}/32", "${var.public_ip2}/32", "0.0.0.0/0"]
  target_tags   = [var.test_server]

  depends_on = [module.vpc]
}

 

 

 

 

 

 

 

루트 토큰을 사용해서 로그인이 가능하다.

CLUSTER_ROOT_TOKEN=$(cat cluster-keys.json | jq -r ".root_token")

echo $CLUSTER_ROOT_TOKEN
hvs.bOXica9b3vWqfBaiZWfASOC1

 

 

 

 

 

이번엔 User를 생성해서 해당 User로 접속을 해본다.

kubectl exec --stdin=true --tty=true vault-0 -n vault -- /bin/sh

# userpass 활성화
vault auth enable userpass

# vault user 등록 예시
vault write auth/userpass/users/<username> password=<password> policies=<policies>

# vault user 등록
vault write auth/userpass/users/somaz password=somaz@2023 

# vault CLI 로그인 예시
vault login -method=userpass username=<username> password=<password>

 

 

 

접속이 잘된다.

 

 

 

 


 

 

 

 

 

Vault 활용

 

 

Vault Secret 설정

kv-v2 엔진은 `Key-Vaule` 엔진이다. 보통 ID/PW 또는 모든 `Key-Value` 값들을 사용할 때 쓴다.

# vault-0 pod 접속
kubectl exec --stdin=true --tty=true vault-0 -n vault -- /bin/sh
/ $

# kv-v2 Secret 활성화
vault secrets enable -path=secret kv-v2

Success! Enabled the kv-v2 secrets engine at: secret/

# secret/devwebapp/config를 사용하여 경로에 Secret을 생성
vault kv put secret/devdb/config username='somaz' password='somazpassword'

====== Secret Path ======
secret/data/devdb/config

======= Metadata =======
Key                Value
---                -----
created_time       2024-01-11T05:44:17.023693128Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1

# secret/devwebapp/config에 Secret 생성 확인
vault kv get secret/devdb/config

====== Secret Path ======
secret/data/devdb/config

======= Metadata =======
Key                Value
---                -----
created_time       2024-01-11T05:44:17.023693128Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1

====== Data ======
Key         Value
---         -----
password    somazpassword
username    somaz

 

 

 

 


 

 

 

 

 

 

Kubernetes 인증 구성

초기 루트 토큰은 모든 경로에서 모든 작업을 수행할 수 있는 권한이 있는 사용자이다.

웹 애플리케이션에는 단일 경로에 정의된 비밀을 읽는 기능만 필요하다. 이 애플리케이션은 액세스가 제한된 토큰을 인증하고 부여 받아야 한다.

# vault-0 pod 접속
kubectl exec --stdin=true --tty=true vault-0 -n vault -- /bin/sh
/ $

# Kubernetes 인증방법 활성화
vault auth enable kubernetes
  • Vault는 Kubernetes 클러스터 내의 모든 클라이언트로부터 서비스 토큰을 허용한다.
  • 인증 중에 Vault는 토큰 검토 Kubernetes 엔드포인트를 쿼리하여 서비스 계정 토큰이 유효한지 확인한다.
  • Kubernetes API의 위치를 사용하도록 Kubernetes 인증 방법을 구성한다.
  • 토큰 검토 API를 쿼리할 때 Pod의 자체 ID를 자동으로 사용하여 Kubernetes에 인증한다.

 

 

환경 변수가 `KUBERNETES_PORT_443_TCP_ADDR` 정의되고 Kubernetes 호스트의 내부 네트워크 주소를 참조한다.

vault write auth/kubernetes/config \\
    kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443"

 

 

Vault `devdb`를 활성화하는 정책을 작성한다.

# 정책 생성
vault policy write devdb - <<EOF
path "secret/data/devdb/config" {
  capabilities = ["read"]
}
EOF

# 정책 리스트 확인
vault policy list
default
devdb
root

# 정책 확인
vault policy read devdb
path "secret/data/devdb/config" {
  capabilities = ["read"]
}

 

 

Kubernetes 인증 역할을 생성한다.

# 역할 생성
vault write auth/kubernetes/role/db-app \\
        bound_service_account_names=somaz-app \\
        bound_service_account_namespaces=somaz \\
        policies=devdb \\
        ttl=24h

# 역할 확인
vault read auth/kubernetes/role/db-app
Key                                 Value
---                                 -----
alias_name_source                   serviceaccount_uid
bound_service_account_names         [somaz-app]
bound_service_account_namespaces    [somaz]
policies                            [devdb]
token_bound_cidrs                   []
token_explicit_max_ttl              0s
token_max_ttl                       0s
token_no_default_policy             false
token_num_uses                      0
token_period                        0s
token_policies                      [devdb]
token_ttl                           24h
token_type                          default
ttl                                 24h

 

웹 애플리케이션 배포

Vault Kubernetes 인증 역할에 지정된 Kubernetes 서비스 계정을 생성해야 한다.

# 네임스페이스와 서비스어카운트 생성
k create ns somaz
k create sa somaz-app -n somaz

 

 

웹 애플리케이션을 생성한다.

# yaml 파일생성
cat > devsomazapp.yaml <<EOF
---
apiVersion: v1
kind: Pod
metadata:
  name: devsomazapp
  labels:
    app: devsomazapp
  annotations:
    vault.hashicorp.com/agent-inject: "true"
    vault.hashicorp.com/role: "db-app"
    vault.hashicorp.com/agent-inject-secret-credentials.txt: "secret/data/devdb/config"
spec:
  serviceAccountName: somaz-app
  containers:
    - name: devsomazapp
      image: jweissig/app:0.0.1
EOF

# 생성
k apply -f devsomazapp.yaml -n somaz

# secret 확인
k exec --stdin=true --tty=true devsomazapp -c devsomazapp -n somaz -- cat /vault/secrets/credentials.txt
data: map[password:somazpassword username:somaz]
metadata: map[created_time:2024-01-11T05:44:17.023693128Z custom_metadata:<nil> deletion_time: destroyed:false version:1]

 

 

 

 

 


 

 

 

 

 

 

주석의 형식

주요한 점은 주석의 형식은 다음과 같아야 한다.

vault.hashicorp.com/agent-inject-secret-<unique-name>: /path/to/secret

 

 

2개 이상의 Secret을 정책으로 지정했다면, 아래와 같이 설정해줘야 한다.

vault.hashicorp.com/agent-inject-secret-devdb: secret/data/devdb/config
vault.hashicorp.com/agent-inject-secret-qadb: secret/data/qadb/config
vault.hashicorp.com/role: 'devdb-app'

 

 

테스트해본다.

# pod 접속
kubectl exec --stdin=true --tty=true vault-0 -n vault -- /bin/sh

# secret 생성
vault kv put secret/qadb/config username='qasomaz' password='qasomazpassword'

# 정책 생성
vault policy write qadb - <<EOF
path "secret/data/qadb/config" {
  capabilities = ["read"]
}
EOF

# 역할 추가
vault write auth/kubernetes/role/db-app \\
        bound_service_account_names=somaz-app \\
        bound_service_account_namespaces=somaz \\
        policies=devdb,qadb \\
        ttl=24h

# 역할 확인
vault read auth/kubernetes/role/db-app
Key                                 Value
---                                 -----
alias_name_source                   serviceaccount_uid
bound_service_account_names         [somaz-app]
bound_service_account_namespaces    [somaz]
policies                            [devdb qadb]
token_bound_cidrs                   []
token_explicit_max_ttl              0s
token_max_ttl                       0s
token_no_default_policy             false
token_num_uses                      0
token_period                        0s
token_policies                      [devdb qadb]
token_ttl                           24h
token_type                          default
ttl                                 24h

 

 

웹 애플리케이션 `yaml` 파일 변경 후 확인

# yaml 파일생성
cat > devsomazapp.yaml <<EOF
---
apiVersion: v1
kind: Pod
metadata:
  name: devsomazapp
  labels:
    app: devsomazapp
  annotations:
    vault.hashicorp.com/agent-inject: "true"
    vault.hashicorp.com/role: "db-app"
    vault.hashicorp.com/agent-inject-secret-devdb.txt: "secret/data/devdb/config"
    vault.hashicorp.com/agent-inject-secret-qadb.txt: "secret/data/qadb/config"
spec:
  serviceAccountName: somaz-app
  containers:
    - name: devsomazapp
      image: jweissig/app:0.0.1
EOF

# 생성
k apply -f devsomazapp.yaml -n somaz

# pod 재시작
k delete po -n somaz devsomazapp
k apply -f devsomazapp.yaml -n somaz

# 확인
k exec --stdin=true --tty=true devsomazapp -c devsomazapp -n somaz -- ls /vault/secrets/
devdb.txt  qadb.txt

k exec --stdin=true --tty=true devsomazapp -c devsomazapp -n somaz -- cat /vault/secrets/devdb.txt
data: map[password:somazpassword username:somaz]
metadata: map[created_time:2024-01-11T05:44:17.023693128Z custom_metadata:<nil> deletion_time: destroyed:false version:1]

k exec --stdin=true --tty=true devsomazapp -c devsomazapp -n somaz -- cat /vault/secrets/qadb.txt
data: map[password:qasomazpassword username:qasomaz]
metadata: map[created_time:2024-01-11T06:32:34.680311571Z custom_metadata:<nil> deletion_time: destroyed:false version:1]

 

 

연결 로그 확인

==> Vault Agent started! Log data will stream in below:

==> Vault Agent configuration:

           Api Address 1: <http://bufconn>
                     Cgo: disabled
               Log Level: info
                 Version: Vault v1.15.2, built 2023-11-06T11:33:28Z
             Version Sha: cf1b5cafa047bc8e4a3f93444fcb4011593b92cb

2024-01-11T09:49:04.605Z [INFO]  agent.sink.file: creating file sink
2024-01-11T09:49:04.605Z [INFO]  agent.sink.file: file sink configured: path=/home/vault/.vault-token mode=-rw-r-----
2024-01-11T09:49:04.605Z [INFO]  agent.exec.server: starting exec server
2024-01-11T09:49:04.605Z [INFO]  agent.exec.server: no env templates or exec config, exiting
2024-01-11T09:49:04.605Z [INFO]  agent.auth.handler: starting auth handler
2024-01-11T09:49:04.605Z [INFO]  agent.auth.handler: authenticating
2024-01-11T09:49:04.605Z [INFO]  agent.sink.server: starting sink server
2024-01-11T09:49:04.605Z [INFO]  agent.template.server: starting template server
2024-01-11T09:49:04.606Z [INFO] (runner) creating new runner (dry: false, once: false)
2024-01-11T09:49:04.606Z [INFO] (runner) creating watcher
2024-01-11T09:49:04.626Z [INFO]  agent.auth.handler: authentication successful, sending token to sinks
2024-01-11T09:49:04.626Z [INFO]  agent.auth.handler: starting renewal process
2024-01-11T09:49:04.626Z [INFO]  agent.sink.file: token written: path=/home/vault/.vault-token
2024-01-11T09:49:04.626Z [INFO]  agent.sink.server: sink server stopped
2024-01-11T09:49:04.627Z [INFO]  agent: sinks finished, exiting
2024-01-11T09:49:04.626Z [INFO]  agent.template.server: template server received new token
2024-01-11T09:49:04.627Z [INFO] (runner) stopping
2024-01-11T09:49:04.627Z [INFO] (runner) creating new runner (dry: false, once: false)
2024-01-11T09:49:04.627Z [INFO] (runner) creating watcher
2024-01-11T09:49:04.627Z [INFO] (runner) starting
2024-01-11T09:49:04.636Z [INFO] (runner) rendered "(dynamic)" => "/vault/secrets/devdb.txt"
2024-01-11T09:49:04.637Z [INFO] (runner) rendered "(dynamic)" => "/vault/secrets/qadb.txt"
2024-01-11T09:49:04.637Z [INFO] (runner) stopping
2024-01-11T09:49:04.637Z [INFO]  agent.template.server: template server stopped
2024-01-11T09:49:04.637Z [INFO] (runner) received finish
2024-01-11T09:49:04.637Z [INFO]  agent.auth.handler: shutdown triggered, stopping lifetime watcher
2024-01-11T09:49:04.637Z [INFO]  agent.auth.handler: auth handler stopped
2024-01-11T09:49:04.637Z [INFO]  agent.exec.server: exec server stopped

 

 


 

 

 

User Policy 설정

Web에서 Root Token을 사용하여 Policy를 확인해보면 전부 보인다.

 

 

 

Somaz 라는 User로 접속하면 아무것도 보이지 않는다.

 

 

 

CLI로 로그인해서 확인해본다.

vault login -method=userpass username=somaz password=somaz@2023
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.CAESIIh84VvEP8vneC8oT3zzVIdxXCEHTGwAhkRdcViPeUA0Gh4KHGh2cy5SUHNuTjRTVmIxWWRNZHFPUUo2ZTBIVEg
token_accessor         mwevmGz8mEe6tKF8HfguHExl
token_duration         768h
token_renewable        true
token_policies         ["default" "devdb"]
identity_policies      []
policies               ["default" "devdb"]
token_meta_username    somaz

vault token lookup
Key                 Value
---                 -----
accessor            mwevmGz8mEe6tKF8HfguHExl
creation_time       1705632637
creation_ttl        768h
display_name        userpass-somaz
entity_id           5d390cf1-92cb-3d74-c3ef-d70682544cd7
expire_time         2024-02-20T02:50:37.201652198Z
explicit_max_ttl    0s
id                  hvs.CAESIIh84VvEP8vneC8oT3zzVIdxXCEHTGwAhkRdcViPeUA0Gh4KHGh2cy5SUHNuTjRTVmIxWWRNZHFPUUo2ZTBIVEg
issue_time          2024-01-19T02:50:37.201659907Z
meta                map[username:somaz]
num_uses            0
orphan              true
path                auth/userpass/login/somaz
policies            [default devdb]
renewable           true
ttl                 767h59m47s
type                service

 

 

정책 생성 후 할당한다.

# root로 로그인
vault login hvs.bOXica9b3vWqfBaiZWfASOC1

# 정책 생성
vault policy write admin - <<EOF
path "*" {
  capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}
EOF

vault write auth/userpass/users/somaz policies=devdb policies=admin

vault read auth/userpass/users/somaz
Key                        Value
---                        -----
policies                   [admin devdb]
token_bound_cidrs          []
token_explicit_max_ttl     0s
token_max_ttl              0s
token_no_default_policy    false
token_num_uses             0
token_period               0s
token_policies             [admin devdb]
token_ttl                  0s
token_type                 default

 

 

잘 보인다. 모든 권한을 다줬다.

 

 

 

 

 


 

 

 

 

마무리

이번 글에서는 HashiCorp Vault를 Kubernetes 환경에 설치하고 운영하는 방법을 kind 클러스터 기반으로 실습해보았다.

 

Helm Chart를 이용한 Vault 설치부터 HA 구성, 초기화 및 클러스터 조인 과정을 통해 실제 운영 환경에서 Vault를 어떻게 활용할 수 있는지 체험할 수 있었다.

 

또한 Vault의 핵심 기능인 Secret 엔진, 인증 방식(userpass, kubernetes) 설정과 함께, Vault Agent Injector를 통한 Kubernetes Pod의 Secret 자동 주입 기능까지 실습하였다.

 

특히 다음과 같은 내용을 직접 다뤄보면서 Vault의 실전 운영 역량을 키울 수 있었다.

  • Vault의 초기화 및 unseal 과정 이해
  • HA 구성을 통한 고가용성 클러스터 구축
  • Kubernetes 서비스 계정 기반 인증 연동
  • Secret 관리 및 Policy 기반 접근 제어
  • 애플리케이션에 Secret을 안전하게 주입하는 Vault Agent 활용법

Vault는 단순한 Secret 저장소를 넘어 정교한 보안 정책, 동적 인증, 자동화된 인증 연동, 서비스 메시 통합 등 클라우드 네이티브 환경에서의 신뢰 기반 인프라를 구성하는 데 핵심적인 역할을 한다.

 

 

향후 운영 환경에서는 External Secrets Operator(ESO), 자동 인증 갱신(Renew), Audit Logging, PKI Secret Engine 등 고급 기능들도 함께 고려해보면 좋다.

 

보안은 인프라의 가장 기본이자 핵심이다. Vault를 잘 이해하고 익혀두면, 어떠한 클라우드 환경에서도 신뢰성 있고 확장 가능한 보안 인프라를 구현할 수 있을 것이다.

 

 

 

 

 

 

 

 


Reference

https://developer.hashicorp.com/vault/tutorials/kubernetes/kubernetes-raft-deployment-guide

https://github.com/hashicorp/vault-helm

https://developer.hashicorp.com/vault/tutorials/kubernetes/kubernetes-google-cloud-gke

https://developer.hashicorp.com/vault/tutorials/kubernetes/kubernetes-external-vault#deploy-service-and-endpoints-to-address-an-external-vault

https://www.freshbrewed.science/vault-on-kubernetes-part-2-multiple-k8s-templates-and-external-ips/index.html

https://earthly.dev/blog/eso-with-hashicorp-vault/

https://devpress.csdn.net/k8s/62fce3137e66823466190e0a.html

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

728x90
반응형

'Networking, Security, Protocols' 카테고리의 다른 글

haproxy 개념 및 구성 가이드  (0) 2024.04.09
Cilium이란?  (2) 2024.02.06
Curl(Client URL)이란?  (0) 2023.05.26
SSID(Service Set Identifier)란?  (0) 2023.04.05
CDN이란?  (2) 2022.11.11