본문 바로가기

Security/vault

[Vault] minikube에 vault 설치하기(with HA, TLS)

Vault란?

secret들을 저장하고 관리해 주는 도구이다. 오픈소스로 사용할 수도 있고 enterprise로 사용할 수도 있다. 근데 오픈소스로도 충분히 많은 기능을 가지고 있다.

secret이란 EC2나 다른 서버에 접근할 수 있는 SSH, IAM role 또는 user의 access key, 그 외 key/value값의 secret 등을 말한다.

Go로 작성되었고, 다양한 운영체제를 지원한다. 클라이언트와 서버모두 동일한 바이너리 파일을 사용한다.

 

설치

문서에서 설치(Install)로 나와있는 내용들을 보면 dev mode를 말한다.

dev와 production 모드는 다음과 같은 차이점이 있다.

✔️ dev는 SSL없이 localhost에서 띄운다.
      (production에서 ssl을 지원하지만 사용하지 않을 수도 있음)

✔️ dev는 in-memory 저장소에 저장된다. 즉 instance를 종료하면 디스크에 아무것도 남아있지 않는다.
      (production도 in-memory storage를 선택할 수는 있음)
✔️ 토큰이 캐시로 남아 unsealed 한 상태로 시작한다.

 

dev instance 실행 방법

1. 볼트 binary 다운로드

a. windows - use chocolatey

choco install vault

 

b. mac - use homebrew

brew tap hashicorp/tap
brew install hashicorp/tap/vault

 

c. linux - use your package manager

curl -fsSL <https://apt.releases.hashicorp.com/gpg> | sudo apt-key add -

sudo apt-add-repository "deb [arch=amd64] <https://apt.releases.hashicorp.com> $(lsb_release -cs) main"

sudo apt-get update && sudo apt-get install vault

 

2. 볼트 실행

vault server -dev

# Mac and macOS
export VAULT_ADDR=http://127.0.0.1:8200
# Windows PowerShell
$env:VAULT_ADDR="<http://127.0.0.1:8200>"

vault login

 

배포

볼트를 운영환경에서 사용하기 위해서는 볼트 서버를 배포(deploy)해야 한다. 그래서 문서에서 production의 설치 부분을 deploy 아래 적어놨다.

볼트 1.4 이전의 버전에서는 storage로 consul을 추천하는데, 1.4 이후부터는 통합 저장소(integrated storage)인 raft를 추천하고 있다.

통합 스토리지를 사용하면 vault와 따로 설치하지 않고 한번에 설치할 수 있어 모니터링이나 관리포인트가 줄어서 더 편하다.

실습 배포환경은 minikube이고 helm을 이용하여 배포한다.

 

1. helm chart 다운로드 와 압축해제

helm repo add hashicorp https://helm.releases.hashicorp.com
helm repo update
helm pull hashicorp/vault --untar

 

2. TLS설정을 위한 인증서 생성

a. 실습에 사용하기 위한 변수 설정

export VAULT_K8S_NAMESPACE="vault" \
export VAULT_HELM_RELEASE_NAME="vault" \
export VAULT_SERVICE_NAME="vault-internal" \
export K8S_CLUSTER_NAME="cluster.local" \
export WORKDIR="원하는 위치"

 

b. 2048bit의 개인키 생성

openssl genrsa -out ${WORKDIR}/vault.key 2048

 

c. CSR 생성. CSR은 인증서를 생성하기 위한 신청서라고 생각하면 된다. csr의 구성파일을 이용하여 csr을 생성한다.

# CSR 구성파일 생성
cat > ${WORKDIR}/vault-csr.conf <<EOF
[req]
default_bits = 2048
prompt = no
encrypt_key = yes
default_md = sha256
distinguished_name = kubelet_serving
req_extensions = v3_req
[ kubelet_serving ]
O = system:nodes
CN = system:node:*.${VAULT_K8S_NAMESPACE}.svc.${K8S_CLUSTER_NAME}
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = *.${VAULT_SERVICE_NAME}
DNS.2 = *.${VAULT_SERVICE_NAME}.${VAULT_K8S_NAMESPACE}.svc.${K8S_CLUSTER_NAME}
DNS.3 = *.${VAULT_K8S_NAMESPACE}
IP.1 = 127.0.0.1
EOF

# 개인키를 이용하여 CSR 생성. CSR이란 인증서 신청 형식 데이터이다. 즉 인증서를 생성하기위해 나 이런 사람이에요 하고 작성해놓은 신청서 같은 것
openssl req -new -key ${WORKDIR}/vault.key -out ${WORKDIR}/vault.csr -config ${WORKDIR}/vault-csr.conf

 

d. 쿠버네티스로 csr을 전송하여 인증한다.

# 쿠버네티스에서 이용할 수 있는 CSR을 생성
cat > ${WORKDIR}/csr.yaml <<EOF
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
   name: vault.svc
spec:
   signerName: kubernetes.io/kubelet-serving
   expirationSeconds: 8640000
   request: $(cat ${WORKDIR}/vault.csr|base64|tr -d '\n')
   usages:
   - digital signature
   - key encipherment
   - server auth
EOF

# 생성된 CSR을 쿠버네티스에 전송
kubectl create -f ${WORKDIR}/csr.yaml
# 인증서 승인
kubectl certificate approve vault.svc

 

e. crt 인증서를 생성하고 쿠버네티스 클러스터의 ca인증서를 추출한다. 여기서 말하는 ca인증서는 클러스터의 root CA를 말하고 클러스터의 정보를 담고 있는 ~/. kube/config 파일에서도 확인할 수 있다.

kubectl config view \
--raw \
--minify \
--flatten \
-o jsonpath='{.clusters[].cluster.certificate-authority-data}' \
| base64 -d > ${WORKDIR}/vault.ca

 

