【AzureVM】サーバー証明書を自動更新する！【acmebot】

# はじめに

どうも、ATL のヴィリアスです。

サーバー証明書の有効期間が将来的に 47 日間に短縮されることが決定されたため、今後、手動での証明書更新が現実的ではなくなっていきます！
そこで acmebot を使い、AzureVM 上の IIS で動作する Webアプリ の証明書自動更新を実装しました。

意外と情報が少なく、実装に難航した部分があったので、忘れる前に記事として実装メモを残しておきます！

# 実装の背景・経緯

## 証明書有効期間の短縮

https://pki.world-tls.com/20241010-tls-validity-period/

Apple などの主要ブラウザベンダーにより、TLS 証明書の有効期間を47日間に短縮することが決定されました。
これによって、手動での証明書更新では運用が困難になるため、自動化の実装がもはや必須となりました。

# acmebot とは？

https://github.com/shibayan/keyvault-acmebot

acmebotは、Let's Encrypt などの証明書の自動取得・更新を行う Azure Functions アプリケーションです。

## 特徴
- Let's Encrypt などから無料で TLS 証明書を取得
- Azure Key Vault に証明書を自動保存
- DNS-01 チャレンジによるドメイン認証
- 証明書の自動更新 (期限切れ前に実行)
- Webhook による更新通知
- 低コストで運用可能

# 実装概要

基本的に Wiki を参考に進めていけば、実装できます！

https://github.com/shibayan/keyvault-acmebot/wiki

このサイトも非常に参考になりました。

https://cloudbuild.co.uk/free-automated-lets-encrypt-certificates-in-azure-key-vault-with-acme-bot-a-step-by-step-guide/

なお今回の実装後、大まかには以下のとおりに証明書が自動更新されるようになります。

1. **acmebot** が Let's Encrypt から証明書を取得
2. **Azure Key Vault** に証明書を保存
3. **Azure VM 拡張機能** が Key Vault から証明書を取得して VM に配置
4. VM 上の **IIS** で証明書が自動適用される

# 実装のイメージ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2124150/670591c5-029f-4ea7-95fc-9e58223b9426.png)

# 実装手順

## 1. Azure リソースの作成

証明書の自動更新実装にあたり、ARM テンプレートを使用しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2124150/fdd270e3-8558-40f0-b72a-2dabf9586014.png)

### テンプレート展開後に作成されるリソース

- **func-acmebot-pcie**: 関数アプリ (acmebot本体)
- **plan-acmebot-pcie**: App Service プラン (Y1：従量課金)
- **stpcieahoscajn6func**: ストレージアカウント
- **appi-acmebot-pcie**: Application Insights
- **log-acmebot-pcie**: Log Analytics ワークスペース

### 補足: 月額コスト

https://github.com/shibayan/keyvault-acmebot/wiki/Frequently-Asked-Questions#how-much-does-azure-cost

- 関数アプリ: $0.01 未満
- ストレージアカウント: $0.1 ~ 0.2
- Application Insights: $0.01 ~ 0.02

## 2. 仮想マシンの設定

### マネージド ID の有効化
VM のマネージド ID を有効にします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2124150/8b5afb38-1380-4509-96ff-27fbf6412b4a.png)

これは「Windows 用の Azure Key Vault 仮想マシン拡張機能」(後ほどインストールする) によって、証明書を自動更新するために必要です。

#### 参考

https://learn.microsoft.com/ja-jp/azure/virtual-machines/extensions/key-vault-windows?tabs=version3#prerequisites

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2124150/70a11fb0-28e9-4c03-b02f-b873f94330e4.png)

### マネージド ID へのロール割り当て
有効にした VM のマネージド ID に対して、証明書を格納する Key Vault の IAM 設定から `Key Vault Certificate User` ロールを割り当てます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2124150/416b7f14-8da5-4f31-b2a9-941b0598d733.png)

これは Key Vault 内に格納されている証明書に VM からアクセスするために必要です。

## 3. VM 拡張機能のインストール

Azure Key Vault VM 拡張機能をインストールして、Key Vault から証明書を自動取得するように設定します。

https://learn.microsoft.com/ja-jp/azure/virtual-machines/extensions/key-vault-windows?tabs=version3

こちらの記事でもわかりやすく説明されています！

https://ayuina.github.io/ainaba-csa-blog/vmextension-for-azurekeyvault/

