虎の穴開発室ブログ

虎の穴ラボ株式会社所属のエンジニアが書く技術ブログです

MENU

HashiCorp Vault でのシークレット管理 #虎の穴ラボ Advent Calendar 2023

本記事は 虎の穴ラボ Advent Calendar 2023 14日目の記事です。

  • 本記事のテーマは「HashiCorp Vault でのシークレット管理」です!
  • 前回はY.N.さんによる「モダンなshell 「Nushell」 を触ってみる」でした。
  • 次回はgodanさんによる「Akihabara入門 at akihabara」です。ご期待ください!

はじめに

こんにちは。虎の穴ラボ株式会社のかなざわです。

KubernetesのSecretリソースで管理される秘密情報は、マニフェスト記載のままだとbase64でエンコードされているのみのため、簡単に復号できてしまいます。そのような情報をGitなどのVCSへコミットして管理する場合は、第三者が復号できないように気をつける必要があります。

個人で構築し利用しているKubernetesでも、GitでSecretを管理する場合はSealedしたものをコミットするようにしていました。第三者が復号できないという観点ではこの方法でもよいのですが、他の管理方法として HashiCorp Vaultを使用する方法もよさそうでしたので、今回試してみました。

HashiCorp Vault とは

HashiCorp VaultはHashiCorp社で開発されているシークレット管理ソリューションです。2023年12月現在、以下の形で利用できるものとなっています。

  • 無償版 (Vault OSS)
  • 商用版 (Hashicrop Vault Enterprise)
  • マネージド版 (HCP Vault)

以下のようなことが機能として存在しています。ユーザーもしくはアプリケーションに対して必要とされるシークレットをアクセス制御に基づき提供する機能と理解しています。

  • 秘密情報の集中管理
  • 秘密情報を暗号化して保存
  • 秘密情報へのアクセスを制御(認証認可)
  • 秘密情報のローテーション

アクセス手段

GUIが提供されます。ブラウザからアクセスして、秘密情報やアクセスする権限を設定できます。

また、CLIも提供されています。vaultコマンドを利用してコマンドラインからVaultを操作することが可能です。APIも提供されているため、例えばアプリケーションから秘密情報の取得や登録・暗号化といったことが可能になります。

環境情報

今回はKubernetesにVault(Vault OSS)をデプロイし内容を見ていきます。KubernetesとVault OSSの環境情報は以下となります。公式ページにサポートされているKubernetesバージョンの記載があるのでそちらも参考にします。

  • Kubernetes
HW: Raspberry Pi4 Model B x3 (ControlNode x1, WorkerNode x2)
Version: v1.25.4
  • Vault OSS
Version: 1.15.2

Kubernetes へのデプロイ

Vault OSSをKubernetesへデプロイします。今回はHelmを利用してデプロイを進めていきます。Helmのページを参考にして行います。Helmを利用してデプロイが進められるように、事前にHelmのインストールを実施しておきます。

Helmのリポジトリリストに今回のリポジトリを加えます。リポジトリを加えた後にアップデートを行い、最新化しておきます。

❯ helm repo add hashicorp https://helm.releases.hashicorp.com
"hashicorp" has been added to your repositories

❯ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "hashicorp" chart repository
Update Complete. ⎈Happy Helming!⎈

早速デプロイを行います。-n vault --create-namespaceの引数を付け、今回用のnamespaceであるvaultも同時に作成しています。

❯ helm install vault hashicorp/vault  -n vault --create-namespace
NAME: vault
LAST DEPLOYED: Sat Dec  2 15:50:07 2023
NAMESPACE: vault
STATUS: deployed
REVISION: 1
NOTES:
Thank you for installing HashiCorp Vault!

Now that you have deployed Vault, you should look over the docs on using
Vault with Kubernetes available here:

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


Your release is named vault. To learn more about the release, try:

  $ helm status vault
  $ helm get manifest vault

デプロイされたようです。Podがデプロイされたかどうかを確認します。

❯ kubectl get pod -n vault
NAME                                   READY   STATUS    RESTARTS   AGE
vault-0                                0/1     Pending   0          11m
vault-agent-injector-dc89c4549-crdql   1/1     Running   0          11m

vault-0Pendingとなっており、正常にデプロイされていませんでした。ログを確認します。 - PersistenceVolumeClaimログ

Events:
   Type    Reason         Age                    From                         Message
----    ------         ----                   ----                         -------
 Normal  FailedBinding  27s (x922 over 3h50m)  persistentvolume-controller  no persistent volumes available for this claimand no storage class is set

