Spring Boot から Key Vault の値を Managed IDを使って読み取る

# Spring Boot から Key Vault の値を Managed IDを使って読み取る

今回は、Managed ID（管理ID）についてです。Managed IDについては、以下にがっつりと解説がありますが、簡単に言ってしまえば、例えば、WebApps から SQL DB 、ストレージ、Key Vault など他コンポーネントのアクセスにパスワードなどのシークレットを必要としない仕組みとなります。

[Azure リソースのマネージド ID | Microsoft Docs](https://docs.microsoft.com/ja-jp/azure/active-directory/managed-identities-azure-resources/overview?WT.mc_id=AZ-MVP-5001265)

もちろん、WebAppsから、他のAzureリソースへ勝手にアクセスできるわけではありませんから、Azure 上でしかるべき手順を踏んで設定を行っておく必要があります。設定は、ポータル以外にも Azure CLI などのコマンドラインツールでも可能です。

# WebApps + Key Vault での例

先だっての記事の続きというわけではありませんが、前提とする環境は、 Spring Boot + WebApps/Key Vault で試してみたいと思います。

## WebAppsでの設定

ポータルで確認すると、WebApps には、 「ID」という管理項目があります。Managed ID を有効にするには、ここでこの機能をオンにします。

オンにするとなにが起こるのかというと、Azure AD 上に、このWebAppsに紐づいたサービスプリンシパルと呼ばれる資格情報がこっそり作成されます。WebApps 側の設定はこれだけです。この時に表示されるオブジェクトID （クライアントID）は、あとで必要ですのでメモっておきます。またサービスプリンシパルの名前はWebAppsの名前と同じ名前になります。後ほどアクセス権を設定するところで使います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34017/2d27919c-0f7e-c755-92ac-10723c656a6e.png)


## Key Vault での設定

Key Vaultには、キー、シークレット、証明書の3つの種類をストアできます。いわゆる構成設定値のような、キーバリュー型のものはシークレットに保存します。以下のクラスを用意したとき、 `kvconfig.messsage` を設定しないといけませんが、Key Vault では「ドット」を使用できなので、`kvconfig-message` で代用します。

```java
@Configuration
@ConfigurationProperties("kvconfig")
public class KvConfig {

    private String message;

    public String getMessage() {
        return this.message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

}
```

シークレットの作成を選んで、`hirakegema` という値を定義しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34017/81c69ff7-b4d3-290a-d878-8ece5594b90d.png)


次にこの Key Vault へのアクセス許可を設定します。「アクセスポリシ－」を開きポリシーを追加します。キー、シークレット、証明書に対して、それぞれ CRUD + List のような権限設定ができます。ここでは、シークレットの読込み権限が必要ですので、読み取りと一覧を付与します（一覧へのアクセス権限がないと失敗するのでご注意）。権限の付与は最低にし無駄に編集権限とかを与えるのはやめましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34017/b18baeb1-6db7-0f14-3efa-9bfdfefde341.png)


プリンシパルの選択は、WebAppsの名前を指定すると、同名のサービスプリンシパルが指定できるでしょう。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34017/1dd3c78a-edb3-bf1f-fcd1-20b8204faae4.png)

## Spring Boot アプリの設定

依存に、`azure-spring-boot-starter-keyvault-secrets` を設定します。

```xml
    <dependency>
      <groupId>com.azure.spring</groupId>
      <artifactId>azure-spring-boot-starter-keyvault-secrets</artifactId>
    </dependency>

```

application.properties には、以下の情報が必要です。

* テナントID
* Key Vault のURL
* クライアントID（Web Apps で Managed IDをオンにしたときに表示されるGUID）

```properties
azure.keyvault.enabled=true
azure.keyvault.client-id=ManagedIDのオブジェクトID
azure.keyvault.tenant-id=Azure AD のテナントID
azure.keyvault.uri=https://xxxxxxx.vault.azure.net/
```

コントローラークラスは、構成クラスをDIして表示するだけです。

```java
 @RestController
public class KvController {

    private KvConfig config;

    public KvController(KvConfig config) {
        this.config = config;
    }

    @GetMapping("kv")
    public String getMessage() {
        return config.getMessage();
    }
}
```

## デプロイ

デプロイしたエンドポイントに curl で APIを叩いてみましょう。 `hirakegoma` が表示されていれば正しく動作しています。これで Key Vault にアクセスするために、キーを管理する必要がなくなりました。

```sh
$ curl https://config-sample-1620013095057.azurewebsites.net/kv
hirakegoma
```

## うまく動かない場合

うまく動かない場合は、だいたい設定に失敗して、Spring Boot 起動時に例外が発生していることが多いです。私も何度かやりました。そのような場合は、App Service から 「問題の診断と解決」というメニューを開いて、Application Log を参照すると、起動時の例外を見ることが出来るので、参考にしてください。

以下失敗例。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34017/39e4473d-3eda-8aff-3dcf-8136170fbad2.png)


# ローカル環境で試したい場合

.NET ですと、Visual Studio 自身が認証した情報をつかって Key Vault へアクセスしてくれるのですが、Javaのこのシナリオの場合は、Azure CLI の認証情報とかを勝手に使ってくれません。

このような場合は、手動でサービスプリンシパルを作成し、Key Vault のアクセス件を設定する必要があります。Azure CLI ですと以下のコマンドで作成できます。作成したサービスプリンシパルは、前述したKey Vault の設定でアクセス権を設定できます。


```
az ad sp create-for-rbac --name xxxxxxxxx
```

コマンドを実行すると、アプリケーションID（クラインとID）と、パスワードが表示されるので、それぞれ、`client-id` と `client-key` に設定します。

```properties
azure.keyvault.enabled=true
azure.keyvault.client-id=ManagedIDのオブジェクトID
azure.keyvault.tenant-id=Azure AD のテナントID
azure.keyvault.uri=https://xxxxxxx.vault.azure.net/
azure.keyvault.client-key=パスワード
```

これでローカル実行出来ると思います。


# まとめ

今回は、先にWebAppsにデプロイした例を説明しましたが、ローカル環境からやったほうが簡単だと思います。意外とくだらない設定ミスで時間を潰したりするので、例外メッセージはきちんと読みましょう（＞自分）