AD FS サーバーのトークン署名証明書の有効期限が迫った時に気づけるイベント ログが記録されるか調べてみた

#はじめに
本記事は、 [Microsoft Azure Tech | Advent Calendar 2021](https://qiita.com/advent-calendar/2021/microsoft-azure-tech) 18 日目の記事です。

AD FS サーバーが既定で 1 年間有効な**トークン署名証明書**は AD FS サーバーが発行するトークンに**署名**を行う際に必ず必要なもので連携をしている RP (Relying Party) や SP (Service Provider) 側は AD FS サーバーが発行したトークンであるかどうかを**検証**を行うために公開鍵が必要で、公開鍵はこのトークン署名証明書内に含まれています。

つまり、AD FS サーバーと連携するアプリケーション側においても有効なトークン署名証明書を管理する必要があります。
また、AD FS サーバーは**自動ロールオーバー**という機能があります。

今回はこの自動ロールオーバーの機能とこの機能を利用して自動生成されたトークン署名証明書の有効期限が近付いてきていることを AD FS サーバー上のイベント ログで気づけることが可能かどうか調べてみました。

#自動ロールオーバーの仕組み
自動と書いているとおり自動で新しいトークン署名証明書を**AD FS サーバー** (AD FS ファームに複数台 AD FS サーバーが構成されている場合はプライマリ AD FS サーバー) 上に生成してくれますが、そのステップを箇条書きにすると以下 2 ステップになります。

まず、前提としてトークン署名証明書 (トークン暗号化解除証明書も同様の動作をします)には**プライマリ**と**セカンダリ**という概念があり、AD FS サーバーは**プライマリ**のトークン署名証明書を利用して署名を行います。

従ってセカンダリのトークン署名証明書の有効期限がまだ切れているから問題ない、という訳にはいきません。
常に意識するべきは**プライマリ**として定義されているトークン署名証明書の有効期限となります。

1. 自動ロールオーバーの機能が有効の場合プライマリのトークン署名証明書の有効期限が切れる 20 日前になるとセカンダリとして新しく規定で 1 年間有効なトークン署名証明書が作成されます。
2. セカンダリのトークン署名証明書が自動生成されてから 5 日後にセカンダリのトークン署名証明書がプライマリに自動的に昇格します。


上記 1. の段階ではセカンダリとして生成されるので、このタイミングで連携するアプリケーション側に新しくセカンダリとして生成されたトークン署名証明書 (正確にはトークン暗号化解除証明書を含む) をアップロードしなくてもまだ影響は受けません。
なぜなら上記で説明したとおり、あくまで署名に利用されるのは**プライマリ**のトークン署名証明書であるからです。

次に上記 2. の動作が行われるまでは 5 日間の猶予があります (5 日間しかないと言えなくもありませんが…) ので、この間にセカンダリのトークン署名証明書を連携するアプリケーション側にもアップロードし、5 日後にプライマリとして自動で切り替わっても署名の検証が行えるように対処します。

#トークン署名証明書の設定状態を確認する
AD FS の機能をインストールとすると自動追加される 「AD FS の管理」で確認する方法と PowerShell コマンドレットで確認する方法を紹介します。

AD FS 管理コンソール上では「AD FS」→「サービス」→「証明書」の順に選択すると以下画面ショットが表示されます。
「トークン署名」の欄で設定状態が確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/a126635a-29e2-5add-2efd-97f0605ef83c.png)

PowerShell で確認する場合には以下コマンドレットを 1 行で実行することで、フェデレーション関係を結んでいる Azure AD 側で持っているトークン署名証明書の設定状態も確認ができます。
Source が **ADFS Server** となっているのが AD FS 管理コンソールで確認できる AD FS サーバー側のトークン署名証明書の設定状態となります。

TokenSigningCertificate が**プライマリ**となり NextTokenSigningCertificate が**セカンダリ**となります。

