Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new module for OrchardCore.KeyVault.Azure #6422

Merged
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions OrchardCore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Shortcodes.Abst
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.Settings.Core", "src\OrchardCore\OrchardCore.Settings.Core\OrchardCore.Settings.Core.csproj", "{1286E568-BAFE-45E2-BF60-4350DB99FF83}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OrchardCore.KeyVault.Azure", "src\OrchardCore\OrchardCore.KeyVault.Azure\OrchardCore.KeyVault.Azure.csproj", "{13A89FAE-EDEA-4ED1-8C8E-13AB78F1FD03}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OrchardCore.ContentPreview.Abstractions", "src\OrchardCore\OrchardCore.ContentPreview.Abstractions\OrchardCore.ContentPreview.Abstractions.csproj", "{57EA05E8-0275-4163-8FD1-2B244E396F91}"
EndProject
Global
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ nav:
- GraphQL queries: docs/reference/core/Apis.GraphQL.Abstractions/README.md
- Health Check: docs/reference/modules/HealthChecks/README.md
- HTTPS: docs/reference/modules/Https/README.md
- Key Vault (Azure): docs/reference/core/KeyVault.Azure/README.md
- Localization: docs/reference/modules/Localize/README.md
- Logging Serilog: docs/reference/core/Logging.Serilog/readme.md
- Mini Profiler: docs/reference/modules/MiniProfiler/README.md
Expand Down
1 change: 1 addition & 0 deletions src/OrchardCore.Build/Dependencies.AspNetCore.props
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<PackageManagement Include="Microsoft.AspNetCore.Mvc.Testing" Version="$(AspNetCoreVersion)" />
<PackageManagement Include="Microsoft.AspNetCore.Owin" Version="$(AspNetCoreVersion)" />
<PackageManagement Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="$(AspNetCoreVersion)" />
<PackageManagement Include="Microsoft.Extensions.Configuration.AzureKeyVault" Version="$(AspNetCoreVersion)" />
<PackageManagement Include="Microsoft.Extensions.Http.Polly" Version="$(AspNetCoreVersion)" />
</ItemGroup>
</Project>
7 changes: 7 additions & 0 deletions src/OrchardCore.Cms.Web/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@
// "BlobName": "", // Optional, defaults to Sites/tenant_name/DataProtectionKeys.xml. Templatable, refer docs.
// "CreateContainer": true // Creates the container during app startup if it does not already exist.
//},
// Uncomment to load app configuration values from Azure Key Vault.
// Add '.AddOrchardCoreAzureKeyVault()' to the Generic Host in CreateHostBuilder()
//"OrchardCore_Azure_KeyVault": {
// "KeyVaultName": "", // Set the name of your Azure Key Vault.
// "AzureADApplicationId": "", // Set the Azure AD Application Id
// "AzureADApplicationSecret": "" //The Azure AD Application Secret should never be checked into source control. Instead use an environment variable.
// }
// Uncomment to configure content localization.
//"OrchardCore_ContentLocalization_CulturePicker": {
// "CookieLifeTime": 14 // Set the culture picker cookie life time (in days).
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using OrchardCore.Azure.KeyVault.Services;

