以前、Key Vaultを作成しKey Vaultのシークレットを取得するサンプルプログラムについて記載したが、今回は、App ServiceとAzure Functionsそれぞれについて、システム割り当てマネージドIDを利用してKey Vaultのシークレットを取得してみたので、そのサンプルプログラムを共有する。
なお、マネージドID とは、Azureリソース自身で認証を行うことのできる、Azure ADで管理されるIDのことをいう。マネージドIDを利用すると、サービスプリンシパルの利用は不要になり、application.propertiesからサービスプリンシパルのクライアントID・テナントID・パスワードを削除することができる。
前提条件
下記記事の実装が完了していること。
やってみたこと
- システムマネージドID割り当て(App Service)
- システムマネージドID割り当て(Azure Functions)
- キーコンテナーのアクセスポリシーの変更
- サンプルプログラムの作成
- サンプルプログラムの実行結果
システムマネージドID割り当て(App Service)
App ServiceのシステムマネージドIDは、Azure Portal上で割り当てられる。その手順は以下の通り。
1) Azure Portalにログインし、App Service「azureAppDemoService」を開き、「ID」メニューを選択する。
2) 以下のように、システム割り当て済みのマネージドIDが割り振られていないことが確認できる。
4) 以下のように確認ダイアログが表示されるため、「はい」ボタンを押下する。
5) 以下のように、システム割り当てマネージド ID(下図のオブジェクトID)が付与されることが確認できる。
システムマネージドID割り当て(Azure Functions)
Azure FunctionsのシステムマネージドIDも、Azure Portal上で割り当てられる。その手順は以下の通り。
1) Azure Portalにログインし、Azure Functions「azureFuncDemoApp」を開き、「ID」メニューを選択する。
2) 以下のように、システム割り当て済みのマネージドIDが割り振られていないことが確認できる。
4) 以下のように確認ダイアログが表示されるため、「はい」ボタンを押下する。
5) 以下のように、システム割り当てマネージド ID(下図のオブジェクトID)が付与されることが確認できる。
キーコンテナーのアクセスポリシーの変更
キーコンテナから先ほど作成したサービスプリンシパルにアクセスできるようにするために、キーコンテナーのアクセスポリシーを変更する。その手順は以下の通り。
1) キーコンテナーの概要を表示し、「アクセスポリシー」メニューを選択する。
3) アクセスポリシーの追加画面が表示されるため、プリンシパルの選択の「選択されていません」リンクを押下する。
4) App Service「azureAppDemoService」を選択し、「選択」ボタンを押下する。
5)「シークレットのアクセス許可」で、「取得」「一覧」を選択する。
6) 下記内容が指定されていることを確認し、「追加」ボタンを押下する。
7) 以下のように、アクセスポリシーの一覧にApp Service「azureAppDemoService」が表示されることが確認できる。
8) 同様に、Azure Functions「azureFuncDemoApp」のアクセスポリシーも追加する。
9) サービスプリンシパルのアクセスポリシーを削除するため、一覧の「削除」ボタンを押下する。
10) 下記状態になったことを確認し、「保存」ボタンを押下する。
11) アクセスポリシー削除の確認メッセージが表示されるため、「OK」ボタンを押下する。
12) 保存すると、以下のように、保存ボタンが非活性になる。
サンプルプログラムの作成
作成したサンプルプログラム(App Service側)の構成は以下の通り。
なお、上記の赤枠は、前提条件のプログラムから追加・変更したプログラムである。
application.propertiesの内容は以下の通りで、サービスプリンシパルのクライアントID・テナントID・パスワードを削除している。
1 2 3 4 5 6 7 | server.port = 8084 #demoAzureFunc.urlBase = http://localhost:7071/api/ demoAzureFunc.urlBase = https://azurefuncdemoapp.azurewebsites.net/api/ # Key Vaultへの設定 keyVaultUri=https://keypurinit.vault.azure.net/ keyVaultSecretKey=keySecret |
また、コントローラクラスの内容は以下の通りで、AppServiceMSICredentialsクラスを利用し認証情報を指定しないように変更している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | package com.example.demo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.client.RestTemplate; import com.fasterxml.jackson.databind.ObjectMapper; import com.microsoft.azure.AzureEnvironment; import com.microsoft.azure.credentials.AppServiceMSICredentials; import com.microsoft.azure.keyvault.KeyVaultClient; import com.microsoft.azure.keyvault.models.SecretBundle; @Controller public class DemoController { /** RestTemplateオブジェクト */ @Autowired private RestTemplate restTemplate; /** ObjectMapperオブジェクト */ @Autowired private ObjectMapper objectMapper; /** application.propertiesからdemoAzureFunc.urlBaseの値を取得 */ @Value("${demoAzureFunc.urlBase}") private String demoAzureFuncBase; /** keyVaultのURI */ @Value("${keyVaultUri}") private String keyVaultUri; /** keyVaultのシークレットのキー */ @Value("${keyVaultSecretKey}") private String keyVaultSecretKey; @GetMapping("/") public String index(Model model) { // Azure FunctionsのgetKeySecret関数を呼び出すためのヘッダー情報を設定する HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); // Azure FunctionsのgetKeySecret関数を呼び出すための引数を設定する MultiValueMap<String, String> map = new LinkedMultiValueMap<>(); try { map.add("key", "keySecret"); } catch (Exception ex) { throw new RuntimeException(ex); } HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(map, headers); // Azure FunctionsのgetKeySecret関数を呼び出す ResponseEntity<String> response = restTemplate.exchange( demoAzureFuncBase + "getKeySecret", HttpMethod.POST, entity, String.class); // Azure Functionsの呼出結果のシークレットを、Modelオブジェクトに設定する try { GetKeySecretResult getKeySecretResult = objectMapper.readValue( response.getBody(), GetKeySecretResult.class); model.addAttribute("keySecretFunctions", getKeySecretResult.getSecret()); } catch (Exception ex) { throw new RuntimeException(ex); } // Azure App Serviceでのシークレットを、Modelオブジェクトに設定する model.addAttribute("keySecretAppService", getKeySecret()); return "index"; } /** * KeyVaultからシークレットを取得する * * @return シークレット値 */ private String getKeySecret() { AppServiceMSICredentials credentials = new AppServiceMSICredentials(AzureEnvironment.AZURE); KeyVaultClient keyVaultClient = new KeyVaultClient(credentials); SecretBundle bundle = keyVaultClient.getSecret(keyVaultUri, keyVaultSecretKey); return bundle.value(); } } |
その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/azure/tree/master/azure-key-vault-3/demoAzureApp
さらに、作成したサンプルプログラム(Azure Functions側)の構成は以下の通り。
なお、上記の赤枠は、前提条件のプログラムから追加・変更したプログラムである。
<2021年4月13日 追記>
spring-cloud-function-dependenciesのバージョンは、2021年3月16日にリリースしたバージョン3.1.2を利用すると、1つのAzure Functions内に複数のファンクションを含む場合の不具合が解消できている。
その場合、Handlerクラスの継承するクラスを「AzureSpringBootRequestHandler」クラスから「FunctionInvoker」クラスに変更する。
spring-cloud-function-dependenciesの3.1.2を利用した実装サンプルは、以下の記事を参照のこと。
application.propertiesの内容は以下の通りで、App Service側と同様に、サービスプリンシパルのクライアントID・テナントID・パスワードを削除している。
1 2 3 | # Key Vaultへの設定 keyVaultUri=https://keypurinit.vault.azure.net/ keyVaultSecretKey=keySecret |
また、サービスクラスの内容は以下の通りで、App Service側と同様に、AppServiceMSICredentialsクラスを利用し認証情報を指定しないように変更している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | package com.example.service; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import com.example.model.GetKeySecretForm; import com.example.model.GetKeySecretResult; import com.microsoft.azure.AzureEnvironment; import com.microsoft.azure.credentials.AppServiceMSICredentials; import com.microsoft.azure.keyvault.KeyVaultClient; import com.microsoft.azure.keyvault.models.SecretBundle; @Service public class GetKeySecretService { /** keyVaultのURI */ @Value("${keyVaultUri}") private String keyVaultUri; /** keyVaultのシークレットのキー */ @Value("${keyVaultSecretKey}") private String keyVaultSecretKey; /** * 検索用Formに設定したキーにあてはまるシークレットを取得する * * @param getKeySecretForm 検索用Form * @return 結果情報オブジェクト */ public GetKeySecretResult getKeySecret(GetKeySecretForm getKeySecretForm) { GetKeySecretResult getKeySecretResult = new GetKeySecretResult(); if (keyVaultSecretKey.equals(getKeySecretForm.getKey())) { // KeyVaultからシークレットを取得し設定する getKeySecretResult.setSecret(getKeySecret()); } return getKeySecretResult; } /** * KeyVaultからシークレットを取得する * * @return シークレット値 */ private String getKeySecret() { AppServiceMSICredentials credentials = new AppServiceMSICredentials(AzureEnvironment.AZURE); KeyVaultClient keyVaultClient = new KeyVaultClient(credentials); SecretBundle bundle = keyVaultClient.getSecret(keyVaultUri, keyVaultSecretKey); return bundle.value(); } } |
その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/azure/tree/master/azure-key-vault-3/demoAzureFunc
サンプルプログラムの実行結果
サンプルプログラムの実行結果は、以下の通り。
1)「mvn azure-functions:deploy」コマンドによって、Azure Functions上にサンプルプログラムをデプロイする。
なお、Azure Functionsにデプロイする過程は、以下の記事の「Azure FunctionsへのSpring Bootを利用したJavaアプリケーションのデプロイ」を参照のこと。
2)「mvn azure-webapp:deploy」コマンドによって、Azure App Service上にサンプルプログラムをデプロイする。
なお、Azure App Serviceにデプロイする過程は、以下の記事の「App ServiceへのSpring Bootを利用したJavaアプリケーションのデプロイ」を参照のこと。
3) Azure App ServiceのURL「https://azureappdemoservice.azurewebsites.net/」とアクセスした場合の実行結果は、以下の通りで、シークレットの値が表示される。ただし、ローカル環境では実行できない。
なお、上記URLは、下記Azure App ServiceのURLから確認できる。
要点まとめ
- Key Vaultのシークレットを取得する際にマネージドIDを利用すると、サービスプリンシパルの利用は不要になり、application.propertiesからサービスプリンシパルのクライアントID・テナントID・パスワードを削除することができる。