From 61ad43d4672de605ef1e7f7a493695cf1efdffb4 Mon Sep 17 00:00:00 2001 From: janskoruba Date: Sat, 26 Sep 2020 15:09:39 +0200 Subject: [PATCH 1/5] Add loading certificates from Azure Key Vault --- .../IdentityServerBuilderExtensions.cs | 19 ++++ .../appsettings.json | 19 +++- .../Common/AzureKeyVaultConfiguration.cs | 19 ++++ .../Common}/CertificateConfiguration.cs | 6 +- .../Helpers/AzureKeyVaultHelpers.cs | 27 +++++ .../Services/AzureKeyVaultService.cs | 105 ++++++++++++++++++ .../Skoruba.IdentityServer4.Shared.csproj | 2 + 7 files changed, 192 insertions(+), 5 deletions(-) create mode 100644 src/Skoruba.IdentityServer4.Shared/Configuration/Common/AzureKeyVaultConfiguration.cs rename src/{Skoruba.IdentityServer4.STS.Identity/Configuration => Skoruba.IdentityServer4.Shared/Configuration/Common}/CertificateConfiguration.cs (81%) create mode 100644 src/Skoruba.IdentityServer4.Shared/Helpers/AzureKeyVaultHelpers.cs create mode 100644 src/Skoruba.IdentityServer4.Shared/Services/AzureKeyVaultService.cs diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Helpers/IdentityServerBuilderExtensions.cs b/src/Skoruba.IdentityServer4.STS.Identity/Helpers/IdentityServerBuilderExtensions.cs index 2b9e68668..e3559b92f 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Helpers/IdentityServerBuilderExtensions.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/Helpers/IdentityServerBuilderExtensions.cs @@ -5,6 +5,8 @@ using System.Security.Cryptography.X509Certificates; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; +using Skoruba.IdentityServer4.Shared.Configuration.Common; +using Skoruba.IdentityServer4.Shared.Helpers; using Skoruba.IdentityServer4.STS.Identity.Configuration; namespace Skoruba.IdentityServer4.STS.Identity.Helpers @@ -27,6 +29,7 @@ public static class IdentityServerBuilderExtensions public static IIdentityServerBuilder AddCustomSigningCredential(this IIdentityServerBuilder builder, IConfiguration configuration) { var certificateConfiguration = configuration.GetSection(nameof(CertificateConfiguration)).Get(); + var azureKeyVaultConfiguration = configuration.GetSection(nameof(AzureKeyVaultConfiguration)).Get(); if (certificateConfiguration.UseSigningCertificateThumbprint) { @@ -66,6 +69,12 @@ public static IIdentityServerBuilder AddCustomSigningCredential(this IIdentitySe builder.AddSigningCredential(certificate); } + else if (certificateConfiguration.UseSigningCertificateForAzureKeyVault) + { + var x509Certificate2Certs = AzureKeyVaultHelpers.GetCertificates(azureKeyVaultConfiguration).GetAwaiter().GetResult(); + + builder.AddSigningCredential(x509Certificate2Certs.ActiveCertificate); + } else if (certificateConfiguration.UseSigningCertificatePfxFile) { if (string.IsNullOrWhiteSpace(certificateConfiguration.SigningCertificatePfxFilePath)) @@ -112,6 +121,7 @@ public static IIdentityServerBuilder AddCustomSigningCredential(this IIdentitySe public static IIdentityServerBuilder AddCustomValidationKey(this IIdentityServerBuilder builder, IConfiguration configuration) { var certificateConfiguration = configuration.GetSection(nameof(CertificateConfiguration)).Get(); + var azureKeyVaultConfiguration = configuration.GetSection(nameof(AzureKeyVaultConfiguration)).Get(); if (certificateConfiguration.UseValidationCertificateThumbprint) { @@ -134,6 +144,15 @@ public static IIdentityServerBuilder AddCustomValidationKey(this IIdentityServer builder.AddValidationKey(certificate); } + else if (certificateConfiguration.UseValidationCertificateForAzureKeyVault) + { + var x509Certificate2Certs = AzureKeyVaultHelpers.GetCertificates(azureKeyVaultConfiguration).GetAwaiter().GetResult(); + + if (x509Certificate2Certs.SecondaryCertificate != null) + { + builder.AddValidationKey(x509Certificate2Certs.SecondaryCertificate); + } + } else if (certificateConfiguration.UseValidationCertificatePfxFile) { if (string.IsNullOrWhiteSpace(certificateConfiguration.ValidationCertificatePfxFilePath)) diff --git a/src/Skoruba.IdentityServer4.STS.Identity/appsettings.json b/src/Skoruba.IdentityServer4.STS.Identity/appsettings.json index c156f7bcb..b6b7eaddb 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/appsettings.json +++ b/src/Skoruba.IdentityServer4.STS.Identity/appsettings.json @@ -27,7 +27,18 @@ "ValidationCertificatePfxFilePassword": "", "UseValidationCertificateThumbprint": false, - "ValidationCertificateThumbprint": "" + "ValidationCertificateThumbprint": "", + + "UseSigningCertificateForAzureKeyVault": false, + "UseValidationCertificateForAzureKeyVault": false + }, + "AzureKeyVaultConfiguration": { + "AzureKeyVaultEndpoint": "", + "ClientId": "", + "ClientSecret": "", + "UseClientCredentials": true, + "IdentityServerCertificateName": "", + "DataProtectionKeyIdentifier": "" }, "RegisterConfiguration": { "Enabled": true @@ -67,13 +78,13 @@ "BasePath": "", "IdentityOptions": { "Password": { - "RequiredLength": 8 + "RequiredLength": 8 }, "User": { - "RequireUniqueEmail": true + "RequireUniqueEmail": true }, "SignIn": { - "RequireConfirmedAccount": false + "RequireConfirmedAccount": false } } } \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.Shared/Configuration/Common/AzureKeyVaultConfiguration.cs b/src/Skoruba.IdentityServer4.Shared/Configuration/Common/AzureKeyVaultConfiguration.cs new file mode 100644 index 000000000..1517faa05 --- /dev/null +++ b/src/Skoruba.IdentityServer4.Shared/Configuration/Common/AzureKeyVaultConfiguration.cs @@ -0,0 +1,19 @@ +using System.Diagnostics; + +namespace Skoruba.IdentityServer4.Shared.Configuration.Common +{ + public class AzureKeyVaultConfiguration + { + public string AzureKeyVaultEndpoint { get; set; } + + public string ClientId { get; set; } + + public string ClientSecret { get; set; } + + public bool UseClientCredentials { get; set; } + + public string IdentityServerCertificateName { get; set; } + + public string DataProtectionKeyIdentifier { get; set; } + } +} \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Configuration/CertificateConfiguration.cs b/src/Skoruba.IdentityServer4.Shared/Configuration/Common/CertificateConfiguration.cs similarity index 81% rename from src/Skoruba.IdentityServer4.STS.Identity/Configuration/CertificateConfiguration.cs rename to src/Skoruba.IdentityServer4.Shared/Configuration/Common/CertificateConfiguration.cs index 99e8f00c1..cf9783023 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Configuration/CertificateConfiguration.cs +++ b/src/Skoruba.IdentityServer4.Shared/Configuration/Common/CertificateConfiguration.cs @@ -1,4 +1,4 @@ -namespace Skoruba.IdentityServer4.STS.Identity.Configuration +namespace Skoruba.IdentityServer4.Shared.Configuration.Common { public class CertificateConfiguration { @@ -26,5 +26,9 @@ public class CertificateConfiguration public string ValidationCertificatePfxFilePath { get; set; } public string ValidationCertificatePfxFilePassword { get; set; } + + public bool UseSigningCertificateForAzureKeyVault { get; set; } + + public bool UseValidationCertificateForAzureKeyVault { get; set; } } } diff --git a/src/Skoruba.IdentityServer4.Shared/Helpers/AzureKeyVaultHelpers.cs b/src/Skoruba.IdentityServer4.Shared/Helpers/AzureKeyVaultHelpers.cs new file mode 100644 index 000000000..0b54103c3 --- /dev/null +++ b/src/Skoruba.IdentityServer4.Shared/Helpers/AzureKeyVaultHelpers.cs @@ -0,0 +1,27 @@ +// Original file comes from: https://github.com/damienbod/IdentityServer4AspNetCoreIdentityTemplate +// Modified by Jan Škoruba + +using System.Security.Cryptography.X509Certificates; +using System.Threading.Tasks; +using Skoruba.IdentityServer4.Shared.Configuration.Common; +using Skoruba.IdentityServer4.Shared.Services; + +namespace Skoruba.IdentityServer4.Shared.Helpers +{ + public class AzureKeyVaultHelpers + { + public static async Task<(X509Certificate2 ActiveCertificate, X509Certificate2 SecondaryCertificate)> GetCertificates(AzureKeyVaultConfiguration certificateConfiguration) + { + (X509Certificate2 ActiveCertificate, X509Certificate2 SecondaryCertificate) certs = (null, null); + + if (!string.IsNullOrEmpty(certificateConfiguration.AzureKeyVaultEndpoint)) + { + var keyVaultCertificateService = new AzureKeyVaultService(certificateConfiguration); + + certs = await keyVaultCertificateService.GetCertificatesFromKeyVault().ConfigureAwait(false); + } + + return certs; + } + } +} \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.Shared/Services/AzureKeyVaultService.cs b/src/Skoruba.IdentityServer4.Shared/Services/AzureKeyVaultService.cs new file mode 100644 index 000000000..8115e36ea --- /dev/null +++ b/src/Skoruba.IdentityServer4.Shared/Services/AzureKeyVaultService.cs @@ -0,0 +1,105 @@ +// Original file comes from: https://github.com/damienbod/IdentityServer4AspNetCoreIdentityTemplate +// Modified by Jan Škoruba + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography.X509Certificates; +using System.Threading.Tasks; +using Microsoft.Azure.KeyVault; +using Microsoft.Azure.KeyVault.Models; +using Microsoft.Azure.Services.AppAuthentication; +using Microsoft.IdentityModel.Clients.ActiveDirectory; +using Skoruba.IdentityServer4.Shared.Configuration.Common; + +namespace Skoruba.IdentityServer4.Shared.Services +{ + public class AzureKeyVaultService + { + private readonly AzureKeyVaultConfiguration _azureKeyVaultConfiguration; + + public AzureKeyVaultService(AzureKeyVaultConfiguration azureKeyVaultConfiguration) + { + if (azureKeyVaultConfiguration == null) + { + throw new ArgumentException("missing azureKeyVaultConfiguration"); + } + + if (string.IsNullOrEmpty(azureKeyVaultConfiguration.AzureKeyVaultEndpoint)) + { + throw new ArgumentException("missing keyVaultEndpoint"); + } + + _azureKeyVaultConfiguration = azureKeyVaultConfiguration; + } + + public async Task<(X509Certificate2 ActiveCertificate, X509Certificate2 SecondaryCertificate)> GetCertificatesFromKeyVault() + { + (X509Certificate2 ActiveCertificate, X509Certificate2 SecondaryCertificate) certs = (null, null); + + var keyVaultClient = BuildKeyVaultClient(); + + var certificateItems = await GetAllEnabledCertificateVersionsAsync(keyVaultClient); + var item = certificateItems.FirstOrDefault(); + if (item != null) + { + certs.ActiveCertificate = await GetCertificateAsync(item.Identifier.Identifier, keyVaultClient); + } + + if (certificateItems.Count > 1) + { + certs.SecondaryCertificate = await GetCertificateAsync(certificateItems[1].Identifier.Identifier, keyVaultClient); + } + + return certs; + } + + /// + /// Build KeyVaultClient according to authentication method + /// + /// + public IKeyVaultClient BuildKeyVaultClient() + { + IKeyVaultClient keyVaultClient; + + if (_azureKeyVaultConfiguration.UseClientCredentials) + { + keyVaultClient = new KeyVaultClient(async (authority, resource, scope) => + { + var adCredential = new ClientCredential(_azureKeyVaultConfiguration.ClientId, _azureKeyVaultConfiguration.ClientSecret); + var authenticationContext = new AuthenticationContext(authority, null); + return (await authenticationContext.AcquireTokenAsync(resource, adCredential)).AccessToken; + }); + } + else + { + var azureServiceTokenProvider = new AzureServiceTokenProvider(); + keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback)); + } + + return keyVaultClient; + } + + private async Task> GetAllEnabledCertificateVersionsAsync(IKeyVaultClient keyVaultClient) + { + // Get all the certificate versions (this will also get the current active version) + var certificateVersions = await keyVaultClient.GetCertificateVersionsAsync(_azureKeyVaultConfiguration.AzureKeyVaultEndpoint, _azureKeyVaultConfiguration.IdentityServerCertificateName); + + // Find all enabled versions of the certificate and sort them by creation date in descending order + return certificateVersions + .Where(certVersion => certVersion.Attributes.Enabled.HasValue && certVersion.Attributes.Enabled.Value) + .OrderByDescending(certVersion => certVersion.Attributes.Created) + .ToList(); + } + + private async Task GetCertificateAsync(string identifier, IKeyVaultClient keyVaultClient) + { + var certificateVersionBundle = await keyVaultClient.GetCertificateAsync(identifier); + var certificatePrivateKeySecretBundle = await keyVaultClient.GetSecretAsync(certificateVersionBundle.SecretIdentifier.Identifier); + var privateKeyBytes = Convert.FromBase64String(certificatePrivateKeySecretBundle.Value); + var certificateWithPrivateKey = new X509Certificate2(privateKeyBytes, (string)null, X509KeyStorageFlags.MachineKeySet); + + return certificateWithPrivateKey; + } + } +} diff --git a/src/Skoruba.IdentityServer4.Shared/Skoruba.IdentityServer4.Shared.csproj b/src/Skoruba.IdentityServer4.Shared/Skoruba.IdentityServer4.Shared.csproj index 7cff92f2d..1be7c473a 100644 --- a/src/Skoruba.IdentityServer4.Shared/Skoruba.IdentityServer4.Shared.csproj +++ b/src/Skoruba.IdentityServer4.Shared/Skoruba.IdentityServer4.Shared.csproj @@ -13,6 +13,8 @@ + + From 80e09981ceacd8f925cb9b1a4d52dbbc232651c2 Mon Sep 17 00:00:00 2001 From: janskoruba Date: Sat, 26 Sep 2020 15:39:47 +0200 Subject: [PATCH 2/5] Add support for ProtectKeysWithAzureKeyVault --- .../Startup.cs | 4 +-- src/Skoruba.IdentityServer4.Admin/Startup.cs | 4 +-- .../Startup.cs | 4 +-- .../Common/DataProtectionConfiguration.cs | 7 ++++ .../Helpers/StartupHelpers.cs | 34 ++++++++++++++++++- .../Skoruba.IdentityServer4.Shared.csproj | 4 ++- 6 files changed, 46 insertions(+), 11 deletions(-) create mode 100644 src/Skoruba.IdentityServer4.Shared/Configuration/Common/DataProtectionConfiguration.cs diff --git a/src/Skoruba.IdentityServer4.Admin.Api/Startup.cs b/src/Skoruba.IdentityServer4.Admin.Api/Startup.cs index 3fd2d0fb8..63b95040b 100644 --- a/src/Skoruba.IdentityServer4.Admin.Api/Startup.cs +++ b/src/Skoruba.IdentityServer4.Admin.Api/Startup.cs @@ -45,9 +45,7 @@ public void ConfigureServices(IServiceCollection services) // Add DbContexts RegisterDbContexts(services); - services.AddDataProtection() - .SetApplicationName("Skoruba.IdentityServer4") - .PersistKeysToDbContext(); + services.AddDataProtection(Configuration); // Add email senders which is currently setup for SendGrid and SMTP services.AddEmailSenders(Configuration); diff --git a/src/Skoruba.IdentityServer4.Admin/Startup.cs b/src/Skoruba.IdentityServer4.Admin/Startup.cs index 1b0522b9e..e4c907f47 100644 --- a/src/Skoruba.IdentityServer4.Admin/Startup.cs +++ b/src/Skoruba.IdentityServer4.Admin/Startup.cs @@ -45,9 +45,7 @@ public void ConfigureServices(IServiceCollection services) RegisterDbContexts(services); // Save data protection keys to db, using a common application name shared between Admin and STS - services.AddDataProtection() - .SetApplicationName("Skoruba.IdentityServer4") - .PersistKeysToDbContext(); + services.AddDataProtection(Configuration); // Add email senders which is currently setup for SendGrid and SMTP services.AddEmailSenders(Configuration); diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Startup.cs b/src/Skoruba.IdentityServer4.STS.Identity/Startup.cs index 61c887227..88516dee0 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Startup.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/Startup.cs @@ -36,9 +36,7 @@ public void ConfigureServices(IServiceCollection services) RegisterDbContexts(services); // Save data protection keys to db, using a common application name shared between Admin and STS - services.AddDataProtection() - .SetApplicationName("Skoruba.IdentityServer4") - .PersistKeysToDbContext(); + services.AddDataProtection(Configuration); // Add email senders which is currently setup for SendGrid and SMTP services.AddEmailSenders(Configuration); diff --git a/src/Skoruba.IdentityServer4.Shared/Configuration/Common/DataProtectionConfiguration.cs b/src/Skoruba.IdentityServer4.Shared/Configuration/Common/DataProtectionConfiguration.cs new file mode 100644 index 000000000..1ac4444fb --- /dev/null +++ b/src/Skoruba.IdentityServer4.Shared/Configuration/Common/DataProtectionConfiguration.cs @@ -0,0 +1,7 @@ +namespace Skoruba.IdentityServer4.Shared.Configuration.Common +{ + public class DataProtectionConfiguration + { + public bool ProtectKeysWithAzureKeyVault { get; set; } + } +} \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.Shared/Helpers/StartupHelpers.cs b/src/Skoruba.IdentityServer4.Shared/Helpers/StartupHelpers.cs index 45c2d9ada..637272ece 100644 --- a/src/Skoruba.IdentityServer4.Shared/Helpers/StartupHelpers.cs +++ b/src/Skoruba.IdentityServer4.Shared/Helpers/StartupHelpers.cs @@ -1,7 +1,13 @@ -using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore; +using Microsoft.AspNetCore.Identity.UI.Services; +using Microsoft.Azure.KeyVault; +using Microsoft.Azure.Services.AppAuthentication; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using SendGrid; +using Skoruba.IdentityServer4.Shared.Configuration.Common; using Skoruba.IdentityServer4.Shared.Configuration.Email; using Skoruba.IdentityServer4.Shared.Email; @@ -35,5 +41,31 @@ public static void AddEmailSenders(this IServiceCollection services, IConfigurat services.AddSingleton(); } } + + public static void AddDataProtection(this IServiceCollection services, IConfiguration configuration) + where TDbContext : DbContext, IDataProtectionKeyContext + { + var dataProtectionConfiguration = configuration.GetSection(nameof(DataProtectionConfiguration)).Get(); + var azureKeyVaultConfiguration = configuration.GetSection(nameof(AzureKeyVaultConfiguration)).Get(); + + var dataProtectionBuilder = services.AddDataProtection() + .SetApplicationName("Skoruba.IdentityServer4") + .PersistKeysToDbContext(); + + if (dataProtectionConfiguration.ProtectKeysWithAzureKeyVault) + { + if (azureKeyVaultConfiguration.UseClientCredentials) + { + dataProtectionBuilder.ProtectKeysWithAzureKeyVault(azureKeyVaultConfiguration.DataProtectionKeyIdentifier, azureKeyVaultConfiguration.ClientId, azureKeyVaultConfiguration.ClientSecret); + } + else + { + var azureServiceTokenProvider = new AzureServiceTokenProvider(); + var keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback)); + + dataProtectionBuilder.ProtectKeysWithAzureKeyVault(keyVaultClient, azureKeyVaultConfiguration.DataProtectionKeyIdentifier); + } + } + } } } diff --git a/src/Skoruba.IdentityServer4.Shared/Skoruba.IdentityServer4.Shared.csproj b/src/Skoruba.IdentityServer4.Shared/Skoruba.IdentityServer4.Shared.csproj index 1be7c473a..3a00d6acd 100644 --- a/src/Skoruba.IdentityServer4.Shared/Skoruba.IdentityServer4.Shared.csproj +++ b/src/Skoruba.IdentityServer4.Shared/Skoruba.IdentityServer4.Shared.csproj @@ -12,9 +12,11 @@ + + - + From 1d3e9246a4f3fe8e4a28f8f1679250e800e7c577 Mon Sep 17 00:00:00 2001 From: janskoruba Date: Sat, 26 Sep 2020 15:41:35 +0200 Subject: [PATCH 3/5] Add DataProtectionConfiguration --- .../appsettings.json | 9 ++++++--- src/Skoruba.IdentityServer4.Admin/appsettings.json | 13 ++++++++----- .../appsettings.json | 3 +++ 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/Skoruba.IdentityServer4.Admin.Api/appsettings.json b/src/Skoruba.IdentityServer4.Admin.Api/appsettings.json index d7d2aff40..1005977bf 100644 --- a/src/Skoruba.IdentityServer4.Admin.Api/appsettings.json +++ b/src/Skoruba.IdentityServer4.Admin.Api/appsettings.json @@ -40,13 +40,16 @@ }, "IdentityOptions": { "Password": { - "RequiredLength": 8 + "RequiredLength": 8 }, "User": { - "RequireUniqueEmail": true + "RequireUniqueEmail": true }, "SignIn": { - "RequireConfirmedAccount": false + "RequireConfirmedAccount": false } + }, + "DataProtectionConfiguration": { + "ProtectKeysWithAzureKeyVault": false } } \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.Admin/appsettings.json b/src/Skoruba.IdentityServer4.Admin/appsettings.json index ddd8eadf8..c4117c36e 100644 --- a/src/Skoruba.IdentityServer4.Admin/appsettings.json +++ b/src/Skoruba.IdentityServer4.Admin/appsettings.json @@ -61,13 +61,16 @@ "BasePath": "", "IdentityOptions": { "Password": { - "RequiredLength": 8 + "RequiredLength": 8 }, "User": { - "RequireUniqueEmail": true + "RequireUniqueEmail": true }, - "SignIn": { - "RequireConfirmedAccount": false - } + "SignIn": { + "RequireConfirmedAccount": false + } + }, + "DataProtectionConfiguration": { + "ProtectKeysWithAzureKeyVault": false } } \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/appsettings.json b/src/Skoruba.IdentityServer4.STS.Identity/appsettings.json index b6b7eaddb..942017d6d 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/appsettings.json +++ b/src/Skoruba.IdentityServer4.STS.Identity/appsettings.json @@ -86,5 +86,8 @@ "SignIn": { "RequireConfirmedAccount": false } + }, + "DataProtectionConfiguration": { + "ProtectKeysWithAzureKeyVault": false } } \ No newline at end of file From d15542b419373db32a3c17b8110c8ca1b0cf4366 Mon Sep 17 00:00:00 2001 From: janskoruba Date: Sat, 26 Sep 2020 16:04:55 +0200 Subject: [PATCH 4/5] Add Azure KeyVault for reading secrets --- .../Program.cs | 8 ++++++ .../appsettings.json | 8 ++++++ src/Skoruba.IdentityServer4.Admin/Program.cs | 14 +++++++++- .../appsettings.json | 9 +++++++ .../Program.cs | 8 ++++++ .../appsettings.json | 17 ++++++------ .../Common/AzureKeyVaultConfiguration.cs | 2 ++ .../Helpers/StartupHelpers.cs | 27 +++++++++++++++++++ 8 files changed, 84 insertions(+), 9 deletions(-) diff --git a/src/Skoruba.IdentityServer4.Admin.Api/Program.cs b/src/Skoruba.IdentityServer4.Admin.Api/Program.cs index 47d0deb20..bd431d3c1 100644 --- a/src/Skoruba.IdentityServer4.Admin.Api/Program.cs +++ b/src/Skoruba.IdentityServer4.Admin.Api/Program.cs @@ -50,6 +50,10 @@ private static IConfiguration GetConfiguration(string[] args) configurationBuilder.AddUserSecrets(); } + var configuration = configurationBuilder.Build(); + + configuration.AddAzureKeyVaultConfiguration(configurationBuilder); + configurationBuilder.AddCommandLine(args); configurationBuilder.AddEnvironmentVariables(); @@ -60,6 +64,8 @@ public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostContext, configApp) => { + var configurationRoot = configApp.Build(); + configApp.AddJsonFile("serilog.json", optional: true, reloadOnChange: true); var env = hostContext.HostingEnvironment; @@ -71,6 +77,8 @@ public static IHostBuilder CreateHostBuilder(string[] args) => configApp.AddUserSecrets(); } + configurationRoot.AddAzureKeyVaultConfiguration(configApp); + configApp.AddEnvironmentVariables(); configApp.AddCommandLine(args); }) diff --git a/src/Skoruba.IdentityServer4.Admin.Api/appsettings.json b/src/Skoruba.IdentityServer4.Admin.Api/appsettings.json index 1005977bf..06a004bb1 100644 --- a/src/Skoruba.IdentityServer4.Admin.Api/appsettings.json +++ b/src/Skoruba.IdentityServer4.Admin.Api/appsettings.json @@ -51,5 +51,13 @@ }, "DataProtectionConfiguration": { "ProtectKeysWithAzureKeyVault": false + }, + "AzureKeyVaultConfiguration": { + "AzureKeyVaultEndpoint": "", + "ClientId": "", + "ClientSecret": "", + "UseClientCredentials": true, + "DataProtectionKeyIdentifier": "", + "ReadConfigurationFromKeyVault": false } } \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.Admin/Program.cs b/src/Skoruba.IdentityServer4.Admin/Program.cs index 7f2199272..e298c76ff 100644 --- a/src/Skoruba.IdentityServer4.Admin/Program.cs +++ b/src/Skoruba.IdentityServer4.Admin/Program.cs @@ -3,13 +3,17 @@ using System.Linq; using System.Threading.Tasks; 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; using Serilog; using Skoruba.IdentityServer4.Admin.Configuration; using Skoruba.IdentityServer4.Admin.EntityFramework.Shared.DbContexts; using Skoruba.IdentityServer4.Admin.EntityFramework.Shared.Entities.Identity; using Skoruba.IdentityServer4.Admin.Helpers; +using Skoruba.IdentityServer4.Shared.Configuration.Common; using Skoruba.IdentityServer4.Shared.Helpers; namespace Skoruba.IdentityServer4.Admin @@ -49,7 +53,7 @@ public static async Task Main(string[] args) private static async Task ApplyDbMigrationsWithDataSeedAsync(string[] args, IConfiguration configuration, IHost host) { var applyDbMigrationWithDataSeedFromProgramArguments = args.Any(x => x == SeedArgs); - if (applyDbMigrationWithDataSeedFromProgramArguments) args = args.Except(new[] {SeedArgs}).ToArray(); + if (applyDbMigrationWithDataSeedFromProgramArguments) args = args.Except(new[] { SeedArgs }).ToArray(); var seedConfiguration = configuration.GetSection(nameof(SeedConfiguration)).Get(); var databaseMigrationsConfiguration = configuration.GetSection(nameof(DatabaseMigrationsConfiguration)) @@ -79,6 +83,10 @@ private static IConfiguration GetConfiguration(string[] args) configurationBuilder.AddUserSecrets(); } + var configuration = configurationBuilder.Build(); + + configuration.AddAzureKeyVaultConfiguration(configurationBuilder); + configurationBuilder.AddCommandLine(args); configurationBuilder.AddEnvironmentVariables(); @@ -89,6 +97,8 @@ public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostContext, configApp) => { + var configurationRoot = configApp.Build(); + configApp.AddJsonFile("serilog.json", optional: true, reloadOnChange: true); configApp.AddJsonFile("identitydata.json", optional: true, reloadOnChange: true); configApp.AddJsonFile("identityserverdata.json", optional: true, reloadOnChange: true); @@ -104,6 +114,8 @@ public static IHostBuilder CreateHostBuilder(string[] args) => configApp.AddUserSecrets(); } + configurationRoot.AddAzureKeyVaultConfiguration(configApp); + configApp.AddEnvironmentVariables(); configApp.AddCommandLine(args); }) diff --git a/src/Skoruba.IdentityServer4.Admin/appsettings.json b/src/Skoruba.IdentityServer4.Admin/appsettings.json index c4117c36e..cc0c121b4 100644 --- a/src/Skoruba.IdentityServer4.Admin/appsettings.json +++ b/src/Skoruba.IdentityServer4.Admin/appsettings.json @@ -72,5 +72,14 @@ }, "DataProtectionConfiguration": { "ProtectKeysWithAzureKeyVault": false + }, + + "AzureKeyVaultConfiguration": { + "AzureKeyVaultEndpoint": "", + "ClientId": "", + "ClientSecret": "", + "UseClientCredentials": true, + "DataProtectionKeyIdentifier": "", + "ReadConfigurationFromKeyVault": false } } \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Program.cs b/src/Skoruba.IdentityServer4.STS.Identity/Program.cs index 227ddd10d..2f09930ca 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Program.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/Program.cs @@ -50,6 +50,10 @@ private static IConfiguration GetConfiguration(string[] args) configurationBuilder.AddUserSecrets(); } + var configuration = configurationBuilder.Build(); + + configuration.AddAzureKeyVaultConfiguration(configurationBuilder); + configurationBuilder.AddCommandLine(args); configurationBuilder.AddEnvironmentVariables(); @@ -60,6 +64,8 @@ public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostContext, configApp) => { + var configurationRoot = configApp.Build(); + configApp.AddJsonFile("serilog.json", optional: true, reloadOnChange: true); var env = hostContext.HostingEnvironment; @@ -71,6 +77,8 @@ public static IHostBuilder CreateHostBuilder(string[] args) => configApp.AddUserSecrets(); } + configurationRoot.AddAzureKeyVaultConfiguration(configApp); + configApp.AddEnvironmentVariables(); configApp.AddCommandLine(args); }) diff --git a/src/Skoruba.IdentityServer4.STS.Identity/appsettings.json b/src/Skoruba.IdentityServer4.STS.Identity/appsettings.json index 942017d6d..0a686cbf8 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/appsettings.json +++ b/src/Skoruba.IdentityServer4.STS.Identity/appsettings.json @@ -32,14 +32,6 @@ "UseSigningCertificateForAzureKeyVault": false, "UseValidationCertificateForAzureKeyVault": false }, - "AzureKeyVaultConfiguration": { - "AzureKeyVaultEndpoint": "", - "ClientId": "", - "ClientSecret": "", - "UseClientCredentials": true, - "IdentityServerCertificateName": "", - "DataProtectionKeyIdentifier": "" - }, "RegisterConfiguration": { "Enabled": true }, @@ -89,5 +81,14 @@ }, "DataProtectionConfiguration": { "ProtectKeysWithAzureKeyVault": false + }, + "AzureKeyVaultConfiguration": { + "AzureKeyVaultEndpoint": "", + "ClientId": "", + "ClientSecret": "", + "UseClientCredentials": true, + "IdentityServerCertificateName": "", + "DataProtectionKeyIdentifier": "", + "ReadConfigurationFromKeyVault": false } } \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.Shared/Configuration/Common/AzureKeyVaultConfiguration.cs b/src/Skoruba.IdentityServer4.Shared/Configuration/Common/AzureKeyVaultConfiguration.cs index 1517faa05..a68dde187 100644 --- a/src/Skoruba.IdentityServer4.Shared/Configuration/Common/AzureKeyVaultConfiguration.cs +++ b/src/Skoruba.IdentityServer4.Shared/Configuration/Common/AzureKeyVaultConfiguration.cs @@ -15,5 +15,7 @@ public class AzureKeyVaultConfiguration public string IdentityServerCertificateName { get; set; } public string DataProtectionKeyIdentifier { get; set; } + + public bool ReadConfigurationFromKeyVault { get; set; } } } \ No newline at end of file diff --git a/src/Skoruba.IdentityServer4.Shared/Helpers/StartupHelpers.cs b/src/Skoruba.IdentityServer4.Shared/Helpers/StartupHelpers.cs index 637272ece..df1a492e5 100644 --- a/src/Skoruba.IdentityServer4.Shared/Helpers/StartupHelpers.cs +++ b/src/Skoruba.IdentityServer4.Shared/Helpers/StartupHelpers.cs @@ -5,6 +5,7 @@ using Microsoft.Azure.Services.AppAuthentication; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration.AzureKeyVault; using Microsoft.Extensions.DependencyInjection; using SendGrid; using Skoruba.IdentityServer4.Shared.Configuration.Common; @@ -67,5 +68,31 @@ public static void AddDataProtection(this IServiceCollection service } } } + + public static void AddAzureKeyVaultConfiguration(this IConfiguration configuration, IConfigurationBuilder configurationBuilder) + { + if (configuration.GetSection(nameof(AzureKeyVaultConfiguration)).Exists()) + { + var azureKeyVaultConfiguration = configuration.GetSection(nameof(AzureKeyVaultConfiguration)).Get(); + + if (azureKeyVaultConfiguration.ReadConfigurationFromKeyVault) + { + if (azureKeyVaultConfiguration.UseClientCredentials) + { + configurationBuilder.AddAzureKeyVault(azureKeyVaultConfiguration.AzureKeyVaultEndpoint, + azureKeyVaultConfiguration.ClientId, azureKeyVaultConfiguration.ClientSecret); + } + else + { + var keyVaultClient = new KeyVaultClient( + new KeyVaultClient.AuthenticationCallback(new AzureServiceTokenProvider() + .KeyVaultTokenCallback)); + + configurationBuilder.AddAzureKeyVault(azureKeyVaultConfiguration.AzureKeyVaultEndpoint, + keyVaultClient, new DefaultKeyVaultSecretManager()); + } + } + } + } } } From 2bd4c5bce36496f7c47aa01932d467ef8fd50eca Mon Sep 17 00:00:00 2001 From: janskoruba Date: Wed, 30 Sep 2020 22:35:55 +0200 Subject: [PATCH 5/5] Fix #589 --- .../Helpers/IdentityServerBuilderExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Helpers/IdentityServerBuilderExtensions.cs b/src/Skoruba.IdentityServer4.STS.Identity/Helpers/IdentityServerBuilderExtensions.cs index e3559b92f..69b78e771 100644 --- a/src/Skoruba.IdentityServer4.STS.Identity/Helpers/IdentityServerBuilderExtensions.cs +++ b/src/Skoruba.IdentityServer4.STS.Identity/Helpers/IdentityServerBuilderExtensions.cs @@ -174,7 +174,7 @@ public static IIdentityServerBuilder AddCustomValidationKey(this IIdentityServer } else { - throw new Exception($"Validation key file: {certificateConfiguration.SigningCertificatePfxFilePath} not found"); + throw new Exception($"Validation key file: {certificateConfiguration.ValidationCertificatePfxFilePath} not found"); } }