diff --git a/src/ServiceControl.Audit.Persistence.RavenDB/RavenExternalPersistenceLifecycle.cs b/src/ServiceControl.Audit.Persistence.RavenDB/RavenExternalPersistenceLifecycle.cs index 769588337f..325a7b1733 100644 --- a/src/ServiceControl.Audit.Persistence.RavenDB/RavenExternalPersistenceLifecycle.cs +++ b/src/ServiceControl.Audit.Persistence.RavenDB/RavenExternalPersistenceLifecycle.cs @@ -39,7 +39,7 @@ public async Task Initialize(CancellationToken cancellationToken = default) { Database = configuration.Name, Urls = [configuration.ServerConfiguration.ConnectionString], - Certificate = RavenClientCertificate.FindClientCertificate(), + Certificate = RavenClientCertificate.FindClientCertificate(configuration.ServerConfiguration.ClientCertificateBase64), Conventions = new DocumentConventions { SaveEnumsAsIntegers = true diff --git a/src/ServiceControl.Audit.Persistence.RavenDB/RavenPersistenceConfiguration.cs b/src/ServiceControl.Audit.Persistence.RavenDB/RavenPersistenceConfiguration.cs index 12a1e7a5e7..994b44373b 100644 --- a/src/ServiceControl.Audit.Persistence.RavenDB/RavenPersistenceConfiguration.cs +++ b/src/ServiceControl.Audit.Persistence.RavenDB/RavenPersistenceConfiguration.cs @@ -12,6 +12,7 @@ public class RavenPersistenceConfiguration : IPersistenceConfiguration public const string DatabaseNameKey = "RavenDB/DatabaseName"; public const string DatabasePathKey = "DbPath"; public const string ConnectionStringKey = "RavenDB/ConnectionString"; + public const string ClientCertificateBase64Key = "RavenDB/ClientCertificateBase64"; public const string DatabaseMaintenancePortKey = "DatabaseMaintenancePort"; public const string ExpirationProcessTimerInSecondsKey = "ExpirationProcessTimerInSeconds"; public const string LogPathKey = "LogPath"; @@ -24,6 +25,7 @@ public class RavenPersistenceConfiguration : IPersistenceConfiguration DatabaseNameKey, DatabasePathKey, ConnectionStringKey, + ClientCertificateBase64Key, DatabaseMaintenancePortKey, ExpirationProcessTimerInSecondsKey, LogPathKey, @@ -59,6 +61,11 @@ internal static DatabaseConfiguration GetDatabaseConfiguration(PersistenceSettin } serverConfiguration = new ServerConfiguration(connectionString); + + if (settings.PersisterSpecificSettings.TryGetValue(ClientCertificateBase64Key, out var clientCertificateBase64)) + { + serverConfiguration.ClientCertificateBase64 = clientCertificateBase64; + } } else { diff --git a/src/ServiceControl.Audit.Persistence.RavenDB/ServerConfiguration.cs b/src/ServiceControl.Audit.Persistence.RavenDB/ServerConfiguration.cs index d276becdee..dff5a87ebc 100644 --- a/src/ServiceControl.Audit.Persistence.RavenDB/ServerConfiguration.cs +++ b/src/ServiceControl.Audit.Persistence.RavenDB/ServerConfiguration.cs @@ -18,6 +18,7 @@ public ServerConfiguration(string dbPath, string serverUrl, string logPath, stri } public string ConnectionString { get; } + public string ClientCertificateBase64 { get; internal set; } public bool UseEmbeddedServer { get; } public string DbPath { get; internal set; } //Setter for ATT only public string ServerUrl { get; } diff --git a/src/ServiceControl.Persistence.RavenDB/RavenBootstrapper.cs b/src/ServiceControl.Persistence.RavenDB/RavenBootstrapper.cs index 0c8b143d67..08d75d30cc 100644 --- a/src/ServiceControl.Persistence.RavenDB/RavenBootstrapper.cs +++ b/src/ServiceControl.Persistence.RavenDB/RavenBootstrapper.cs @@ -7,6 +7,7 @@ static class RavenBootstrapper public const string DatabaseMaintenancePortKey = "DatabaseMaintenancePort"; public const string ExpirationProcessTimerInSecondsKey = "ExpirationProcessTimerInSeconds"; public const string ConnectionStringKey = "RavenDB/ConnectionString"; + public const string ClientCertificateBase64Key = "RavenDB/ClientCertificateBase64"; public const string MinimumStorageLeftRequiredForIngestionKey = "MinimumStorageLeftRequiredForIngestion"; public const string DatabaseNameKey = "RavenDB/DatabaseName"; public const string LogsPathKey = "LogPath"; diff --git a/src/ServiceControl.Persistence.RavenDB/RavenExternalPersistenceLifecycle.cs b/src/ServiceControl.Persistence.RavenDB/RavenExternalPersistenceLifecycle.cs index 912779490b..5049dff9d7 100644 --- a/src/ServiceControl.Persistence.RavenDB/RavenExternalPersistenceLifecycle.cs +++ b/src/ServiceControl.Persistence.RavenDB/RavenExternalPersistenceLifecycle.cs @@ -39,7 +39,7 @@ public async Task Initialize(CancellationToken cancellationToken) { Database = settings.DatabaseName, Urls = [settings.ConnectionString], - Certificate = RavenClientCertificate.FindClientCertificate(), + Certificate = RavenClientCertificate.FindClientCertificate(settings.ClientCertificateBase64), Conventions = new DocumentConventions { SaveEnumsAsIntegers = true diff --git a/src/ServiceControl.Persistence.RavenDB/RavenPersistenceConfiguration.cs b/src/ServiceControl.Persistence.RavenDB/RavenPersistenceConfiguration.cs index d4e22b7487..0a151360ae 100644 --- a/src/ServiceControl.Persistence.RavenDB/RavenPersistenceConfiguration.cs +++ b/src/ServiceControl.Persistence.RavenDB/RavenPersistenceConfiguration.cs @@ -34,6 +34,7 @@ static T GetRequiredSetting(SettingsRootNamespace settingsRootNamespace, stri var settings = new RavenPersisterSettings { ConnectionString = SettingsReader.Read(settingsRootNamespace, RavenBootstrapper.ConnectionStringKey), + ClientCertificateBase64 = SettingsReader.Read(settingsRootNamespace, RavenBootstrapper.ClientCertificateBase64Key), DatabaseName = SettingsReader.Read(settingsRootNamespace, RavenBootstrapper.DatabaseNameKey, RavenPersisterSettings.DatabaseNameDefault), DatabasePath = SettingsReader.Read(settingsRootNamespace, RavenBootstrapper.DatabasePathKey, DefaultDatabaseLocation()), DatabaseMaintenancePort = SettingsReader.Read(settingsRootNamespace, RavenBootstrapper.DatabaseMaintenancePortKey, RavenPersisterSettings.DatabaseMaintenancePortDefault), diff --git a/src/ServiceControl.Persistence.RavenDB/RavenPersisterSettings.cs b/src/ServiceControl.Persistence.RavenDB/RavenPersisterSettings.cs index 5488bb985e..31fa107108 100644 --- a/src/ServiceControl.Persistence.RavenDB/RavenPersisterSettings.cs +++ b/src/ServiceControl.Persistence.RavenDB/RavenPersisterSettings.cs @@ -23,6 +23,7 @@ class RavenPersisterSettings : PersistenceSettings /// User provided external RavenDB instance connection string /// public string ConnectionString { get; set; } + public string ClientCertificateBase64 { get; set; } public bool UseEmbeddedServer => string.IsNullOrWhiteSpace(ConnectionString); public string LogPath { get; set; } public string LogsMode { get; set; } = LogsModeDefault; diff --git a/src/ServiceControl.RavenDB/RavenClientCertificate.cs b/src/ServiceControl.RavenDB/RavenClientCertificate.cs index a5920bd10a..472f943858 100644 --- a/src/ServiceControl.RavenDB/RavenClientCertificate.cs +++ b/src/ServiceControl.RavenDB/RavenClientCertificate.cs @@ -3,12 +3,26 @@ namespace ServiceControl.RavenDB; using System.Reflection; +using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; public static class RavenClientCertificate { - public static X509Certificate2? FindClientCertificate() + public static X509Certificate2? FindClientCertificate(string? base64String) { + if (base64String is not null) + { + try + { + var bytes = Convert.FromBase64String(base64String); + return new X509Certificate2(bytes); + } + catch (Exception x) when (x is FormatException or CryptographicException) + { + throw new Exception("Could not read the RavenDB client certificate from the configured Base64 value.", x); + } + } + var applicationDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location) ?? string.Empty; var certificatePath = Path.Combine(applicationDirectory, "raven-client-certificate.pfx");