App ServiceによるWebサイト/APIに対して、Azure外で発行したSSLサーバ証明書を割り当てることもあるだろう。この際、App Serviceに直接インポートすると更新時の手間が増えてしまう。機密情報を安全に取り扱える「Azure Key Vault」をPowerShellで操作することで、効率良く証明書を更新できるようにする。
対象:Azure Key Vault(キーコンテナ)、Azure App Service、Azure PowerShell
Azure App ServiceでWebサイトやAPIを構築する際、Azure外で発行されたSSLサーバ証明書を割り当てなければならない場合もあるだろう(つまりApp Serviceマネージド証明書は使えない状況)。
App Serviceには、.pfxファイルに格納された証明書を直接インポートして割り当てる機能がある。これを使えばAzure外で発行されたSSLサーバ証明書の割り当ても可能だ。しかし、この方法だと証明書の更新のたびに、そのインポートとカスタムドメインへの割り当てという作業が強いられる。もし負荷分散や耐障害性のために、同じWebサイトを複数リージョンに展開している場合、その手間はサイト数に比例して増えてしまう。
そこで本Tech TIPでは、この更新の手間を省ける証明書のインポート方法を紹介する。証明書の保存には、同じAzureの「Key Vault(キーコンテナー)」というサービスを利用する。Key Vaultには、証明書やシークレット(パスワードなどの秘密情報)を安全かつ効率良く扱うための機能が備わっている。
このKey Vaultを利用して、.pfxファイルからの証明書のインポートや、App Serviceへのインポート、カスタムドメインへの割り当てといった一通りの手順を説明したい。
Azureのリソースの操作にはAzure PowerShellを用いる。Azure CLIでの操作方法については、「【Azure】Key Vault(キーコンテナ)にSSLサーバ証明書をインポートして利用する(App Service編)」を参照していただきたい。
Key Vault自体の作成については、Microsoft Learnの解説記事を参考にしていただきたい。またKey Vaultの種類のうち、HSM(ハードウェアによる保護)は対象外としている。
ここからAzure PowerShellでAzureのリソースを操作していく。あらかじめ「Connect-AzAccount」コマンドレットなどで、Azureへのログインとサブスクリプションの選択をしておくこと。
ローカルにある.pfxファイルからKey Vaultへ証明書をインポート(保存)するには、以下のように「ConvertTo-SecureString」「Import-AzKeyVaultCertificate」コマンドレットを実行する。これはインポートのたびに実行する必要がある。
$EncryptedPFXPassword = ConvertTo-SecureString `
-String <.pfxパスワード> `
-AsPlainText -Force
Import-AzKeyVaultCertificate `
-VaultName <Key Vault名> `
-Name <Key Vault内での証明書名> `
-FilePath <.pfxファイル> `
-Password $EncryptedPFXPassword
Import-AzKeyVaultCertificateの「-Password」パラメーターで.pfxファイルのパスワードを渡す場合、あらかじめ暗号化しておく必要がある。上記リストでは、ConvertTo-SecureStringでその暗号化をしている。
「-Name」パラメーターで指定する<Key Vault内での証明書名>には、他の証明書と区別できる一意な名称を指定する。筆者は以下の例のように、「cert-<FQDNの「.」を「-」に差し替えた文字列>-<必要なら付ける接尾辞>」としている。
管理しやすくするために、この名称に証明書の有効期限など日付を表す文字列を加えたくなるかもしれない(.pfxファイル名ではそのようにしている人も多いのではないだろうか)。しかし、それは止めておいた方がよい。Key Vaultの場合、同じ名称を維持したまま、新しい証明書へ更新していくことで、App Serviceの設定を変えることなく自動的に証明書を更新できるからだ。
Key VaultからApp Serviceへ証明書をインポートするには、App ServiceにKey Vaultへのアクセス許可を明示的に与える必要がある。許可していないと、インポート時に以下のようなエラーが発生する。
Unable to verify Key Vault permissions.
You may need to grant Microsoft.Azure.WebSites service principal the Secret:Get permission
アクセス許可を与えるには、Key Vaultの「アクセスポリシー」を設定する。それには、以下のように「Set-AzKeyVaultAccessPolicy」コマンドレットを実行する。これはKey Vaultごとに1回だけ実行すればよい。
Set-AzKeyVaultAccessPolicy `
-VaultName <Key Vault名> `
-PermissionsToSecrets get `
-PermissionsToCertificates get `
-ObjectId abfa0a7c-a6b6-4736-8310-5855508787cd
「-PermissionsToCertificates」「-PermissionsToSecrets」パラメーターの後には、Key Vault内の証明書に対して許可したい操作を下表から選んで「,」(半角コンマ)で区切って列挙する。
パラメーターに指定する単語 | 許可される操作の内容 |
---|---|
All | Purge(消去)以外の全て |
Get | 取得 |
List | 一覧を取得 |
Set | 設定 |
Delete | 削除 |
Recover | 回復 |
Backup | バックアップ |
Restore | 復元 |
Purge | 消去 |
Set-AzKeyVaultAccessPolicyコマンドレットの-PermissionsToCertificatesに指定できる単語 複数の単語を指定する場合は、「,」(半角コンマ)で区切って列挙すること。 |
上記リストでは、それぞれ「get(取得)」のみを許可している。
また「-ObjectId」パラメーターの後には、許可を与える先のリソースに割り当てられているサービスプリンシパルのオブジェクトIDを指定する。上記で指定している「abfa0a7c-a6b6-4736-8310-5855508787cd」は、App ServiceのサービスプリンシパルのオブジェクトIDだ。これは一意に決まっていて、通常は他のサブスクリプションでも共通である。
ただAzure Governmentなど別の環境では、このオブジェクトIDが異なることもある。そのような場合は、以下のように「Get-AzADServicePrincipal」コマンドレットを実行してみよう。
$ServicePrincipal = Get-AzADServicePrincipal `
-DisplayName "Microsoft.Azure.Websites"
$ServicePrincipal.ServicePrincipalName
表示されたサービスプリンシパル名の中に、GUID形式のオブジェクトIDが含まれているはずだ。
Key Vaultに保存した証明書をApp Serviceで利用するには、まずそれをApp Serviceにインポートする必要がある。それには以下のように「Import-AzWebAppKeyVaultCertificate」コマンドレットを実行する。これは原則として、証明書ごとに1回だけ実行すればよい(更新された証明書を手動で即座にApp Serviceへ反映したいなら、再度実行する必要がある)。
Import-AzWebAppKeyVaultCertificate `
-WebAppName <App Service名> `
-ResourceGroupName <App Serviceリソースグループ名> `
-KeyVaultName <Key Vault名> `
-CertName <Key Vault内での証明書名>
「-ResourceGroupName」パラメーターで指定するのは、App Serviceの方のリソースグループであり、Key Vaultの方ではないので気を付けよう。また、App Serviceにインポートされた証明書のリソースは、ここで指定したリソースグループ内に作られる。
上記のコマンドレットで証明書をインポートした場合、App Service内での証明書名は「<Key Vault名>-<Key Vault内での証明書名>」というように自動で設定される。任意の名称を指定することはできないようだ。
最後に、上記の手順でインポートしたSSLサーバ証明書を、App Serviceのカスタムドメインに割り当てる(ひも付ける)。それには以下のように「New-AzWebAppSSLBinding」コマンドレットを実行する。
$AppServiceName = "<App Service名>"
$AppServiceRGName = "<App Serviceリソースグループ名>"
$CustomDomainName = "<カスタムドメイン名>" # 例: www.example.jp
# App Serviceのリソースグループにひも付けられた証明書(複数)を取得して一覧表示
$Certs = Get-AzWebAppCertificate -ResourceGroupName $AppServiceRGName
$Certs | Select-Object -Property KeyVaultSecretName,SubjectName,Thumbprint | Format-Table
# Key Vault内での証明書名が有意で、かつサブジェクト名がカスタムドメイン名に一致する証明書を抽出
$Cert = $Certs | Where-Object {
($_.KeyVaultSecretName -ne "") -and ($_.SubjectName -eq $CustomDomainName)
}
# 対象の証明書の母印(サムプリント)を特定
$Thumbprint = $Cert.Thumbprint
#対象の証明書をカスタムドメインに割り当て
New-AzWebAppSSLBinding `
-WebAppName $AppServiceName `
-ResourceGroupName $AppServiceRGName `
-Name $CustomDomainName `
-Thumbprint $Thumbprint `
-SslState SniEnabled
上記リストでは、Key Vaultからインポートした証明書のサブジェクト名が、割り当てたいカスタムドメイン名と一致することを前提としている。これは大多数の状況で当てはまるだろう。しかし、カスタムドメイン名がSAN(Subject Alternative Name: サブジェクト代替名)に含まれていても、サブジェクト名とは異なっている、といった可能性もある。そうした場合は、上記リストの変数「$Certs」に代入された各証明書のプロパティを調べて対象の証明書を特定し、その母印(Thumbprint)を直接指定する必要がある。
「-SslState」パラメーターでは、「SniEnabled」「IpBasedEnabled」のいずれかを指定する。通常は前者を指定して、一般的なSNI(Server Name Indication: 1つのIPアドレスで複数のドメインを運用するための仕組み)を適用する。IPアドレスとSSLサーバ証明書を1対1で割り当てるなら後者を選択する(ただし有料)。
上記の手順で設定したSSLサーバ証明書の有効期限が間近となり、その更新版(新しい証明書)を.pfxファイルで入手したら、Key Vault内の同じ証明書名に対して前述のImport-AzKeyVaultCertificateコマンドレットでインポートする。これにより、更新版が最新バージョンとしてKey Vaultに追加されるとともに、デフォルトでその更新版が他のサービスへ提供されるようになる。
そのまま放置していると、以前にKey Vaultからインポートされた(ひも付けられた)App Serviceの証明書は、24時間以内に自動で最新バージョン(更新版)に差し替わる。
すぐに最新バージョンへ差し替えたい場合は、証明書名などを変えることなく前述のImport-AzWebAppKeyVaultCertificateコマンドレットを実行するとよい。即座にKey VaultとApp Serviceの間で証明書の同期が取られて、最新バージョンに差し替わる。
■関連リンク
Copyright© Digital Advantage Corp. All Rights Reserved.