1
1

More than 3 years have passed since last update.

NodeアプリからAzure Key Vaultに接続する方法

Last updated at Posted at 2021-05-25

Key Vaultに接続するためには必ず認証を行う必要がある。
主に どうやって認証を行うか が課題。
また前提条件として、Key Vaultにアクセスする際に APIキーのようなものを使わない (いくらkey-vault上に暗号化キーをセキュアに保存しても、APIキーなどをコード内に置いていたら意味ないため)
Azure App ServiceにNodeアプリを デプロイ してからKey Vaultに接続する場合と、 ローカル のNodeアプリからKey Vaultに接続する場合に分けて紹介する。

Azure App ServiceにNodeアプリをデプロイしてからKey Vaultに接続する方法

マネージドID を使ってKey Vaultに接続するための認証を行う。

マネージドIDとは

システム割り当てマネージド IDとユーザー割り当てマネージド IDの2種類がある。
簡単に説明すると、システム割り当てマネージドIDはAzureサービス(今回でいうとAzure App Service)の特有のIDで、このIDからの接続をKey Vaultの方で許可することで認証をパスすることができる。
ユーザー割り当てマネージドIDは、これ自体が1つのリソースとなっており、このIDをユーザーがAzureサービスに割り当てることで、マネージドIDをAzureサービスに持たせることができる。
今回はシステム割り当てマネージドIDを使う。
詳しくはAzure リソースのマネージド ID とは を参照。

設定方法

  1. Azure App Serviceのリソースを作成する。
  2. Azure App Serviceの設定→IDからシステム割り当て済みの状態を「オン」にする。

7d0c5357-bca4-e085-e307-861b35995661.png
3. NodeアプリをApp Serviceにデプロイする。
4. Azure Key Vaultに戻り、設定→アクセスポリシーから「アクセスポリシーの追加」を押す。
5. 適当な許可を選択し、プリンシパルの選択から先程のApp Serviceを選択し追加。

1e2b078c-cd4d-c0c7-b27c-e58fbb0657b7.png

以上で設定は完了。

コードの中身と結果

まずはモジュールをインストール

npm install @azure/identity
npm install @azure/keyvault-keys
npm install @azure/keyvault-secrets

コードの中身

index.js

const { ManagedIdentityCredential } = require("@azure/identity");
const { KeyClient,CryptographyClient } = require("@azure/keyvault-keys");
const { SecretClient } = require('@azure/keyvault-secrets')

// Build the URL to reach your key vault
const vaultName = "xxxxxxxxxxxxx"; //Key Vaultのリソース名
const url = `https://${vaultName}.vault.azure.net`;


// Lastly, create our keys client and connect to the service
const credential = new ManagedIdentityCredential()

const keyName = "MyKeyName"; //キーの名前
const secretName = "secret-test" //シークレットの名前

async function main() {
    const client = new KeyClient(url, credential); //キー取得のためのclient
    const secretClient = new SecretClient(url, credential) //シークレット取得のためのclient
    const key = await client.getKey(keyName, "RSA");
    const cryptographyClient = new CryptographyClient(key.id, credential) //暗号化、復号化のclient
    const encryptResult = await cryptographyClient.encrypt("RSA1_5", Buffer.from("My Message")); //暗号化結果
    console.log("encrypt result: ", encryptResult.result);
    const decryptResult = await cryptographyClient.decrypt("RSA1_5", encryptResult.result); //復号化結果
    console.log("decrypt result: ", decryptResult.result.toString());
    const secretResult = await secretClient.getSecret(secretName) //シークレットの取得結果
    console.log("secret result: ",secretResult)
}

main();

結果

encrypt result:  <Buffer xx xx xx xx xx xx xx xx xx xx ... 206 more bytes>
decrypt result:  My Message
secret result:  {
  value: 'test',
  name: 'secret-test',
  properties: {
    vaultUrl: 'https://xxxxxxxxxxxxxxxxx.vault.azure.net',
    expiresOn: undefined,
    createdOn: 2020-03-31T08:38:01.000Z,
    updatedOn: 2020-04-10T07:15:30.000Z,
    value: 'test',
    id: 'https://xxxxxxxxxxxxxx.vault.azure.net/secrets/secret-test/xxxxxxxxxxxxxxx',
    name: 'secret-test',
    version: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
    enabled: true,
    recoveryLevel: 'Recoverable+Purgeable'
  }
}