namespace OrchardCore.Azure.KeyVault.Extensions
{
public static class AzureKeyVaultWebHostBuilderExtension
{
/// <summary>
/// Adds Azure Key Vault as a Configuration Source.
/// </summary>
/// <param name="builder">The web host builder to configure.</param>
/// <returns>The web host builder.</returns>
public static IHostBuilder AddOrchardCoreAzureKeyVault(this IHostBuilder builder)
{
if (builder == null) throw new ArgumentNullException(nameof(builder));

builder.ConfigureAppConfiguration((context, configuration) =>
{
var builtConfig = configuration.Build();
var keyVaultName = builtConfig["OrchardCore:OrchardCore_Azure_KeyVault:KeyVaultName"];
var clientId = builtConfig["OrchardCore:OrchardCore_Azure_KeyVault:AzureADApplicationId"];
var clientSecret = builtConfig["OrchardCore:OrchardCore_Azure_KeyVault:AzureADApplicationSecret"];

var keyVaultEndpoint = "https://" + keyVaultName + ".vault.azure.net";
configuration.AddAzureKeyVault(
keyVaultEndpoint,
clientId,
clientSecret,
new AzureKeyVaultSecretManager()
);
});

return builder;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>$(AspNetCoreTargetFramework)</TargetFramework>
</PropertyGroup>

<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\OrchardCore.Abstractions\OrchardCore.Abstractions.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.AzureKeyVault" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using Microsoft.Azure.KeyVault.Models;
using Microsoft.Extensions.Configuration.AzureKeyVault;

namespace OrchardCore.Azure.KeyVault.Services
{
public class AzureKeyVaultSecretManager : DefaultKeyVaultSecretManager
{
public override string GetKey(SecretBundle secret) =>
secret.SecretIdentifier.Name.Replace("---", "_").Replace("--", ":");
}
}
41 changes: 41 additions & 0 deletions src/docs/reference/core/KeyVault.Azure/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Azure Key Vault (`OrchardCore.Azure.KeyVault`)

The Azure Key Vault configuration provider adds app configuration values from the Azure Key Vault in order to safeguard your cryptographic keys and secrets used by your app. It also contains custom override of the DefaultKeyVaultManager class that retrieves secrets from Azure Key Vault and translates ---
to an underscore (_) and -- to a colon (:). Both underscores and colons are illegal characters in Azure KeyVault.

Example:
Key Vault Input: "OrchardCore--OrchardCore---Shells---Database--ConnectionString".
Output: "OrchardCore:OrchardCore_Shells_Database:ConnectionString".
See https://github.com/OrchardCMS/OrchardCore/issues/6359.

## Configuration
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have a small example that uses directly AzureKeyVaultSecretManager such that standard ASP.NET tutorials can be followrd?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean an example of IConfiguration? I have been using it to retrieve my media and database connection strings. For example in DatabaseShellConfigurationSources.cs:
image

Using the following Keys:
OrchardCore--OrchardCore---Shells---Database--ConnectionString
OrchardCore--OrchardCore---Shells---Database--DatabaseProvider (Not actually a secret, but required to get the section)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What Seb was asking for is something that matches the getting started guides from the ASP.NET docs here https://docs.microsoft.com/en-us/aspnet/core/security/key-vault-configuration?view=aspnetcore-3.1

Where they are using the AzureServiceTokenProvider callbacks and such.

Which makes me also wonder if we should not be using the callback technique, rather than having our own configuration secrets for it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@deanmarcussen

Here @JoshLefebvre just override the DefaultKeyVaultSecretManager with a new AzureKeyVaultSecretManager and provide an AddOrchardCoreAzureKeyVault() extension that uses it and other config values.

But people can just follow the aspnet documentation and then use new AzureKeyVaultSecretManager() in place of new DefaultKeyVaultSecretManager()

So maybe just remove the AddOrchardCoreAzureKeyVault() that is just an example on how to use the new AzureKeyVaultSecretManager and let people follow the doc and do their own extension if they want.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do I understand your implementation right that you have added replacement of --- to _ along with default -- to : behavior?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xperiandri Exactly, this is what @JoshLefebvre did ;)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that something that is common? Either in OC or ASP.NET with Keyvault?

You will need to specify the name of your Azure Key Vault and [register a service principal](https://docs.microsoft.com/en-us/azure/key-vault/general/group-permissions-for-apps) in Active Directory for accessing your key vault using an access control policy.

```json
"OrchardCore_Azure_KeyVault": {
"KeyVaultName": "", // Set the name of your Azure Key Vault.
"AzureADApplicationId": "", // Set the Azure AD Application Id
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that required when using managed identity in Azure? I believe we can also associated an Azure Web App for instance to be allowed to access a KV account without anything to setup.

Copy link
Contributor

@mariojsnunes mariojsnunes Nov 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not required. We can use the AzureServiceTokenProvider. I got it working on my OrchardCore project.
Some notes:
I was only able to get in working in a .net standard project (which is fine).
I think there is a new API for .net core, but I couldn't get it to work at the time (6 months ago)

"AzureADApplicationSecret": "" //Set the Azure AD Application Secret
}
```

!!! note
You should **never check in your client secret into source control** as this defeats the purpose of using a key vault in the first place. Instead, set your client secret as an environment variable on your machine, create a separate azurekeyvault.json file and add it to your `.gitignore`, or use user secrets.

In the `Program.cs`, add `AddOrchardCoreAzureKeyVault()` to the Generic Host in `CreateHostBuilder()`.

```csharp
using OrchardCore.KeyVault.Azure;
public class Program
{
public static Task Main(string[] args)
=> BuildHost(args).RunAsync();

public static IHost BuildHost(string[] args) =>
Host.CreateDefaultBuilder(args)
.AddOrchardCoreAzureKeyVault()
.ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>())
.Build();
}
```