APC 技術ブログ

株式会社エーピーコミュニケーションズの技術ブログです。

株式会社 エーピーコミュニケーションズの技術ブログです。

HCP Vault Secrets を使えばマルチクラウド Kubernetes クラスタのシークレットを簡単に一元管理できそうなので試してみた

こんにちは、ACS 事業部の埜下です。

前回、GA したばかりの HCP Vault Secrets を使って GitHub Actions との連携を試してみました。

実は前回の記事を執筆している最中にドキュメントを漁っていたところ、Vault Secrets Operator (VSO) が HCP Vault Secrets に対応していることに気づきました。 GitHub のリポジトリを追っていればもっと早く気付けたんでしょうが、9 月前半には対応されていたようです。

developer.hashicorp.com

ご存知の通り、VSO を使えば Vault クラスタに格納されたシークレットを Kubernetes クラスタに同期されることが可能です。 Vault は「ダイナミックシークレット」という強力な機能を持っています。 しかし、「キーバリュー型のシークレットだけ管理したいなぁ」と思う場面もあるでしょう。 そのような場合に Vault クラスタを用意するのは Too Much かもしれませんね。

そこで登場するのが HCP Vault Secrets です! HashiCorp Cloud Platform (HCP) 上で少し操作するだけでクラスタキーバリュー型のシークレットを管理する環境を簡単に用意できます。 このお手軽さを使えばマルチクラウドに展開した Kubernetes クラスタのシークレット管理が簡単にできるのではないでしょうか?

今回は HCP Vault Secrets と Vault Secrets Operator を使って複数の Kubernetes クラスタのシークレットを一元管理して、環境を整える流れを確認してみます。

「HCP Vault Secrets ってなに?」という方はぜひ前回の記事を参照ください。

techblog.ap-com.co.jp

全体構成

今回は以下のような構成を作ってみます。

HCP Vault Secrets を使ったシークレット一元管理構成

2 つ Kubernetes クラスタで VSO を動かして、HCP Vault Secrets を参照して同じ値を持つ Secret リソースを各クラスタに作成します。 ついでに GitHub Actions とも連携させておきましょう。

この構成では 2 つの Kubernetes クラスタ、そして GitHub Actions のシークレットはすべて HCP Vault Secrets で管理されます。 HCP Vault Secrets でシークレットの値を更新すると、同期先のシークレットも自動的に更新されます(タイミングは設定次第です)。 Secret リソースを直接書き換えても VSO がよしなに元に戻してくれます。

以下の流れで構築していきます。

  1. HCP Vault Secrets にアプリケーション、シークレット作成
  2. HCP サービスプリンシパル作成
  3. Kubernetes クラスタに VSO インストール
  4. HCP Vault Secrets 用カスタムリソースデプロイ

HCP Vault Secrets

アプリケーション

はじめにシークレットの管理元となる HCP Vault Secrets のアプリケーションを前回の手順を使って用意し、USERNAMEPASSWORD という 2 つのシークレットを作成します。 この時点で既に GitHub Actions にはシークレットが同期されています。

HCP Vault Secrets に作成したシークレット

サービスプリンシパル

次に、VSO から HCP Vault Secrets を参照するための HCP サービスプリンシパルを作成します(HCP にもサービスプリンシパルがあったんですね!)。 HCP で Organization を表示して、メニューから [Access control (IAM)] を選択します。 プロジェクトにも [Access control (IAM)] がありますが、現在はプロジェクト単位でのサービスプリンシパルは作成できないようです。

Organization の IAM を選択

メニューの [Service principals] から管理画面に移ってサービスプリンシパルを作成します。

サービスプリンシパル管理画面

ロールには「Admin」「Contributor」「Viewer」の 3 種類ありますが、VSO はシークレットの参照しかしないため「Viewer」でよいです。

Viewer ロールを選択してサービスプリンシパルを作成

つづいて、サービスプリンシパルの認証に使うキーを作成します。

