共用方式為


管理多個 Service Fabric 叢集中的憑證

本文說明用於保護 Azure Service Fabric 叢集通訊的憑證的管理層面。 其可補充 Service Fabric 叢集安全性 的簡介,以及 Service Fabric 中 X.509 憑證式驗證的說明

必要條件

開始之前,建議您先熟知基本安全性概念,以及 Service Fabric 公開用於設定叢集安全性的控制項。

免責聲明

本文結合了憑證管理的理論層面與涵蓋服務、技術等細節的實作範例。 由於受眾大多來自 Microsoft 內部,因此本文指涉的是 Azure 特有的服務、技術和產品。 如果某些專屬於 Microsoft 的細節不適用,可在結尾的評論區塊中請求釐清或指引。

定義憑證管理

Service Fabric 叢集中的 X.509 憑證式驗證隨附文章所述,憑證是一種密碼編譯物件,基本上會將非對稱的金鑰組繫結至描述其代表的實體的屬性。

然而,憑證也是一種易消亡物件,因為其存留期有限,而且容易遭到洩露。 以安全性觀點而言,意外洩漏或成功的惡意探索將使憑證變得無用。 有鑒於此特性,您需要定期或因應安全性事件而變更憑證。

憑證管理的另一個層面在於 (其本身也可作為一個獨立的主題), 保護憑證私密金鑰或用於保護與採購和佈建憑證相關的實體身分識別的祕密。

用於取得憑證且安全地將憑證傳送至所需位置的流程或程序,稱為「憑證管理」

本文內容並未涵蓋某些管理作業,例如註冊、原則設定和授權控制。 其他如佈建、更新、金鑰重設或撤銷等作業,只在某些時候才與 Service Fabric 有關。 然而,本文將稍做說明,因為瞭解這些作業有助您適當保護叢集。

您目前的首要目標是盡可能讓憑證管理達到自動化,以確保叢集持續可用。 由於此程序無需使用者觸控,因此您還需要提供安全性保證。 有了 Service Fabric 叢集,就可以達成這個目標。

本文的其餘部分會先解構憑證管理,之後則著重於自動變換的啟用。

具體而言,涵蓋下列主題:

  • 關於擁有者與平台之間屬性分隔的假設
  • 從憑證發行到使用的漫長管道
  • 憑證輪替 - 原因、作法和時機
  • 可能會發生什麼錯誤?

本文並未涵蓋下列主題:

  • 保護和管理網域名稱
  • 註冊憑證
  • 設定授權控制項以強制發行憑證。

如欲了解這些主題,請參閱您慣用的公開金鑰基礎結構 (PKI) 服務的登錄授權單位 (RA)。 如果您是 Microsoft 內部員工,可連絡 Azure Security。

與憑證管理相關的角色和實體

Service Fabric 叢集中的安全性方法是「叢集擁有者宣告,Service Fabric 執行階段強制執行」的情況。這表示,參與叢集運作的憑證、金鑰或其他身分識別認證幾乎都不是來自服務本身。 它們全都由叢集擁有者宣告。 此外,叢集擁有者也負責將憑證佈建至叢集、視需求進行更新,以及隨時確保憑證的安全性。

更具體來說,叢集擁有者必須確保下列事項:

  • 根據展示規則,在叢集資訊清單的 NodeType 區段中所宣告的憑證,可見於該類型的每個節點。
  • 在前述項目符號點中宣告的憑證,安裝時也會包含對應的私密金鑰。
  • 在展示規則中宣告的憑證必須符合驗證規則

Service Fabric 本身需負責下列事項:

  • 尋找符合叢集定義中宣告的憑證
  • 根據需求,將對應私密金鑰的存取權授與 Service Fabric 控制的實體
  • 嚴格遵循已建立的安全性最佳做法和叢集定義來驗證憑證
  • 在憑證即將到期時,或執行憑證驗證的基本步驟失敗時發出警示
  • 根據主機的基礎設定,驗證 (在某個程度上) 叢集定義的憑證相關層面與之相符

由此可知,憑證管理的負擔 (作用中的作業) 完全落在叢集擁有者身上。 在後續章節中,我們將深入了解每一項管理作業,包含可用的機制及其對叢集的影響。

憑證的旅程圖

