HashiCorp VaultのAgent Sidecar Injectorを使う際につまずいた点

だいぶニッチな記事です。

Agent Sidecar Injectorを使う際につまずいた点をいくつかまとめました。

## HashiCorp Vaultとは

HashiCorp Vaultは、APIキー、パスワード、証明書などのシークレットを安全に保存、管理、アクセス制御、および監査するためのセキュリティプラットフォームです。

https://www.vaultproject.io/

## Agent Sidecar Injectorとは

Agent Sidecar Injectorは、[vault-k8s](https://github.com/hashicorp/vault-k8s)に組み込まれた機能の一つで、Kubernetes上のPodにAgent Containerを自動的に追加する機能です。

Podに追加されたAgent Containerは、Vaultへの認証を行い、Podの環境変数やファイルに取得したシークレットを書き出します。

https://developer.hashicorp.com/vault/docs/platform/k8s/injector

上記ドキュメントによると、Podへのコンテナの注入はKubernetesの[Mutating Admission Webhook](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#mutatingadmissionwebhook)という機能を利用しているようで、この機能を使えばK8s上のリソース（今回だとPod）作成時などのタイミングで、動的に内容を変更することができるようです。

Agent Sidecar Injectorでは、以下のような特定のannotationがPodに付与されている場合にAgentコンテナを追加します。

```yaml
annotations:
  "vault.hashicorp.com/agent-inject": "true"
```

### Agent Sidecar Injectorのメリット

Kubernetesの[Secret](https://kubernetes.io/docs/concepts/configuration/secret/)をそのまま利用すると、単にBase64エンコードされた値をyamlに記述するだけなので、シークレットの管理に一手間かかります。

そこで、Vault経由でシークレットを差し込む事によって、シークレットの管理をVaultに任せることができ、手間をある程度削減しつつ、セキュアなシークレット管理が可能になります。

また、導入も簡単で、基本的な使い方であれば、公式が用意しているHelm Chartを使ってインストールし、いくつかの設定を行うだけで利用できます。

https://developer.hashicorp.com/vault/docs/platform/k8s/helm

## 本題

本題に入ります。

Agent Sidecar Injectorを使う際いくつかつまずいた点があるため、以下でそれらを紹介します。

### Job, CronJobの場合は常駐するSidecar Containerを無効にしなければ永遠に完了しない

Agent Sidecar Injectorを使うと、PodにAgent Containerを追加できますが、デフォルト設定では[Init Container](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/)と[Sidecar Container](https://kubernetes.io/docs/concepts/workloads/pods/sidecar-containers/)が追加されます。

Init ContainerはPodの初期化時にシークレットを差し込むために一度だけ実行され、Sidecar ContainerはPodのライフサイクル中に常駐し、Vault側で更新があればそれらをPod側に反映してくれます。

ここで問題になるのは、Sidecar Containerの方で、KubernetesのJobは、すべてのコンテナが終了しないとJobが完了したことになりません。
そのため、Sidecar Containerが常駐してしまうと、永遠にJobが完了しないままになります。

#### 解決方法

Jobの場合は、常駐するSidecar Containerを無効化するために、以下のような設定を行います。

```yaml
vault.hashicorp.com/agent-pre-populate-only: "true"
```

この設定を行うことで、Sidecar Containerは常駐しなくなり、Init Containerのみが実行されます。

Jobにシークレットを差し込む場合は、この設定を行うことをおすすめします。

また、通常のPod（Deploymentなど）を利用している場合でも、アプリケーションへのシークレットの読み込みが初回のみで問題ない仕様であれば、この設定を行うことで、不要なリソースを消費しないようにできます。

Sidecar Containerは意外とリソースを食うので、必要ない場合は無効にすることを検討してみてください。

### vault-agent-injectorより先にPodが作成されるとAgent Containerが追加されない

Agent Sidecar Injectorは、PodにAgent Containerを追加するために`vault-agent-injector`
というDeploymentを作成します。

このDeploymentに属しているPodがシークレット差し込み先のPodより先に作成されないと、Podが作成されるタイミングでAgent Containerが追加されず、シークレットが取得できません。

私の環境だと、同一ノード上に該当するPodがある関係か、クラスターのアップデート時にこの問題が発生しました。アップデート時にすべてのPodが作り直されるため、起動順によって問題が発生したり、しなかったりという状況でした。

#### 解決方法

以下のディスカッションで解決方法が紹介されていました。

https://discuss.hashicorp.com/t/vault-agent-injector-racing-condition-when-application-pods-utilizing-vault-sidecar-come-up-faster-then-vault/31297/8

以下の設定を行うことで、NotInに設定されたNamespace以外のPodは、`vault-agent-injector`が起動するまでブロックされるようになります。

```
injector:
  namespaceSelector:
    matchExpressions:
    - key: kubernetes.io/metadata.name
      operator: NotIn
      values: ["vault","kube-system","kube-public","kube-node-lease"]
```

ただし、[このIssue](https://github.com/hashicorp/vault-helm/issues/609)によると、annotationがないPodも含め、NotInに指定されていないNamespaceのPodがすべてブロックされるようなので、注意が必要です。

## まとめ

全体的にニッチな内容だと思っているのですが、Agent Sidecar Injectorに関しては日本語の情報が少ないと感じているので、誰かの助けになれば幸いです。
（試しに`Agent Sidecar Injector`で検索してみると2件ぐらいしかありませんでした...`HashiCorp Vault Kubernetes`などで検索するともう少しヒットすると思います）

また、Vaultに関しての公式ドキュメントはわかりにくい部分もありますが、かなり充実しているので、そちらも参考にしてみてください。

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