Service Fabric クラスターで証明書を管理する

この記事では、Azure Service Fabric クラスターで通信をセキュリティで保護するために使用される証明書の管理面について説明します。 これは、Service Fabric クラスターのセキュリティの概要と、Service Fabric での X.509 証明書ベースの認証に関する説明を補完します。

前提条件

開始する前に、基本的なセキュリティの概念と、クラスターのセキュリティを構成するために Service Fabric で公開されているコントロールについて理解している必要があります。

免責情報

この記事では、証明書管理の理論的側面と、サービス、テクノロジなどの詳細をカバーする実践的な例を組み合わせて説明します。 この記事の対象読者の多くは Microsoft 内部のユーザーのため、この記事では Azure に固有のサービス、テクノロジ、および製品を参照しています。 特定の Microsoft 固有の詳細が該当しない場合は、最後のコメント セクションで説明やガイダンスを求めてください。

証明書管理の定義

コンパニオン記事の「Service Fabric クラスターでの X.509 証明書ベースの認証」で確認できるように、証明書とは、基本的に非対称キー ペアを、それが表すエンティティを記述する属性とバインドする暗号化オブジェクトです。

ただし、証明書は、有効期間が有限であり、侵害の影響を受けやすいことから、"生鮮" 品でもあります。 誤って開示されたり、悪用されたりすると、セキュリティの観点から証明書は役に立たなくなる可能性があります。 この特性は、定期的に、またはセキュリティ インシデントに応じて、証明書を変更する必要があることを意味します。

証明書管理のもう 1 つの側面 (そして完全に別個のトピック) は、証明書の取得とプロビジョニングに関係するエンティティの ID を保護する、証明書の秘密キーまたはシークレットの保護です。

ここでは、"証明書管理" を、証明書を取得し、それを必要な場所に安全かつセキュアに転送するために使用されるプロセスと手順として説明します。

登録、ポリシー設定、承認制御などの一部の管理操作は、この記事では取り上げません。 プロビジョニング、更新、キー更新、失効など、偶発的にしか Service Fabric に関連しない操作もあります。 しかし、これらの操作を理解しておくとクラスターを適切にセキュリティで保護するのに役立つため、この記事では若干取り上げています。

当面の目標は、中断なくクラスターの可用性を確保するために、証明書管理を可能な限り自動化することです。 このプロセスではユーザー操作が不要なため、セキュリティ保証も提供する必要があります。 Service Fabric クラスターでは、この目標を達成できます。

記事のこの後の部分では、まず証明書管理を分解し、後で自動ロールオーバーの有効化に焦点を当てます。

具体的には、次のトピックについて説明します。

  • 所有者とプラットフォーム間の属性の分離に関する前提
  • 証明書の発行から使用までの長いパイプライン
  • 証明書ローテーション: 理由、方法、タイミング
  • 発生する可能性のある問題

この記事では、次のトピックは取り上げません。

  • ドメイン名のセキュリティ保護と管理
  • 証明書への登録
  • 証明書の発行を実施するための承認コントロールの設定

これらのトピックについては、任意の公開キー基盤 (PKI) サービスの登録機関 (RA) を参照してください。 Microsoft 内部の読者の場合は、Azure Security に連絡できます。

証明書管理に関係するロールとエンティティ

Service Fabric クラスターでのセキュリティ アプローチは、「クラスター所有者が宣言し、Service Fabric ランタイムによって実施される」というケースです。つまり、クラスターの機能に関与する ID の証明書、キー、またはその他の資格情報で、サービス自体から取得されるものはほぼありません。 これらはすべてクラスター所有者によって宣言されます。 また、クラスター所有者は、証明書をクラスターにプロビジョニングし、必要に応じて更新し、常に証明書のセキュリティを確保する役割も担っています。

具体的には、クラスター所有者は次のことを確保する必要があります。

  • クラスター マニフェストの NodeType セクションで宣言された証明書を、プレゼンテーション規則に従って、その種類の各ノードで見つけることができる。
  • 前述のように宣言されている証明書が、対応する秘密キーを含めてインストールされている。
  • プレゼンテーション規則で宣言されている証明書が検証規則に合格している。

Service Fabric には次の役割があります。

  • クラスター定義の宣言と一致する証明書を検索する
  • "必要" に応じて、Service Fabric で制御されているエンティティに、対応する秘密キーへのアクセス権を付与する
  • 確立されたセキュリティのベスト プラクティスとクラスター定義に厳密に従って証明書を検証する
  • 証明書の有効期限が迫っている場合、または証明書の検証の基本手順を行えなかった場合にアラートを生成する
  • クラスター定義の証明書関連の側面がホストの基となる構成によって満たされていることを (ある程度) 検証する

そのため、(アクティブな操作としての) 証明書管理の負担は、クラスター所有者にのみ課せられます。 以下のセクションでは、使用できるメカニズムとそのクラスターに与える影響を含め、各管理操作を詳しく見ていきます。

証明書のプロセス

