Pod から外部のVault を利用する - その1

自分備忘録用メモ。前の記事でデプロイしたTKGm v1.4 環境にデプロイしたPod から、外部Vault を利用する際の手順です。


手順

Vault のデプロイ

Vault のバックエンドにはConsul を利用し、Vault をデプロイしていきます。Vault のconfiguration は以下の様な形で設定しています。

storage "consul" {
   address = "127.0.0.1:8500"
   path = "vault"
}

listener "tcp" {
  address     = "127.0.0.1:8200"
  tls_disable = 1
}

listener "tcp" {
  address     = "<VAULT_CONSUL_HOST_IP_ADDRESS>"
  tls_disable = 1
}

ui = true
disable_mlock = true

telemetry {
  prometheus_retention_time = "30s"
  disable_hostname = true
}
 
Consul は以下のコマンドでVault と同じホスト上にデプロイしています。
consul agent -server -bind=127.0.0.1 -client=127.0.0.1 \
    -data-dir=/home/demo/consul/localdata -bootstrap-expect=1 \
    -ui -dns-port=8600 -config-dir=/home/demo/consul/consul.d \
    -datacenter=lab 

Consul を起動したら、Vault を以下の様に起動します。
vault server -config /home/demo/vault/cluster.config start
 
別ターミナルから、Vault の初期化を行います。
vault operator init -n 3 -t 2 > init.out
Vault はsealed 状態なので、init.out に記載されているUnseal Key を利用して、unseal します。3つのUnseal Keyの任意の2つを使って、vault operator unsealコマンドを2度実行します。 unseal されたら、vault login $ROOT_TOKEN します。最初の状態で利用出来るシークレットエンジンを確認してみます。
$ vault secrets list
Path          Type         Accessor              Description
----          ----         --------              -----------
cubbyhole/    cubbyhole    cubbyhole_f9f51ce2    per-token private secret storage
identity/     identity     identity_3d9d24b3     identity store
sys/          system       system_7f338446       system endpoints used for control, policy and debugging
 

Vault Key-Value シークレットエンジンの有効化

まず、Vault のKey-Value シークレットエンジンを有効化します。パスはsecret を指定します。
$ vault secrets enable -path=secret -version=2 kv
Success! Enabled the kv secrets engine at: secret/
$ vault secrets list
Path          Type         Accessor              Description
----          ----         --------              -----------
cubbyhole/    cubbyhole    cubbyhole_f9f51ce2    per-token private secret storage
identity/     identity     identity_3d9d24b3     identity store
secret/       kv           kv_62aab966           n/a
sys/          system       system_7f338446       system endpoints used for control, policy and debugging
 
有効化したKVシークレットに値をインプットします。
$ vault kv put secret/devwebapp/config username='giraffe' password='salsa'
Key              Value
---              -----
created_time     2021-10-02T04:16:33.641430016Z
deletion_time    n/a
destroyed        false
version          1
 
入力した値を確認してみます。
$ vault kv get secret/devwebapp/config
====== Metadata ======
Key              Value
---              -----
created_time     2021-10-02T04:16:33.641430016Z
deletion_time    n/a
destroyed        false
version          1

====== Data ======
Key         Value
---         -----
password    salsa
username    giraffe
$ vault kv get -format=json secret/devwebapp/config | jq ""
{
  "request_id": "19c5526f-73df-ee95-2716-f27e1bf17562",
  "lease_id": "",
  "lease_duration": 0,
  "renewable": false,
  "data": {
    "data": {
      "password": "salsa",
      "username": "giraffe"
    },
    "metadata": {
      "created_time": "2021-10-02T04:16:33.641430016Z",
      "deletion_time": "",
      "destroyed": false,
      "version": 1
   },
  "warnings": null
  }
}
$ vault kv get -format=json secret/devwebapp/config | jq ".data.data"
{
  "password": "salsa",
  "username": "giraffe"
}
 
ここでセットしたKVデータを後ほど、Pod から利用します。
 

Kubernetes 環境の準備 

こちらの手順と同様にTanzu Kubernetes Cluster(TKC) を準備します。TKC 上にサンプルPod 等はデプロイしていきます。
 

Pod のデプロイ

TKC 環境にサンプルPod をデプロイします。先ずは、Pod のマニフェストファイルにVault アドレス等を記述する方法で、試してみます。
最初に、TKC を操作するノードから、ちゃんとVault にアクセス出来るか確認します。
EXTERNAL_VAULT_ADDR=<VAULT_CONSUL_HOST_IP_ADDRESS>
$ curl -s http://$EXTERNAL_VAULT_ADDR:8200/v1/sys/seal-status |jq .
{
  "type": "shamir",
  "initialized": true,
  "sealed": false,
  "t": 2,
  "n": 3,
  "progress": 0,
  "nonce": "",
  "version": "1.8.3",
  "migration": false,
  "cluster_name": "vault-cluster-bc9ad85a",
  "cluster_id": "560afc11-e7a3-1e31-c67f-717a4734aab8",
  "recovery_seal": false,
  "storage_type": "consul"
}