参考:
システム割り当てマネージドIDを使用してAzure Key Vaultにアクセスする
クイック スタート:Node.js 用 Azure Key Vault クライアント ライブラリ (v4)
@azure/identity - npm

ローカルのNodeアプリからKey Vaultに接続する方法

こちらでは成功例と失敗例を示す
まずは成功例(1個)を示した後、失敗例(複数個)を示す

成功例

DeviceCodeCredential(モジュール : @azure/identity)を使って認証を行う。
Nodeアプリを起動させると、URLとデバイスコードといわれる9桁のコードがコンソールに出力される。
そのURLにアクセスし、デバイスコードを入力するとAzureのアプリ(後述するところのrpa-test)にサインインすると認証をパスできる。

設定方法

  1. AzureのホームからAzure Active Directory(AAD)にて、管理→アプリの登録→新規作成(今回はrpa-testと名付けた)

0915c473-b594-4898-17e3-1d277de5da0d.png
2. rpa-testの概要からテナントID、クライアントIDを取得
3. これらのIDを環境変数に書き込む(今回は、環境変数に書き込むのは面倒なので、.envファイルに書き込む)
4. rpa-testの認証から下の方にある アプリケーションは、パブリック クライアントとして扱います を「はい」にする
5. サポートされているアカウントの種類を、この組織ディレクトリのみに含まれるアカウント にする

80b40365-ee7a-bd48-a730-73714491c855.png
6. key-vaultの方に戻る
7. key-vaultのアクセスポリシーにアクセスできるユーザーを追加する
aaa2e6cf-c740-ddad-d3f2-8e178d952f13.png

以上で設定は完了

コードの中身と結果

コードの中身

index.js
const { DeviceCodeCredential } = require("@azure/identity");
const { KeyClient,CryptographyClient } = require("@azure/keyvault-keys");
const { SecretClient } = require('@azure/keyvault-secrets')
require('dotenv').config()

// Build the URL to reach your key vault
const vaultName = "xxxxxxxxxxxxxxxx";
const url = `https://${vaultName}.vault.azure.net`;


// DeviceCodeCredentialの引数にさっき取得したテナントIDとクライアントIDを渡す
const credential = new DeviceCodeCredential(process.env.AZURE_TENANT_ID,process.env.AZURE_CLIENT_ID,async function(err,credential){
        if(err) console.log(err)
        else console.log(credential)
    })

const keyName = "MyKeyName";
const secretName = "secret-test"

async function main() {
    const client = new KeyClient(url, credential);
    const secretClient = new SecretClient(url, credential) //シークレットのclient
    const key = await client.getKey(keyName, "RSA");
    const cryptographyClient = new CryptographyClient(key.id, credential) //暗号化、復号化のclient
    const encryptResult = await cryptographyClient.encrypt("RSA1_5", Buffer.from("My Message")); //暗号化結果
    console.log("encrypt result: ", encryptResult.result);
    const decryptResult = await cryptographyClient.decrypt("RSA1_5", encryptResult.result); //復号化結果
    console.log("decrypt result: ", decryptResult.result.toString());
    const secretResult = await secretClient.getSecret(secretName) //シークレットの取得結果
    console.log("secret result: ",secretResult)
}

main();

結果

{ userCode: 'H3LSY66TU',
  verificationUri: 'https://microsoft.com/devicelogin',
  message: 'To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code XXXXXXXXX to authenticate.' }

コンソールに出力されたURLにいくと...

  1. 先程のコンソールに出力されたコードを入力 451c0029-34d6-881d-81ab-9752400dc76e.png
  2. サインインをする f12d8e6a-623b-4047-238d-7640397fa9ad.png
  3. これでコンソールに結果が表示される
{ userCode: 'XXXXXXXXX',
  verificationUri: 'https://microsoft.com/devicelogin',
  message: 'To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code XXXXXXXXX to authenticate.' }
