Updated KeyVaultConfiguration (#20396)

* Updated KeyVaultConfiguration

* Updated highlighted code

* Remove old API reference Documentation about AddAzureKeyVault()

* Updated pacakage, without extra removals.

* Add back 'Use a key name prefix' part, replace IKeyVaultSecretManager with KeyVaultSecretManager

* Update PrefixKeyVaultSecretManager

* Correct the name space.

Co-authored-by: Frank Li (Wicresoft North America Ltd) <v-cheli@microsoft.com>
pull/20706/head
Frank(MSFT) 2020-11-21 06:21:26 +08:00 committed by GitHub
parent aba7aa4130
commit e8ee5b0b96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 44 additions and 48 deletions

View File

@ -15,7 +15,7 @@ By [Andrew Stanton-Nurse](https://github.com/anurse)
::: moniker range=">= aspnetcore-3.0"
This document explains how to use the [Microsoft Azure Key Vault](https://azure.microsoft.com/services/key-vault/) Configuration Provider to load app configuration values from Azure Key Vault secrets. Azure Key Vault is a cloud-based service that assists in safeguarding cryptographic keys and secrets used by apps and services. Common scenarios for using Azure Key Vault with ASP.NET Core apps include:
This document explains how to use the [Azure Key Vault](https://azure.microsoft.com/services/key-vault/) Configuration Provider to load app configuration values from Azure Key Vault secrets. Azure Key Vault is a cloud-based service that assists in safeguarding cryptographic keys and secrets used by apps and services. Common scenarios for using Azure Key Vault with ASP.NET Core apps include:
* Controlling access to sensitive configuration data.
* Meeting the requirement for FIPS 140-2 Level 2 validated Hardware Security Modules (HSM's) when storing configuration data.
@ -24,7 +24,7 @@ This document explains how to use the [Microsoft Azure Key Vault](https://azure.
## Packages
Add a package reference to the [Microsoft.Extensions.Configuration.AzureKeyVault](https://www.nuget.org/packages/Microsoft.Extensions.Configuration.AzureKeyVault/) package.
Add a package reference to the [Azure.Extensions.AspNetCore.Configuration.Secrets](https://www.nuget.org/packages/Azure.Extensions.AspNetCore.Configuration.Secrets/) package.
## Sample app
@ -136,9 +136,9 @@ The `Certificate` sample app obtains its configuration values from `IConfigurati
* `config["Section:SecretName"]`
* `config.GetSection("Section")["SecretName"]`
The X.509 certificate is managed by the OS. The app calls <xref:Microsoft.Extensions.Configuration.AzureKeyVaultConfigurationExtensions.AddAzureKeyVault*> with values supplied by the *appsettings.json* file:
The X.509 certificate is managed by the OS. The app calls **AddAzureKeyVault** with values supplied by the *appsettings.json* file:
[!code-csharp[](key-vault-configuration/samples/3.x/SampleApp/Program.cs?name=snippet1&highlight=20-23)]
[!code-csharp[](key-vault-configuration/samples/3.x/SampleApp/Program.cs?name=snippet1&highlight=46-49)]
Example values:
@ -174,14 +174,14 @@ az keyvault set-policy --name {KEY VAULT NAME} --object-id {OBJECT ID} --secret-
The sample app:
* Creates an instance of the `AzureServiceTokenProvider` class without a connection string. When a connection string isn't provided, the provider attempts to obtain an access token from Managed identities for Azure resources.
* A new <xref:Microsoft.Azure.KeyVault.KeyVaultClient> is created with the `AzureServiceTokenProvider` instance token callback.
* The <xref:Microsoft.Azure.KeyVault.KeyVaultClient> instance is used with a default implementation of <xref:Microsoft.Extensions.Configuration.AzureKeyVault.IKeyVaultSecretManager> that loads all secret values and replaces double-dashes (`--`) with colons (`:`) in key names.
* Creates an instance of the `DefaultAzureCredential` class, the credential attempts to obtain an access token from environment for Azure resources.
* A new <xref:Azure.Security.KeyVault.Secrets.Secrets> is created with the `DefaultAzureCredential` instance.
* The <xref:Azure.Security.KeyVault.Secrets.Secrets> instance is used with a default implementation of <xref:Azure.Extensions.Aspnetcore.Configuration.Secrets> that loads all secret values and replaces double-dashes (`--`) with colons (`:`) in key names.
[!code-csharp[](key-vault-configuration/samples/3.x/SampleApp/Program.cs?name=snippet2&highlight=13-21)]
[!code-csharp[](key-vault-configuration/samples/3.x/SampleApp/Program.cs?name=snippet2&highlight=12-14)]
Key vault name example value: `contosovault`
*appsettings.json*:
```json
@ -198,11 +198,11 @@ For information on using the provider with a managed identity and an Azure DevOp
## Configuration options
<xref:Microsoft.Extensions.Configuration.AzureKeyVaultConfigurationExtensions.AddAzureKeyVault*> can accept an <xref:Microsoft.Extensions.Configuration.AzureKeyVault.AzureKeyVaultConfigurationOptions>:
AddAzureKeyVault can accept an AzureKeyVaultConfigurationOptions:
```csharp
config.AddAzureKeyVault(
new AzureKeyVaultConfigurationOptions()
config.AddAzureKeyVault(new SecretClient(new URI("Your Key Vault Endpoint"), new DefaultAzureCredential()),
new AzureKeyVaultConfigurationOptions())
{
...
});
@ -210,25 +210,23 @@ config.AddAzureKeyVault(
| Property | Description |
| ---------------- | ----------- |
| `Client` | <xref:Microsoft.Azure.KeyVault.KeyVaultClient> to use for retrieving values. |
| `Manager` | <xref:Microsoft.Extensions.Configuration.AzureKeyVault.IKeyVaultSecretManager> instance used to control secret loading. |
| `Manager` | <xref:Azure.Extensions.Aspnetcore.Configuration.Secrets> instance used to control secret loading. |
| `ReloadInterval` | `Timespan` to wait between attempts at polling the key vault for changes. The default value is `null` (configuration isn't reloaded). |
| `Vault` | Key vault URI. |
## Use a key name prefix
<xref:Microsoft.Extensions.Configuration.AzureKeyVaultConfigurationExtensions.AddAzureKeyVault*> provides an overload that accepts an implementation of <xref:Microsoft.Extensions.Configuration.AzureKeyVault.IKeyVaultSecretManager>, which allows you to control how key vault secrets are converted into configuration keys. For example, you can implement the interface to load secret values based on a prefix value you provide at app startup. This allows you, for example, to load secrets based on the version of the app.
AddAzureKeyVault provides an overload that accepts an implementation of <xref:Azure.Extensions.Aspnetcore.Configuration.Secrets>, which allows you to control how key vault secrets are converted into configuration keys. For example, you can implement the interface to load secret values based on a prefix value you provide at app startup. This allows you, for example, to load secrets based on the version of the app.
> [!WARNING]
> Don't use prefixes on key vault secrets to place secrets for multiple apps into the same key vault or to place environmental secrets (for example, *development* versus *production* secrets) into the same vault. We recommend that different apps and development/production environments use separate key vaults to isolate app environments for the highest level of security.
In the following example, a secret is established in the key vault (and using the Secret Manager tool for the Development environment) for `5000-AppSecret` (periods aren't allowed in key vault secret names). This secret represents an app secret for version 5.0.0.0 of the app. For another version of the app, 5.1.0.0, a secret is added to the key vault (and using the Secret Manager tool) for `5100-AppSecret`. Each app version loads its versioned secret value into its configuration as `AppSecret`, stripping off the version as it loads the secret.
<xref:Microsoft.Extensions.Configuration.AzureKeyVaultConfigurationExtensions.AddAzureKeyVault*> is called with a custom <xref:Microsoft.Extensions.Configuration.AzureKeyVault.IKeyVaultSecretManager>:
AddAzureKeyVault is called with a custom <xref:Azure.Extensions.Aspnetcore.Configuration.Secrets>:
[!code-csharp[](key-vault-configuration/samples_snapshot/Program.cs)]
The <xref:Microsoft.Extensions.Configuration.AzureKeyVault.IKeyVaultSecretManager> implementation reacts to the version prefixes of secrets to load the proper secret into configuration:
The <xref:Azure.Extensions.Aspnetcore.Configuration.Secrets> implementation reacts to the version prefixes of secrets to load the proper secret into configuration:
* `Load` loads a secret when its name starts with the prefix. Other secrets aren't loaded.
* `GetKey`:
@ -278,7 +276,7 @@ When this approach is implemented:
1. If the app's version is changed in the project file to `5.1.0.0` and the app is run again, the secret value returned is `5.1.0.0_secret_value_dev` in the Development environment and `5.1.0.0_secret_value_prod` in Production.
> [!NOTE]
> You can also provide your own <xref:Microsoft.Azure.KeyVault.KeyVaultClient> implementation to <xref:Microsoft.Extensions.Configuration.AzureKeyVaultConfigurationExtensions.AddAzureKeyVault*>. A custom client permits sharing a single instance of the client across the app.
> You can also provide your own <xref:Azure.Security.KeyVault.Secrets.SecretClient> implementation to AddAzureKeyVault. A custom client permits sharing a single instance of the client across the app.
## Bind an array to a class
@ -332,7 +330,7 @@ Configuration.Reload();
## Disabled and expired secrets
Disabled and expired secrets throw a <xref:Microsoft.Azure.KeyVault.Models.KeyVaultErrorException>. To prevent the app from throwing, provide the configuration using a different configuration provider or update the disabled or expired secret.
Disabled and expired secrets throw a <xref:Azure.RequestFailedException>. To prevent the app from throwing, provide the configuration using a different configuration provider or update the disabled or expired secret.
## Troubleshoot
@ -343,7 +341,7 @@ When the app fails to load configuration using the provider, an error message is
* The app isn't authorized to access the key vault.
* The access policy doesn't include `Get` and `List` permissions.
* In the key vault, the configuration data (name-value pair) is incorrectly named, missing, disabled, or expired.
* The app has the wrong key vault name (`KeyVaultName`), Azure AD Application Id (`AzureADApplicationId`), or Azure AD certificate thumbprint (`AzureADCertThumbprint`).
* The app has the wrong key vault name (`KeyVaultName`), Azure AD Application Id (`AzureADApplicationId`), or Azure AD certificate thumbprint (`AzureADCertThumbprint`), or Azure AD DirectoryId (`AzureADDirectoryId`).
* The configuration key (name) is incorrect in the app for the value you're trying to load.
* When adding the access policy for the app to the key vault, the policy was created, but the **Save** button wasn't selected in the **Access policies** UI.
@ -685,4 +683,3 @@ When the app fails to load configuration using the provider, an error message is
* [Tutorial: How to use Azure Key Vault with Azure Windows Virtual Machine in .NET](/azure/key-vault/tutorial-net-windows-virtual-machine)
::: moniker-end

View File

@ -3,13 +3,14 @@
// For details, see the Azure Key Vault Configuration Provider topic:
// https://docs.microsoft.com/aspnet/core/security/key-vault-configuration
using Azure.Extensions.AspNetCore.Configuration.Secrets;
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
using System;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Azure.KeyVault;
using Microsoft.Azure.Services.AppAuthentication;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.AzureKeyVault;
using Microsoft.Extensions.Hosting;
namespace SampleApp
@ -25,7 +26,8 @@ namespace SampleApp
#region snippet1
// using System.Linq;
// using System.Security.Cryptography.X509Certificates;
// using Microsoft.Extensions.Configuration;
// using Azure.Extensions.AspNetCore.Configuration.Secrets;
// using Azure.Identity;
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
@ -42,10 +44,10 @@ namespace SampleApp
.Find(X509FindType.FindByThumbprint,
builtConfig["AzureADCertThumbprint"], false);
config.AddAzureKeyVault(
$"https://{builtConfig["KeyVaultName"]}.vault.azure.net/",
builtConfig["AzureADApplicationId"],
certs.OfType<X509Certificate2>().Single());
config.AddAzureKeyVault(new Uri($"https://{builtConfig["KeyVaultName"]}.vault.azure.net/"),
new ClientCertificateCredential(builtConfig["AzureADDirectoryId"], builtConfig["AzureADApplicationId"], certs.OfType<X509Certificate2>().Single()),
new KeyVaultSecretManager());
store.Close();
}
@ -60,9 +62,9 @@ namespace SampleApp
#if Managed
#region snippet2
// using Microsoft.Azure.KeyVault;
// using Microsoft.Azure.Services.AppAuthentication;
// using Microsoft.Extensions.Configuration.AzureKeyVault;
// using Azure.Security.KeyVault.Secrets;
// using Azure.Identity;
// using Azure.Extensions.AspNetCore.Configuration.Secrets;
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
@ -71,16 +73,11 @@ namespace SampleApp
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());
var azureServiceTokenProvider = new AzureServiceTokenProvider();
var keyVaultClient = new KeyVaultClient(
new KeyVaultClient.AuthenticationCallback(
azureServiceTokenProvider.KeyVaultTokenCallback));
config.AddAzureKeyVault(
$"https://{builtConfig["KeyVaultName"]}.vault.azure.net/",
keyVaultClient,
new DefaultKeyVaultSecretManager());
}
})
.ConfigureWebHostDefaults(webBuilder =>

View File

@ -7,7 +7,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.AzureKeyVault" Version="3.1.0" />
<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.0.1" />
<PackageReference Include="Azure.Identity" Version="1.2.3" />
</ItemGroup>
</Project>

View File

@ -1,5 +1,6 @@
{
"KeyVaultName": "Key Vault Name",
"AzureADApplicationId": "Azure AD Application ID",
"AzureADCertThumbprint": "Azure AD Certificate Thumbprint"
"AzureADCertThumbprint": "Azure AD Certificate Thumbprint",
"AzureADDirectoryId": "Azure AD Directory ID"
}

View File

@ -1,4 +1,4 @@
public class PrefixKeyVaultSecretManager : IKeyVaultSecretManager
public class PrefixKeyVaultSecretManager : KeyVaultSecretManager
{
private readonly string _prefix;
@ -7,14 +7,14 @@ public class PrefixKeyVaultSecretManager : IKeyVaultSecretManager
_prefix = $"{prefix}-";
}
public bool Load(SecretItem secret)
public override bool Load(SecretProperties secret)
{
return secret.Identifier.Name.StartsWith(_prefix);
return secret.Name.StartsWith(_prefix);
}
public string GetKey(SecretBundle secret)
public override string GetKey(KeyVaultSecret secret)
{
return secret.SecretIdentifier.Name
return secret.Name
.Substring(_prefix.Length)
.Replace("--", ConfigurationPath.KeyDelimiter);
}