この記事はKubernetes Advent Calendar 2019の12日目の記事です。

こんにちは、普段はWebの会社でインフラエンジニアとして働いてます、gotoken(@kennygt51)です。今回は、Vault × Kubernetesというテーマで、以下の3点についてまとめました。

HashiCorp社のVaultとはなにか

Vault on Kubernetesを構築するチュートリアルのまとめ

Production-ready Vault on Kubernetesに向けての考慮ポイント

HashiCorp社のVaultとはなにか

Vaultとは

そもそもVaultが何なのかよく知らないという人もいるかと思いますので、概要をおさらいしましょう。Vaultを使ったことがある人は読み飛ばして頂いて構いません。

Vaultとは、HashiCorp社が開発したOSSで、秘匿情報を管理・保護する為のソフトウェアです。 秘匿情報をセキュアに保存するとともに、秘匿情報へのアクセスコントロールを適切に制御する機能を提供します。

公式ドキュメントによると、以下の特徴があります。

Secrets Management

  • アプリケーション・インフラ・システム全体で秘匿情報を集中管理する
    • クラウドとDCをまたいでいても関係ない
  • Dynamic Secrets
  • 暗号化された秘匿情報を保存する
  • 秘匿情報の保存先は拡張性が高い
    • Secret Engineという仕組みで、様々な秘匿情報を管理できる
  • 監査ログを細かく保存できる
  • TokenのRevokeを時間ベースでおこなうなど、セキュアな設計

Data Encryption

  • API-driven Encryption
    • アプリケーションのデータを暗号化して保存する
  • データを暗号化する為のキーを提供する
  • 分散されたインフラ全体で、キーのアップデート・更新ができる

Identity-based Access

  • IdentityにPolicyを割り当てることで柔軟な認可の制御ができる
  • Multi-Factor Authentication

Vaultのユースケース

Kubernetes環境で秘匿情報を管理する方法としては、Vaultの他にも様々な手法があります。個人的な見解としては、以下のような秘匿情報管理の要件がある場合にVaultは技術選定の候補として挙がるかなと考えています。

  • 管理すべき秘匿情報が大量にある場合
  • 各アプリケーションやチーム毎に秘匿情報に対する適切なアクセスコントロールを実現したい場合
  • 秘匿情報の管理をセントラルなインフラチームから開発チームに委譲していきたい場合
  • Audit Logを適切に取得・管理したい場合(統制的な要件)

Architecture

VaultをKubernetes上に構築するにあたって、最低限知っておくべきVaultのアーキテクチャについて説明します。

基本

基本的にはクライアントサーバモデルです。Vault Servertに対して、クライアントからHTTP APIでアクセスします。curlなどを使って直接APIを叩く方法もありますが、APIをラップして簡単にVaultにアクセスできるVault CLIを使うのが便利です。Vault CLIは単一のバイナリで動くので、インストールも簡単です。公式ドキュメントからダウンロードできます。

Storage Backend

Storage Backendとは暗号化されたデータの恒久的な保存に責務を持つコンポーネントです。Vault Serverのconfigに設定するだけで、秘匿情報の保存先を柔軟に選択することができます。

設定可能なStorage Backendはこちらに一覧があります。MySQLやPostgreSQLなどのRDBに加えて、S3やDynamoDBなどのマネージドサービスを使うこともできます。

以下は、DynamoDBをStorage Backendに設定したconfigの例です。

storage "dynamodb" {
  ha_enabled = "true"
  region     = "ap-northeast-1"
  table      = "vault-table"
}

Seal / Unseal

Vault Serverが起動すると、最初はsealedというステータスで起動します。この状態では、保存されたSecretを取得することができません(Vaultは起動時にデータ復号用のMaste Keyを持っていない)

Vaultに対するあらゆるオペレーションを行う際にはUnsealというプロセスを通してsealedステータスをunsealedに変更する必要があります。

High Availability

Vaultは、Production運用時のダウンタイムを極小化する為に、HAをサポートしています。ただし選択するStorage BackendによってはHAをサポートしていないケースがあるので、注意が必要です。

例えば、S3はHAをサポートしていないので、Storage BackendにS3を選択すると、HAモードでVaultを起動することができません。

Policy

Policyによって、保存した秘匿情報に対するアクセス制御をおこないます。

より詳しくVaultのアーキテクチャについて知りたい場合は、公式ドキュメントを参照ください。 また以前に僕が書いた公式ドキュメントをざっくりと訳してまとめた記事も参考になるかと思います。

Vault on Kubernetesを構築するチュートリアルのまとめ

さっそく、Vault on Kubernetesをローカル環境で動かしてみましょう。大変有り難いことに、2019年に公式のVault Helm Chartが公開されました。

注意として、デフォルトではセキュアではない状態でinstallされる為、Production環境で動かすには各種設定を変更する必要があります。 ただ、ローカル環境でサンプルで動かす分にはこちらを利用すれば問題ないので、早速動かしてみましょう。

公式ドキュメントの手順を参考に進めていきます。

事前準備

  • 「docker-for-desktop」なり「minikube」なりで、ローカルにKubernetes環境を構築しておく
  • kubectl及びhelmを使えるようにしておく

作業

  • 公式のVault Helm Chartからgit cloneします
    • 特定のタグがついているVersionをCheckoutすることを推奨しているので、それに従っておきます
git clone https://github.com/hashicorp/vault-helm.git
cd vault-helm
  • --dry-runします
    • values.yamlを書き換えることで、設定値を書き換えます。今回はサンプルなので特に変更はしません。
helm install --dry-run ./ --generate-name
  • VaultをDeployします
