[Vault] Kubernetes에서 Vault HA 구성하기

김도훈 - DevOps Engineer·2024년 1월 28일
1

Vault

목록 보기
1/2
post-thumbnail

이 글은 kubernetes 클러스터, Helm 설정 및 설치되어 있다고 가정하고, Vault 설정 및 설치를 다룹니다.

Vault란?

  • HashiCorp Vault는 데이터 암호화와 접근 제어를 통해 시크릿이나 민감한 데이터를 저장 및 관리하는 도구이다.
  • 여기서 민감한 데이터는 API Key, Password, 인증서 등을 안전하게 저장하고, 사용자 인증과 권한 부여를 통해 엄격하게 제어한다.
  • Vault를 사용하면 UI, CLI, HTTP API를 제공하여 민감 데이터를 다룰 수 있다.

Vault WorkFlow

Vault는 클라이언트(사용자, 시스템, 앱)에게 비밀 또는 저장된 민감한 데이터에 대한 액세스 권한을 제공하기 전에 유효성을 검사하고 승인합니다.


출처 : https://developer.hashicorp.com/vault/docs/what-is-vault

Vault 설치(Helm)

차트를 Pull 받아서 value를 override하여 배포하는 방식으로 설명합니다.

# Helm Repo 등록
$ helm repo add hashicorp https://helm.releases.hashicorp.com

$ helm repo update

# vault helm chart pull
$ helm pull hashicorp/vault

# 차트 압축해제
$ tar -xvf vault-0.27.0.tgz

  • override-values.yaml
server:
  enabled: true
  ha:
    enabled: true
    replicas: 3
    raft:
      enabled: true
  image:
    repository: "hashicorp/vault"
    tag: "1.15.2"
    pullPolicy: IfNotPresent
  resources:
    requests:
      memory: 256Mi
      cpu: 250m
    limits:
      memory: 256Mi
      cpu: 250m


injector:
  hostNetwork: true #calico cni를 사용하는 경우 true 설정
  enabled: true
  image:
    repository: "hashicorp/vault-k8s"
    tag: "1.3.1"
    pullPolicy: IfNotPresent
  resources:
    requests:
      memory: 256Mi
      cpu: 250m
    limits:
      memory: 256Mi
      cpu: 250m
      
ui:
  enabled: true
  serviceType: NodePort
  serviceNodePort: 8200

vault 데이터를 저장할 backend storage는 raft 방식을 통해서 HA로 구성하고, 3개의 Vault 서버를 사용한다.
Vault UI는 nodeport 8200번을 사용한다.

만약 Calico CNI를 사용하고 있다면 injector.hostNetwork 옵션을 true로 선택해야 한다. (default 옵션은 false이며, Calico 환경에서 실행 시 Vault Injector가 제대로 동작하지 않음)
Vault Agent Injector Annotations are not creating : External EKS #731

Raft란?

Raft는 분산 시스템에서 일관성을 유지하기 위한 합의 알고리즘 중 하나로, 클러스터 내의 서버간에 데이터 일관성을 보장하고 다수결 합의를 기반으로 리더 서버를 선택하여 동작한다. Raft는 다운되거나 장애가 발생한 서버를 자동으로 복구할 수 있으며, 높은 가용성과 안정적인 운영을 보장한다.

1. vault 설치

# vault  네임스페이스 생성
$ kubectl crate ns vault


# helm install
$ cd vault
$ helm install vault . -f override-values.yaml

2. vault 초기화 및 봉인 해제

Vault Helm 차트를 설치한 후 Vault 서버 중 하나를 초기화 해야 합니다 . 초기화하면 모든 Vault 서버의 봉인을 해제하는 데 필요한 자격 증명이 생성된다.

  • pod 상태 확인
$ kubectl get pods --selector='app.kubernetes.io/name=vault' -n vault

NAME      READY   STATUS    RESTARTS   AGE
vault-0   0/1     Running   0          45s
vault-1   0/1     Running   0          45s
vault-2   0/1     Running   0          45s

초기에 READY 0/1 상태로 나오는 것이 정상이다. 여기서 초기화 및 봉인해제를 해야 1/1 로 변경된다.


  • vault 서버 초기화
$ kubectl exec -it vault-0 -n vault -- /bin/sh

#기본값 key-share=5 key-threshold=3
$ vault operator init