我們來快速回顧一下,在 Service Fabric 叢集內容中,憑證從發行到使用的整個過程:

  1. 網域擁有者會向 PKI 的 RA 註冊他們希望透過後續憑證存取的網域或主體。 接著,憑證會構成網域或主體的所有權證明。

  2. 網域擁有者也會在 RA 中指定授權要求者的身分識別,也就是有權向指定網域或主體要求註冊憑證的實體。

  3. 之後,獲得授權的要求者便可透過祕密管理服務註冊憑證。 在 Azure 中,祕密管理服務由 Azure Key Vault 提供,其可安全儲存並允許授權實體擷取祕密和憑證。 Key Vault 也會根據相關憑證原則更新憑證與重設憑證金鑰。 Key Vault 會使用 Microsoft Entra ID 作為識別提供者。

  4. 獲得授權的擷取器或佈建代理程式可從金鑰保存庫擷取憑證 (包括其私密金鑰),並將其安裝在叢集的主機上。

  5. Service Fabric 服務 (在每個節點上提高權限執行) 會將憑證的存取權授與經過許可的 Service Fabric 實體;這些實體由本機群組指定,分為 ServiceFabricAdministrators 與 ServiceFabricAllowedUsers。

  6. Service Fabric 執行階段會存取並使用憑證來建立同盟,或驗證來自已授權用戶端的輸入請求。

  7. 佈建代理程式會監視金鑰保存庫憑證,並在偵測到更新時觸發佈建流程。 接著,叢集擁有者將視需求更新叢集定義,以指明變換憑證的意圖。

  8. 佈建代理程式或叢集擁有者也負責清除與刪除未使用的憑證。

前述序列中的頭兩個步驟幾乎與本文目的無關。 唯一的關聯是,憑證的主體通用名稱為叢集定義所宣告的 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 虛擬機器擴展集常見問題

  • 在部署過程中作為虛擬機器擴展集的祕密:計算服務會代表操作員使用其第一方身分,從範本部署所啟用的保存庫中擷取憑證,然後將憑證安裝在虛擬機器擴展集的每個節點上,如 Azure 虛擬機器擴展集常見問題所述。

    注意

    這個方法僅限於佈建已建立版本的祕密。

  • 使用 Key Vault VM 擴充功能。 如此一來,您可使用無版本宣告來佈建憑證,定期重新整理觀察到的憑證。 在此情況下,VM/VMSS 預期會有受控身分識別,而該身分識別有權存取包含觀察到的憑證的金鑰保存庫。

VMSS/計算型佈建具有安全性和可用性優點,但也有一些限制。 依其設計,您必須將憑證宣告為已建立版本的祕密。 基於這項需求,VMSS/計算型佈建將僅適用於由指紋宣告的憑證所保護的叢集。

相對地,Key Vault VM 擴充功能型佈建一律會安裝每個觀察到的憑證的最新版本。 這使其僅適用於由主體通用名稱宣告的憑證所保護的叢集。 針對執行個體 (也就是指紋) 所宣告的憑證,請勿採用自動重新整理佈建機制 (例如 Key Vault VM 擴充功能)。 如此很有可能會失去可用性。

您也可採用其他佈建機制,但這裡提到的方法為 Azure Service Fabric 叢集目前接受的選項。

憑證取用和監視

如先前所述,Service Fabric 執行階段負責尋找和使用叢集定義所宣告的憑證。 Service Fabric 叢集中的 X.509 憑證式驗證一文詳細說明了 Service Fabric 如何展示與實作驗證規則,因此這裡不會再次提及。 本文將探討存取權和權限授與,以及監視功能。

先前提到,Service Fabric 中的憑證具有許多用途,從同盟層的相互驗證到傳輸層安全性 (TLS) 驗證的管理端點。 您需要各種元件或系統服務,才能存取憑證的私密金鑰。 Service Fabric 執行階段會定期掃描憑證存放區,尋找符合每個已知展示規則的憑證。

每個相符的憑證會有對應的私密金鑰,而其任意存取控制清單也會進行更新,以包含授與給需要它們的身分識別的權限 (一般為讀取和執行)。

此程序的非正式名稱為 ACLing。 此程序會執行 1 分鐘,也會涵蓋應用程式 憑證,例如用來加密設定或作為端點憑證的憑證。 ACLing 依照展示規則運作,因此請謹記,指紋所宣告的憑證、以及自動重新整理卻未進行後續叢集設定的憑證,將無法被存取。