```powershell
Get-MsolFederationProperty -DomainName <フェデレーション ドメイン名> | FL Source, TokenSigningCertificate, NextTokenSigningCertificate

※実際には xxx でマスク表記はされません。

Source                      : ADFS Server
TokenSigningCertificate     : [Subject]
                                CN=ADFS Signing - sts.xxx.xxx

                              [Issuer]
                                CN=ADFS Signing - sts.xxx.xxx

                              [Serial Number]
                                5077F116999FFBA249541522AD474634

                              [Not Before]
                                2021/12/18 0:00:10

                              [Not After]
                                2022/12/18 0:00:10

                              [Thumbprint]
                                80294B8E4401F34712223D4757720C78D5E1BCE9

NextTokenSigningCertificate : [Subject]
                                CN=ADFS Signing - sts.xxx.xxx

                              [Issuer]
                                CN=ADFS Signing - sts.xxx.xxx

                              [Serial Number]
                                1A893B21E4C7E2A84FA9E13217056CB6

                              [Not Before]
                                2022/12/10 0:36:55

                              [Not After]
                                2023/12/10 0:36:55

                              [Thumbprint]
                                A53CC075C2FE1093233971806AD8D701991DFA35


Source                      : Microsoft Office 365
TokenSigningCertificate     : [Subject]
                                CN=ADFS Signing - sts.xxx.xxx

                              [Issuer]
                                CN=ADFS Signing - sts.xxx.xxx

                              [Serial Number]
                                5077F116999FFBA249541522AD474634

                              [Not Before]
                                2021/12/18 0:00:10

                              [Not After]
                                2022/12/18 0:00:10

                              [Thumbprint]
                                80294B8E4401F34712223D4757720C78D5E1BCE9

NextTokenSigningCertificate : [Subject]
                                CN=ADFS Signing - sts.xxx.xxx

                              [Issuer]
                                CN=ADFS Signing - sts.xxx.xxx

                              [Serial Number]
                                1A893B21E4C7E2A84FA9E13217056CB6

                              [Not Before]
                                2022/12/10 0:36:55

                              [Not After]
                                2023/12/10 0:36:55

                              [Thumbprint]
                                A53CC075C2FE1093233971806AD8D701991DFA35
```

#####トークン暗号化解除証明書について
気になっている方もいるかもしれないので簡単にここで捕捉します。
例えば別ドメイン環境の AD FS サーバーとフェデレーション関係を結んでいる AD FS サーバーがあるとします。
フェデレーション先の AD FS サーバーがトークンを発行する側 (IdP) となりトークンを受け取る側の AD FS サーバーが (RP) ある構成において、 IdP 側の AD FS がトークンを暗号化して送ってきた際に復号化するために利用される証明書になります。

#イベント ログが出るのか確認
AD FS 管理コンソールや PowerShell で確認することでトークン署名証明書の有効期限を確認できるのですが、確認できる手段は多いに越したことはないと思います。
既定の有効期間のまま運用する場合でも年次の運用となるため、更新処理を忘れてしまう可能性も 0 ではありません。

トークン署名証明書の有効期間が近付いた時に AD FS サーバー上のイベント ログにログが記録されるのかどうか確認を行いました。
その結果、以下イベント ログが記録されていることが確認できました。

>1年先まで待てないので意図的に Windows Server OS の時刻を未来にして確認していますが、本番環境では絶対に試さないでください。
もし確認されたい場合は自由にいじり倒せる検証環境で確認してください。

結果を先にまとめると以下になります。

出力先：「アプリケーションとサービス ログ」→「AD FS」→「Admin」



| レベル | イベント ID | ソース | メッセージ | 
|:-----------|:------------|:------------|:------------|
| 警告   | 435 | AD FS  | プライマリ AD FS トークン署名証明書 (拇印 AA33D26FE551B58C4C2984740930463636A2BD90) の有効期限が 8/3/2022 1:44:47 AM UTC に切れます。 証明書ロールオーバー サービスは、12/22/2021 3:00:10 PM UTC に現在のセカンダリ (拇印80294B8E4401F34712223D4757720C78D5E1BCE9) にロールオーバーされます。 フェデレーション メタデータに依存している証明書利用者には、自動的に通知されます。フェデレーション メタデータに依存していない証明書利用者には、12/22/2021 3:00:10 PM UTC にロールオーバーが発生する前に新しい証明書について通知する必要があります。|
| 警告   | 385 | AD FS | AD FS で、AD FS 構成データベース内の 1 つ以上の証明書の有効期限が切れているか間もなく切れるため、手動で更新する必要があることが検出されました。詳細については、追加情報を参照してください。 追加情報:MSIS8052: Token-signing certificate with thumbprint '80294B8E4401F34712223D4757720C78D5E1BCE9' is set to expire on 12/18/2022 12:00:10 AM.MSIS8052: Token-decrypting certificate with thumbprint '7345F5E5C96556DD225B5E80403D7EA9E899A5F3' is set to expire on 12/18/2022 12:00:09 AM. |
| 警告   | 436 | AD FS  | プライマリ AD FS トークン暗号化解除証明書 (拇印 B797AD5DB29FCD9AB33B838CB160F37373358DD9) の有効期限が 9/6/2022 2:10:41 AM UTC に切れます。 証明書ロールオーバー サービスは、12/22/2021 3:00:09 PM UTC に現在のセカンダリ (拇印 7345F5E5C96556DD225B5E80403D7EA9E899A5F3) にロールオーバーされます。 フェデレーション メタデータに依存している ID プロバイダーには、自動的に通知されます。暗号化されたトークンを AD FS に送信し、フェデレーション メタデータに依存していない ID プロバイダーには、9/6/2022 2:10:41 AM UTC に有効期限が切れる前に、新しい証明書について通知する必要があります。|

画面ショットはそれぞれ以下のとおりです。
※イベント ID 435
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/ee56e694-8094-a250-9c06-7aae274495a8.png)

※イベント ID 436
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/72eb890a-f7d4-8570-7449-76a8bed25f71.png)