次に、サービスアカウントを作成します。
k create ns vault-test
k -n vault-test create sa internal-app
 
続いて、マニフェストファイルを作成します。
$ cat > devwebapp.yaml <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: devwebapp
  labels:
    app: devwebapp
spec:
  serviceAccountName: internal-app
  containers:
    - name: app
      image: burtlo/devwebapp-ruby:k8s
      env:
      - name: VAULT_ADDR
        value: "http://$EXTERNAL_VAULT_ADDR:8200"
      - name: VAULT_TOKEN
        value: "s.********"
EOF

作成したマニフェストを利用し、Pod を作成します。
k -n vault-test apply -f devwebapp.yaml
$ k -n vault-test get pods
NAME        READY   STATUS    RESTARTS   AGE
devwebapp   1/1     Running   0          98s

作成したPod でコマンドを実行し、Vault のK/V シークレットエンジンにストアした値を取得します。
$ k -n vault-test exec devwebapp -- curl -s localhost:8080; echo
{"password"=>"salsa", "username"=>"giraffe"}
 
これは作成したPod devwebapp からWeb アプリケーションは、マニフェストファイルに記載されたトークンを使い、外部の Vault サーバーにリクエストを行い、secret/data/devwebapp/config というパスに定義されたシークレットの情報を、Vault からのレスポンスという形で取得しています。
次に、Vault 用のサービスをTKC に作成する形で外部のVault とやり取りする方法を確認します。

外部Vault 用のサービスとエンドポイントのデプロイ

TKC 環境にサービスとエンドポイントをデプロイし、そのサービスを利用し、Pod と外部のVault でやり取り出来る様に設定します。
$ cat > external-vault.yaml <<EOF
---
apiVersion: v1
kind: Service
metadata:
  name: external-vault
  namespace: vault-test
spec:
  ports:
  - protocol: TCP
    port: 8200
---
apiVersion: v1
kind: Endpoints
metadata:
  name: external-vault
subsets:
  - addresses:
      - ip: <VAULT_CONSUL_HOST_IP_ADDRESS>
    ports:
      - port: 8200
EOF
k -n vault-test apply -f external-vault.yaml
$ k -n vault-test get all
NAME            READY   STATUS    RESTARTS   AGE
pod/devwebapp   1/1     Running   0          3m42s

NAME                     TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/external-vault   ClusterIP   100.67.97.253   <none>        8200/TCP   4s

デプロイしてあるdevwebapp からサービスを経由し、外部のVault にアクセス出来るか確認してみます。
$ kubectl -n vault-test exec devwebapp -- curl -s http://external-vault:8200/v1/sys/seal-status | jq
{
  "type": "shamir",
  "initialized": true,
  "sealed": false,
  "t": 2,
  "n": 3,
  "progress": 0,
  "nonce": "",
  "version": "1.8.3",
  "migration": false,
  "cluster_name": "vault-cluster-bc9ad85a",
  "cluster_id": "560afc11-e7a3-1e31-c67f-717a4734aab8",
  "recovery_seal": false,
  "storage_type": "consul"
}

次にマニフェストファイルをdevwebapp-through-service.yaml として作成し、上で作成したサービス経由でVault のシークレットを取得出来るか確認します。
$ cat > pod-devwebapp-through-service.yaml <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: devwebapp-through-service
  namespace: vault-test
  labels:
    app: devwebapp-through-service
spec:
  serviceAccountName: internal-app
  containers:
    - name: app
      image: burtlo/devwebapp-ruby:k8s
      env:
      - name: VAULT_ADDR
        value: "http://external-vault:8200"
      - name: VAULT_TOKEN
        value: "s.********"
EOF
k -n vault-test apply -f pod-devwebapp-through-service.yaml
$ k -n vault-test get pods
NAME                        READY   STATUS    RESTARTS   AGE
devwebapp                   1/1     Running   0          9m16s
devwebapp-through-service   1/1     Running   0          81s
$ k -n vault-test exec devwebapp-through-service -- curl -s localhost:8080 ; echo
{"password"=>"salsa", "username"=>"giraffe"}
 
無事サービス経由で外部のVault からシークレットを取得する事が出来ました。
次に、Helm チャートを用いて、外部Vault と連携するためのVault Agent Injector を利用した方法を試してみます。
Vault Agent Injector は、Pod のマニフェストに記述したアノテーションの情報を元に、Vault の認証とシークレットの取得が出来る様になります。

このブログの人気の投稿

Tanzu Mission Control(TMC) の NetworkPolicy を試してみる