ASP.NET Core 中的 Azure Key Vault 組態提供者
本文說明如何使用 Azure Key Vault 組態提供者,從 Azure Key Vault 祕密載入應用程式組態值。 Azure Key Vault 是雲端式服務,可協助保護應用程式和服務所使用的密碼編譯金鑰及祕密。 搭配 ASP.NET Core 應用程式使用 Azure Key Vault 的常見案例包括:
- 控制機密組態資料的存取權。
- 儲存組態資料時,符合 FIPS 140-2 等級 2 驗證的硬體安全性模組 (HSM) 需求。
套件
新增以下套件的套件參考:
範例應用程式
Program.cs
最上方的 #define
前置處理器指示詞會決定範例應用程式的執行模式,而該模式有以下兩種:
Certificate
:示範如何使用 Azure Key Vault 用戶端識別碼和 X.509 憑證來存取儲存在 Azure Key Vault 中的祕密。 此範例可以從任何位置執行,無論是部署至 Azure App Service 的位置,或是可以提供 ASP.NET Core 應用程式的任何主機。Managed
:示範如何使用 Azure 資源受控識別。 受控 identity 會使用 Azure 資源受控識別向 Azure Key Vault 驗證應用程式,而不須將認證資料儲存在應用程式的程式碼或設定中。 範例的Managed
版本必須部署至 Azure。 請遵循使用 Azure 資源受控識別一節中的指引。
如需使用前置處理器指示詞 (#define
) 設定範例應用程式的詳細資訊,請參閱 ASP.NET Core 概觀。
檢視或下載範例程式碼 \(英文\) (如何下載)
開發環境中的祕密存放區
使用祕密管理員在本機設定祕密。 範例應用程式在開發環境中的本機電腦執行時,會從本機使用者祕密存放區載入祕密。
祕密管理員需要應用程式專案檔中的 <UserSecretsId>
屬性。 將屬性值 ({GUID}
) 設定為任何唯一 GUID:
<PropertyGroup>
<UserSecretsId>{GUID}</UserSecretsId>
</PropertyGroup>
祕密會建立為成對的名稱和數值。 階層值 (組態區段) 使用 :
(冒號) 作為 ASP.NET Core 組態金鑰名稱中的分隔符號。
祕密管理員是從開啟至專案內容根目錄的命令殼層中使用,其中 {SECRET NAME}
是名稱,{SECRET VALUE}
是值:
dotnet user-secrets set "{SECRET NAME}" "{SECRET VALUE}"
在專案內容根目錄的命令殼層中執行以下命令,為範例應用程式設定祕密:
dotnet user-secrets set "SecretName" "secret_value_1_dev"
dotnet user-secrets set "Section:SecretName" "secret_value_2_dev"
若這些祕密存放在使用 Azure Key Vault 在實際執行環境中建立祕密存放區一節所建立的 Azure Key Vault,後置詞 _dev
會變更為 _prod
。 後置詞會在應用程式的輸出中提供視覺提示,指出組態值的來源。
使用 Azure Key Vault 在實際執行環境中建立祕密存放區
完成下列步驟來建立 Azure Key Vault,並將範例應用程式的祕密存放於此。 如需詳細資訊,請參閱快速入門:使用 Azure CLI 從 Azure Key Vault 設定和擷取祕密。
在 Azure 入口網站中使用下列任一方法開啟 Azure Cloud Shell:
- 選取程式碼區塊右上角的 [試試看]。 在文字方塊中使用搜尋字串「Azure CLI」。
- 使用 [啟動 Cloud Shell] 按鈕在瀏覽器中開啟 Cloud Shell。
- 選取 Azure 入口網站右上角功能表中的 [Cloud Shell] 按鈕。
如需詳細資訊,請參閱 Azure CLI 和 Azure Cloud Shell 概觀。
如果您尚未通過驗證,請使用
az login
命令登入。使用下列命令建立資源群組,其中
{RESOURCE GROUP NAME}
是新資源群組的名稱,而{LOCATION}
是 Azure 區域:az group create --name "{RESOURCE GROUP NAME}" --location {LOCATION}
使用下列命令在資源群組中建立 Key Vault ,其中
{KEY VAULT NAME}
是新的保存庫名稱,而{LOCATION}
是 Azure 區域:az keyvault create --name {KEY VAULT NAME} --resource-group "{RESOURCE GROUP NAME}" --location {LOCATION}
在保存庫中以成對的名稱和數值格式建立祕密。
Azure Key Vault 祕密名稱僅限使用英數字元和虛線。 階層值 (組態區段) 使用
--
(雙虛線) 作為分隔符號,因為 Key Vault 祕密名稱中不允許冒號。 冒號會從 ASP.NET Core 組態中的子機碼分隔區段。 在祕密載入到應用程式組態時,冒號會取代雙虛線序列。下列祕密可用於範例應用程式。 這些值包含
_prod
後置詞,以區別從祕密管理員載入到開發環境中的_dev
後置詞。 將{KEY VAULT NAME}
取代為您在上一個步驟建立的 Key Vault 名稱:az keyvault secret set --vault-name {KEY VAULT NAME} --name "SecretName" --value "secret_value_1_prod" az keyvault secret set --vault-name {KEY VAULT NAME} --name "Section--SecretName" --value "secret_value_2_prod"
針對非 Azure 裝載的應用程式使用應用程式識別碼和 X.509 憑證
將 Azure Key Vault 和應用程式設定為使用 Microsoft Entra ID 應用程式識別碼和 X.509 憑證,以便在應用程式裝載於 Azure 外部時,向保存庫進行驗證。 如需詳細資訊,請參閱關於金鑰、祕密與憑證。
注意
雖然 Azure 裝載的應用程式支援使用應用程式識別碼和 X.509 憑證,但不建議這麼做。 在 Azure 中裝載應用程式時,請改用 Azure 資源受控識別。 受控識別不需要將憑證存放在應用程式或開發環境中。
當 Program.cs
最上方的 #define
前置處理器指示詞設定為 Certificate
時,範例應用程式會使用應用程式識別碼和 X.509 憑證。
- 建立 PKCS#12 封存 (.pfx) 憑證。 建立憑證的選項包括 Windows 上的 New-SelfSignedCertificate 和 OpenSSL。
- 將憑證安裝至目前使用者的個人憑證存放區。 您可以選擇是否將金鑰標示為可匯出。 請記下憑證指紋,以供此流程稍後使用。
- 將 PKCS#12 封存 (.pfx) 憑證匯出為 DER 編碼憑證 (.cer)。
- 向 Microsoft Entra ID 註冊應用程式 (應用程式註冊)。
- 將 DER 編碼憑證 (.cer) 上傳至 Microsoft Entra ID:
- 在 Microsoft Entra ID 中選取應用程式。
- 瀏覽至 [憑證 & 祕密]。
- 選取 [上傳憑證] 以上傳包含公開金鑰的憑證。 可接受 .cer、.pem 或 .crt 憑證。
- 將 Key Vault 名稱、應用程式識別碼和憑證指紋儲存在應用程式的
appsettings.json
檔案中。 - 瀏覽至 Azure 入口網站中的 [Key Vault]。
- 選取您在使用 Azure Key Vault 在實際執行環境中建立祕密存放區一節所建立的 Key Vault。
- 選取存取原則。
- 選取新增存取原則。
- 開啟 [祕密權限],並提供應用程式 [取得] 和 [清單] 權限。
- 選取 [選取主體],然後依名稱選取已註冊的應用程式。 選取 [選取] 按鈕。
- 選取 [確定]。
- 選取 [儲存]。
- 部署應用程式。
Certificate
範例應用程式會使用與祕密名稱相同的名稱從 IConfigurationRoot 取得組態值:
- 非階層值:使用
config["SecretName"]
取得SecretName
的值。 - 階層值 (區段):使用
:
(冒號) 標記法或 GetSection 方法。 使用下列其中一種方法來取得組態值:config["Section:SecretName"]
config.GetSection("Section")["SecretName"]
X.509 憑證由 OS 管理。 應用程式會使用檔案所提供的 appsettings.json
值來呼叫 AddAzureKeyVault :
using System.Security.Cryptography.X509Certificates;
using Azure.Identity;
var builder = WebApplication.CreateBuilder(args);
if (builder.Environment.IsProduction())
{
using var x509Store = new X509Store(StoreLocation.CurrentUser);
x509Store.Open(OpenFlags.ReadOnly);
var x509Certificate = x509Store.Certificates
.Find(
X509FindType.FindByThumbprint,
builder.Configuration["AzureADCertThumbprint"],
validOnly: false)
.OfType<X509Certificate2>()
.Single();
builder.Configuration.AddAzureKeyVault(
new Uri($"https://{builder.Configuration["KeyVaultName"]}.vault.azure.net/"),
new ClientCertificateCredential(
builder.Configuration["AzureADDirectoryId"],
builder.Configuration["AzureADApplicationId"],
x509Certificate));
}
var app = builder.Build();
範例值:
- Key Vault 名稱:
contosovault
- 應用程式識別碼:
00001111-aaaa-2222-bbbb-3333cccc4444
- 憑證指紋:
fe14593dd66b2406c5269d742d04b6e1ab03adb1
appsettings.json
:
{
"KeyVaultName": "Key Vault Name",
"AzureADApplicationId": "Azure AD Application ID",
"AzureADCertThumbprint": "Azure AD Certificate Thumbprint",
"AzureADDirectoryId": "Azure AD Directory ID"
}
您執行應用程式時,網頁會顯示載入的祕密值。 在開發環境中,祕密值載入時帶有 _dev
後置詞。 在實際執行環境中,祕密值載入時則帶有 _prod
後置詞。
使用 Azure 資源受控識別
部署至 Azure 的應用程式可以使用 Azure 資源受控識別。 受控 identity 可讓應用程式使用 Microsoft Entra ID 驗證機制向 Azure Key Vault 進行驗證,而不須將認證資料儲存在應用程式的程式碼或設定中。
當 Program.cs
最上方的 #define
前置處理器指示詞設定為 Managed
時,樣本應用程式會使用系統指派的受控 identity 。 若要建立 Azure App Service 應用程式的受控 identity ,請參閱 如何使用 App Service 和 Azure Functions 的受控身分識別。 一旦建立受控 identity 之後,請前往 Azure 入口網站,記下 App Service 的 Identity 面板顯示的應用程式物件識別碼。
在應用程式的 appsettings.json
檔案中輸入保存庫名稱。 設定為 Managed
版本時,範例應用程式不需要應用程式識別碼和密碼 (用戶端密碼),因此您可以忽略這些組態項目。 應用程式會部署至 Azure,而 Azure 只會使用存放在 appsettings.json
檔案中的保存庫名稱來驗證應用程式以存取 Azure Key Vault。
將範例應用程式部署至 Azure App Service。
使用 Azure CLI 和應用程式的物件識別碼,為應用程式提供存取保存庫的 list
和 get
權限:
az keyvault set-policy --name {KEY VAULT NAME} --object-id {OBJECT ID} --secret-permissions get list
使用 Azure CLI、PowerShell 或 Azure 入口網站重新啟動應用程式。
範例應用程式會建立 DefaultAzureCredential 類別的執行個體。 認證會嘗試從 Azure 資源的環境取得存取權杖:
using Azure.Identity;
var builder = WebApplication.CreateBuilder(args);
if (builder.Environment.IsProduction())
{
builder.Configuration.AddAzureKeyVault(
new Uri($"https://{builder.Configuration["KeyVaultName"]}.vault.azure.net/"),
new DefaultAzureCredential());
}
Key Vault 名稱範例值:contosovault
appsettings.json
:
{
"KeyVaultName": "Key Vault Name"
}
針對使用使用者指派受控 identity的應用程式,請採用下列其中一種方法來設定受控 identity的用戶端識別碼:
設定
AZURE_CLIENT_ID
環境變數。呼叫
AddAzureKeyVault
時設定 DefaultAzureCredentialOptions.ManagedIdentityClientId 屬性:builder.Configuration.AddAzureKeyVault( new Uri($"https://{builder.Configuration["KeyVaultName"]}.vault.azure.net/"), new DefaultAzureCredential(new DefaultAzureCredentialOptions { ManagedIdentityClientId = builder.Configuration["AzureADManagedIdentityClientId"] }));
您執行應用程式時,網頁會顯示載入的祕密值。 在開發環境中,祕密值是由祕密管理員提供,因此帶有 _dev
後置詞。 在實際執行環境中,祕密值是由 Azure Key Vault 提供,因此載入時帶有 _prod
後置詞。
如果您收到 Access denied
錯誤,請確認應用程式已向 Microsoft Entra ID 註冊,並提供保存庫的存取權。 確認您已在 Azure 中重新啟動服務。
如需搭配受控 identity 和 Azure Pipelines 使用提供者的相關資訊,請參閱 使用受管理的服務 identity建立與 VM 的 Azure Resource Manager 服務連線。
設定選項
AddAzureKeyVault
可接受 AzureKeyVaultConfigurationOptions 物件:
// using Azure.Extensions.AspNetCore.Configuration.Secrets;
builder.Configuration.AddAzureKeyVault(
new Uri($"https://{builder.Configuration["KeyVaultName"]}.vault.azure.net/"),
new DefaultAzureCredential(),
new AzureKeyVaultConfigurationOptions
{
// ...
});
AzureKeyVaultConfigurationOptions
物件包含下列屬性:
屬性 | 說明 |
---|---|
Manager | 用於控制祕密載入的 KeyVaultSecretManager 執行個體。 |
ReloadInterval | 在輪詢保存庫以進行變更的過程中,每次嘗試作業之間的等待 TimeSpan 。 預設值為 null (未重新載入組態)。 |
使用金鑰名稱前置詞
AddAzureKeyVault
提供多載能力以接受 KeyVaultSecretManager 的實作,可讓您控制 Key Vault 祕密轉換為組態金鑰的方式。 例如,您可以實作介面,根據您在應用程式啟動時提供的前置詞值載入祕密值。 此技術可讓您根據應用程式版本載入祕密。
警告
請勿在 Key Vault 祕密上使用前置詞進行下列事項:
- 將多個應用程式的祕密放在相同的保存庫。
- 將環境祕密 (例如開發與實際執行環境的祕密) 放在相同的保存庫。
不同的應用程式和開發/實際執行環境應該使用不同的 Key Vault 來隔離應用程式環境,以達到最高安全性層級。
在下列範例中,Key Vault 中會建立 5000-AppSecret
的祕密 (並使用開發環境的祕密管理員);Key Vault 祕密名稱不允許使用句點。 此祕密代表應用程式 5.0.0.0 版的應用程式祕密。 對於另一個版本的應用程式 (5.1.0.0),5100-AppSecret
的祕密會新增至保存庫 (並使用祕密管理員)。 每個應用程式版本會將其版本祕密值以 AppSecret
形式載入至組態,並移除載入祕密時的版本。
使用自訂 KeyVaultSecretManager
實作呼叫 AddAzureKeyVault
:
// using Azure.Extensions.AspNetCore.Configuration.Secrets;
builder.Configuration.AddAzureKeyVault(
new Uri($"https://{builder.Configuration["KeyVaultName"]}.vault.azure.net/"),
new DefaultAzureCredential(),
new SamplePrefixKeyVaultSecretManager("5000"));
實作會回應祕密的版本前置詞,將正確的祕密載入組態:
Load
會載入名稱以前置詞為開頭的祕密。 其他祕密則不會載入。GetKey
%- 從祕密名稱中移除前置詞。
- 將任何名稱中的雙虛線取代為
KeyDelimiter
,這是組態中使用的分隔符號 (通常為冒號)。 Azure Key Vault 不允許祕密名稱使用冒號。
public class SamplePrefixKeyVaultSecretManager : KeyVaultSecretManager
{
private readonly string _prefix;
public SamplePrefixKeyVaultSecretManager(string prefix)
=> _prefix = $"{prefix}-";
public override bool Load(SecretProperties properties)
=> properties.Name.StartsWith(_prefix);
public override string GetKey(KeyVaultSecret secret)
=> secret.Name[_prefix.Length..].Replace("--", ConfigurationPath.KeyDelimiter);
}
Load
方法是由提供者演算法呼叫,該演算法會逐一查看保存庫祕密,以尋找版本前置祕密。 演算法找到具有 Load
的版本前置詞時,會使用 GetKey
方法來傳回祕密名稱的組態名稱。 它會從祕密名稱中移除版本前置詞。 祕密名稱的 rest 則會傳回,以載入應用程式設定的成對名稱和數值。
實作此方法時:
應用程式的專案檔中有指定的應用程式版本。 在下列範例中,應用程式版本設為
5.0.0.0
:<PropertyGroup> <Version>5.0.0.0</Version> </PropertyGroup>
確認應用程式的專案檔中存在
<UserSecretsId>
屬性,其中{GUID}
是使用者提供的 GUID:<PropertyGroup> <UserSecretsId>{GUID}</UserSecretsId> </PropertyGroup>
使用祕密管理員在本機儲存下列祕密:
dotnet user-secrets set "5000-AppSecret" "5.0.0.0_secret_value_dev" dotnet user-secrets set "5100-AppSecret" "5.1.0.0_secret_value_dev"
使用下列 Azure CLI 命令將祕密儲存在 Azure Key Vault 中:
az keyvault secret set --vault-name {KEY VAULT NAME} --name "5000-AppSecret" --value "5.0.0.0_secret_value_prod" az keyvault secret set --vault-name {KEY VAULT NAME} --name "5100-AppSecret" --value "5.1.0.0_secret_value_prod"
執行應用程式時,載入 Key Vault 祕密。
5000-AppSecret
的字串祕密與應用程式專案檔中的指定應用程式版本相符 (5.0.0.0
)。版本
5000
(含虛線) 會從金鑰名稱中去除。 在整個應用程式中,讀取具有金鑰AppSecret
的組態時會載入祕密值。如果專案檔中的應用程式版本變更為
5.1.0.0
,且應用程式再次執行,則傳回的祕密值在開發環境中為5.1.0.0_secret_value_dev
,在實際執行環境中為5.1.0.0_secret_value_prod
。
注意
您也可以將自己的 SecretClient 實作提供給 AddAzureKeyVault
。 自訂用戶端允許跨應用程式共用用戶端的單一執行個體。
將陣列繫結到類別
提供者可以將組態值讀入陣列,以便繫結 POCO 陣列。
從允許金鑰包含冒號 (:
) 分隔符號的組態來源進行讀取時,會使用數值金鑰區段來區分組成陣列的金鑰 (:0:
、:1:
、... :{n}:
)。 如需詳細資訊,請參閱組態:將陣列繫結到類別。
Azure Key Vault 金鑰無法使用冒號作為分隔符號。 本文所述的方法是使用雙虛線 (--
) 作為階層值 (區段) 的分隔符號。 陣列金鑰會儲存在 Azure Key Vault 中,且包含雙虛線和數值金鑰區段 (--0--
、--1--
、... --{n}--
)。
檢查 JSON 檔案提供的以下 Serilog 記錄提供者設定。 WriteTo
陣列中有兩個定義的物件常值以反映兩個 Serilog 接收器,它們描述了記錄輸出的目的地:
"Serilog": {
"WriteTo": [
{
"Name": "AzureTableStorage",
"Args": {
"storageTableName": "logs",
"connectionString": "DefaultEnd...ountKey=Eby8...GMGw=="
}
},
{
"Name": "AzureDocumentDB",
"Args": {
"endpointUrl": "https://contoso.documents.azure.com:443",
"authorizationKey": "Eby8...GMGw=="
}
}
]
}
上述 JSON 檔案中顯示的設定會以雙虛線 (--
) 標記法和數值區段儲存在 Azure Key Vault 中:
機碼 | 值 |
---|---|
Serilog--WriteTo--0--Name |
AzureTableStorage |
Serilog--WriteTo--0--Args--storageTableName |
logs |
Serilog--WriteTo--0--Args--connectionString |
DefaultEnd...ountKey=Eby8...GMGw== |
Serilog--WriteTo--1--Name |
AzureDocumentDB |
Serilog--WriteTo--1--Args--endpointUrl |
https://contoso.documents.azure.com:443 |
Serilog--WriteTo--1--Args--authorizationKey |
Eby8...GMGw== |
重新載入祕密
根據預設,組態提供者會快取應用程式存留期的祕密。 應用程式會忽略後續停用或更新保存庫中的秘密。
若要重新載入祕密,請呼叫 IConfigurationRoot.Reload:
config.Reload();
若要以指定的間隔定期重新載入祕密,請設定 AzureKeyVaultConfigurationOptions.ReloadInterval 屬性。 如需詳細資訊,請參閱組態選項。
已停用和過期的祕密
根據預設,組態提供者中包含已過期的祕密。 若要在應用程式組態中排除這些祕密的值,請更新已過期的祕密,或使用自訂組態提供者提供組態:
class SampleKeyVaultSecretManager : KeyVaultSecretManager
{
public override bool Load(SecretProperties properties) =>
properties.ExpiresOn.HasValue &&
properties.ExpiresOn.Value > DateTimeOffset.Now;
}
將此自訂的 KeyVaultSecretManager
傳遞至 AddAzureKeyVault
:
// using Azure.Extensions.AspNetCore.Configuration.Secrets;
builder.Configuration.AddAzureKeyVault(
new Uri($"https://{builder.Configuration["KeyVaultName"]}.vault.azure.net/"),
new DefaultAzureCredential(),
new SampleKeyVaultSecretManager());
停用的祕密無法從 Key Vault 擷取,且永遠不會包含。
疑難排解
應用程式無法使用提供者載入組態時,錯誤訊息會寫入至 ASP.NET Core 記錄基礎結構。 下列條件會防止組態載入:
- Microsoft Entra ID 中未正確設定應用程式或憑證。
- 保存庫不存在於 Azure Key Vault 中。
- 應用程式無權存取保存庫。
- 存取原則不包含
Get
和List
權限。 - 在保存庫中,組態資料 (成對的名稱和數值) 命名錯誤、遺失或已停用。
- 應用程式有錯誤的 Key Vault 名稱 (
KeyVaultName
)、Microsoft Entra ID 應用程式識別碼 (AzureADApplicationId
),或 Microsoft Entra ID 憑證指紋 (AzureADCertThumbprint
),或 Microsoft Entra ID Directory ID (AzureADDirectoryId
)。 - 為應用程式新增 Key Vault 存取原則時,該原則已經建立,但 [存取原則] 使用者介面中的 [儲存] 按鈕並未選取。
其他資源
本文說明如何使用 Azure Key Vault 組態提供者,從 Azure Key Vault 祕密載入應用程式組態值。 Azure Key Vault 是雲端式服務,可協助保護應用程式和服務所使用的密碼編譯金鑰及祕密。 搭配 ASP.NET Core 應用程式使用 Azure Key Vault 的常見案例包括:
- 控制機密組態資料的存取權。
- 儲存組態資料時,符合 FIPS 140-2 等級 2 驗證的硬體安全性模組 (HSM) 需求。
套件
新增以下套件的套件參考:
範例應用程式
Program.cs
最上方的 #define
前置處理器指示詞會決定範例應用程式的執行模式,而該模式有以下兩種:
Certificate
:示範如何使用 Azure Key Vault 用戶端識別碼和 X.509 憑證來存取儲存在 Azure Key Vault 中的祕密。 此範例可以從任何位置執行,無論是部署至 Azure App Service 的位置,或是可以提供 ASP.NET Core 應用程式的任何主機。Managed
:示範如何使用 Azure 資源受控識別。 受控 identity 會使用 Azure 資源受控識別向 Azure Key Vault 驗證應用程式,而不須將認證資料儲存在應用程式的程式碼或設定中。 使用受控識別進行驗證時,不需要 Azure 資源的受控識別應用程式識別碼和密碼 (用戶端密碼)。 範例的Managed
版本必須部署至 Azure。 請遵循使用 Azure 資源受控識別一節中的指引。
如需使用前置處理器指示詞 (#define
) 設定範例應用程式的詳細資訊,請參閱 ASP.NET Core 概觀。
檢視或下載範例程式碼 \(英文\) (如何下載)
開發環境中的祕密存放區
使用祕密管理員在本機設定祕密。 範例應用程式在開發環境中的本機電腦執行時,會從本機使用者祕密存放區載入祕密。
祕密管理員需要應用程式專案檔中的 <UserSecretsId>
屬性。 將屬性值 ({GUID}
) 設定為任何唯一 GUID:
<PropertyGroup>
<UserSecretsId>{GUID}</UserSecretsId>
</PropertyGroup>
祕密會建立為成對的名稱和數值。 階層值 (組態區段) 使用 :
(冒號) 作為 ASP.NET Core 組態金鑰名稱中的分隔符號。
祕密管理員是從開啟至專案內容根目錄的命令殼層中使用,其中 {SECRET NAME}
是名稱,{SECRET VALUE}
是值:
dotnet user-secrets set "{SECRET NAME}" "{SECRET VALUE}"
在專案內容根目錄的命令殼層中執行以下命令,為範例應用程式設定祕密:
dotnet user-secrets set "SecretName" "secret_value_1_dev"
dotnet user-secrets set "Section:SecretName" "secret_value_2_dev"
若這些祕密存放在使用 Azure Key Vault 在實際執行環境中建立祕密存放區一節所建立的 Azure Key Vault,後置詞 _dev
會變更為 _prod
。 後置詞會在應用程式的輸出中提供視覺提示,指出組態值的來源。
使用 Azure Key Vault 在實際執行環境中建立祕密存放區
完成下列步驟來建立 Azure Key Vault,並將範例應用程式的祕密存放於此。 如需詳細資訊,請參閱快速入門:使用 Azure CLI 從 Azure Key Vault 設定和擷取祕密。
在 Azure 入口網站中使用下列任一方法開啟 Azure Cloud Shell:
- 選取程式碼區塊右上角的 [試試看]。 在文字方塊中使用搜尋字串「Azure CLI」。
- 使用 [啟動 Cloud Shell] 按鈕在瀏覽器中開啟 Cloud Shell。
- 選取 Azure 入口網站右上角功能表中的 [Cloud Shell] 按鈕。
如需詳細資訊,請參閱 Azure CLI 和 Azure Cloud Shell 概觀。
如果您尚未通過驗證,請使用
az login
命令登入。使用下列命令建立資源群組,其中
{RESOURCE GROUP NAME}
是新資源群組的名稱,而{LOCATION}
是 Azure 區域:az group create --name "{RESOURCE GROUP NAME}" --location {LOCATION}
使用下列命令在資源群組中建立 Key Vault ,其中
{KEY VAULT NAME}
是新的保存庫名稱,而{LOCATION}
是 Azure 區域:az keyvault create --name {KEY VAULT NAME} --resource-group "{RESOURCE GROUP NAME}" --location {LOCATION}
在保存庫中以成對的名稱和數值格式建立祕密。
Azure Key Vault 祕密名稱僅限使用英數字元和虛線。 階層值 (組態區段) 使用
--
(雙虛線) 作為分隔符號,因為 Key Vault 祕密名稱中不允許冒號。 冒號會從 ASP.NET Core 組態中的子機碼分隔區段。 在祕密載入到應用程式組態時,冒號會取代雙虛線序列。下列祕密可用於範例應用程式。 這些值包含
_prod
後置詞,以區別從祕密管理員載入到開發環境中的_dev
後置詞。 將{KEY VAULT NAME}
取代為您在上一個步驟建立的 Key Vault 名稱:az keyvault secret set --vault-name {KEY VAULT NAME} --name "SecretName" --value "secret_value_1_prod" az keyvault secret set --vault-name {KEY VAULT NAME} --name "Section--SecretName" --value "secret_value_2_prod"
針對非 Azure 裝載的應用程式使用應用程式識別碼和 X.509 憑證
將 Azure Key Vault 和應用程式設定為使用 Microsoft Entra ID 應用程式識別碼和 X.509 憑證,以便在應用程式裝載於 Azure 外部時,向保存庫進行驗證。 如需詳細資訊,請參閱關於金鑰、祕密與憑證。
注意
雖然 Azure 裝載的應用程式支援使用應用程式識別碼和 X.509 憑證,但不建議這麼做。 在 Azure 中裝載應用程式時,請改用 Azure 資源受控識別。 受控識別不需要將憑證存放在應用程式或開發環境中。
當 Program.cs
最上方的 #define
前置處理器指示詞設定為 Certificate
時,範例應用程式會使用應用程式識別碼和 X.509 憑證。
- 建立 PKCS#12 封存 (.pfx) 憑證。 建立憑證的選項包括 Windows 上的 New-SelfSignedCertificate 和 OpenSSL。
- 將憑證安裝至目前使用者的個人憑證存放區。 您可以選擇是否將金鑰標示為可匯出。 請記下憑證指紋,以供此流程稍後使用。
- 將 PKCS#12 封存 (.pfx) 憑證匯出為 DER 編碼憑證 (.cer)。
- 向 Microsoft Entra ID 註冊應用程式 (應用程式註冊)。
- 將 DER 編碼憑證 (.cer) 上傳至 Microsoft Entra ID:
- 在 Microsoft Entra ID 中選取應用程式。
- 瀏覽至 [憑證 & 祕密]。
- 選取 [上傳憑證] 以上傳包含公開金鑰的憑證。 可接受 .cer、.pem 或 .crt 憑證。
- 將 Key Vault 名稱、應用程式識別碼和憑證指紋儲存在應用程式的
appsettings.json
檔案中。 - 瀏覽至 Azure 入口網站中的 [Key Vault]。
- 選取您在使用 Azure Key Vault 在實際執行環境中建立祕密存放區一節所建立的 Key Vault。
- 選取存取原則。
- 選取新增存取原則。
- 開啟 [祕密權限],並提供應用程式 [取得] 和 [清單] 權限。
- 選取 [選取主體],然後依名稱選取已註冊的應用程式。 選取 [選取] 按鈕。
- 選取 [確定]。
- 選取 [儲存]。
- 部署應用程式。
Certificate
範例應用程式會使用與祕密名稱相同的名稱從 IConfigurationRoot 取得組態值:
- 非階層值:使用
config["SecretName"]
取得SecretName
的值。 - 階層值 (區段):使用
:
(冒號) 標記法或 GetSection 方法。 使用下列其中一種方法來取得組態值:config["Section:SecretName"]
config.GetSection("Section")["SecretName"]
X.509 憑證由 OS 管理。 應用程式會使用檔案所提供的 appsettings.json
值來呼叫 AddAzureKeyVault :
// using System.Linq;
// using System.Security.Cryptography.X509Certificates;
// using Azure.Extensions.AspNetCore.Configuration.Secrets;
// using Azure.Identity;
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
if (context.HostingEnvironment.IsProduction())
{
var builtConfig = config.Build();
using var store = new X509Store(StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var certs = store.Certificates.Find(
X509FindType.FindByThumbprint,
builtConfig["AzureADCertThumbprint"], false);
config.AddAzureKeyVault(new Uri($"https://{builtConfig["KeyVaultName"]}.vault.azure.net/"),
new ClientCertificateCredential(builtConfig["AzureADDirectoryId"], builtConfig["AzureADApplicationId"], certs.OfType<X509Certificate2>().Single()),
new KeyVaultSecretManager());
store.Close();
}
})
.ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>());
範例值:
- Key Vault 名稱:
contosovault
- 應用程式識別碼:
00001111-aaaa-2222-bbbb-3333cccc4444
- 憑證指紋:
fe14593dd66b2406c5269d742d04b6e1ab03adb1
appsettings.json
:
{
"KeyVaultName": "Key Vault Name",
"AzureADApplicationId": "Azure AD Application ID",
"AzureADCertThumbprint": "Azure AD Certificate Thumbprint",
"AzureADDirectoryId": "Azure AD Directory ID"
}
您執行應用程式時,網頁會顯示載入的祕密值。 在開發環境中,祕密值載入時帶有 _dev
後置詞。 在實際執行環境中,祕密值載入時則帶有 _prod
後置詞。
使用 Azure 資源受控識別
部署至 Azure 的應用程式可以使用 Azure 資源受控識別。 受控 identity 可讓應用程式使用 Microsoft Entra ID 驗證機制向 Azure Key Vault 進行驗證,而不須將認證資料 (應用程式識別碼和密碼/用戶端密碼) 儲存在應用程式中。
當 Program.cs
最上方的 #define
前置處理器指示詞設定為 Managed
時,範例應用程式會使用 Azure 資源受控識別。
在應用程式的 appsettings.json
檔案中輸入保存庫名稱。 設定為 Managed
版本時,範例應用程式不需要應用程式識別碼和密碼 (用戶端密碼),因此您可以忽略這些組態項目。 應用程式會部署至 Azure,而 Azure 只會使用存放在 appsettings.json
檔案中的保存庫名稱來驗證應用程式以存取 Azure Key Vault。
將範例應用程式部署至 Azure App Service。
部署至 Azure App Service 的應用程式會在建立服務時自動向 Microsoft Entra ID 註冊。 從部署取得物件識別碼並用於下列命令。 物件識別碼會顯示在 Azure 入口網站中 App Service 的 Identity 面板。
使用 Azure CLI 和應用程式的物件識別碼,為應用程式提供存取保存庫的 list
和 get
權限:
az keyvault set-policy --name {KEY VAULT NAME} --object-id {OBJECT ID} --secret-permissions get list
使用 Azure CLI、PowerShell 或 Azure 入口網站重新啟動應用程式。
範例應用程式:
- 建立 DefaultAzureCredential 類別的執行個體。 認證會嘗試從 Azure 資源的環境取得存取權杖。
- 使用
DefaultAzureCredential
執行個體建立新的 SecretClient。 SecretClient
執行個體會與 KeyVaultSecretManager 執行個體搭配使用,後者會載入祕密值,並以金鑰名稱中的冒號 (:
) 取代雙虛線 (--
)。
// using Azure.Security.KeyVault.Secrets;
// using Azure.Identity;
// using Azure.Extensions.AspNetCore.Configuration.Secrets;
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
if (context.HostingEnvironment.IsProduction())
{
var builtConfig = config.Build();
var secretClient = new SecretClient(
new Uri($"https://{builtConfig["KeyVaultName"]}.vault.azure.net/"),
new DefaultAzureCredential());
config.AddAzureKeyVault(secretClient, new KeyVaultSecretManager());
}
})
.ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>());
Key Vault 名稱範例值:contosovault
appsettings.json
:
{
"KeyVaultName": "Key Vault Name"
}
您執行應用程式時,網頁會顯示載入的祕密值。 在開發環境中,祕密值是由祕密管理員提供,因此帶有 _dev
後置詞。 在實際執行環境中,祕密值是由 Azure Key Vault 提供,因此載入時帶有 _prod
後置詞。
如果您收到 Access denied
錯誤,請確認應用程式已向 Microsoft Entra ID 註冊,並提供保存庫的存取權。 確認您已在 Azure 中重新啟動服務。
如需搭配受控 identity 和 Azure Pipelines 使用提供者的相關資訊,請參閱 使用受管理的服務 identity建立與 VM 的 Azure Resource Manager 服務連線。
設定選項
AddAzureKeyVault
可接受 AzureKeyVaultConfigurationOptions 物件:
config.AddAzureKeyVault(
new SecretClient(
new Uri("Your Key Vault Endpoint"),
new DefaultAzureCredential(),
new AzureKeyVaultConfigurationOptions())
{
...
});
AzureKeyVaultConfigurationOptions
物件包含下列屬性。
屬性 | 說明 |
---|---|
Manager | 用於控制祕密載入的 KeyVaultSecretManager 執行個體。 |
ReloadInterval | 在輪詢保存庫以進行變更的過程中,每次嘗試作業之間的等待 TimeSpan 。 預設值為 null (未重新載入組態)。 |
使用金鑰名稱前置詞
AddAzureKeyVault
提供多載能力以接受 KeyVaultSecretManager 的實作,可讓您控制 Key Vault 祕密轉換為組態金鑰的方式。 例如,您可以實作介面,根據您在應用程式啟動時提供的前置詞值載入祕密值。 此技術可讓您根據應用程式版本載入祕密。
警告
請勿在 Key Vault 祕密上使用前置詞進行下列事項:
- 將多個應用程式的祕密放在相同的保存庫。
- 將環境祕密 (例如開發與實際執行環境的祕密) 放在相同的保存庫。
不同的應用程式和開發/實際執行環境應該使用不同的 Key Vault 來隔離應用程式環境,以達到最高安全性層級。
在下列範例中,Key Vault 中會建立 5000-AppSecret
的祕密 (並使用開發環境的祕密管理員);Key Vault 祕密名稱不允許使用句點。 此祕密代表應用程式 5.0.0.0 版的應用程式祕密。 對於另一個版本的應用程式 (5.1.0.0),5100-AppSecret
的祕密會新增至保存庫 (並使用祕密管理員)。 每個應用程式版本會將其版本祕密值以 AppSecret
形式載入至組態,並移除載入祕密時的版本。
使用自訂 KeyVaultSecretManager
實作呼叫 AddAzureKeyVault
:
config.AddAzureKeyVault(
$"https://{builtConfig["KeyVaultName"]}.vault.azure.net/",
builtConfig["AzureADApplicationId"],
certs.OfType<X509Certificate2>().Single(),
new PrefixKeyVaultSecretManager(versionPrefix));
實作會回應祕密的版本前置詞,將正確的祕密載入組態:
Load
會載入名稱以前置詞為開頭的祕密。 其他祕密則不會載入。GetKey
%- 從祕密名稱中移除前置詞。
- 將任何名稱中的雙虛線取代為
KeyDelimiter
,這是組態中使用的分隔符號 (通常為冒號)。 Azure Key Vault 不允許祕密名稱使用冒號。
public class PrefixKeyVaultSecretManager : KeyVaultSecretManager
{
private readonly string _prefix;
public PrefixKeyVaultSecretManager(string prefix)
{
_prefix = $"{prefix}-";
}
public override bool Load(SecretProperties secret)
{
return secret.Name.StartsWith(_prefix);
}
public override string GetKey(KeyVaultSecret secret)
{
return secret.Name
.Substring(_prefix.Length)
.Replace("--", ConfigurationPath.KeyDelimiter);
}
}
Load
方法是由提供者演算法呼叫,該演算法會逐一查看保存庫祕密,以尋找版本前置祕密。 演算法找到具有 Load
的版本前置詞時,會使用 GetKey
方法來傳回祕密名稱的組態名稱。 它會從祕密名稱中移除版本前置詞。 祕密名稱的 rest 則會傳回,以載入應用程式設定的成對名稱和數值。
實作此方法時:
應用程式的專案檔中有指定的應用程式版本。 在下列範例中,應用程式版本設為
5.0.0.0
:<PropertyGroup> <Version>5.0.0.0</Version> </PropertyGroup>
確認應用程式的專案檔中存在
<UserSecretsId>
屬性,其中{GUID}
是使用者提供的 GUID:<PropertyGroup> <UserSecretsId>{GUID}</UserSecretsId> </PropertyGroup>
使用祕密管理員在本機儲存下列祕密:
dotnet user-secrets set "5000-AppSecret" "5.0.0.0_secret_value_dev" dotnet user-secrets set "5100-AppSecret" "5.1.0.0_secret_value_dev"
使用下列 Azure CLI 命令將祕密儲存在 Azure Key Vault 中:
az keyvault secret set --vault-name {KEY VAULT NAME} --name "5000-AppSecret" --value "5.0.0.0_secret_value_prod" az keyvault secret set --vault-name {KEY VAULT NAME} --name "5100-AppSecret" --value "5.1.0.0_secret_value_prod"
執行應用程式時,載入 Key Vault 祕密。
5000-AppSecret
的字串祕密與應用程式專案檔中的指定應用程式版本相符 (5.0.0.0
)。版本
5000
(含虛線) 會從金鑰名稱中去除。 在整個應用程式中,讀取具有金鑰AppSecret
的組態時會載入祕密值。如果專案檔中的應用程式版本變更為
5.1.0.0
,且應用程式再次執行,則傳回的祕密值在開發環境中為5.1.0.0_secret_value_dev
,在實際執行環境中為5.1.0.0_secret_value_prod
。
注意
您也可以將自己的 SecretClient 實作提供給 AddAzureKeyVault
。 自訂用戶端允許跨應用程式共用用戶端的單一執行個體。
將陣列繫結到類別
提供者可以將組態值讀入陣列,以便繫結 POCO 陣列。
從允許金鑰包含冒號 (:
) 分隔符號的組態來源進行讀取時,會使用數值金鑰區段來區分組成陣列的金鑰 (:0:
、:1:
、... :{n}:
)。 如需詳細資訊,請參閱組態:將陣列繫結到類別。
Azure Key Vault 金鑰無法使用冒號作為分隔符號。 本文所述的方法是使用雙虛線 (--
) 作為階層值 (區段) 的分隔符號。 陣列金鑰會儲存在 Azure Key Vault 中,且包含雙虛線和數值金鑰區段 (--0--
、--1--
、... --{n}--
)。
檢查 JSON 檔案提供的以下 Serilog 記錄提供者設定。 WriteTo
陣列中有兩個定義的物件常值以反映兩個 Serilog 接收器,它們描述了記錄輸出的目的地:
"Serilog": {
"WriteTo": [
{
"Name": "AzureTableStorage",
"Args": {
"storageTableName": "logs",
"connectionString": "DefaultEnd...ountKey=Eby8...GMGw=="
}
},
{
"Name": "AzureDocumentDB",
"Args": {
"endpointUrl": "https://contoso.documents.azure.com:443",
"authorizationKey": "Eby8...GMGw=="
}
}
]
}
上述 JSON 檔案中顯示的設定會以雙虛線 (--
) 標記法和數值區段儲存在 Azure Key Vault 中:
機碼 | 值 |
---|---|
Serilog--WriteTo--0--Name |
AzureTableStorage |
Serilog--WriteTo--0--Args--storageTableName |
logs |
Serilog--WriteTo--0--Args--connectionString |
DefaultEnd...ountKey=Eby8...GMGw== |
Serilog--WriteTo--1--Name |
AzureDocumentDB |
Serilog--WriteTo--1--Args--endpointUrl |
https://contoso.documents.azure.com:443 |
Serilog--WriteTo--1--Args--authorizationKey |
Eby8...GMGw== |
重新載入祕密
在 IConfigurationRoot.Reload 被呼叫之前,系統會快取祕密。 在執行 Reload
之前,應用程式不會採用保存庫中隨後停用或更新的祕密。
Configuration.Reload();
已停用和過期的祕密
根據預設,組態提供者中包含已過期的祕密。 若要在應用程式組態中排除這些祕密的值,請更新已過期的祕密,或使用自訂組態提供者提供組態:
class SampleKeyVaultSecretManager : KeyVaultSecretManager
{
public override bool Load(SecretProperties properties) =>
properties.ExpiresOn.HasValue &&
properties.ExpiresOn.Value > DateTimeOffset.Now;
}
將此自訂的 KeyVaultSecretManager
傳遞至 AddAzureKeyVault
:
// using Azure.Extensions.AspNetCore.Configuration.Secrets;
config.AddAzureKeyVault(
new Uri($"https://{builder.Configuration["KeyVaultName"]}.vault.azure.net/"),
new DefaultAzureCredential(),
new SampleKeyVaultSecretManager());
停用的祕密無法從 Key Vault 擷取,且永遠不會包含。
疑難排解
應用程式無法使用提供者載入組態時,錯誤訊息會寫入至 ASP.NET Core 記錄基礎結構。 下列條件會防止組態載入:
- Microsoft Entra ID 中未正確設定應用程式或憑證。
- 保存庫不存在於 Azure Key Vault 中。
- 應用程式無權存取保存庫。
- 存取原則不包含
Get
和List
權限。 - 在保存庫中,組態資料 (成對的名稱和數值) 命名錯誤、遺失或已停用。
- 應用程式有錯誤的 Key Vault 名稱 (
KeyVaultName
)、Microsoft Entra ID 應用程式識別碼 (AzureADApplicationId
),或 Microsoft Entra ID 憑證指紋 (AzureADCertThumbprint
),或 Microsoft Entra ID Directory ID (AzureADDirectoryId
)。 - 為應用程式新增 Key Vault 存取原則時,該原則已經建立,但 [存取原則] 使用者介面中的 [儲存] 按鈕並未選取。