※イベント ID 385
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/c0e51129-bb74-f604-195e-ce32282e7412.png)

※イベント ID 385 (サービス通信証明書)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/3805ef6e-e9d4-fb20-c6b6-7da131ae2939.png)

一部内容は推測とはなりますが、まとめると以下の通りです。

| イベント ID | 対象 |説明 |
|:-----------|:------------|:------------|
|435|トークン署名証明書 |イベント ログ内に記載のプライマリ トークン署名証明書の有効期限が近付いている警告メッセージ。連携するアプリケーション側で AD FS サーバー側のフェデレーション メタデータを自動で更新する仕組みになっていない場合は手動で更新する必要がある。 |
|436 |トークン暗号化解除証明書 |イベント ログ内に記載のトークン暗号化解除証明書の有効期限が近付いている警告メッセージ。連携するアプリケーション側で AD FS サーバー側のフェデレーション メタデータを自動で更新する仕組みになっていない場合は手動で更新する必要がある。 |
|385 |トークン署名証明書、トークン暗号化解除証明書、サービス通信証明書 |イベント ログ内に記載の拇印を持つトークン署名証明書 or トークン暗号化解除証明書 or サービス通信証明書の有効期限切れが近付いているイベント。サービス通信証明書は SSL/TLS 証明書とは用途が異なるが、SSL/TLS 証明書と同一の証明書をサービス通信証明書と利用するケースがほとんどのため、サービス通信証明書がこのイベント ID を検知した場合は AD FS サーバー上のSSL/TLS 証明書の有効期限が近いことが分かる。 |

上記イベント ID を監視の対象とすることでトークン署名証明書を含む各証明書の有効期限が近付いていることに気づけると思います。
AD FS サーバー自身にはメール通知する機能は用意されていないため、イベント ID をトリガーとして監視する必要があります。

#イベント ログ以外に気づける手段ないのか。
あります。
Azure AD Connect Health for AD FS を監視対象の AD FS サーバーに導入することでアラートを検知できるようになります。
画面イメージとしては以下のような感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/53cb7484-188d-82a1-5cda-892099986c4a.png)

上記「通知設定」をクリックすることで、以下画面ショットのように Azure AD テナントのグローバル管理者以外にも通知先のメールを指定することができますので、運用管理者用のアドレスを設定することでイベント ログを直接参照しなくても気づけるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373423/f037f8d0-7349-b551-b5e5-d51af7e21504.png)

Azure AD Connect Health for AD FS は AD FS サーバー側の監査ログを有効化することでエージェント経由で Azure AD 上のサインイン ログに AD FS 認証したログも吸い上げてくれるようになるため、 AD FS 認証から Azure AD 認証に移行する際の AD FS 利用状況の分析にも使えます。

インストール要件などは参考情報欄にある公開情報を参照いただければ幸いです。

#最後に
今回は AD FS サーバーが発行するトークンに署名をする際に利用するトークン署名証明書の有効期限が近付いて来たときに気づける手段がないのか実際に確認をしてみました。

もちろん各証明書の有効期限を適切に管理していただいているのであれば逐一イベント ログの内容を確認していただく必要はありませんが、イベント ID やイベント ログ内の文字列をトリガーに運用監視されている環境でございましたら、監視対象として検討いただければ幸いです。

#参考情報
Microsoft 公式 Blog になります。手動で更新する手順が網羅されています。
[AD FS の証明書更新手順（トークン署名証明書、トークン暗号化解除証明書）](https://jpazureid.github.io/blog/active-directory-federation-service/update-token-certificate/)

Microsoft 公式 Blog になります。自動ロールオーバーの機能について丁寧に解説しています。
[AD FS の自動証明書ロールオーバー機能について](https://jpazureid.github.io/blog/active-directory-federation-service/ad-fs-auto-rollover/)

AD FS サーバー上で管理する各証明書の用途と要件について説明している公開情報になります。
[Windows サーバーの AD FS の要件　証明書の要件](https://docs.microsoft.com/ja-jp/windows-server/identity/ad-fs/design/ad-fs-requirements#BKMK_1)

AD FS と Azure AD をフェデレーション関係を結んでいる環境において、トークン署名証明書を更新、確認するための手順が解説されている公開情報になります。
[Microsoft 365 および Azure Active Directory 用のフェデレーション証明書の更新](https://docs.microsoft.com/ja-jp/azure/active-directory/hybrid/how-to-connect-fed-o365-certs#renew-the-token-signing-certificate-automatically-recommended)

各 Windows Server OS 上で記録される AD FS イベント ログの一覧が確認できるサイトになります。
[AD FS Event Viewer](https://adfshelp.microsoft.com/AdfsEventViewer/GetAdfsEventList)

Azure AD Connect Health for AD FS のインストール要件とインストール手順が解説されている公開情報になります。
[Azure AD Connect Health エージェントのインストール](https://docs.microsoft.com/ja-jp/azure/active-directory/hybrid/how-to-connect-health-agent-install)