helm install ./ --generate-name
  • vault operator initというコマンドをVault Serverに対して実行し、Vaultをinitializeします
    • Vault ServerのPodからVault CLIを使えるようになっています(ローカル環境にVault CLIがインストールされていればそれでも可)
    • なので、Vault ServerのPodにkubectl execしてVault CLIを実行します
    • 今回は検証のため-key-shares=1 -key-threshold=1というオプションを付与してUnseal Keyのシャードを1つにしています(デフォルトは5つ)
kubectl exec -it chart-XXXXX-vault-0 -- vault operator init -key-shares=1 -key-threshold=1
  • 以下のように出力されます(<UNSEAL_KEY>と<INITIAL_ROOT_TOKEN>は、次移行の手順で使用します)
Unseal Key 1: <UNSEAL_KEY>

Initial Root Token: <INITIAL_ROOT_TOKEN>
・・・
  • vault operator unsealというコマンドをVault Serverに対して実行し、Vault ServerをUnsealします
    • 対話モードになるので、先程のvault operator init実行時に出力された<UNSEAL_KEY>を入力します
kubectl exec -it chart-XXXXX-vault-0 -- vault operator unseal
  • SealedKeyのValueがfalseになればUnsealに成功しています
Key             Value
---             -----
Seal Type       shamir
Initialized     true
Sealed          false
  • ここまででVaultを利用する準備ができたので、試しに秘匿情報を登録してみましょう

  • まずはVaultにログインします

    • 先程のvault operator init実行時に出力された<INITIAL_ROOT_TOKEN>を使ってroot権限でVaultにログインします
kubectl exec -it chart-XXXXX-vault-0 sh
vault login
# <INITIAL_ROOT_TOKEN>を入力
  • Key-Value形式で秘匿情報を保存できるようにします
vault secrets enable -path=secret kv
  • Vault CLIを使って秘匿情報を保存します
    • sample-keyというKeyに対してsample-valueというValueを持つ秘匿情報を登録します
vault kv put secret/my-secret sample-key=sample-value
  • Vault CLIを使って、登録した秘匿情報を参照してみます
vault kv get secret/my-secret
======= Data =======
Key           Value
---           -----
sample-key    sample-value
  • Vaultは非常に使いやすいWeb UIを持っています。登録した秘匿情報をWeb UIから参照してみましょう
  • kubectl port-forwardしてブラウザからVaultにアクセスできるようにしておきます
kubectl port-forward chart-XXXXX-vault-0 8200:8200
  • http://localhost:8200/uiにブラウザからアクセスします
    • Tokenは先程の<INITIAL_ROOT_TOKEN>を入力します

VaultUI

  • 無事にログインできました。Secret Enginesという画面が表示されているかと思うので、secret/というリンクをクリックすると、先程登録したmy-secretを参照することができます

VaultUI

  • 検証が終わったらhelm uninstallしておきます
helm list
helm uninstall chart-XXXXX

Production-ready Vault on Kubernetesに向けての考慮ポイント

ここまで説明してきたとおり、公式のVault Helm Chartが用意されています。 ただし、デフォルトの設定値のままではProduction Readyな設定で動かないですし、マニフェストをHelmで管理していないケース(kustomizeを使っている場合など)もあり、まだ色々と考えることは多そうです。

そこで、公式ドキュメントを元に、KubernetesでProduction ReadyなVaultを構築する時にどうしていけばいいかを考えてみました。

Storage Backendの選定

データの永続化はStorage Backendが担うという点については説明したとおりです。Storage BackendはHAを有効化できるものと出来ないものがあります。例えば、S3はHAモードを有効化できず、DynamoDBやConsulはHAを有効化できます。これはStorage Backendを選定する際の大きな判断軸になるでしょう。例えばAWS EKS上に構築するのであればDynamoDBなどが有力な選択肢になります。

Storage Backendへのアクセス制御

秘匿情報そのものは全てStorage Backendに保存されます。データそのものはVaultによって暗号化されて保存されていますが、誤ってStorage Backendのデータを破損するオペレーションをすると、Vaultが利用できなくなります。人がアクセスする必要はないので、Storage Backendに対するアクセスはVault Severのみに制限するべきでしょう。

Auto-unsealの有効化

Vaultを(ライフサイクルの短い)コンテナで動かす以上、Vault Serverを起動する度に手動でUnseal作業を行うのは、現実的ではありません。AWSの場合、KMSと連携することでコンテナ起動時のUnsealプロセスを自動化できます。

Vault ServerのUpgrade

Vault Serverをアップグレードを行う際には、Storage Backendのバックアップを取ることが強く推奨されています。 これは、Vaultはデータストアのbackward-compatibility(後方互換性)を保証しないからです。(Vaultのバージョンが上がると、それによりデータ構造が変更される可能性がある) つまりダウングレードする際には、Vault Serverのバイナリのバージョンを戻すだけではなく、データストアをバックアップからロールバックする必要があります。

Upgrade時に古いバージョンのActive系にフェイルオーバーすることは避けなければいけません。なので、公式のHelm Chartでは、Vault ServerをStatefulSetで構築したうえでOnDeleteupdate strategyを採用しています。Activeの前にStandbyを更新する必要があるので、Rolling Updateの代わりにOnDeleteを使用するのは非常に重要とのことです。

おわりに

実際にVaultを運用する際には、アクセスコントロール(Auth Methodを使った認証認可)やアプリケーション展開の仕組みなど、運用や利用の方が工夫・検討ポイントが多岐にわたります。今後何か機会があれば、運用面も含めてアウトプットしていきたいと思います。