PVCのログから、StorageClassとPersistenceVolumeを作成していないことが原因のように見えました。一度、今回のデプロイをhelm uninstallで削除したあとに、上記2つのリソースを作成した上で、helm installを行います。--setでstorageClassの名前を指定し、作成したリソースを参照させるようにしました。

❯ helm install vault hashicorp/vault --set "server.dataStorage.storageClass=vault-storage" -n vault --create-namespace

再びPodステータスを確認すると、STATUSRunningとなりました。vault-0READY0/1となっていることは問題ありません。

❯ kubectl get pod -n vault
NAME                                   READY   STATUS    RESTARTS   AGE
vault-0                                0/1     Running   0          35s
vault-agent-injector-dc89c4549-t5wkr   1/1     Running   0          35s

Vaultの初期化とUnseal

Vaultの起動直後はSealedという状態となっており使用することができません。使用できるようにするには、初期化時に払い出されるUnsealキーを渡します。

Vaultは秘密情報を登録するときに暗号化して保存されます。暗号化に用いるキーも、別のキー(ルートキー)によって暗号化されています。そして、このルートキーもまた別のキー(シェアリングキー)で暗号化されています。このシェアリングキーがUnsealキーとなり、Unsealキーの提供でルートキーが復号できる(データの暗号化・復号化が可能になった)ようになることを、Unsealingと呼び実施しています。公式ではこちらのページで説明されています。

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

初期化および必要な認証情報を生成するために、pod 内でvault operator initのコマンドを実施し処理を進めます。

❯  kubectl exec -it vault-0 -n vault -- vault operator init
Unseal Key 1: WEpA+0/QSBZi78aXRFnaUDMGrD+2ttUwRjm3pIwiD9SH
Unseal Key 2: xmcGOmd4Xum7j6E1qhCOs+rpxJoE1NWFJu7pzs5Gnnlx
Unseal Key 3: 147ibpMgnjTxQQeXP+uZZCetKoiiWOT+gvp2uR2aZJpR
Unseal Key 4: sc67bm1+xGUwfS7TxJmCoWWqms7dznJHr/YMck9kkyNF
Unseal Key 5: 7pOwuFatKykqbiU4VHQbB6odtFpcGqnhYJ7izYwzVnrX

Initial Root Token: hvs.1IoD1jqLjsQTNmPGhUl1RA1B

Vault initialized with 5 key shares and a key threshold of 3. Please securely
distribute the key shares printed above. When the Vault is re-sealed,
restarted, or stopped, you must supply at least 3 of these keys to unseal it
before it can start servicing requests.

(略)

払い出されたUnsealキーを使用してUnsealingを行います。基本的に3回行う(Unsealキーを3つ使用する)ことでVaultが使用できるようになります。下記のように、Sealedの値がfalseとなったことを確認します。

❯ kubectl exec -it vault-0 -n vault -- vault operator unseal
❯ kubectl exec -it vault-0 -n vault -- vault operator unseal
❯ kubectl exec -it vault-0 -n vault -- vault operator unseal
Unseal Key (will be hidden):

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    file
Cluster Name    vault-cluster-c65d5f88
Cluster ID      ffebfd89-df37-7323-47bc-c8496cd5aa0a
HA Enabled      false

ここで再びPodステータスを確認すると、vault-0READY1/1となりました。

❯ kubectl get pod -n vault
NAME                                   READY   STATUS    RESTARTS   AGE
vault-0                                1/1     Running   0          10m
vault-agent-injector-dc89c4549-t5wkr   1/1     Running   0          10m

Vaultへの秘密情報の登録

Vaultへの認証

Vaultの使用準備が整いましたので、Vaultへ秘密情報を登録していきます。

登録方法は、Vaultが動作しているPodへシェルログインのほかに、Kubernetesの外からvaultコマンドを使用する方法があります。vaultコマンドを使用してアクセスする場合はvaultコマンドのインストールおよび、以下のようにServiceリソースのポートフォワードなどを行い、遠隔からアクセスできるようにしてあげる必要があります。

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

今回はブラウザからアクセスし設定を行います。上記の設定を施した状態で、http://127.0.0.1:8200とアクセスすると、Vault UIにアクセスできます。Vaultの初期化時に払い出されたRootTokenを入力してログインします。

Vault への秘密情報の登録

秘密情報の形式を指定して登録できるように有効化したあと、秘密情報を登録します。Vaultでは、こちらのページに記載されているようなさまざまな秘密情報の形式に対応しています。