サービスプリンシパル キーを作成

クライアント ID とクライアントシークレットが表示されるので控えておいてください。

クライアント ID とクライアントシークレット

環境情報

Kubernetes にインストールする VSO が HCP Vault Secrets への接続に使用する環境情報を取得します。 HCP Vault Secrets には vlt という専用のコマンドが用意されているので、クライアントの環境に合わせてインストールします。

developer.hashicorp.com

vlt コマンドをインストールしたら、さきほど作成したサービスプリンシパルのクライアント ID とクライアントシークレットを環境変数に設定します。

$ export HCP_CLIENT_ID=<クライアント ID>
$ export HCP_CLIENT_SECRET=<クライアントシークレット>

vlt secrets コマンドで HCP Vault Secrets に格納されたシークレットを表示できますが、初回実行時はどのアプリケーションを参照するか vlt config init コマンドで指定する必要があります。

# init 前にシークレットを参照するとエラー
$ vlt secrets
Error: organization ID must be set via `vlt config init` or passed in

# 参照する HCP Vault Secrets アプリケーションを選択
$ vlt config init
Organization with ID 00000000-0000-0000-0000-000000000000 selected
Project with ID 00000000-0000-0000-0000-000000000000 selected
✔ vso-demo
Successfully wrote configuration to system

# アプリケーション内のシークレット(キー)が表示される
$ vlt secrets
Name      Latest Version  Created At                
PASSWORD  1               2023-10-12T08:14:25.329Z  
USERNAME  1               2023-10-12T06:56:20.671Z  

vlt コマンドでシークレットを参照できたら、vlt config コマンドで HCP Vault Secrets の環境情報を表示します。

$ vlt config
App Name: vso-demo
Project ID: 00000000-0000-0000-0000-000000000000
Organization ID: 00000000-0000-0000-0000-000000000000

これらの情報を VSO のマニフェスト内で使います。 今回はマニフェストもコマンドで流し込むため環境変数に設定しておきます。

$ export HCP_ORG_ID=00000000-0000-0000-0000-000000000000
$ export HCP_PROJECT_ID=00000000-0000-0000-0000-000000000000
$ export APP_NAME=vso-demo

HCP Vault Secrets 側の設定は以上です。

Kubernetes

Vault Secrets Operator

次にシークレット同期する先の Kubernetes クラスタを作成して VSO をインストールします。 今回は AKS を使ってクラスタを 2 つ用意しました。

VSO は Helm を使ってインストールできます。 まずは Helm リポジトリの登録/更新です。 こちらはクラスタの数に関わらず一回実行するだけでよいです。

# リポジトリ未登録の場合はこちら
$ helm repo add hashicorp https://helm.releases.hashicorp.com

# リポジトリ登録済みの場合はこちら
$ helm repo update hashicorp

リポジトリを登録できたら各クラスタに VSO をインストールしてきます。

$ helm install vault-secrets-operator hashicorp/vault-secrets-operator \
     --namespace vault-secrets-operator-system \
     --create-namespace

VSO の Pod が正常に起動していることを確認できたら、HCP サービスプリンシパルの資格情報を Secret リソースとして作成します。 どうやら HCP サービスプリンシパルの資格情報は動的生成できないようです。 そのため VSO が使う際に Secret リソースとして作成しておく必要があります(この Secret リソースの扱いはどうましょうね…)。

$ kubectl create secret generic vso-demo-sp \
    --namespace default \
    --from-literal=clientID=$HCP_CLIENT_ID \
    --from-literal=clientSecret=$HCP_CLIENT_SECRET

カスタムリソース

次に HCP サービスプリンシパルを使った認証をするカスタムリソース HCPAuth と、参照先 HCP Vault Secrets アプリケーションと同期先 Secret リソースを指定するカスタムリソース HCPVaultSecretsApp をデプロイします。

$ kubectl apply -f - <<EOF
---
apiVersion: secrets.hashicorp.com/v1beta1
kind: HCPAuth
metadata:
  name: default
  namespace: vault-secrets-operator-system
