From b5d2a1da75a75969dc42e430ea4cf5d123f6e90e Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Wed, 10 Jul 2019 20:05:07 -0400 Subject: [PATCH] load certs from azure storage --- src/Core/GlobalSettings.cs | 2 ++ .../Implementations/LicensingService.cs | 22 +++++++++++-- src/Core/Utilities/CoreHelpers.cs | 20 ++++++++++++ .../Utilities/ServiceCollectionExtensions.cs | 32 +++++++++++++++---- 4 files changed, 67 insertions(+), 9 deletions(-) diff --git a/src/Core/GlobalSettings.cs b/src/Core/GlobalSettings.cs index 21f3446505..e6bc27af6b 100644 --- a/src/Core/GlobalSettings.cs +++ b/src/Core/GlobalSettings.cs @@ -11,6 +11,7 @@ namespace Bit.Core public virtual string ProjectName { get; set; } public virtual string LogDirectory { get; set; } public virtual string LicenseDirectory { get; set; } + public string LicenseCertificatePassword { get; set; } public virtual string PushRelayBaseUri { get; set; } public virtual string InternalIdentityKey { get; set; } public virtual string HibpBreachApiKey { get; set; } @@ -141,6 +142,7 @@ namespace Bit.Core public class DataProtectionSettings { public string CertificateThumbprint { get; set; } + public string CertificatePassword { get; set; } public string Directory { get; set; } } diff --git a/src/Core/Services/Implementations/LicensingService.cs b/src/Core/Services/Implementations/LicensingService.cs index ef1af3041d..3dff230cf3 100644 --- a/src/Core/Services/Implementations/LicensingService.cs +++ b/src/Core/Services/Implementations/LicensingService.cs @@ -4,6 +4,7 @@ using Bit.Core.Repositories; using Bit.Core.Utilities; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Logging; +using Microsoft.WindowsAzure.Storage; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -38,12 +39,27 @@ namespace Bit.Core.Services _organizationRepository = organizationRepository; _organizationUserRepository = organizationUserRepository; _logger = logger; + _globalSettings = globalSettings; var certThumbprint = environment.IsDevelopment() ? "207E64A231E8AA32AAF68A61037C075EBEBD553F" : "‎B34876439FCDA2846505B2EFBBA6C4A951313EBE"; - _globalSettings = globalSettings; - _certificate = !_globalSettings.SelfHosted ? CoreHelpers.GetCertificate(certThumbprint) - : CoreHelpers.GetEmbeddedCertificate("licensing.cer", null); + if(_globalSettings.SelfHosted) + { + _certificate = CoreHelpers.GetEmbeddedCertificate("licensing.cer", null); + } + else if(CoreHelpers.SettingHasValue(_globalSettings.Storage?.ConnectionString) && + CoreHelpers.SettingHasValue(_globalSettings.LicenseCertificatePassword)) + { + var storageAccount = CloudStorageAccount.Parse(globalSettings.Storage.ConnectionString); + _certificate = CoreHelpers.GetBlobCertificateAsync(storageAccount, "certificates", + "licensing.pfx", _globalSettings.LicenseCertificatePassword) + .GetAwaiter().GetResult(); + } + else + { + _certificate = CoreHelpers.GetCertificate(certThumbprint); + } + if(_certificate == null || !_certificate.Thumbprint.Equals(CoreHelpers.CleanCertificateThumbprint(certThumbprint), StringComparison.InvariantCultureIgnoreCase)) { diff --git a/src/Core/Utilities/CoreHelpers.cs b/src/Core/Utilities/CoreHelpers.cs index cf05134015..ac927e488e 100644 --- a/src/Core/Utilities/CoreHelpers.cs +++ b/src/Core/Utilities/CoreHelpers.cs @@ -15,6 +15,8 @@ using System.Globalization; using System.Web; using Microsoft.AspNetCore.DataProtection; using Bit.Core.Enums; +using System.Threading.Tasks; +using Microsoft.WindowsAzure.Storage; namespace Bit.Core.Utilities { @@ -149,6 +151,24 @@ namespace Bit.Core.Utilities } } + public async static Task GetBlobCertificateAsync(CloudStorageAccount cloudStorageAccount, + string container, string file, string password) + { + var blobClient = cloudStorageAccount.CreateCloudBlobClient(); + var containerRef = blobClient.GetContainerReference(container); + if(await containerRef.ExistsAsync()) + { + var blobRef = containerRef.GetBlobReference(file); + if(await blobRef.ExistsAsync()) + { + var blobBytes = new byte[blobRef.Properties.Length]; + await blobRef.DownloadToByteArrayAsync(blobBytes, 0); + return new X509Certificate2(blobBytes, password); + } + } + return null; + } + public static long ToEpocMilliseconds(DateTime date) { return (long)Math.Round((date - _epoc).TotalMilliseconds, 0); diff --git a/src/Core/Utilities/ServiceCollectionExtensions.cs b/src/Core/Utilities/ServiceCollectionExtensions.cs index ff5689c073..8fc90ff6af 100644 --- a/src/Core/Utilities/ServiceCollectionExtensions.cs +++ b/src/Core/Utilities/ServiceCollectionExtensions.cs @@ -29,6 +29,7 @@ using System.Security.Claims; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.HttpOverrides; using System.Linq; +using System.Security.Cryptography.X509Certificates; namespace Bit.Core.Utilities { @@ -131,7 +132,7 @@ namespace Bit.Core.Utilities services.AddSingleton(); } - if(!globalSettings.SelfHosted && CoreHelpers.SettingHasValue(globalSettings.Storage.ConnectionString)) + if(!globalSettings.SelfHosted && CoreHelpers.SettingHasValue(globalSettings.Storage?.ConnectionString)) { services.AddSingleton(); } @@ -326,7 +327,8 @@ namespace Bit.Core.Utilities { identityServerBuilder.AddDeveloperSigningCredential(false); } - else if(!string.IsNullOrWhiteSpace(globalSettings.IdentityServer.CertificatePassword) + else if(globalSettings.SelfHosted && + !string.IsNullOrWhiteSpace(globalSettings.IdentityServer.CertificatePassword) && File.Exists("identity.pfx")) { var identityServerCert = CoreHelpers.GetCertificate("identity.pfx", @@ -338,6 +340,15 @@ namespace Bit.Core.Utilities var identityServerCert = CoreHelpers.GetCertificate(globalSettings.IdentityServer.CertificateThumbprint); identityServerBuilder.AddSigningCredential(identityServerCert); } + else if(!globalSettings.SelfHosted && + CoreHelpers.SettingHasValue(globalSettings.Storage?.ConnectionString) && + CoreHelpers.SettingHasValue(globalSettings.IdentityServer.CertificatePassword)) + { + var storageAccount = CloudStorageAccount.Parse(globalSettings.Storage.ConnectionString); + var identityServerCert = CoreHelpers.GetBlobCertificateAsync(storageAccount, "certificates", + "identity.pfx", globalSettings.IdentityServer.CertificatePassword).GetAwaiter().GetResult(); + identityServerBuilder.AddSigningCredential(identityServerCert); + } else { throw new Exception("No identity certificate to use."); @@ -366,12 +377,21 @@ namespace Bit.Core.Utilities .PersistKeysToFileSystem(new DirectoryInfo(globalSettings.DataProtection.Directory)); } - if(!globalSettings.SelfHosted && CoreHelpers.SettingHasValue(globalSettings.Storage.ConnectionString) && - CoreHelpers.SettingHasValue(globalSettings.DataProtection.CertificateThumbprint)) + if(!globalSettings.SelfHosted && CoreHelpers.SettingHasValue(globalSettings.Storage?.ConnectionString)) { - var dataProtectionCert = CoreHelpers.GetCertificate( - globalSettings.DataProtection.CertificateThumbprint); var storageAccount = CloudStorageAccount.Parse(globalSettings.Storage.ConnectionString); + X509Certificate2 dataProtectionCert = null; + if(CoreHelpers.SettingHasValue(globalSettings.DataProtection.CertificateThumbprint)) + { + dataProtectionCert = CoreHelpers.GetCertificate( + globalSettings.DataProtection.CertificateThumbprint); + } + else if(CoreHelpers.SettingHasValue(globalSettings.DataProtection.CertificatePassword)) + { + dataProtectionCert = CoreHelpers.GetBlobCertificateAsync(storageAccount, "certificates", + "dataprotection.pfx", globalSettings.DataProtection.CertificatePassword) + .GetAwaiter().GetResult(); + } services.AddDataProtection() .PersistKeysToAzureBlobStorage(storageAccount, "aspnet-dataprotection/keys.xml") .ProtectKeysWithCertificate(dataProtectionCert);