ASP.NET Core 中的 Azure 金鑰保存庫 組態提供者
本文說明如何使用Azure 金鑰保存庫設定提供者,從 Azure 金鑰保存庫秘密載入應用程式組態值。 Azure 金鑰保存庫是雲端式服務,可協助保護應用程式和服務所使用的密碼編譯金鑰和秘密。 搭配 ASP.NET Core應用程式使用 Azure 金鑰保存庫 的常見案例包括:
- 控制敏感性組態資料的存取。
- 符合儲存設定資料時,FIPS 140-2 層級 2 驗證的硬體安全性模組 (HSM) 的需求。
套件
新增下列套件的套件參考:
範例應用程式
範例應用程式會以兩種模式的其中一種執行,由 頂端 Program.cs
的 #define
預處理器指示詞所決定:
Certificate
:示範如何使用 Azure 金鑰保存庫 用戶端識別碼和 X.509 憑證來存取儲存在 Azure 金鑰保存庫 中的秘密。 此範例可從任何位置執行,無論是部署至 Azure App 服務,還是任何可以提供 ASP.NET Core應用程式的主機。Managed
:示範如何使用 Azure 資源的受控識別。 受控識別會使用 Azure Active Directory (AD) 驗證向 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 金鑰保存庫 Azure 金鑰保存庫 區段中生產環境中的秘密儲存體時, _dev
尾碼會變更為 _prod
。 尾碼會在應用程式的輸出中提供視覺化提示,指出組態值的來源。
使用 Azure 金鑰保存庫 在生產環境中使用秘密儲存體
完成下列步驟來建立 Azure 金鑰保存庫,並將範例應用程式的秘密儲存在其中。 如需詳細資訊,請參閱快速入門:使用 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 NAME}
是新的金鑰保存庫名稱,而{LOCATION}
是 Azure 區域:az keyvault create --name {KEY VAULT NAME} --resource-group "{RESOURCE GROUP NAME}" --location {LOCATION}
在金鑰保存庫中建立秘密作為名稱/值組。
Azure 金鑰保存庫秘密名稱僅限於英數位元和連字號。 階層式值 (組態區段) (使用
--
兩個連字號) 做為分隔符號,因為金鑰保存庫秘密名稱中不允許冒號。 冒號會從ASP.NET Core組態中的子機碼分隔區段。 當秘密載入至應用程式的組態時,會以冒號取代雙虛線序列。下列秘密可與範例應用程式搭配使用。 這些值包含
_prod
尾碼,以區別它們與_dev
在開發環境中載入的後置詞值與秘密管理員。 將 取代{KEY VAULT NAME}
為您在上一個步驟中建立的金鑰保存庫名稱: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 AD、Azure 金鑰保存庫和應用程式,以在應用程式裝載于 Azure 外部時,使用 Azure AD 應用程式識別碼和 X.509 憑證向金鑰保存庫進行驗證。 如需詳細資訊,請參閱關於金鑰、祕密和憑證。
注意
雖然 Azure 中裝載的應用程式支援使用應用程式識別碼和 X.509 憑證,但不建議這麼做。 相反地,在 Azure 中裝載應用程式時 ,請針對 Azure 資源使用受控識別 。 受控識別不需要將憑證儲存在應用程式或開發環境中。
當 頂端 Program.cs
的預處理器指示詞設定為 Certificate
時, #define
範例應用程式會使用應用程式識別碼和 X.509 憑證。
- 建立 PKCS#12 封存 (.pfx) 憑證。 建立憑證的選項包括 Windows 和OpenSSL上的 New-SelfSignedCertificate。
- 將憑證安裝至目前使用者的個人憑證存儲。 將金鑰標示為可匯出是選擇性的。 記下憑證的指紋,此指紋稍後會在此程式中使用。
- 將 PKCS#12 封存 (.pfx) 憑證匯出為 DER 編碼憑證, (.cer) 。
- 向 Azure AD (註冊應用程式應用程式註冊) 。
- 將 DER 編碼憑證 (.cer) 上傳至 Azure AD:
- 在 Azure AD 中選取應用程式。
- 流覽至 [ 憑證 & 秘密]。
- 選取 [上傳憑證 ] 以上傳包含公開金鑰的憑證。 可接受 .cer、.pem或.crt憑證。
- 將金鑰保存庫名稱、應用程式識別碼和憑證指紋儲存在應用程式的
appsettings.json
檔案中。 - 流覽至Azure 入口網站中的金鑰保存庫。
- 選取您在 [使用 Azure 金鑰保存庫] 生產環境中[秘密儲存體] 區段中建立的金鑰保存庫。
- 選取 [存取原則]。
- 選取 [新增存取原則]。
- 開啟 [秘密] 許可權 ,並提供 具有 [取得 ] 和 [列出 ] 許可權的應用程式。
- 選取 [選取主體],然後依名稱選取已註冊的應用程式。 選取 [選取] 按鈕。
- 選取 [確定]。
- 選取 [儲存]。
- 部署應用程式。
範例 Certificate
應用程式會從 IConfigurationRoot 取得其組態值,其名稱與秘密名稱相同:
- 非階層式值:的值
SecretName
是使用config["SecretName"]
取得。 - 階層式值 (區段) :使用
:
(冒號) 標記法或 GetSection 方法。 使用下列其中一種方法來取得組態值:config["Section:SecretName"]
config.GetSection("Section")["SecretName"]
X.509 憑證是由 OS 管理。 應用程式會呼叫 AddAzureKeyVault 檔案所提供的 appsettings.json
值:
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();
範例值:
- 金鑰保存庫名稱:
contosovault
- 應用程式識別碼:
627e911e-43cc-61d4-992e-12db9c81b413
- 憑證指紋:
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 資源的受控識別。 受控識別可讓應用程式使用 Azure AD 驗證向 Azure 金鑰保存庫進行驗證,而不需將認證儲存在應用程式的程式碼或設定中。
當 頂端 Program.cs
的預處理器指示詞設定 Managed
為 時, #define
範例應用程式會使用系統指派的受控識別。 若要為Azure App 服務應用程式建立受控識別,請參閱如何使用受控識別進行App Service和Azure Functions。 建立受控識別之後,請注意App Service面板上Azure 入口網站 Identity 中顯示的應用程式物件識別碼。
在應用程式的 appsettings.json
檔案中輸入保存庫名稱。 當設定為 Managed
版本時,範例應用程式不需要應用程式識別碼和密碼 (用戶端密碼) ,因此您可以忽略這些設定專案。 應用程式會部署至 Azure,而 Azure 只會使用儲存在檔案中的 appsettings.json
保存庫名稱來驗證應用程式以存取 Azure 金鑰保存庫。
將範例應用程式部署至 Azure App 服務。
使用 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());
}
金鑰保存庫名稱範例值: contosovault
appsettings.json
:
{
"KeyVaultName": "Key Vault Name"
}
對於使用使用者指派受控識別的應用程式,請使用下列其中一種方法來設定受控識別的用戶端識別碼:
設定
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
尾碼,因為它們是由秘密管理員提供。 在生產環境中,值會以 _prod
尾碼載入,因為它們是由 Azure 金鑰保存庫所提供。
如果您收到 Access denied
錯誤,請確認應用程式已向 Azure AD 註冊,並提供金鑰保存庫的存取權。 確認您已在 Azure 中重新開機服務。
如需搭配受控識別和 Azure Pipelines 使用提供者的相關資訊,請參閱使用受控服務識別建立與 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 載,可讓您控制金鑰保存庫秘密轉換成設定金鑰的方式。 例如,您可以實作 介面,根據您在應用程式啟動時提供的前置詞值載入秘密值。 例如,這項技術可讓您根據應用程式的版本載入秘密。
警告
請勿在金鑰保存庫秘密上使用前置詞來:
- 將多個應用程式的秘密放在相同的保存庫中。
- 例如,將環境秘密 (, 開發 與 生產 秘密) 放在相同的保存庫中。
不同的應用程式和開發/生產環境應該使用不同的金鑰保存庫來隔離應用程式環境,以提供最高層級的安全性。
在下列範例中,金鑰保存庫中會建立秘密 (,並在金鑰保存庫秘密名稱中 (期間) 使用秘密管理員 5000-AppSecret
) 。 此秘密代表應用程式 5.0.0.0 版的應用程式密碼。 針對另一個版本的應用程式,5.1.0.0 會將秘密新增至金鑰保存庫 (並使用秘密管理員) 。 5100-AppSecret
每個應用程式版本都會將其已建立版本的秘密值載入其設定中, AppSecret
並移除載入秘密時的版本。
AddAzureKeyVault
使用自訂 KeyVaultSecretManager
實作呼叫:
// 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 金鑰保存庫不允許秘密名稱中的冒號。
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
使用 方法來傳回秘密名稱的組態名稱。 它會從秘密的名稱中移除版本前置詞。 會傳回其餘的秘密名稱,以載入應用程式的組態名稱/值組。
實作此方法時:
應用程式專案檔中指定的應用程式版本。 在下列範例中,應用程式的版本會設定為
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 金鑰保存庫:
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"
執行應用程式時,會載入金鑰保存庫秘密。 的
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 金鑰保存庫金鑰無法使用冒號作為分隔符號。 本文所述的方法會使用雙虛線 (--
) 做為階層式值的分隔符號, (區段) 。 陣列索引鍵會儲存在 Azure 金鑰保存庫中,並以雙虛線和數值索引鍵區段 (--0--
、 --1--
... --{n}--
) 。
檢查 ON 檔案所提供的 JS 下列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=="
}
}
]
}
上述 JS ON 檔案中顯示的設定會使用雙虛線 () --
標記法和數值區段,儲存在 Azure 金鑰保存庫:
Key | 值 |
---|---|
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.Enabled.HasValue &&
properties.Enabled.Value &&
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());
疑難排解
當應用程式無法使用提供者載入設定時,會將錯誤訊息寫入ASP.NET Core記錄基礎結構。 下列條件會防止組態載入:
- Azure AD 中未正確設定應用程式或憑證。
- 金鑰保存庫不存在於 Azure 金鑰保存庫中。
- 應用程式未獲授權存取金鑰保存庫。
- 存取原則不包含
Get
和List
許可權。 - 應用程式 (金鑰保存庫名稱 (
KeyVaultName
) 、Azure AD 應用程式識別碼 (AzureADApplicationId
) 或 Azure AD 憑證指紋AzureADCertThumbprint
() ,或 Azure AD 目錄識別碼 (AzureADDirectoryId
) 。 - 新增應用程式的金鑰保存庫存取原則時,已建立原則,但 [存取原則] UI 中未選取 [儲存] 按鈕。
其他資源
本文說明如何使用 Azure 金鑰保存庫組態提供者,從Azure 金鑰保存庫秘密載入應用程式組態值。 Azure 金鑰保存庫是雲端式服務,可協助保護應用程式和服務所使用的密碼編譯金鑰和秘密。 搭配 ASP.NET Core應用程式使用 Azure 金鑰保存庫 的常見案例包括:
- 控制敏感性組態資料的存取。
- 符合儲存設定資料時,FIPS 140-2 層級 2 驗證的硬體安全性模組 (HSM) 的需求。
檢視或下載範例程式碼 \(英文\) (如何下載)
套件
新增下列套件的套件參考:
範例應用程式
範例應用程式會以兩種模式的其中一種執行,由 #define
頂端 Program.cs
的預處理器指示詞所決定:
Certificate
:示範如何使用 Azure 金鑰保存庫用戶端識別碼和 X.509 憑證來存取儲存在 Azure 金鑰保存庫中的秘密。 您可以從任何位置執行此範例,無論是部署至 Azure App 服務,或是任何可以提供 ASP.NET Core應用程式的主機。Managed
:示範如何使用 Azure 資源的受控識別。 受控識別會使用 Azure Active Directory (AD) 驗證向 Azure 金鑰保存庫驗證應用程式,而不需將認證儲存在應用程式的程式碼或設定中。 使用受控識別進行驗證時,不需要 Azure AD 應用程式識別碼和密碼 (用戶端密碼) 。 範例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 金鑰保存庫使用Azure 金鑰保存庫 生產環境中的秘密儲存體時, _dev
尾碼會變更為 _prod
。 尾碼會在應用程式的輸出中提供視覺提示,指出組態值的來源。
在生產環境中使用 Azure 金鑰保存庫的秘密儲存體
完成下列步驟來建立 Azure 金鑰保存庫,並將範例應用程式的秘密儲存在其中。 如需詳細資訊,請參閱快速入門:使用 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 NAME}
是新的金鑰保存庫名稱,而且{LOCATION}
是 Azure 區域:az keyvault create --name {KEY VAULT NAME} --resource-group "{RESOURCE GROUP NAME}" --location {LOCATION}
在金鑰保存庫中建立秘密作為名稱/值組。
Azure 金鑰保存庫秘密名稱僅限於英數位元和虛線。 階層式值 (組態區段) 使用
--
(兩個破折號) 做為分隔符號,因為金鑰保存庫秘密名稱中不允許冒號。 冒號會分隔 ASP.NET Core組態中子機碼的區段。 當秘密載入至應用程式的組態時,會將雙虛線序列取代為冒號。下列秘密可用於範例應用程式。 這些值包含
_prod
尾碼,以區別它們與_dev
在開發環境中載入的尾碼值與秘密管理員。 將 取代{KEY VAULT NAME}
為您在上一個步驟中建立的金鑰保存庫名稱: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 AD、Azure 金鑰保存庫和應用程式,以在應用程式裝載于 Azure 外部時,使用 Azure AD 應用程式識別碼和 X.509 憑證向金鑰保存庫進行驗證。 如需詳細資訊,請參閱關於金鑰、祕密和憑證。
注意
雖然 Azure 中裝載的應用程式支援使用應用程式識別碼和 X.509 憑證,但不建議這麼做。 相反地,在 Azure 中裝載應用程式時 ,請針對 Azure 資源使用受控識別 。 受控識別不需要將憑證儲存在應用程式或開發環境中。
當 頂端 Program.cs
的預處理器指示詞設定 Certificate
為 時, #define
範例應用程式會使用應用程式識別碼和 X.509 憑證。
- 建立 PKCS#12 封存 (.pfx) 憑證。 建立憑證的選項包括 Windows 和OpenSSL上的 New-SelfSignedCertificate。
- 將憑證安裝到目前使用者的個人憑證存儲中。 將金鑰標示為可匯出是選擇性的。 請注意憑證的指紋,稍後在此程式中使用。
- 將 PKCS#12 封存 (.pfx) 憑證匯出為 DER 編碼憑證, (.cer) 。
- 使用 Azure AD (應用程式註冊) 註冊應用程式。
- 將 DER 編碼憑證 (.cer) 上傳至 Azure AD:
- 在 Azure AD 中選取應用程式。
- 流覽至 [憑證 & 密碼]。
- 選取 [上傳憑證 ] 以上傳包含公開金鑰的憑證。 可接受 .cer、.pem或.crt憑證。
- 將金鑰保存庫名稱、應用程式識別碼和憑證指紋儲存在應用程式的
appsettings.json
檔案中。 - 流覽至Azure 入口網站中的金鑰保存庫。
- 使用Azure 金鑰保存庫 在生產環境中選取您在 [秘密儲存體] 中建立的金鑰保存庫金鑰保存庫。
- 選取 [存取原則]。
- 選取 [新增存取原則]。
- 開啟 [秘密] 許可權 ,並提供具有 [取得 ] 和 [ 清單 ] 許可權的應用程式。
- 選取 [選取主體 ],然後依名稱選取已註冊的應用程式。 選取 [選取] 按鈕。
- 選取 [確定]。
- 選取 [儲存]。
- 部署應用程式。
範例 Certificate
應用程式會從 IConfigurationRoot 取得其組態值,其名稱與秘密名稱相同:
- 非階層式值:使用
SecretName
取得config["SecretName"]
的值。 - 階層式值 (區段) :使用
:
(冒號) 標記法或 GetSection 方法。 使用下列其中一種方法來取得組態值:config["Section:SecretName"]
config.GetSection("Section")["SecretName"]
X.509 憑證是由 OS 管理。 應用程式會呼叫 AddAzureKeyVault 檔案所提供的 appsettings.json
值:
// 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>());
範例值:
- 金鑰保存庫名稱:
contosovault
- 應用程式識別碼:
627e911e-43cc-61d4-992e-12db9c81b413
- 憑證指紋:
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 資源的受控識別。 受控識別可讓應用程式使用 Azure AD 驗證向 Azure 金鑰保存庫進行驗證,而不需認證 (應用程式識別碼和密碼/用戶端密碼) 儲存在應用程式中。
當 頂端 Program.cs
的預處理器指示詞設定 Managed
為 時, #define
範例應用程式會使用 Azure 資源的受控識別。
在應用程式的 appsettings.json
檔案中輸入保存庫名稱。 當設定為 Managed
版本時,範例應用程式不需要應用程式識別碼和密碼 (用戶端密碼) ,因此您可以忽略這些設定專案。 應用程式會部署至 Azure,而 Azure 只會使用儲存在檔案中的 appsettings.json
保存庫名稱來驗證應用程式以存取 Azure 金鑰保存庫。
將範例應用程式部署至 Azure App 服務。
部署至Azure App 服務的應用程式會在建立服務時自動向 Azure AD 註冊。 從部署取得物件識別碼,以用於下列命令。 物件識別碼會顯示在App Service面板的Azure 入口網站 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>());
金鑰保存庫名稱範例值: contosovault
appsettings.json
:
{
"KeyVaultName": "Key Vault Name"
}
當您執行應用程式時,網頁會顯示載入的秘密值。 在開發環境中,秘密值具有 _dev
尾碼,因為它們是由秘密管理員提供。 在生產環境中,值會以 _prod
尾碼載入,因為它們是由 Azure 金鑰保存庫所提供。
如果您收到 Access denied
錯誤,請確認應用程式已向 Azure AD 註冊,並提供金鑰保存庫的存取權。 確認您已在 Azure 中重新開機服務。
如需搭配受控識別和 Azure Pipelines 使用提供者的相關資訊,請參閱使用受控服務識別建立與 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 載,可讓您控制金鑰保存庫秘密轉換成設定金鑰的方式。 例如,您可以實作 介面,根據您在應用程式啟動時提供的前置詞值載入秘密值。 例如,這項技術可讓您根據應用程式的版本載入秘密。
警告
請勿在金鑰保存庫秘密上使用前置詞來:
- 將多個應用程式的秘密放在相同的保存庫中。
- 例如,將環境秘密 (, 開發 與 生產 秘密) 放在相同的保存庫中。
不同的應用程式和開發/生產環境應該使用不同的金鑰保存庫來隔離應用程式環境,以提供最高層級的安全性。
在下列範例中,金鑰保存庫中會建立秘密 (,並在金鑰保存庫秘密名稱中 (期間) 使用秘密管理員 5000-AppSecret
) 。 此秘密代表應用程式 5.0.0.0 版的應用程式密碼。 針對另一個版本的應用程式,5.1.0.0 會將秘密新增至金鑰保存庫 (並使用秘密管理員) 。 5100-AppSecret
每個應用程式版本都會將其已建立版本的秘密值載入其設定中, AppSecret
並移除載入秘密時的版本。
AddAzureKeyVault
使用自訂 KeyVaultSecretManager
實作呼叫:
config.AddAzureKeyVault(
$"https://{builtConfig["KeyVaultName"]}.vault.azure.net/",
builtConfig["AzureADApplicationId"],
certs.OfType<X509Certificate2>().Single(),
new PrefixKeyVaultSecretManager(versionPrefix));
實作會回應秘密的版本前置詞,以將適當的秘密載入組態:
Load
當秘密的名稱以前置詞開頭時,會載入秘密。 不會載入其他秘密。GetKey
:- 從秘密名稱中移除前置詞。
- 以 取代任何名稱
KeyDelimiter
中的兩個虛線,這是組態中使用的分隔符號 (通常是冒號) 。 Azure 金鑰保存庫不允許秘密名稱中的冒號。
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
使用 方法來傳回秘密名稱的組態名稱。 它會從秘密的名稱中移除版本前置詞。 會傳回其餘的秘密名稱,以載入應用程式的組態名稱/值組。
實作此方法時:
應用程式專案檔中指定的應用程式版本。 在下列範例中,應用程式的版本會設定為
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 金鑰保存庫:
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"
執行應用程式時,會載入金鑰保存庫秘密。 的
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 金鑰保存庫金鑰無法使用冒號作為分隔符號。 本文所述的方法會使用雙虛線 (--
) 做為階層式值的分隔符號, (區段) 。 陣列索引鍵會儲存在 Azure 金鑰保存庫中,並以雙虛線和數值索引鍵區段 (--0--
、 --1--
... --{n}--
) 。
檢查 ON 檔案所提供的 JS 下列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=="
}
}
]
}
上述 JS ON 檔案中顯示的設定會使用雙虛線 () --
標記法和數值區段,儲存在 Azure 金鑰保存庫:
Key | 值 |
---|---|
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();
已停用和過期的秘密
停用和過期的秘密會擲回 RequestFailedException 。 若要防止應用程式擲回,請使用不同的組態提供者提供設定,或更新已停用或過期的秘密。
疑難排解
當應用程式無法使用提供者載入設定時,會將錯誤訊息寫入ASP.NET Core記錄基礎結構。 下列條件會防止組態載入:
- Azure AD 中未正確設定應用程式或憑證。
- 金鑰保存庫不存在於 Azure 金鑰保存庫中。
- 應用程式未獲授權存取金鑰保存庫。
- 存取原則不包含
Get
和List
許可權。 - 在金鑰保存庫中,組態資料 (名稱/值組) 名稱不正確、遺失、停用或過期。
- 應用程式 (金鑰保存庫名稱 (
KeyVaultName
) 、Azure AD 應用程式識別碼 (AzureADApplicationId
) 或 Azure AD 憑證指紋AzureADCertThumbprint
() ,或 Azure AD 目錄識別碼 (AzureADDirectoryId
) 。 - 在應用程式中,組態索引鍵 (名稱) 不正確,表示您嘗試載入的值。
- 新增應用程式的金鑰保存庫存取原則時,已建立原則,但 [存取原則] UI 中未選取 [儲存] 按鈕。