Service Fabric クラスターのコンテキストで、証明書の発行から使用までの過程を簡単におさらいしましょう。

  1. ドメイン所有者が、証明書に関連付けるドメインまたはサブジェクトを PKI の RA に登録します。 次に、証明書でドメインまたはサブジェクトの所有権の証拠が設定されます。

  2. また、ドメイン所有者は、承認された要求者の ID (指定されたドメインまたはサブジェクトの証明書の登録を要求する資格があるエンティティ) をRA で指定します。

  3. 次に、承認された要求者が、シークレット管理サービスを介して証明書に登録します。 Azure では、シークレット管理サービスとして Azure Key Vault が選択されています。これによって、承認されたエンティティではシークレットと証明書を安全に格納し、それらを取得できます。 また、Key Vault では、関連付けられている証明書ポリシーの構成に従って、証明書の更新とキー更新が行われます。 Key Vault では、ID プロバイダーとして Microsoft Entra ID を使います。

  4. 承認された取得者 ("プロビジョニング エージェント") は、秘密キーを含め、キー コンテナーから証明書を取得して、クラスターをホストしているマシンにインストールします。

  5. (各ノードで管理者特権で実行されている) Service Fabric サービスによって、許可された Service Fabric エンティティに証明書へのアクセス権が付与されます。それらは、ローカル グループによって指定されており、ServiceFabricAdministrators と ServiceFabricAllowedUsers に分割されています。

  6. Service Fabric ランタイムからは、フェデレーションを確立したり、承認されたクライアントからの受信要求を認証したりする目的で証明書にアクセスし、使用します。

  7. プロビジョニング エージェントでは、キー コンテナー証明書を監視し、更新が検出されるとプロビジョニング フローをトリガーします。 次に、クラスター所有者が必要に応じてクラスター定義を更新し、証明書をロールオーバーする意図を示します。

  8. プロビジョニング エージェントまたはクラスター所有者は、使用されていない証明書のクリーンアップと削除も担当します。

この記事の目的では、前のシーケンスの最初の 2 つの手順はほとんど関係ありません。 それらの唯一の関連性は、証明書のサブジェクトの共通名が、クラスター定義で宣言されている DNS 名であることです。

証明書の発行とプロビジョニングのフローを次の図に示します。

拇印によって宣言されている証明書の場合

Diagram of provisioning certificates that are declared by thumbprint.

サブジェクトの共通名によって宣言されている証明書の場合

Diagram of provisioning certificates that are declared by subject common name.

証明書の登録

証明書の登録トピックについては、Key Vault ドキュメントで詳しく説明しています。 ここには、継続性と簡単な参照のために概要が含まれています。

引き続き Azure をコンテキストとして使用し、Key Vault をシークレット管理サービスとして使用する場合、承認された証明書要求者は、キー コンテナーに対して、少なくともキー コンテナー所有者から付与された証明書管理アクセス許可を持っている必要があります。 その後、要求者は次のように証明書に登録します。

  • 要求者は Key Vault に証明書ポリシーを作成します。これにより、証明書のドメインとサブジェクト、目的の発行者、キーの種類と長さ、目的のキー使用法などが指定されます。 詳細については、Azure Key Vault での証明書に関するページを参照してください。

  • 要求者は、前の手順で指定されたポリシーを使用して、同じコンテナーに証明書を作成します。 これにより、コンテナー オブジェクトとしてキー ペアと、秘密キーで署名された証明書署名要求が生成され、署名のために指定された発行者に転送されます。

  • 発行者 (つまり証明機関 (CA)) が署名入り証明書を使用して応答すると、結果がキー コンテナーにマージされ、次のように証明書を使用できるようになります。

    • {vaultUri}/certificates/{name}: 公開キーとメタデータを含む証明書
    • {vaultUri}/keys/{name}: 暗号化操作 (ラップとラップ解除、署名と検証) に使用できる証明書の秘密キー
    • {vaultUri}/secrets/{name}: 秘密キーを含む証明書。保護されていない PFX または PEM ファイルとしてダウンロードできます。

キー コンテナー内の証明書には、ポリシーを共有する証明書インスタンスの時系列リストが含まれていることを思い出してください。 証明書のバージョンは、このポリシーの有効期間と更新の属性に従って作成されます。 コンテナー証明書では、サブジェクト、ドメイン、または DNS 名を共有しないことを強くお勧めします。これは、サブジェクトが同じで、発行者、キーの使用法などのその他の属性が大幅に異なる別のコンテナー証明書からの証明書インスタンスをプロビジョニングすると、クラスター内で混乱を招く可能性があるからです。 この時点で、キー コンテナーに証明書が存在し、使用できるようになります。 次に、プロセスの残りの部分を調べてみましょう。

証明書のプロビジョニング

"プロビジョニング エージェント" について触れました。これは、秘密キーを含む証明書をキー コンテナーから取得し、それをクラスターの各ホストにインストールするエンティティです。 (Service Fabric によって証明書がプロビジョニングされないことを思い出してください)。

