akv://<vault-name>/<secret-name> 形式の文字列を Azure Key Vault secrets で差し替えるツールを作った
akv というツールを作りました。次のように標準入力に含まれる akv://<vault-name>/<secret-name>
形式の文字列や、同形式の環境変数を Azure Key Vault secret(AWS だと parameter store に相当)の値で差し替えることができます。
$ az keyvault secret set --vault-name example --name password --value 'C@6LWQnuKDjQYHNE'
$ echo 'password: akv://example/password' | akv inject
password: C@6LWQnuKDjQYHNE
$ env PASSWORD=akv://example/password akv exec -- printenv PASSWORD
C@6LWQnuKDjQYHNE
inject subcommand は 1Password CLI の inject subcommand みたいなもので、exec subcommand は berglas の exec subcommand が begras://
や sm://
で始まる環境変数を差し替えたり、1Password CLI の run subcommand が op://
で始まる環境変数を差し替えるのと同じような感じです。
背景
Argo Workflows の events API では、GitHub の webhook のように Authorization ヘッダをリクエストに含めることができないケースのための仕組みがあります。
cf. Webhooks - Argo Workflows - The workflow engine for Kubernetes
これは、予め argo-workflows-webhook-clients という名前で service account ごとに type, secret 情報を Secret オブジェクトに保存しておけば、Authorization ヘッダがないリクエストを受け取った場合にそれらの情報を使ってリクエストの signature を検証し、検証に成功した service account の token を Authorization ヘッダに付与してくれるというものです。
cf. addWebhookAuthorization
なので、Kubernetes manifest で Secret オブジェクトを管理しつつ、webhook secret の値は manifest を見ただけではわからない形にしたいという思いがありました。
次の記事ややり取りを見ても色んな人が Secret オブジェクトの管理には頭を悩ませている印象です。
- 悩みに悩んだ Kubernetes Secrets の管理方法、External Secrets を選んだ理由 | PLAID engineer blog
- [Question] Best practices for handling secrets in kustomize · Issue #4633 · kubernetes-sigs/kustomize
どちらでも言及されている external-secrets や sealed-secrets が広く使われてそうですが、今回は手軽に実現したかったのでどちらも大仰に感じました。
また、秘匿情報の管理は Azure Key Vault secrets に集約したいという思いもありました。Azure Kubernetes Service では Azure Key Vault secrets を CSI volume として利用できるんですが、前述のとおり events API で利用するには Secret オブジェクトでなければいけません。
というわけで、akv://<vault-name>/<secret-name>
といった形式の文字列を Azure Key Vault secret の値で差し替えるツールを作って、Kustomize の exec KRM function から利用するのが手軽と考えました。
Kustomize のプラグイン機能はアルファのまま何年も進展がないのが気になるところではありますが…
Kustomize の exec KRM function による secret の差し替え
exec KRM function の詳細についてはドキュメントに譲りますが、最低限次のようなファイルが必要です。ファイル名は kustomization.yaml 以外は好きな名前で大丈夫です。
$ tree .
.
├── key-vault-secrets.yaml
├── kustomization.yaml
├── plugins
│ └── inject-key-vault-secrets.sh
└── secret.yaml
2 directories, 4 files
それぞれのファイルの内容は以下のとおりで、secret.yaml が差し替え対象の Secret オブジェクト、key-vault-secrets.yaml が KRM function の定義です。
このディレクトリに対して --enable-alpha-plugins --enable-exec
オプションを付けて実行すると secret の内容が差し替わります。
$ kustomize build --enable-alpha-plugins --enable-exec .
apiVersion: v1
kind: Secret
metadata:
name: some-secret
stringData:
password: C@6LWQnuKDjQYHNE
なお、akv inject
に --quote
オプションを付けているのは secret に改行が含まれるケースのためです。例えば SSH の private key を Secret オブジェクトで管理するケースが当てはまります。1
もし --quote
オプションがないと、YAML が壊れてしまうので次のようなエラーになります。
$ kustomize build --enable-alpha-plugins --enable-exec .
Error: couldn't execute function: MalformedYAMLError: yaml: line 20: could not find expected ':'
既にダブルクォートで囲まれているケースのために --escape
オプションもあるんですが、仮に次のように Secret オブジェクトの定義の方でダブルクォートで囲っていたとしても Kustomize が不要なダブルクォートとみなして KRM function を実行するまでに取り除いてしまうので、Kustomize で利用する際には --quote
オプションにする必要があります。
apiVersion: v1
kind: Secret
metadata:
name: some-secret
stringData:
password: "akv://example/password"
-
Argo Workflows の git artifact で GitHub App の private key から token を生成して checkout できると良いんですが、現状は artifact を利用せずに自前で checkout するか、deploy key または machine user の private key を使うしかなさそうです cf. Git artifact using GitHub App Credentials · argoproj/argo-workflows · Discussion #11028 ↩