今回はKeyVaule形式の秘密情報を有効化します。メニューの「Secret Engines」から「Enable new engine」と進み、秘密情報の形式をkvを選択して「Next」をクリックします。「Path」をkv-secretとして「Enable Engine」を選択します。

秘密情報を登録します。先程作成された「secret」をクリックし「Create Secret」と進み、

  • Path for this secret: info
  • Secret data: SECRET_KEYSECRET_VALUE

を入力して、「Save」をクリックします。なおPathは空欄で設定できません。

Kubernetesから秘密情報を参照する

Vaultに秘密情報を登録したので、Kubernetesリソース(Pod)から秘密情報が参照できることを確認します。

Kubernetes Auth Method のリソースを作成する

まずは、KubernetesからVaultにアクセスするための準備を行います。Kubernetesに認証用のサービスアカウントを作成します。

❯ kubectl create sa app -n default
serviceaccount/app created

続いてVaultへ認証方法を設定します。Vaultには認証方法が複数用意されておりその中のKubernetesを有効化します。あわせて秘密情報に対する権限も設定します。

メニューの「Access」から「Authentication Methods」と進み、「Enable new method」をクリックします。そこで「Kubernetes」を選択して「Next」をクリックします。Pathを「kubernetes」として、「Enable Method」を選択します。Configuration画面で、Kubernetes hostの情報を入力して「Save」をクリックします。

続いて、KubernetesからVaultにアクセスする際の権限をポリシーで作成します。メニューの「Policies」から、「Create ACL policy」と進み、

  • Name: allow-secret-access
  • Policy: 以下参照

を入力し、「Create policy」をクリックします。

path "kv-secret/data/info" {
 capabilities = ["read"]
}

最後にこのポリシーを先程の認証情報に適用します。「Authentication Methods」の「Kubernetes」を選択し、「Create role」をクリックします。

  • Name: kubernetes-role
  • Bound service account names: app
  • Bound service account namespaces: default
  • Generated Token's Policies: allow-secret-access
  • Generated Token's Initial TTL: 86400
  • Generated Token's Type: default

を入力して「Save」をクリックします。Tokensをクリックすると、追加設定項目があるため忘れずに設定します。

Kubernetesのリソースから秘密情報を参照する

それではKubernetesリソースから秘密情報を参照してみます。Podをデプロイし意図した秘密情報が設定されていることを確認します。

Podのマニフェストはこちらを使用します。Agent Sidecar Injector を使用しており、具体的な設定はこちらを参考に実施しています。

Podデプロイ後に、Podにシェルログインを行います。/vault/secrets/info.txtを閲覧すると秘密情報が記載されていることが確認できました。

❯ kubectl apply -f vault-test-pod.yaml
pod/vault-test-pod created

❯ kubectl exec -it vault-test-pod -c app -- /bin/bash
root@vault-test-pod:/# cat /vault/secrets/info.txt
SECRET_KEY: SECRET_VALUE

まとめ

KubernetesにおけるSecretの管理方法として、Vaultを使用する方法を見てきました。

Vaultに秘密情報を管理するようにしたため、秘密情報を記載したマニフェストファイルは無くなり漏洩や第三者による復号の可能性が減少したことがわかりました。

オンプレやコンテナによるVaultの運用を考えると、別途トークンやポリシー管理などを考慮する必要があります。VaultにはHashiCorp社が運用するマネージドサービスであるHCP Vaultも存在しているため、Vault利用検討時における一つの選択肢となります。

今後見たい箇所

今回は書ききれなかった箇所も含めた、今後見ていきたい内容を記載します。

  • Vaultへのログイン方法

今回はRootTokenによるトークンログインを実施しましたが、Vaultは外部IdPとの連携によるログインが可能です。

  • 秘密情報のローテーション

Vaultには秘密情報を暗号化し管理する機能の他に、秘密情報をローテーションする機能が存在します。

  • 秘密情報を更新した場合の反映

Podをデプロイ後にVault GUIなどから秘密情報が更新された場合、今回の方法ですとPod内の秘密情報は置き換わりません。しかしながら、HashiCorp社からリリースされたVault Secrets Operatorを用いることで可能となります。

今回はKubernetesとVault間の秘密情報の共有中心の内容となりましたが、こちらの内容も引き続き見ていきたいと思います。

採用情報

虎の穴では一緒に働く仲間を募集中です!

この記事を読んで、興味を持っていただけた方はぜひ弊社の採用情報をご覧下さい。

カジュアル面談やエンジニア向けイベントも随時開催中です。ぜひチェックしてみてください。 toranoana-lab.co.jp