今回は Azure CLI 経由で `settings.json` を読み込み、拡張機能をインストールしています。

```bash
# VM拡張機能の設定
az vm extension set `
    --name "KeyVaultForWindows" `
    --publisher Microsoft.Azure.KeyVault `
    --resource-group "<リソースグループの名前>" `
    --vm-name "<仮想マシンの名前>" `
    --settings "@settings.json"
```

```json:settings.json
{   
    "secretsManagementSettings": {
        "pollingIntervalInS": "3600",
        "linkOnRenewal": true,
        "observedCertificates": [
            {
                "url": "https://<Key Vault の名前>.vault.azure.net/secrets/<シークレットの名前>",
                "certificateStoreName": "MY",
                "certificateStoreLocation": "LocalMachine",
                "accounts": [
                    "Network Service",
                    "Local Service"
                ]
            }
        ]
    }
}
```

`settings.json` に記述するキーと値のペアについては、こちらに説明があります。

https://learn.microsoft.com/ja-jp/azure/architecture/example-scenario/certificate-lifecycle/#key-vault-extension

拡張機能のインストールに成功すると、VM の [拡張機能とアプリケーション] に `KeyVaultForWindows` が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2124150/1218e6fe-3bed-47fc-8741-ce63f583fed1.png)

### 補足: 設定のポイント
- [証明書は Key Vault の [証明書] に格納するけど、`url` は `https://~/secrets/~` にする。](https://learn.microsoft.com/ja-jp/azure/virtual-machines/extensions/key-vault-windows?tabs=version3#extension-schema)
    - `https://~/certificates/~` ではないので注意！
- `linkOnRenewal: true`: 証明書更新時に IIS に自動適用
- `certificateStoreLocation: "LocalMachine"`: IIS がアクセス可能な証明書ストア
- `accounts: ["Network Service"]`: IIS ワーカープロセスのアカウント

:::note warn
**Azure Portal でのVM 拡張機能設定でエラーが発生**
Azure Portal での GUI 操作では設定がうまくいかず、デプロイエラーが頻発しました。
Azure CLI だとすんなり実行できました。
:::

## 4. 証明書の自動更新

拡張機能をインストール後、拡張機能によって以下の処理が自動的に実行されます！

1. Key Vault から証明書を取得
2. `LocalMachine\MY` ストアに証明書を配置
3. IIS に証明書が**自動適用**される (すでに証明書エラーが発生していた場合、ここで解消)

今回、証明書は「**45 日** ごとに更新 + VM 配置」が自動で行われるように設定しています。
`45` という数値 (自動更新間隔) は 関数アプリの [環境変数] で設定可能です！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2124150/3d23f082-701d-4ff0-9039-c9b93a572da5.png)

https://github.com/shibayan/keyvault-acmebot/wiki/Getting-Started#3-add-application-settings

**ここまで来れば、acmebot でサーバー証明書の自動更新が実装は完了です！** 

### 補足: 証明書が自動適用される理由

頼れる相棒、[Copilot](https://copilot.microsoft.com/) によるとこんなイメージみたいです。

**1. linkOnRenewal: true の設定**
- この設定により、証明書が更新されたときや初回デプロイ時、証明書を使用しているサービス（例: IIS）に自動的にリンクされるようになります。
- これは Microsoft の拡張機能が内部的に netsh や certutil などを使ってバインドを更新している可能性があります。

**2. 証明書の配置先が LocalMachine\MY**
- IIS は通常このストアから証明書を参照します。
- 既存の HTTPS バインドが「サムプリント一致」などで構成されていた場合、新しい証明書が同じ名前やサムプリントで置き換わると、IIS 側で自動的に新しい証明書が使われることがあります。

**3. 対象アカウントに Network Service が含まれている**
- IIS のワーカープロセスは通常 Network Service アカウントで動作しており、このアカウントに証明書のアクセス権があることで、IIS が証明書を読み取れる状態になっています。

# おわりに

acmebotを使用することで、低コスト・低負荷でサーバー証明書の自動更新が実現できました。
証明書有効期間の短縮に対応するための実装として、非常に効果的だと思います！

Azure 環境で同様の実装を行う際は、ぜひご参考までに！

それでは、また次の記事で。

# そのほか参考にしたもの

https://zenn.dev/yotan/articles/3c1f5e3457b5ac

https://zenn.dev/yotan/articles/abfd178caae809