spec:
  organizationID: $HCP_ORG_ID
  projectID: $HCP_PROJECT_ID
  servicePrincipal:
    secretRef: vso-demo-sp
---
apiVersion: secrets.hashicorp.com/v1beta1
kind: HCPVaultSecretsApp
metadata:
  name: vso-demo
  namespace: default
spec:
  appName: $APP_NAME
  destination:
    create: true
    labels:
      hvs: "true"
    name: vso-demo-secret
  refreshAfter: 30s
EOF

VSO はこれらのカスタムリソースがデプロイされたことを検知すると、サービスプリンシパルを使って HCP にログインし、HCP Vault Secrets アプリケーションに格納されたシークレットを Secret リソースとして作成してくれます。

$ kubectl get secret
NAME              TYPE     DATA   AGE
vso-demo-secret   Opaque   3      1s
vso-demo-sp       Opaque   2      3m22s

$ kubectl get secret vso-demo-secret -o jsonpath='{.data.USERNAME}' | base64 --decode  ; echo
hoge
$ kubectl get secret vso-demo-secret -o jsonpath='{.data.PASSWORD}' | base64 --decode ; echo
fuga

あとはアプリケーションからこの Secret リソースをいい感じに利用してください。 また、HCPVaultSecretsApp カスタムリソースではシークレット更新と合わせて Deployment などをロールアウトする設定もありますので。 「HCP Vault Secrets でシークレットを更新したのにアプリケーションは古いシークレットを使っていた!」なんてことにはなりませんのでご安心ください。

複数クラスタ間のシークレット確認

忘れそうになっていましたが、今回は複数クラスタでのシークレット管理が目的でした。 2 つのクラスタで同じシークレットが作成されているか確認してみましょう。

あまり説得力がないですが、Azure Portal から各 AKS に対してコマンドを実行したところのスクリーンショットです。 同じシークレットが存在していることが確認できました。

Kubernetes クラスタ 1 号機のシークレット

Kubernetes クラスタ 2 号機のシークレット

シークレット更新

最後に HCP Vault Secrets でシークレットを更新して各クラスタの Secret リソースが同期されるか確認します。 シークレット PASSWORD の値を fuga から piyo に変更しました。

PASSWORD を更新

今回は VSO が HCP Vault Secrets を参照する頻度を 30 秒に設定していたので、少し待って確認すると各クラスタの Secret リソースも更新されていました。 またもや説得力ないですがスクリーンショットを貼っておきます。

Kubernetes クラスタ 1 号機のシークレット(シークレット更新後)

Kubernetes クラスタ 2 号機のシークレット(シークレット更新後)

GitHub Actions へ更新されたかどうかは、HCP Vault Secrets の Integrations 画面から最終同期時間を確認できます。

GitHub Actions への同期ステータス

おわりに

複数の Kubernetes クラスタで扱うシークレットを HCP Vault Secrets で一元管理できることが確認できました。 Kubernetes クラスタに対するシークレット管理は今までの Vault クラスタでも可能ですが、HCP Vault Secrets を使うことで Vault 側の運用負荷を大幅に減らせるのではないでしょうか。

今回は AKS だけで Kubernetes クラスタを構築したので Azure Key Vault でもシークレットを管理することは可能です。 しかし、EKS や GKE といった他クラウドのマネージド Kubernetes サービスやセルフマネージドの Kubernetes に跨っていても、HCP Vault Secrets はシークレット管理が可能です(どちらかと言うと VSO の功績ですが)。 また、GitHub Actions 等との連携も HCP Vault Secrets ならでは(Vault Enterprise にはベータ版が来たようですが)なので、Kubernetes に閉じず各種クラウドサービスのシークレットを一元管理できる力を秘めたサービスだと感じました。

本記事の投稿者: 埜下 太一
Azure をメインにインフラ系のご支援を担当しています。
個人ブログ