この記事のコンテキストでは、クラスターは Azure 仮想マシン (VM) または仮想マシン スケール セットのコレクションでホストされます。 Azure では、次のメカニズムを使用して、コンテナーから VM/VMSS に証明書をプロビジョニングできます。 これは、以前と同様に、プロビジョニング エージェントに、キー コンテナーに対する "シークレットの取得" アクセス許可がキー コンテナー所有者によって付与されていたことを前提としています。

  • アドホック: オペレーターは (PFX/PKCS#12 または PEM として) キー コンテナーから証明書を取得し、それを各ノードにインストールします。

    アドホック メカニズムは、セキュリティから可用性までさまざまな理由からお勧めできません。また、ここではこれ以上説明しません。 詳細については、「Azure 仮想マシン スケール セットに関してよくあるご質問」を参照してください。

  • デプロイ中に仮想マシン スケール セットの "シークレット" として: コンピューティング サービスでは、オペレーターに代わってファースト パーティ ID を使用することにより、テンプレートのデプロイが有効になっているコンテナーから証明書を取得し、仮想マシン スケール セットの各ノードにインストールします。これについては、「Azure 仮想マシン スケール セットに関してよくあるご質問」の説明を参照してください。

    注意

    この方法を使用すると、バージョン管理されたシークレットのプロビジョニングのみを実行できます。

  • Key Vault VM 拡張機能を使用する。 これにより、バージョンレス宣言を使用して証明書をプロビジョニングし、監視対象の証明書を定期的に更新できます。 この場合、VM/VMSS には、監視対象の証明書が含まれたキー コンテナーへのアクセス権が付与されている ID であるマネージド ID が必要です。

VMSS またはコンピューティング ベースのプロビジョニングには、セキュリティと可用性の利点がありますが、制限もあります。 設計上、証明書をバージョン管理されたシークレットとして宣言する必要があります。 この要件により、VMSS またはコンピューティング ベースのプロビジョニングは、拇印で宣言された証明書で保護されたクラスターにのみ適しています。

対照的に、Key Vault VM 拡張機能ベースのプロビジョニングでは、監視対象の各証明書の最新バージョンが常にインストールされます。 そのため、これはサブジェクトの共通名で宣言された証明書で保護されたクラスターにのみ適しています。 特に注意が必要な点として、インスタンスによって (つまり、拇印によって) 宣言された証明書には、自動更新プロビジョニング メカニズム (Key Vault VM 拡張など) を使用しないでください。 可用性を失うリスクがかなりあります。

他のプロビジョニング メカニズムも存在しますが、ここで説明した方法は、Azure Service Fabric クラスターで現在受け入れられているオプションです。

証明書の使用と監視

前述のように、Service Fabric ランタイムは、クラスター定義で宣言された証明書を特定して使用する役割を担っています。 「Service Fabric クラスターでの X.509 証明書ベースの認証」の記事では、Service Fabric でのプレゼンテーションと検証規則の実装方法について詳しく説明しており、ここでは再び取り上げません。 この記事では、アクセス権とアクセス許可の付与、および監視について見ていきます。

Service Fabric の証明書は、フェデレーション レイヤーでの相互認証から管理エンドポイントのトランスポート層セキュリティ (TLS) 認証まで、さまざまな目的で使用されることを思い出してください。 このためには、さまざまなコンポーネントやシステム サービスが証明書の秘密キーにアクセスできる必要があります。 Service Fabric ランタイムによって証明書ストアが定期的にスキャンされ、既知の各プレゼンテーション規則に一致するものが検索されます。

一致する証明書ごとに、対応する秘密キーが特定され、必要とする ID に付与されるアクセス許可 (通常は読み取りと実行) が含まれるようにその随意アクセス制御リストが更新されます。

このプロセスは、非公式に "ACL 処理 (ACLing)" と呼ばれます。 このプロセスは 1 分間隔で実行され、設定の暗号化やエンドポイント証明書などに使用される "アプリケーション" 証明書も対象となります。 ACL 処理はプレゼンテーション規則に従っているため、拇印によって宣言され、その後のクラスター構成の更新なしで自動更新される証明書にはアクセスできないことに注意する必要があります。

証明書ローテーション

注意

インターネット技術標準化委員会 (IETF: Internet Engineering Task Force) の RFC 3647 では、"更新" とは、置き換えられる証明書と同じ属性を持つ証明書の発行であると正式に定義されています。 発行者、サブジェクトの公開キー、および情報は保持されます。 "キー更新" とは、発行者が変更できるかどうかに関する制限なしに、新しいキー ペアを持つ証明書の発行です。 区別が重要になる場合があるため (発行者が固定された、サブジェクトの共通名で宣言された証明書の場合を考えてみてください)、この記事では、両方のシナリオに対応するために中立的な用語である "ローテーション" を使用します。 "更新" は、非公式に使用されている場合、"キーの更新" を指すことに注意してください。

前述のように、Key Vault では証明書の自動ローテーションがサポートされています。 つまり、関連付けられた証明書ポリシーには、証明書がキー コンテナーでローテーションされる時点が、有効期限までの日数または全有効期間に占める割合として定義されています。 この新しい証明書をクラスターのすべてのノードに配布するには、この時点の後、以前の証明書の有効期限が切れる前に、プロビジョニング エージェントを呼び出す必要があります。

Service Fabric を使用すると、このプロセスで、クラスターで現在使用されている証明書の有効期限が事前に指定した間隔よりも早く発生したときに、正常性に関する警告を受け取ることができます。 キー コンテナー証明書を監視するように構成された自動プロビジョニング エージェントである Key Vault VM 拡張機能は、キー コンテナーを定期的にポーリングし、ローテーションを検出し、新しい証明書を取得してインストールします。 VM/VMSS の "シークレット" 機能を使用してプロビジョニングを行うには、承認されたオペレーターが、新しい証明書に対応するバージョン管理されたKey Vault URI で VM/VMSS を更新する必要があります。

これで、ローテーションされた証明書がすべてのノードにプロビジョニングされるようになりました。 ここで、クラスター証明書に適用されたローテーションがサブジェクトの共通名で宣言されたと仮定して、次に何が起こるかを調べてみましょう。

  • 内部およびクラスターへの新しい接続については、Service Fabric ランタイムによって、最後に発行された一致する証明書 (NotBefore プロパティの最大値) が検索されて選択されます。 これは、以前のバージョンの Service Fabric ランタイムからの変更点になります。

  • 既存の接続はそのまま維持するか、自然に期限切れになるか、または終了でき、内部ハンドラーには、新しい一致が存在することが通知されます。

注意

現在 (バージョン 7.2 CU4 以降)、Service Fabric では、最も大きい (最後に発行された) NotBefore プロパティ値を持つ証明書が選択されます。 7.2 CU4 より前については、Service Fabric では、最も大きい (最新の期限切れの) NotAfter 値を持つ有効な証明書が選択されました。

これは、次の重要な観察に変わります。

  • クラスターまたはホストされているアプリケーションの可用性は、証明書をローテーションするディレクティブよりも優先されます。 クラスターは最終的に新しい証明書に収束しますが、タイミングの保証はありません。 その結果、次のようになります。

    • 監視者から見て、ローテーションされた証明書によって以前のものが完全に置き換えられたことがすぐに分からない可能性があります。 現在使用されている証明書の即時置換を強制する唯一の方法は、ホスト マシンを再起動することです。 Service Fabric ノードを再起動するだけでは不十分です。クラスター内でリース接続を形成するカーネル モード コンポーネントは影響を受けないからです。 また、VM/VMSS を再起動すると、一時的に可用性が失われる可能性があります。 アプリケーション証明書の場合は、それぞれのアプリケーション インスタンスのみを再起動するだけで十分です。

    • キーが更新されて検証規則を満たしていない証明書を導入すると、クラスターは実質的に中断されます。 この最も一般的な例は、予期しない発行者の場合です。その場合、クラスター証明書は、発行者が固定されたサブジェクトの共通名を使用して宣言されますが、ローテーションされた証明書は、新しい、または宣言されていない発行者によって発行されています。

証明書のクリーンアップ

現時点では、証明書を明示的に削除するための Azure のプロビジョニングはありません。 多くの場合、特定の証明書が特定の時間に使用されているかどうかを判断することは容易ではありません。 これはクラスター証明書よりもアプリケーション証明書の方が困難です。 Service Fabric 自体はプロビジョニング エージェントではないため、どのような状況でもユーザーが宣言した証明書が削除されることはありません。 標準的なプロビジョニング メカニズムの場合は次のようになります。

  • VM/VMSS シークレットとして宣言されている証明書は、VM/VMSS 定義で参照され、キー コンテナーから取得できる限りプロビジョニングされます。 キー コンテナーのシークレットまたは証明書を削除すると、後続の VM/VMSS のデプロイは失敗します。 同様に、キー コンテナーでシークレット バージョンを無効にすると、シークレット バージョンを参照する VM/VMSS のデプロイも失敗します。

  • Key Vault VM 拡張機能を使用してプロビジョニングされた以前のバージョンの証明書は、VM/VMSS ノードに存在する場合と存在しない場合があります。 エージェントでは、現在のバージョンが取得されてインストールされるだけで、証明書は削除されません。 ノードを再イメージ化すると (通常は毎月実行されます)、証明書ストアが OS イメージのコンテンツにリセットされるため、以前のバージョンは暗黙的に削除されます。 ただし、仮想マシン スケール セットをスケールアップすると、監視対象の証明書の最新バージョンのみがインストールされることを考慮してください。 インストールされた証明書に関して、ノードの均一性は想定しないでください。

管理の簡略化: 自動ロールオーバーの例

これまでに、この記事ではメカニズムと制限について説明し、複雑な規則と定義の概要を説明し、停止の悲観的予測を行いました。 こうしたすべての懸念を回避するために、今こそ自動証明書管理を設定するときです。 PaaS v2 仮想マシン スケール セットで実行されている Azure Service Fabric クラスターのコンテキストでそれを行いましょう。次のように、シークレット管理に Azure Key Vault を使用し、マネージド ID を利用します。

  • 証明書の検証は、拇印の固定からサブジェクトと発行者の固定に変更されます。 特定の発行者からの特定のサブジェクトを持つ証明書はすべて同等に信頼されます。
  • 証明書は、信頼されたストア (Key Vault) に登録され、そこから取得され、エージェント (ここでは、Key Vault VM 拡張機能) によって更新されます。
  • 証明書のプロビジョニングは、(Azure Compute リソース プロバイダーによって行われる) デプロイ時およびバージョンベースから、デプロイ後、バージョンのない Key Vault URI を使用するように変更されます。
  • キー コンテナーへのアクセス権は、デプロイ時に作成されて仮想マシン スケール セットに割り当てられるユーザー割り当てマネージド ID を介して付与されます。
  • デプロイ後、エージェント (Key Vault VM 拡張機能) によって、仮想マシン スケール セットの各ノードで監視対象の証明書がポーリングされて更新されます。 証明書のローテーションはこのようにして完全に自動化されます。これは、Service Fabric によって最新の有効な証明書が自動的に取得されるからです。

このシーケンスは完全にスクリプト化または自動化されており、証明書の自動ロールオーバー用に構成されたクラスターの初回のデプロイは、ユーザーが操作することなく実行できます。 次のセクションでは、PowerShell コマンドレットと JSON テンプレートのフラグメントを組み合わせた詳細な手順について説明します。 Azure との対話をサポートするすべての方法を使用して、同じ機能を実現できます。

Note

この例では、証明書がキー コンテナーに既に存在することを前提としています。 Key Vault マネージド証明書を登録および更新するには、この記事で前述したように、前提条件の手動手順が必要です。 運用環境では、Key Vault で管理されている証明書を使用します。 Microsoft 内部の PKI に固有のサンプル スクリプトが含まれています。

注意

証明書の自動登録は、CA によって発行された証明書に対してのみ有効です。 自己署名証明書 (Azure portal での Service Fabric クラスターのデプロイ中に生成されるものなど) を使用することは無意味ですが、発行者の拇印がリーフ証明書と同じであると宣言した場合、ローカルまたは開発者がホストするデプロイには引き続き使用できます。

開始ポイント

簡潔にするために、次のような開始状態を想定しましょう。

  • Service Fabric クラスターが存在し、拇印によって宣言された CA 発行の証明書で保護されています。
  • 証明書はキー コンテナーに格納され、仮想マシン スケール セットのシークレットとしてプロビジョニングされています。
  • クラスターを共通名ベースの証明書宣言に変換するために同じ証明書が使用されるため、サブジェクトと発行者で検証できます。 そうでない場合は、この目的のために CA によって発行された証明書を取得し、拇印でクラスター定義に追加します。 このプロセスは、Azure での Service Fabric クラスターの証明書の追加または削除に関する記事で説明されています。

このような状態に対応するテンプレートからの JSON の抜粋を次に示します。 抜粋では、多くの必須設定が省略され、証明書に関連する側面のみが示されています。

  "resources": [
    {   ## VMSS definition
      "apiVersion": "[variables('vmssApiVersion')]",
      "type": "Microsoft.Compute/virtualMachineScaleSets",
      "name": "[variables('vmNodeTypeName')]",
      "location": "[variables('computeLocation')]",
      "properties": {
        "virtualMachineProfile": {
          "extensionProfile": {
            "extensions": [
            {
                "name": "[concat('ServiceFabricNodeVmExt','_vmNodeTypeName')]",
                "properties": {
                  "type": "ServiceFabricNode",
                  "autoUpgradeMinorVersion": true,
                  "publisher": "Microsoft.Azure.ServiceFabric",
                  "settings": {
                    "clusterEndpoint": "[reference(parameters('clusterName')).clusterEndpoint]",
                    "nodeTypeRef": "[variables('vmNodeTypeName')]",
                    "dataPath": "D:\\SvcFab",
                    "durabilityLevel": "Bronze",
                    "certificate": {
                        "thumbprint": "[parameters('primaryClusterCertificateTP')]",
                        "x509StoreName": "[parameters('certificateStoreValue')]"
                    }
                  },
                  "typeHandlerVersion": "1.1"
                }
            },}},
          "osProfile": {
            "adminPassword": "[parameters('adminPassword')]",
            "adminUsername": "[parameters('adminUsername')]",
            "secrets": [
            {
                "sourceVault": {
                    "id": "[resourceId('Microsoft.KeyVault/vaults', parameters('keyVaultName'))]"
                },
                "vaultCertificates": [
                {
                    "certificateStore": "[parameters('certificateStoreValue')]",
                    "certificateUrl": "[parameters('clusterCertificateUrlValue')]"
                },
            ]}]
        },
    },
    {   ## cluster definition
        "apiVersion": "[variables('sfrpApiVersion')]",
        "type": "Microsoft.ServiceFabric/clusters",
        "name": "[parameters('clusterName')]",
        "location": "[parameters('clusterLocation')]",
        "certificate": {
            "thumbprint": "[parameters('primaryClusterCertificateTP')]",
            "x509StoreName": "[parameters('certificateStoreValue')]"
        },
    }
  ]

上のコードは、基本的に、拇印 json [parameters('primaryClusterCertificateTP')] があり、Key Vault URI json [parameters('clusterCertificateUrlValue')] で見つかった証明書が、拇印によってクラスターの唯一の証明書として宣言されていることを示しています。

次に、証明書の自動ロールオーバーを確実にするために必要な追加のリソースを設定しましょう。

前提条件のリソースを設定する

前述のように、仮想マシン スケール セットのシークレットとしてプロビジョニングされた証明書は、Microsoft Compute リソース プロバイダー サービスによってキー コンテナーから取得されます。 これは、デプロイ オペレーターの代わりにファースト パーティ ID を使用して行われます。 このプロセスは自動ロールオーバーのために変更されます。 仮想マシン スケール セットに割り当てられ、そのコンテナー内のシークレットに対する GET アクセス許可が付与されているマネージド ID を使用するように切り替えます。

次の抜粋を同時にデプロイする必要があります。 これらは、実行ごとの分析と説明のためにのみ個別に一覧表示されます。

まず、ユーザー割り当て ID を定義します (既定値が例として含まれています)。 詳細については、公式ドキュメントを参照してください。

{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "userAssignedIdentityName": {
      "type": "string",
      "defaultValue": "sftstuaicus",
      "metadata": {
        "description": "User-assigned managed identity name"
      }
    },
  },
  "variables": {
      "vmssApiVersion": "2018-06-01",
      "sfrpApiVersion": "2018-02-01",
      "miApiVersion": "2018-11-30",
      "kvApiVersion": "2018-02-14",
      "userAssignedIdentityResourceId": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('userAssignedIdentityName'))]"
  },    
  "resources": [
    {
      "type": "Microsoft.ManagedIdentity/userAssignedIdentities",
      "name": "[parameters('userAssignedIdentityName')]",
      "apiVersion": "[variables('miApiVersion')]",
      "location": "[resourceGroup().location]"
    },
  ]}