憑證輪換

注意

Internet Engineering Task Force (IETF) 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 來管理祕密,以及利用受控識別,如下所示:

  • 憑證驗證會從指紋鎖定變更為主體 + 簽發者鎖定。 具有特定簽發者發出的特定主體的任何憑證都一樣可信。
  • 憑證會在信任的存放區 (Key Vault) 中註冊及取得,並由代理程式重新整理 (在此案例中,代理程式為 KeyVault VM 擴充功能)。
  • 憑證的佈建會從部署時間和版本為基礎的方式 (如同 Azure Compute Resource Provider 的執行方式),變更為透過無版本的 Key Vault URI 執行的後部署。
  • 金鑰保存庫的存取權會透過使用者指派的受控識別來授與;其會在部署期間建立並指派給虛擬機器擴展集。
  • 部署之後,代理程式 (Key Vault VM 擴充功能) 會在虛擬機器擴展集的每個節點上輪詢與重新整理所觀察到的憑證。 因此,憑證輪替完全自動化,因為 Service Fabric 會自動挑選最新的有效憑證。

此序列可完全進行指令碼編寫與自動化,並可對憑證自動變換的叢集進行無需使用者觸控的初始部署。 後續章節將詳細說明步驟,包含各種 PowerShell Cmdlet 和 JSON 範本片段。 所有與 Azure 互動的支援方法都可達成相同的功能。

注意

此範例假設憑證已存在於您的金鑰保存庫中。 註冊和更新 Key Vault 管理的憑證需要先決條件手動步驟,如本文稍早所述。 針對實際執行環境,請使用 Key Vault 管理的憑證。 我們已包含 Microsoft 內部 PKI 特有的範例指令碼。

注意

憑證自動註冊僅適用於 CA 發行的憑證。 不建議使用自我簽署憑證,包括 Azure 入口網站中 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 Resource Provider 服務會從金鑰保存庫擷取佈建為虛擬機器擴展集祕密的憑證。 方法是代表部署操作員使用其第一方身分識別。 該程序將隨著自動變換而變更。 您將改為使用指派給虛擬機器擴展集、且已獲取該保存庫祕密的 GET 存取權限的受控識別。

您應該同時部署下一個摘要。 它們會個別列出,僅供逐一播放分析和說明使用。

首先,定義使用者指派的身分識別 (會包含預設值以作為範例)。 如需了解更多資訊,請參閱官方文件

{
  "$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]"
    },
  ]}

下一步,將此身分識別存取權授與金鑰保存庫祕密。 如需最新資訊,請參閱官方文件

  "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"
              ]}}]}}]

在下一個步驟中,您將執行下列動作:

  • 將使用者指派的身分識別指派給虛擬機器擴展集。
  • 宣告虛擬機器擴展集與建立受控識別及授與其保存庫存取權結果的相依性。
  • 宣告 Key Vault VM 擴充功能,並要求它在啟動時擷取觀察到的憑證。 如需更多資訊,請參閱適用於 Windows 官方文件的 Key Vault VM 擴充功能
  • 更新 Service Fabric VM 擴充功能的定義以使用 Key Vault VM 擴充功能,並將叢集憑證宣告從指紋轉換為通用名稱。

注意

這些變更將在單一步驟中進行,因為它們落在相同資源的範圍內。

  "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')]"
        },
  ]

此時,您可在單一部署中執行先前提到的更新。 Service Fabric Resource Provider 服務會分幾個步驟分割叢集升級,如將叢集憑證從指紋轉換為通用名稱的區段所述。

分析和觀察

本節概略說明本文通篇介紹的概念和程式,以及強調其他某些重要層面。

關於憑證的佈建

作為佈建代理程式,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) 作為唯一基礎,而如果有兩個現有的憑證,就能有效進行,如下列範例所示:憑證名稱 (CN) =“Alice's accessories”, SAN = {“alice.universalexports.com”}, 更新 = ‘’ B: CN = “Bob's bits”, SAN = {“bob.universalexports.com”, “bob.universalexports.net”}, 更新 = ‘’