d. 위에서 생성한 개인키(vault.key), 개인키로 생성한 인증서(vault.crt), root ca인증서(vault.ca)의 내용이 담긴 secret을 생성한다. 해당 시크릿은 볼트가 구동될 때 사용된다.

# 네임스페이스 생성
kubectl create namespace $VAULT_K8S_NAMESPACE

# 시크릿 생성
kubectl create secret generic vault-ha-tls \
   -n $VAULT_K8S_NAMESPACE \
   --from-file=vault.key=${WORKDIR}/vault.key \
   --from-file=vault.crt=${WORKDIR}/vault.crt \
   --from-file=vault.ca=${WORKDIR}/vault.ca

 

3. helm으로 vault 배포

위에서 다운받아 압축을 해제한 value를 수정해도 되고, 다음의 value파일을 사용해도 된다.

ha를 위해 raft를 사용하였다.

cat > ${WORKDIR}/overrides.yaml <<EOF
global:
   enabled: true
   tlsDisable: false
injector:
   enabled: true
server:
   extraEnvironmentVars:
      VAULT_CACERT: /vault/userconfig/vault-ha-tls/vault.ca
      VAULT_TLSCERT: /vault/userconfig/vault-ha-tls/vault.crt
      VAULT_TLSKEY: /vault/userconfig/vault-ha-tls/vault.key
   volumes:
      - name: userconfig-vault-ha-tls
        secret:
         defaultMode: 420
         secretName: vault-ha-tls
   volumeMounts:
      - mountPath: /vault/userconfig/vault-ha-tls
        name: userconfig-vault-ha-tls
        readOnly: true
   standalone:
      enabled: false
   affinity: ""
   ha:
      enabled: true
      replicas: 3
      raft:
         enabled: true
         setNodeId: true
         config: |
            ui = true
            listener "tcp" {
               tls_disable = 0
               address = "[::]:8200"
               cluster_address = "[::]:8201"
               tls_cert_file = "/vault/userconfig/vault-ha-tls/vault.crt"
               tls_key_file  = "/vault/userconfig/vault-ha-tls/vault.key"
               tls_client_ca_file = "/vault/userconfig/vault-ha-tls/vault.ca"
            }
            storage "raft" {
               path = "/vault/data"
            }
            disable_mlock = true
            service_registration "kubernetes" {}
EOF

 

헬름 배포

helm install -n $VAULT_K8S_NAMESPACE $VAULT_HELM_RELEASE_NAME hashicorp/vault -f ${WORKDIR}/overrides.yaml

 

4. 봉인해제

vault를 배포하면 바로 사용할 수 없고, 리더 볼트에서 키를 생성하여 다른 볼트들과 공유해주어야 한다.

그래도 일단 배포하면 서비스를 포트포워딩 하여 UI에 접근할 수 있는데, unseal상태에서 UI에 접근하면 다음과 같은 화면이 뜬다.

포트포워딩

kubectl port-forward -n $VAULT_K8S_NAMESPACE service/vault 8200:8200

pod로그는 아래처럼 뜬다.

vault-0에 접근하여 operator init 명령어로 root key를 생성한다. unseal에 이용할 키를 추출한다.

# root key생성
kubectl exec -n $VAULT_K8S_NAMESPACE vault-0 -- vault operator init \
    -key-shares=1 \
    -key-threshold=1 \
    -format=json > ${WORKDIR}/cluster-keys.json

# unseal key 추출
jq -r ".unseal_keys_b64[]" ${WORKDIR}/cluster-keys.json

# 환경변수 설정
VAULT_UNSEAL_KEY=$(jq -r ".unseal_keys_b64[]" ${WORKDIR}/cluster-keys.json)

 

vault-0 봉인해제

kubectl exec -n $VAULT_K8S_NAMESPACE vault-0 -- vault operator unseal $VAULT_UNSEAL_KEY

 

나머지 vault-1 과 vault-2를 raft cluster에 join 시키고 봉인을 해제한다.

# vault-1 접속
kubectl exec -n $VAULT_K8S_NAMESPACE -it vault-1 -- /bin/sh
# vault-1 raft cluster join
vault operator raft join -address=https://vault-1.vault-internal:8200 -leader-ca-cert="$(cat /vault/userconfig/vault-ha-tls/vault.ca)" -leader-client-cert="$(cat /vault/userconfig/vault-ha-tls/vault.crt)" -leader-client-key="$(cat /vault/userconfig/vault-ha-tls/vault.key)" https://vault-0.vault-internal:8200
# vault-1 unseal
kubectl exec -n $VAULT_K8S_NAMESPACE -ti vault-1 -- vault operator unseal $VAULT_UNSEAL_KEY

동일하게 vault-2 반복

 

5. 볼트 로그인

처음에 볼트를 배포하면 토큰으로 밖에 접근할 수 없다. 로그인 후 auth mehod추가하면 다양한 방법으로 로그인할 수 있다.

# root token 추출
export CLUSTER_ROOT_TOKEN=$(cat ${WORKDIR}/cluster-keys.json | jq -r ".root_token")
# vault login
kubectl exec -n $VAULT_K8S_NAMESPACE vault-0 -- vault login $CLUSTER_ROOT_TOKEN

 

해당 토큰 이용하여 UI도 접근할 수 있다.

TLS를 설정하였지만 내가 인증한 인증서라 인정받지 못했다.

그렇다고 https가 아닌 http로 접근하면 https로 이동했다고 뜬다.

 

vault에 로그인 후 raft에 join되어 있는 vault들을 확인할 수 있다.

 


reference : https://developer.hashicorp.com/vault/tutorials/kubernetes/kubernetes-minikube-tls