次に、この ID にキー コンテナーのシークレットへのアクセス権を付与します。 最新情報については、公式ドキュメントを参照してください。

  "resources":
  [{
      "type": "Microsoft.KeyVault/vaults/accessPolicies",
      "name": "[concat(parameters('keyVaultName'), '/add')]",
      "apiVersion": "[variables('kvApiVersion')]",
      "properties": {
        "accessPolicies": [
          {
            "tenantId": "[reference(variables('userAssignedIdentityResourceId'), variables('miApiVersion')).tenantId]",
            "objectId": "[reference(variables('userAssignedIdentityResourceId'), variables('miApiVersion')).principalId]",
            "dependsOn": [
              "[variables('userAssignedIdentityResourceId')]"
            ],
            "permissions": {
              "secrets": [
                "get",
                "list"
              ]}}]}}]

次の手順では、次の操作を行います。

  • ユーザー割り当て ID を仮想マシン スケール セットに割り当てる。
  • マネージド ID の作成と、キー コンテナーへのアクセス権を付与した結果に対する仮想マシン スケール セットの依存関係を宣言する。
  • Key Vault VM 拡張機能を宣言し、起動時に監視対象の証明書を取得するように要求する。 詳細については、Windows 用の Key Vault VM 拡張機能の公式ドキュメントを参照してください。
  • Key Vault VM 拡張機能に依存し、クラスター証明書宣言を拇印から共通名に変換するように、Service Fabric VM 拡張機能の定義を更新する。