Unseal Key 1: q7hVrE9DLGZTTUwI248ep/Yv1551w/3NkAN33fyGSTm7
Unseal Key 2: i/mtNO0j6ACYNw8BtD4MAlHuOef4w0kkxhQaaSUS22iy
Unseal Key 3: gsGhzx09Sc6vdVzvraDTEk4nCEYUmGhJHRy5dbYzzm3k
Unseal Key 4: JJfoCquehit/FjrPz0/x8usFIckD38McNT6aa3Jw3ZUI
Unseal Key 5: 7QEK7gBGAfoJx+lk18jY1eW+TzruyKL7Eq866kOOAzpB

#Vault login시 필요한 인증토큰
Initial Root Token: s.1n5HUMIrUz3wzzHxF2ZEquS4

기본 키 공유 수와 기본 키 임계값을 사용하여 하나의 Vault 서버를 초기화합니다.
초기화 후 key 값과 토큰은 꼭 저장해야 한다.
다음과 같이 생성될 키와 인증할 키의 개수를 지정하는 방식으로도 초기화가 가능하다.
vault operator init -key-shares=3 -key-threshold=2


  • status
$ vault status

Key                Value
---                -----
Seal Type          shamir
Initialized        true
**Sealed             true**
Total Shares       5
Threshold          3
Unseal Progress    0/3
Unseal Nonce       n/a
Version            1.15.2
Build Date         2023-11-06T11:33:28Z
Storage Type       raft
HA Enabled         true

기본적으로 봉인해제를 하지 않으면 위와 같이 Sealed 의 상태가 true로 나오는데 봉인 해제를 해야 false로 변경되고 사용이 가능해진다.


  • unseal
$ vault operator unseal

Unseal Key (will be hidden): {INPUT_YOUR_KEY_LIKE_BELOW}

Key             Value
---             -----
Seal Type       shamir
Initialized     true
**Sealed          false**
Total Shares    5
Threshold       3
Version         1.15.2
Build Date      2023-11-06T11:33:28Z
Storage Type    raft
Cluster Name    vault-cluster-3b4976f8
Cluster ID      0a66788f-05cb-43ab-bf3a-be39e27c6dea
HA Enabled      true

vault operator unseal 명령어를 입력 후 초기화 과정에서 생성한 unseal key를 입력해준다.

기본 설정값은 3개의 키를 입력해야 하도록 설정되어 있어, 이 과정을 3번 반복하면 Sealed 의 상태가 false로 변경된다.

3. vault join

  • vault join
$ kubectl exec -it vault-1 -- /bin/sh

$ vault operator raft join http://vault-0.vault-internal:8200

Key       Value
---       -----
Joined    true

$ kubectl exec -it vault-2 -- /bin/sh

$ vault operator raft join http://vault-0.vault-internal:8200

Key       Value
---       -----
Joined    true

Join을 진행한 후 위에서 진행했던 unseal 과정을 반복

  • peer 확인
$ kubectl exec -it vault-0 -- /bin/sh
$ vault login # root token 입력

$ vault operator raft list-peers

Node                                    Address                        State       Voter
----                                    -------                        -----       -----
1dc07f10-68e5-5a6c-f643-b1e28e65668c    vault-0.vault-internal:8201    leader      true
9920923e-8739-6fde-2ab0-3bc14fe00d9c    vault-1.vault-internal:8201    follower    true
9db399e2-33aa-e5a4-091a-92801bdab300    vault-2.vault-internal:8201    follower    true

처음 봉인해제를 진행한 vault-0 이 leader로 선출되고, 나머지도 피어 리스트 확인


Vault 시크릿 생성하기

1. vault login

$ kubectl exec -it vault-0 -n vault -- /bin/sh

# Vault init 때 발급받은 root token 입력
$ vault login

2. key/value 생성

# 이름이 secrets인 K/V 엔진 생성
$ vault secrets enable -path=secret kv-v2

Success! Enabled the kv-v2 secrets engine at: secret/
# secret에 데이터 저장
$ vault kv put secret/test ID="test" password="passwd"

== Secret Path ==
secret/data/test

======= Metadata =======
Key                Value
---                -----
created_time       2024-01-16T05:59:05.063883379Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1
# 저장된 데이터 확인
$ vault kv get secret/test

== Secret Path ==
secret/data/test

======= Metadata =======
Key                Value
---                -----
created_time       2024-01-16T05:59:05.063883379Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1

