LoginSignup
18
12

More than 3 years have passed since last update.

[Kubernetes] Vaultに格納された機密情報を永続ストレージとしてマウント

Last updated at Posted at 2019-08-13

概要

 Kubernetes環境において、CI/CDによる自動化を行おうと試みた時など、パスワードやアクセストークンなどの機密情報をどうやって保存しようかを悩んだことがある人は多いのではないでしょうか。パスワードを書いたManifestをGitHubなどにPushするのは、抵抗があるかと思います。今回はHashiCorp社が提供している機密情報を管理するためのソフトウェアVaultに格納された機密情報をKuberntes上のストレージとしてマウントするsecrets-store-csi-driverの動作検証を行います。
 なお、secrets-store-csi-driverはKubeCon 2019 EUにてHashiCorp社とMS社のメンバによって発表されています。その時の資料はこちらです。

検証環境

  • Kubernetes v1.14.1
  • secrets-store-csi-driver v0.0.3

事前準備

事前準備として、動作検証で利用するVaultのサーバを立ち上げます。

ダウンロード

GitHubのリポジトリからsecrets-store-csi-driverをダウンロードします。

$ git clone git@github.com:deislabs/secrets-store-csi-driver.git
$ cd secrets-store-csi-driver

Vaultコマンドもダウンロードサイトからダウンロードし、パスの通ったディレクトリにコピーします。

検証用Vaultのデプロイ

次に、検証用に利用するVaultをKubernetes上にデプロイします。
このVaultは、データを永続していないため、Podが削除されるとともに格納したデータは全て削除されます。あくまで検証用として利用してください。

$ kubectl apply -f pkg/providers/vault/examples/vault.yaml

デプロイすると、以下のようにVaultのPodが起動します。

$ kubectl get pod -l app=vault  
NAME                    READY   STATUS    RESTARTS   AGE
vault-f585568d9-9sbp2   1/1     Running   1          3m17s

$ kubectl get service -l app=vault
NAME    TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
vault   NodePort   10.101.58.66   <none>        8200:31899/TCP   67m

VaultをKubernetesクラスタの外部からアクセスできるようにport forwardを行います。

$ kubectl port-forward vault-f585568d9-9sbp2 8200:8200 &

実際に、Vaultにアクセスし、起動していることを確認を行います。

$ export VAULT_ADDR="http://127.0.0.1:8200"
$ export VAULT_TOKEN="root"
i$ vault status
Key             Value
---             -----
Seal Type       shamir
Initialized     true
Sealed          false
Total Shares    1
Threshold       1
Version         1.1.1
Cluster Name    vault-cluster-692252b2
Cluster ID      60c527f1-2e28-70c5-88fc-55afcc01ec43
HA Enabled      false

これで検証に利用するVault(サーバ)のセットアップが終わりました。

動作検証

VaultにKubernetesからのアクセスできるように認証設定したのち、secrets-store-csi-driverをデプロイしていきます。なお、今回の検証では、Kubernetesのdefaultネームスペースを利用します。

Kubernetesでのサービスアカウントの作成

まず、Kubernetes上にVaultへのアクセス用のサービスアカウントを作成します。

$ kubectl create serviceaccount vault-auth
$ kubectl apply -f - <<EOH
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: role-tokenreview-binding
  namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:auth-delegator
subjects:
- kind: ServiceAccount
  name: vault-auth
  namespace: default
EOH

VaultにKubernetesの認証情報を設定

次に、Vaultに、Kubernetesの認証情報を設定します。
まず初めに、必要な証明書やトークンなどの情報をKubernetesの各種リソースから取得します。

$ CLUSTER_NAME="$(kubectl config view -o json | jq -r .clusters[].name)"

$ SECRET_NAME="$(kubectl get serviceaccount vault-auth \
  -o go-template='{{ (index .secrets 0).name }}')"

$ TR_ACCOUNT_TOKEN="$(kubectl get secret ${SECRET_NAME} \
  -o go-template='{{ .data.token }}' | base64 --decode)"