注意

これらの変更は、同じリソースのスコープ内にあるため、1 つの手順として行われています。

  "parameters": {
    "kvvmextPollingInterval": {
      "type": "string",
      "defaultValue": "3600",
      "metadata": {
        "description": "kv vm extension polling interval in seconds"
      }
    },
    "kvvmextLocalStoreName": {
      "type": "string",
      "defaultValue": "MY",
      "metadata": {
        "description": "kv vm extension local store name"
      }
    },
    "kvvmextLocalStoreLocation": {
      "type": "string",
      "defaultValue": "LocalMachine",
      "metadata": {
        "description": "kv vm extension local store location"
      }
    },
    "kvvmextObservedCertificates": {
      "type": "array",
      "defaultValue": [
                "https://sftestcus.vault.azure.net/secrets/sftstcncluster",
                "https://sftestcus.vault.azure.net/secrets/sftstcnserver"
            ],
      "metadata": {
        "description": "kv vm extension observed certificates versionless uri"
      }
    },
    "certificateCommonName": {
      "type": "string",
      "defaultValue": "cus.cluster.sftstcn.system.servicefabric.azure-int",
      "metadata": {
        "description": "Certificate Common name"
      }
    },
  },
  "resources": [
  {
    "apiVersion": "[variables('vmssApiVersion')]",
    "type": "Microsoft.Compute/virtualMachineScaleSets",
    "name": "[variables('vmNodeTypeName')]",
    "location": "[variables('computeLocation')]",
    "dependsOn": [
      "[variables('userAssignedIdentityResourceId')]",
      "[concat('Microsoft.KeyVault/vaults/', concat(parameters('keyVaultName'), '/accessPolicies/add'))]"
    ],
    "identity": {
      "type": "UserAssigned",
      "userAssignedIdentities": {
        "[variables('userAssignedIdentityResourceId')]": {}
      }
    },
    "virtualMachineProfile": {
      "extensionProfile": {
        "extensions": [
        {
          "name": "KVVMExtension",
          "properties": {
            "publisher": "Microsoft.Azure.KeyVault",
            "type": "KeyVaultForWindows",
            "typeHandlerVersion": "1.0",
            "autoUpgradeMinorVersion": true,
            "settings": {
                "secretsManagementSettings": {
                    "pollingIntervalInS": "[parameters('kvvmextPollingInterval')]",
                    "linkOnRenewal": false,
                    "observedCertificates": "[parameters('kvvmextObservedCertificates')]",
                    "requireInitialSync": true
                }
            }
          }
        },
        {
        "name": "[concat('ServiceFabricNodeVmExt','_vmNodeTypeName')]",
        "properties": {
          "type": "ServiceFabricNode",
          "provisionAfterExtensions" : [ "KVVMExtension" ],
          "publisher": "Microsoft.Azure.ServiceFabric",
          "settings": {
            "certificate": {
                "commonNames": [
                    "[parameters('certificateCommonName')]"
                ],
                "x509StoreName": "[parameters('certificateStoreValue')]"
            }
            },
            "typeHandlerVersion": "1.0"
          }
        },
  ] } ## extension profile
  },  ## VM profile
  "osProfile": {
    "adminPassword": "[parameters('adminPassword')]",
    "adminUsername": "[parameters('adminUsername')]",
  } 
  }
  ]