====== Data ======
Key         Value
---         -----
ID          test
password    passwd

Vault 인증 설정 및 RBAC 권한부여

  • Vault Injector가 생성한 Pod의 init container와 sidecar container가 인증 받아야 하기 때문에 Kubernetes 인증관련 설정을 진행한다.
  • Kubernetes 인증은 Service Account의 토큰을 읽어 해당 토큰이 특정 네임스페이스들에 특정 이름들을 가지고 있는지로 검증한다.

1. kubernetes auth 활성화 및 설정

# kubernetes 인증 활성화
$ vault auth enable kubernetes

Success! Enabled kubernetes auth method at: kubernetes/
# kubernetes 인증 설정
$ vault write auth/kubernetes/config \
token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443" \
kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt

Success! Data written to: auth/kubernetes/config

2. Policy 생성 및 Role 연결

# secret 하위는 read만 가능하도록 설정
$ vault policy write secret-read - <<EOF
path "secret/*" {
  capabilities = ["read"]
}
EOF
$ k create serviceaccount test
# scret-read policy와 kubernetes test role 연결
# vault 네임스페이스에 secret-read 서비스어카운트에게 권한부여
# 만료시간 24시간 설정
$ vault write auth/kubernetes/role/test \
bound_service_account_names=test \
bound_service_account_namespaces=vault \
policies=secret-read \
ttl=24h

Vault-Injector를 사용하여 환경변수 주입하기

Vault Secret 사용 방법

  • Vault-Injector는 Sidecar 형태로 Vault agent가 주입된다. 그렇기에 사용되는 Deployment 특정 annotation 값을 설정해야 한다.
  • Vault-Injector를 사용하기 위한 annotation은 여러가지가 있으나, 아래 값만 설정하면 Sidecar 형태로 주입할 수 있다.

Vault annotations

vault.hashicorp.com/agent-inject = vault sidecar 주입 여부
vault.hashicorp.com/role = Vault 에이전트 자동 인증 방법에 사용되는 Vault 역할을 구성
vault.hashicorp.com/agent-inject-template-config = Vault Agent가 secret 렌더링에 사용해야 하는 템플릿을 구성

1. deploy.yaml 생성

apiVersion: apps/v1
kind: Deployment
metadata:
  name: vault-test
  namespace: vault
  labels:
    app: vault-test
spec:
  selector:
    matchLabels:
      app: vault-test
  replicas: 1
  template:
    metadata:
      annotations:
        vault.hashicorp.com/agent-inject: 'true'
        vault.hashicorp.com/role: 'test'
        vault.hashicorp.com/agent-inject-template-config: |
          {{- with secret "secret/data/test" -}}
          export ID="{{ .Data.data.ID }}"
          export password="{{ .Data.data.password }}"
          {{- end -}}
      labels:
        app: vault-test
    spec:
      serviceAccountName: test
      containers:
        - name: nginx
          image: nginx
          args:  ["sh", "-c", ". /vault/secrets/config && nginx -g 'daemon off;'"]
$ kubectl apply -f deployment.yaml

2. 배포된 pod 확인

$ kubectl get pod

NAME                                   READY   STATUS    RESTARTS   AGE
vault-test-77d888f9ff-lvh92            2/2     Running   0          14m

컨테이너는 총 3개이며, init container, vault-agent, nginx 컨테이너가 배포된 걸 확인할 수 있다.

  • init container

init container는 Vault에서 민감 데이터를 가져오는 역할을 한다.


  • vault-agent

vault-agent인 sidecar container는 Init container에서 가져온 정보를 주기적으로 동기화 하는 역할을 한다.

  • nginx

vault-agent가 가져온 시크릿이 vault/secret 경로에 매핑되어 있고, vault.hashicorp.com/agent-inject-template-config 설정에서 시크릿을 export하여 args에서 해당 환경변수를 주입해준다.


  • /vault/secrets 경로의 시크릿 확인
# nginx 컨테이너 진입
$ kubectl exec -it -c nginx vault-test-77d888f9ff-lvh92 -n vault -- /bin/sh

# 생성된 시크릿 확인
$ cat /vault/secrets/config

export ID="test"
export password="passwd"


다음 글은 Vault-Secrets-Operator를 사용하여 더 간편하게 Vault Secret을 사용하는 방법을 가이드합니다.

profile
Email:ehgns5669@gmail.com

0개의 댓글