$ K8S_HOST="$(kubectl config view --raw \
  -o go-template="{{ range .clusters }}{{ if eq .name \"${CLUSTER_NAME}\" }}{{ index .cluster \"server\" }}{{ end }}{{ end }}")"

$ K8S_CACERT="$(kubectl config view --raw \
  -o go-template="{{ range .clusters }}{{ if eq .name \"${CLUSTER_NAME}\" }}{{ index .cluster \"certificate-authority-data\" }}{{ end }}{{ end }}" | base64 --decode)"

なお、幾つかの値は、KuberntesのConfig情報から取得しています。
Config情報に複数のKubernetesクラスタが登録している場合は、正しいKuberntesクラスタの情報が取得されているかを確認してください。
次に、Vaultの認証でkubernetesを有効にします。

$ vault auth enable kubernetes

続いて、kubernetesの認証情報として、先に取得した証明書やトークンの情報を設定します。

$ vault write auth/kubernetes/config \
  kubernetes_host="${K8S_HOST}" \
  kubernetes_ca_cert="${K8S_CACERT}" \
  token_reviewer_jwt="${TR_ACCOUNT_TOKEN}"

続いて、今回の動作検証で利用する秘密情報のアクセスポリシーをVaultに設定します。

$ echo 'path "secret/data/foo" {
  capabilities = ["read", "list"]
}

path "sys/renew/*" {
  capabilities = ["update"]
}' | vault policy write example-readonly -
$ vault write auth/kubernetes/role/example-role \
  bound_service_account_names=csi-driver-registrar \
  bound_service_account_namespaces=default \
  policies=default,example-readonly \
  ttl=20m

これでVaultの認証情報の設定は終わりです。
次に、動作検証で利用するサンプルの機密情報をfooに格納します。

$ vault kv put secret/foo bar=hello

secrets-store-csi-driverのデプロイ

secrets-store-csi-driverをデプロイします。

$ kubectl apply -f deploy/crd-csi-driver-registry.yaml
$ kubectl apply -f deploy/rbac-csi-driver-registrar.yaml
$ kubectl apply -f deploy/rbac-csi-attacher.yaml
$ kubectl apply -f deploy/csi-secrets-store-attacher.yaml
$ kubectl apply -f pkg/providers/vault/examples/secrets-store-csi-driver.yaml

defaultネームスペース以外のNamespaceにデプロイする場合は,Manifestを編集する必要があります。
secrets-store-csi-driverがデプロイされたことを確認します。

$ kubectl get sts,ds
NAME                                          READY   AGE
statefulset.apps/csi-secrets-store-attacher   1/1     6m42s

NAME                                     DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR              AGE
daemonset.extensions/csi-secrets-store   2         2         2       2            2           <none>          112s

Vaultに格納された機密情報をPV,PVCでマウント

Vaultに格納された機密情報をマウントするためのPVのManifest(pv-vault-csi.yaml)を作成します。

pv-vault-csi.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-vault
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadOnlyMany
  persistentVolumeReclaimPolicy: Retain
  csi:
    driver: secrets-store.csi.k8s.com
    readOnly: true
    volumeHandle: kv
    volumeAttributes:
      providerName: "vault"
      roleName: "example-role"
      vaultAddress: "http://10.101.58.66:8200"  # VaultのURL
      vaultSkipTLSVerify: "true"
      objects:  |
        array:
          - |
            objectPath: "/foo"  # Vaultのセットアップにて格納したSecretのパス名
            objectName: "bar"   # 上記Secretに格納したKey名
            objectVersion: ""

続いて、PV(pv-vault)と対になるPVCのManifest(pvc-vault-csi-static.yaml)を作成します。

pvc-vault-csi-static.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: pvc-vault
spec:
  accessModes:
    - ReadOnlyMany
  resources:
    requests:
      storage: 1Gi
  volumeName: pv-vault
  storageClassName: ""

定義したPVとPVCをデプロイします。

$ kubectl apply -f pv-vault-csi.yaml
$ kubectl apply -f pvc-vault-csi-static.yaml

PVCとPVがBoundされたのを確認します。

$ kubectl get pvc,pv
NAME                              STATUS   VOLUME     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/pvc-vault   Bound    pv-vault   1Gi        ROX                           84s

NAME                              CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                      STORAGECLASS    REASON   AGE
persistentvolume/pv-vault         1Gi        ROX            Retain           Bound    default/pvc-vault                                 2m34s

次に、デプロイしたPVC,PVをマウントするPodのManifestを作成します。

nginx-pod-vault.yaml
kind: Pod
apiVersion: v1
metadata:
  name: nginx-vault
spec:
  containers:
  - image: nginx
    name: nginx-vault
    envFrom:
    - configMapRef:
        name: vault-cm
    volumeMounts:
    - name: vault01
      mountPath: "/mnt/vault" # Vault mount point.
      readOnly: true
  volumes:
  - name: vault01
    persistentVolumeClaim:
      claimName: pvc-vault

Podをデプロイします。

$ kubectl apply -f nginx-pod-vault.yaml

続いて、PodがVaultに格納された機密情報をマウントできているかを確認します。
Podにアタッチしたのち、マウントしたディレクトリ/mnt/vaultを確認します。

$ kubectl exec -ti nginx-vault /bin/bash
root@nginx-vault:/# ls /mnt/vault/
foo
root@nginx-vault:/# cat /mnt/vault/foo 
hello

結果、Vaultの機密情報が格納されたパスfooの値が/mnt/vault/fooに格納されています。
このように、Vaultに格納された機密情報をPVC,PVを通じて参照することが可能となります。

感想

今回の動作検証では、機密情報を格納するためのリポジトリであるVaultに格納したデータを、Kubernetesで永続ストレージ(PVC,PV)としてマウントする方法を動作検証しました。これにより、容易にVaultに格納されたパスワードやアクセストークンをKubernetes上で読み出すことが可能となります。
ついつい利便性を優先し、アプリケーションのパスワードやアクセストークンなどの機密情報の管理が甘くなりがちな人もいるかもしれません。今回検証したVaultとKubernetsの連携のハードルは高くないので、ぜひ選択肢のひとつとしてご検討ください。

おまけ(Tips)

環境変数として利用したい場合は以下のように設定できます。

$ export FOO=`cat /mnt/vault/foo`

参考情報

18
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
12