上記のコードでは明示的に示されていませんが、キー コンテナー証明書の URL が仮想マシン スケール セットの OsProfile セクションから削除されていることに注意してください。

最後の手順は、証明書の宣言を拇印から共通名に変更するようにクラスター定義を更新することです。 発行者の拇印も固定します。

  "parameters": {
    "certificateCommonName": {
      "type": "string",
      "defaultValue": "cus.cluster.sftstcn.system.servicefabric.azure-int",
      "metadata": {
        "description": "Certificate Common name"
      }
    },
    "certificateIssuerThumbprint": {
      "type": "string",
      "defaultValue": "1b45ec255e0668375043ed5fe78a09ff1655844d,d7fe717b5ff3593764f4d90654d86e8362ec26c8,3ac7c3cac8de0dd392c02789c8be97474f456960,96ea05926e2e42cc207e358668be2c316857fb5e",
      "metadata": {
        "description": "Certificate issuer thumbprints separated by comma"
      }
    },
  },
  "resources": [
    {
      "apiVersion": "[variables('sfrpApiVersion')]",
      "type": "Microsoft.ServiceFabric/clusters",
      "name": "[parameters('clusterName')]",
      "location": "[parameters('clusterLocation')]",
      "properties": {
        "certificateCommonNames": {
          "commonNames": [{
              "certificateCommonName": "[parameters('certificateCommonName')]",
              "certificateIssuerThumbprint": "[parameters('certificateIssuerThumbprint')]"
          }],
          "x509StoreName": "[parameters('certificateStoreValue')]"
        },
  ]

この時点で、前述の更新プログラムを 1 回のデプロイで実行できます。 クラスター証明書の拇印から共通名への変換に関するセグメントで説明されているように、Service Fabric リソース プロバイダー サービスによってクラスターのアップグレードはいくつかの手順に分割されます。

分析と監視

このセクションは、この記事全体で説明されている概念とプロセスを説明し、他の特定の重要な側面に注意を向けさせるためのキャッチオールです。

証明書のプロビジョニングについて

Key Vault VM 拡張機能は、プロビジョニング エージェントとして、事前に指定された頻度で継続的に実行されます。 監視対象の証明書の取得に失敗すると、その次に進み、その後、次のサイクルまで休止状態になります。 Service Fabric VM 拡張機能には、クラスター ブートストラップ エージェントとして、クラスターを形成する前に宣言された証明書が必要です。 つまり、Service Fabric VM 拡張機能は、ここで json "provisionAfterExtensions" : [ "KVVMExtension" ]" 句と Key Vault VM 拡張機能の json "requireInitialSync": true 設定で示されているクラスター証明書の取得に成功した後にのみ実行できます。

これは、Key Vault VM 拡張機能に対して、最初の実行時 (デプロイ後または再起動後) に、すべてが正常にダウンロードされるまで、監視対象の証明書を循環させる必要があることを示しています。 このパラメーターを false に設定し、さらにクラスター証明書の取得に失敗すると、クラスターのデプロイに失敗します。 逆に、監視対象の証明書の正しくない、または無効なリストとの初期同期が必要になると、Key Vault VM 拡張機能で障害が発生し、その結果としてこの場合もクラスターのデプロイが失敗します。

証明書のリンク、説明

Key Vault VM 拡張機能の linkOnRenewal フラグと、それが false に設定されていることに気づいたかもしれません。 この設定では、このフラグによって制御される動作と、クラスターの機能への影響に対処しています。 この動作は Windows に固有のものです。

その定義に従って次のようにします。

"linkOnRenewal": <Only Windows. This feature enables auto-rotation of SSL certificates, without necessitating a re-deployment or binding. e.g.: false>,

TLS 接続を確立するために使用される証明書は、通常、S チャネル セキュリティ サポート プロバイダーを介してハンドルとして取得されます。 つまり、クライアントからは証明書自体の秘密キーに直接アクセスしません。 S チャネルでは、証明書拡張機能の形式 CERT_RENEWAL_PROP_ID で資格情報のリダイレクト (リンク) がサポートされています。

このプロパティが設定されている場合、その値は "更新" 証明書の拇印を表すため、S チャネルでは、リンクされた証明書の読み込みが代わりに試行されます。 実際、S チャネルでは、このリンクされた (そしてできれば非循環的な) リストを、"最終的な" 証明書 (更新マークのないもの) になるまで走査します。 この機能を慎重に使用すると、たとえば証明書の期限切れなどによる可用性の損失を大幅に軽減できます。

その他の例として、診断や軽減が困難な停止の原因となる場合があります。 S チャネルでは、サブジェクト、発行者、またはクライアントによる結果の証明書の検証に関与するその他の特定の属性に関係なく、更新プロパティに対して無条件に証明書の走査が実行されます。 生成された証明書に秘密キーが関連付けられていないか、またはキーが、想定されるコンシューマーに ACL 処理されていない可能性があります。

リンクが有効な場合、Key Vault VM 拡張機能では、監視対象の証明書をキー コンテナーから取得すると、更新の拡張機能プロパティを介してそれらをリンクするために、一致する既存の証明書を見つけようとします。 一致は、サブジェクトの代替名 (SAN) のみに基づいて行われ、次の例に示すように、2 つの既存の証明書がある場合に機能します: A: Certificate name (CN) = “Alice's accessories”, SAN = {“alice.universalexports.com”}, renewal = ‘’ B: CN = “Bob's bits”, SAN = {“bob.universalexports.com”, “bob.universalexports.net”}, renewal = ‘’

証明書 C が Key Vault VM 拡張機能によって取得されるとします: CN = “Mallory's malware”, SAN = {“alice.universalexports.com”, “bob.universalexports.com”, “mallory.universalexports.com”}

証明書 A の SAN リストは C のものに完全に含まれているため、A.renewal = C.thumbprint となります。 証明書 B の SAN リストには C のものと共通の交差部分がありますが、完全には含まれていないため、B.renewal は空のままです。

証明書 A でこの状態の AcquireCredentialsHandle (S チャネル) を呼び出そうとすると、実際には C がリモート パーティに送信されます。 Service Fabric の場合、クラスターのトランスポート サブシステムによって相互認証に S チャネルが使用されるため、上記の動作はクラスターの基本的な通信に直接影響します。 上記の例を続行し、A がクラスター証明書であると仮定すると、次に起こることは、次の条件によって変わります。

  • C の秘密キーが Service Fabric を実行しているアカウントに ACL 処理されていない場合、秘密キーの取得に失敗します (SEC_E_UNKNOWN_CREDENTIALS など)。
  • C の秘密キーにアクセスできる場合、他のノードから返された承認エラーが表示されます (CertificateNotMatched、unauthorized など)。

いずれの場合も転送は失敗し、クラスターが停止する可能性があります。 現象はさまざまです。 さらに悪いことに、リンクは更新の順序によって変わります。これは、Key Vault VM 拡張機能の監視対象の証明書リストの順序、キー コンテナーの更新スケジュール、さらには取得の順序が変わる一時的なエラーによって決まります。

このようなインシデントを軽減するには、次の推奨事項があります。

  • 異なるコンテナー証明書のサブジェクト代替名を混在させないでください。 各コンテナー証明書で個別の目的を果たし、サブジェクトと SAN はそれを具体的に反映するようにします。

  • SAN リストにサブジェクトの共通名を含めます (文字どおり CN=<subject common name> として)。

  • 進め方がわからない場合は、Key Vault VM 拡張機能でプロビジョニングされている証明書の更新時のリンクを無効にします。

    注意

    リンクの無効化は、Key Vault VM 拡張機能の最上位プロパティであり、個々の監視対象の証明書には設定できません。

ユーザー割り当てマネージド ID を使用する理由と 使用する意味

上記の JSON スニペットから明らかになったように、変換を確実に成功させ、クラスターの可用性を維持するには、操作と更新の特定のシーケンス処理が必要です。 具体的には、仮想マシン スケール セット リソースでその ID を宣言して使用し、(ユーザーの観点から) 1 回の更新でシークレットを取得します。

クラスターをブートストラップする Service Fabric VM 拡張機能は Key Vault VM 拡張機能の完了に依存し、これはさらに監視対象の証明書の正常な取得に依存します。

Key Vault VM 拡張機能では、キー コンテナーへのアクセスに仮想マシン スケール セットの ID が使用されます。つまり、仮想マシン スケール セットをデプロイする前に、キー コンテナーのアクセス ポリシーを更新しておく必要があります。

マネージド ID の作成を破棄する場合や、別のリソースに割り当てる場合、デプロイ オペレーターは、テンプレートで参照される他のリソースを管理するために必要なロールに加えて、サブスクリプションまたはリソース グループに必要なロール (ManagedIdentityOperator) を持っている必要があります。

セキュリティの観点から、仮想マシン スケール セットは、Azure ID に関してセキュリティ境界と見なされることを思い出してください。 つまり、VM でホストされているアプリケーションは、原則として、VM を表すアクセス トークンを取得できます。 マネージド ID アクセス トークンは、認証されていない Instance Metadata Service エンドポイントから取得されます。 VM を共有またはマルチテナント環境と考えている場合、このクラスター証明書の取得方法は示されていない可能性があります。 ただし、証明書の自動ロールオーバーに適した唯一のプロビジョニング メカニズムです。

トラブルシューティングとよく寄せられる質問

Q: Key Vault で管理される証明書にプログラムで登録するにはどうすればよいですか?

Key Vault のドキュメントから発行者の名前を見つけて、以下のスクリプトで置き換えてください。

  $issuerName=<depends on your PKI of choice>
	$clusterVault="sftestcus"
	$clusterCertVaultName="sftstcncluster"
	$clusterCertCN="cus.cluster.sftstcn.system.servicefabric.azure-int"
	Set-AzKeyVaultCertificateIssuer -VaultName $clusterVault -Name $issuerName -IssuerProvider $issuerName
	$distinguishedName="CN=" + $clusterCertCN
	$policy = New-AzKeyVaultCertificatePolicy `
	    -IssuerName $issuerName `
	    -SubjectName $distinguishedName `
	    -SecretContentType 'application/x-pkcs12' `
	    -Ekus "1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.2" `
	    -ValidityInMonths 4 `
	    -KeyType 'RSA' `
	    -RenewAtPercentageLifetime 75        
	Add-AzKeyVaultCertificate -VaultName $clusterVault -Name $clusterCertVaultName -CertificatePolicy $policy
	
	# poll the result of the enrollment
	Get-AzKeyVaultCertificateOperation -VaultName $clusterVault -Name $clusterCertVaultName 

Q: 宣言されていない、または指定されていない発行者が証明書を発行するとどうなりますか? 特定の PKI のアクティブな発行者の完全な一覧はどこで入手できますか?

証明書の宣言で発行者の拇印が指定されていて、証明書の直接の発行者が固定された発行者のリストに含まれていない場合は、そのルートがクライアントによって信頼されているかどうかに関係なく、証明書は無効と見なされます。 そのため、発行者リストが最新であることを確認することが重要です。 新しい発行者の導入はごくまれにしか起こらないため、証明書の発行が開始される前に広く公表しておく必要があります。

一般に、PKI は、IETF RFC 7382 に従って、認証局運用規定を発行および維持します。 他の情報に加えて、この規定にはすべてのアクティブな発行者が含まれています。 プログラムでこのリストを取得する方法は、PKI によって異なる場合があります。

Microsoft 内部の PKI の場合は、承認された発行者を取得するために使用されるエンドポイントと SDK に関する内部ドキュメントを必ず参照してください。 想定される "すべての" 発行者がクラスター定義に含まれるように、このリストを定期的に確認するのはクラスター所有者の責任です。

Q: 複数の PKI がサポートされていますか?

はい。 同じ値を持つクラスター マニフェストで複数の CN エントリを宣言することはできませんが、同じ CN に対応する複数の PKI からの発行者を列挙することはできます。 これは推奨される方法ではなく、証明書の透明性を確保するために、このような証明書は発行されない可能性があります。 ただし、これは、ある PKI から別の PKI に移行する手段として許容されているメカニズムです。

Q: 現在のクラスター証明書が CA によって発行されていない場合、または目的のサブジェクトがない場合はどうなりますか?

目的のサブジェクトを含む証明書を取得し、セカンダリとして拇印によるクラスターの定義に追加します。 アップグレードが正常に完了したら、別のクラスター構成のアップグレードを開始して、証明書の宣言を共通名に変換します。