encrypt result:  <Buffer xx xx xx xx xx xx xx xx xx... >
decrypt result:  My Message
secret result:  { value: 'test',
  name: 'secret-test',
  properties:
   { vaultUrl: 'https://xxxxxxxxxxxxxxx.vault.azure.net',
     expiresOn: undefined,
     createdOn: 2020-03-31T08:38:01.000Z,
     updatedOn: 2020-04-10T07:15:30.000Z,
     value: 'test',
     id: 'https://xxxxxxxxxxxxx.vault.azure.net/secrets/secret-test/xxxxxxxxxxxxxxxxxxx',
     name: 'secret-test',
     version: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
     enabled: true,
     recoveryLevel: 'Recoverable+Purgeable' } }

参考:
DeviceCodeCredential | @azure/identity

失敗例

ここから失敗例を挙げていく
なるべく簡潔に書くように努める

DefaultAzureCredential(モジュール : @azure/identity)

クイック スタート:Node.js 用 Azure Key Vault クライアント ライブラリ (v4) にも書いてあるやり方で、設定方法は途中まで成功例と似ている。コードも上述のURL内に書いてあるため省略。

設定方法

  1. Azure Active Directory(AAD)にて、管理→アプリの登録→新規作成(今回はrpa-testと名付けた)
  2. rpa-testの概要からテナントID、クライアントIDを取得し、管理→証明書とシークレットから新しいクライアントシークレットを作成&クライアントシークレットIDを取得
  3. これらのIDを環境変数に書き込む(今回は、環境変数に書き込むのは面倒なので、.envファイルに書き込む)
  4. Key Vaultに戻り、アクセスポリシーでrpa-testを追加

結果

キーやシークレットを取得でき、使用もできるが
結局、クライアントシークレットIDはAPIキーのようなものなので前提条件に当てはまらないため保留(失敗)

msRestAzure.interactiveLogin(モジュール : ms-rest-azure)

成功例と非常に似ていて、nodeで起動させるとコンソールにURLとコードが出力される。成功例と異なる点は、成功例のDeviceCodeCredentialの引数にアプリケーション(先程のrpa-test)のテナントIDとクライアントIDを渡し、ブラウザではアプリケーションにサインインしていたが、こちらのinteractiveLoginは引数に何も渡さないので単純にAzureにログインしているだけという点。

結果

Key Vaultのアクセスポリシーにサインインするユーザーを追加していれば行けそうな気がしたが、エラーがでた
→credentials must be one of: ApplicationTokenCredentials, UserTokenCredentials, DeviceTokenCredentials, MSITokenCredentials

とのことで、そもそもこの認証ではアクセスできないため失敗

loginWithUsernamePassword(モジュール : ms-rest-azure)

.NETとかNode.jsでKeyVaultを触るを参考にした。
引数にユーザー名とユーザーのパスワード渡すもの。

結果

以下のようなエラーが出た
Error: Failed to acquire token for the user.
you must use multi-factor authentication to access
アクセスするためにはMFA(多段階認証)が必要といわれたため失敗

ほかに様々の方法を試したが、
このloginWithUsernamePasswordにマウスホバーすると
Provides a UserTokenCredentials object. This method is applicable only for organizational ids that are not 2FA enabled. Otherwise please use interactive login.
と書いてあって、2ファクタ認証には対応してない、interactiveなlogin を使えと書いてあるのに気づいた
先程のinteractiveLogin以外で、interactiveなloginする方法はないかと探したところ成功例にたどり着いた。

UserTokenCredentials(モジュール : ms-rest-azure)

アプリケーションのclientIdとtenantId、またユーザー名とユーザーのパスワードを引数に渡す認証

Failed to acquire token for the user
となり失敗

InteractiveBrowserCredential

なぜかnode非対応

UsernamePasswordCredential

引数に要求されているのはアプリケーションのtenantId, clientId, ユーザー名, ユーザーのパスワードのみだが、なぜかエラーが出てアプリケーションのクライアントシークレットを要求されたため却下

http requestでauthorization codeを取得する

OAuth 2.0 auth code grantを参考に、 http requestでauthorization codeを取得→accesstokenを取得→リソースにアクセス
をやってみたが、レスポンスで返ってきたauthorizatin codeが見つからないので、保留

この記事について

この記事はThinkings株式会社にて長期インターンの業務として社内Qiitaに投稿したものを、私の個人用Qiitaに再投稿したものになります。許可を受けて再投稿しております。
また、この記事は2020/04/17に社内Qiitaに投稿されたものです。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1