假設憑證 C 由 KVVM 擴充功能擷取: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 的私密金鑰未以 ACL 設定至 Service Fabric 作為執行身分的帳戶,您將無法取得私密金鑰 (SEC_E_UNKNOWN_CREDENTIALS 或類似內容)。
  • 如果 C 的私密金鑰可供存取,其他節點會傳回授權失敗 (CertificateNotMatched、未經授權等)。

不論是哪一種情況,傳輸都會失敗,而且叢集可能會失效。 徵兆會因情況而異。 更糟的是,連結取決於更新的順序,而更新受制於 Key Vault VM 擴充功能所觀察到的憑證清單的順序、保存庫中的更新排程,或甚至是改變擷取順序的暫時性錯誤。

若要降低這類事件的風險,建議您:

  • 請勿混合不同保存庫憑證的主體別名。 每個保存庫憑證都應該具有不同的用途,其主體和 SAN 也應該明確區分。

  • 將主體通用名稱包含在 SAN 清單中 (也就是 CN=<subject common name>)。

  • 如果您不確定如何繼續進行,請停用透過 Key Vault VM 擴充功能佈建的憑證的更新連結。

    注意

    停用連結為 Key Vault VM 擴充功能的頂層屬性,無法針對觀察到的個別憑證進行設定。

為什麼要使用使用者指派的受控識別? 使用該識別有何影響?

根據前述的 json 程式碼片段,若想確保轉換成功與維護叢集的可用性,便需要特定的作業與更新順序。 具體而言,虛擬機器擴展集資源會宣告並使用其身分識別,從單一更新 (使用者的觀點) 擷取祕密。

用於啟動叢集的 Service Fabric VM 擴充功能取決於是否已完成 Key Vault VM 擴充功能,而這取決於是否成功擷取觀察到的憑證。

Key Vault VM 擴充功能會利用虛擬機器擴展集的身分識別來存取保存庫,這表示在部署虛擬機器擴展集之前,必須先更新保存庫上的存取原則。

若要處置受控識別的建立,或將其指派給另一個資源,部署操作員除了管理範本中參考的其他資源所需的角色之外,還必須在訂用帳戶或資源群組中擁有必要角色 (ManagedIdentityOperator)。

從安全性的觀點來看,別忘了虛擬機器擴展集被視為與其 Azure 身分識別有關的安全性界限。 這表示任何託管於 VM 的應用程式原則上都可取得代表 VM 的存取權杖。 受控識別存取權杖可從未經驗證的 Instance Metadata Service 端點取得。 如果您將 VM 視為共用或多租用戶環境,可能不需要採取這個擷取叢集憑證的方法。 不過,這是唯一適用於憑證自動變換的佈建機制。

疑難排解和常見問題

問題:如何以程式設計方式註冊 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 

問題:如果憑證由未宣告或未指定的簽發者發行,會發生什麼事? 我可以在哪裡取得指定 PKI 的作用中簽發者的完整清單?

如果憑證宣告有指明簽發者指紋,且憑證的直接簽發者未包含在鎖定的簽發者清單中,此憑證將視為無效,不論其用戶端是否信任其來源。 因此,請務必確保簽發者清單為最新版本。 引進新的簽發者屬於罕見情況,應該在開始發行憑證之前先廣泛公告。

一般情況下,PKI 會根據 IETF RFC 7382 來發佈和維護憑證實務聲明。 除了其他資訊之外,聲明也會包含所有作用中的簽發者。 以程式設計方式擷取此清單的方式,會因 PKI 而異。

針對 Microsoft 內部的 PK,請務必參閱關於用以擷取授權簽發者之端點和 SDK 的內部文件。 叢集擁有者有義務定期檢閱此清單,以確保叢集定義包含所有預期的簽發者。

問題:是否支援多個 PKI?

是。 您可能不會以相同值在叢集資訊清單中宣告多個 CN 項目,但可以從多個 PKI 中列出對應至相同 CN 的簽發者。 一般不建議這麼做,而且憑證透明度實務可能會導致這類憑證無法發出。 然而,若要從某個 PKI 移轉至另一個 PKI,可以採取此機制。

問題:如果目前的叢集憑證不是由 CA 簽發或者沒有預定的主體,應該怎麼辦?

取得具有預期主體的憑證,並依兆指紋將其新增至叢集定義中作為次要憑證。 成功完成升級後,請啟用另一個叢集設定升級,將憑證宣告轉換成通用名稱。