AKS の Secrets Store CSI driver 経由で Key Vault を使う

AKS (Azure Kubernetes Service) にて、Secrets Store CSI driver 経由での Key Vault へのアクセスがプレビュー公開されています。

Key Vault を利用することで、シークレット情報をセキュアな方法で格納・取得できることが期待できます。

チュートリアル手順は下記の公式ドキュメントが参考になります。

> [Azure Kubernetes Service シークレットにシークレット ストア CSI ドライバーを使用する - Azure Kubernetes Service | Microsoft Docs](https://docs.microsoft.com/ja-jp/azure/aks/csi-secrets-store-driver)

やってみたのですが、一部詰まる箇所が出てきたこともあり、記事として残しておこうと思います。

# 環境

* AKS (1.19.11)
* Windows 10

# 考慮が必要な箇所

## kubectl の資格情報を保存しておく

『シークレット ストア CSI ドライバーのインストールの確認』にて、手順ではサラっと `kubectl get pods` を叩いていますが、環境によっては前の接続情報を持っている等の理由で接続できない場合があります。

```
kubectl get pods -n kube-system -l 'app in (secrets-store-csi-driver, secrets-store-provider-azure)'
Unable to connect to the server: dial tcp: lookup xxx: no such host
```

下記のコマンドで kubectl での接続先を設定しておきましょう。

```
az aks get-credentials --resource-group myResourceGroup  --name myAKSCluster
```

そのうえで `kubectl get pods` を実行すれば、問題なく通るはずです。

```
> kubectl get pods -n kube-system -l 'app in (secrets-store-csi-driver, secrets-store-provider-azure)'

NAME                                     READY   STATUS    RESTARTS   AGE
aks-secrets-store-csi-driver-j9gjh       3/3     Running   1          4m20s
aks-secrets-store-csi-driver-nmzjs       3/3     Running   2          4m31s
aks-secrets-store-csi-driver-wkhwd       3/3     Running   1          4m20s
aks-secrets-store-provider-azure-54vp5   1/1     Running   0          4m20s
aks-secrets-store-provider-azure-pztzs   1/1     Running   0          4m31s
aks-secrets-store-provider-azure-vms2g   1/1     Running   0          4m20s
```

## シークレットとキーを作成しておく

チュートリアルの yaml ファイルを (ほぼ) そのまま使うのであれば、『Azure Key Vault を作成するか既存のものを使用する』あたりで、対象の Key Vault に `secret1` というシークレットと `key1` というキーを作成しておく必要があります。

## サービスプリンシパルの作成とアクセス許可が必要

『Azure Key Vault にアクセスするために ID を提供する』に手順の記載無くサラっと書いてありますが、サービスプリンシパルの作成と Key Vault へのアクセス許可が必要になります。

この手順を実施しないと、後の `busybox-secrets-store-inline` Pod の作成において `ContainerCreating` のままとなります。その際、`kubectl describe pod` で状況を確認すると下記のエラーが発生していることが確認できます。

```
Events:
  Type     Reason       Age              From               Message
  ----     ------       ----             ----               -------
  Normal   Scheduled    4s               default-scheduler  Successfully assigned default/busybox-secrets-store-inline to aks-nodepool1-42592859-vmss000001
  Warning  FailedMount  1s (x2 over 2s)  kubelet            <下記参照>
```

> MountVolume.SetUp failed for volume "secrets-store-inline" : fetching NodePublishSecretRef default/secrets-store-creds failed: kubernetes.io/csi: failed to find the secret secrets-store-creds in the namespace default with error: secrets "secrets-store-creds" not found

「`secrets-store-creds` というシークレットが無い」と怒られていますが、これを作成する手順が下記のドキュメントに記載されています。

> [Service Principal | Azure Key Vault Provider for Secrets Store CSI Driver](https://azure.github.io/secrets-store-csi-driver-provider-azure/configurations/identity-access-modes/service-principal-mode/)

まずはサービスプリンシパルを作成し、

```
az ad sp create-for-rbac --skip-assignment --name myAKSClusterServicePrincipal

{
  "appId": "06634dda-f18b-4688-b941-ff71d363ca10",
  "displayName": "myAKSClusterServicePrincipal",
  "name": "http://myAKSClusterServicePrincipal",
  "password": "TAy5g~0d-NPskcesNsS9DhFNbxNnFQGYjw",
  "tenant": "xxxxxx"
}
```

Key Vault 上のアクセスポリシーにて、上記のサービスプリンシパルに少なくともシークレットとキーへのアクセス許可を付与します。

> 参考 : [Azure Key Vault アクセス ポリシーを割り当てる (ポータル) | Microsoft Docs](https://docs.microsoft.com/ja-jp/azure/key-vault/general/assign-access-policy-portal)

下記のような感じになれば OK かと。

![aks_csi_kv_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218221/e4039d8d-10b6-ff91-d9a5-0a743cbdc488.png)

そして、`secrets-store-creds` というシークレットを作成しておきます。

```
kubectl create secret generic secrets-store-creds --from-literal clientid="06634dda-f18b-4688-b941-ff71d363ca10" --from-literal clientsecret="TAy5g~0d-NPskcesNsS9DhFNbxNnFQGYjw"
```

再度 Pod を作成して、Pod が `Running` になり、`kubectl describe pod` で下記のようにエラーが無くなれば無事 OK です。

```
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  4s    default-scheduler  Successfully assigned default/busybox-secrets-store-inline to aks-nodepool1-42592859-vmss000001
  Normal  Pulling    4s    kubelet            Pulling image "k8s.gcr.io/e2e-test-images/busybox:1.29"
  Normal  Pulled     2s    kubelet            Successfully pulled image "k8s.gcr.io/e2e-test-images/busybox:1.29" in 2.198869156s
  Normal  Created    1s    kubelet            Created container busybox
  Normal  Started    1s    kubelet            Started container busybox
```

なお、アクセス付与のミスなどの場合は、先ほどのエラーメッセージの箇所に下記のようなアクセス拒否 (403 Forbidden) のエラーが出ますので、これが出たらアクセスポリシーが間違っていないか確認すると良いと思います。

> MountVolume.SetUp failed for volume "secrets-store-inline" : rpc error: code = Unknown desc = failed to mount secrets store objects for pod default/busybox-secrets-store-inline, err: rpc error: code = Unknown desc = failed to mount objects, error: failed to get objectType:secret, objectName:secret1, objectVersion:: keyvault.BaseClient#GetSecret: Failure responding to request: StatusCode=403 -- Original Error: autorest/azure: Service returned an error. Status=403 Code="Forbidden" Message="The user, group or application 'appid=06634dda-f18b-4688-b941-ff71d363ca10;oid=xxxxxx;iss=https://sts.windows.net/xxxxxx/' does not have secrets get permission on key vault 'ndsouMyKeyVault3;location=japaneast'. For help resolving this issue, please see https://go.microsoft.com/fwlink/?linkid=2125287" InnerError={"code":"AccessDenied"}

ここまでできれば、チュートリアルの手順が最後まで実行できると思います。

```
> kubectl exec busybox-secrets-store-inline -- ls /mnt/secrets-store/
key1
secret1

> kubectl exec busybox-secrets-store-inline -- cat /mnt/secrets-store/secret1
P@ssw0rd!!
```

# その他の考慮事項

冒頭の通り、Azure Key Vault を利用することでシークレット情報をセキュアな方法で格納・取得できることが期待できます。

一方で、依存するサービスが増えてしまうことは事実です。サービス全体の可用性の計算式などにおいて、Key Vault の可用性も掛け算しなければならなくなってくることには若干の注意が必要と思われます。便利ですけどね。