Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
29 changes: 16 additions & 13 deletions src/EventStore.ClusterNode/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,38 +141,41 @@ public static async Task<int> Main(string[] args)
"DEV MODE WILL GENERATE AND TRUST DEV CERTIFICATES FOR RUNNING A SINGLE SECURE NODE ON LOCALHOST.\n" +
"==============================================================================================================\n");
var manager = CertificateManager.Instance;
var result =
manager.EnsureDevelopmentCertificate(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddMonths(1));
var result = manager.EnsureDevelopmentCertificate(
DateTimeOffset.UtcNow,
DateTimeOffset.UtcNow.AddMonths(1),
out var devCertificate);
if (result is not (EnsureCertificateResult.Succeeded
or EnsureCertificateResult.ValidCertificatePresent))
{
Log.Fatal("Could not ensure dev certificate is available. Reason: {result}", result);
return 1;
}

var userCerts = manager.ListCertificates(StoreName.My, StoreLocation.CurrentUser, true);
var machineCerts = manager.ListCertificates(StoreName.My, StoreLocation.LocalMachine, true);
var certs = userCerts.Concat(machineCerts).ToList();

if (!certs.Any())
if (devCertificate == null)
{
Log.Fatal("Could not create dev certificate.");
return 1;
}

if (!manager.IsTrusted(certs[0]) && RuntimeInformation.IsWindows)
if (manager.IsTrusted(devCertificate))
{
Log.Information("Dev certificate {cert} is already trusted.", devCertificate);
}
else if (RuntimeInformation.IsWindows)
{
Log.Information("Dev certificate {cert} is not trusted. Adding it to the trusted store.", certs[0]);
manager.TrustCertificate(certs[0]);
Log.Information("Dev certificate {cert} is not trusted. Adding it to the trusted store.",
devCertificate);
manager.TrustCertificate(devCertificate);
}
else
{
Log.Warning("Automatically trusting dev certs is only supported on Windows.\n" +
"Please trust certificate {cert} if it's not trusted already.", certs[0]);
"Please trust certificate {cert} if it's not trusted already.", devCertificate);
}

Log.Information("Running in dev mode using certificate '{cert}'", certs[0]);
certificateProvider = new DevCertificateProvider(certs[0]);
Log.Information("Running in dev mode using certificate '{cert}'", devCertificate);
certificateProvider = new DevCertificateProvider(devCertificate);
}
else
{
Expand Down
39 changes: 35 additions & 4 deletions src/EventStore.Common/DevCertificates/CertificateManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ public IList<X509Certificate2> ListCertificates(
bool isValid,
bool requireExportable = true)
{
if (location == StoreLocation.LocalMachine &&
storeName is not (StoreName.Root or StoreName.CertificateAuthority) &&
!RuntimeInformation.IsWindows && !RuntimeInformation.IsOSX)
{
return [];
}

Log.ListCertificatesStart(location, storeName);
var certificates = new List<X509Certificate2>();
try
Expand Down Expand Up @@ -169,6 +176,32 @@ public EnsureCertificateResult EnsureDevelopmentCertificate(
CertificateKeyExportFormat keyExportFormat = CertificateKeyExportFormat.Pfx,
bool isInteractive = true)
{
var result = EnsureDevelopmentCertificate(
notBefore,
notAfter,
out var certificate,
path,
trust,
includePrivateKey,
password,
keyExportFormat,
isInteractive);
DisposeCertificates(certificate == null ? [] : [certificate]);
return result;
}

public EnsureCertificateResult EnsureDevelopmentCertificate(
DateTimeOffset notBefore,
DateTimeOffset notAfter,
out X509Certificate2 certificate,
string path = null,
bool trust = false,
bool includePrivateKey = false,
string password = null,
CertificateKeyExportFormat keyExportFormat = CertificateKeyExportFormat.Pfx,
bool isInteractive = true)
{
certificate = null;
var result = EnsureCertificateResult.Succeeded;

var currentUserCertificates = ListCertificates(StoreName.My, StoreLocation.CurrentUser, isValid: true,
Expand All @@ -188,8 +221,6 @@ public EnsureCertificateResult EnsureDevelopmentCertificate(

certificates = filteredCertificates;

X509Certificate2 certificate = null;
var isNewCertificate = false;
if (certificates.Any())
{
certificate = certificates.First();
Expand Down Expand Up @@ -252,7 +283,6 @@ public EnsureCertificateResult EnsureDevelopmentCertificate(
try
{
Log.CreateDevelopmentCertificateStart();
isNewCertificate = true;
certificate = CreateDevelopmentCertificate(notBefore, notAfter);
}
catch (Exception e)
Expand Down Expand Up @@ -347,7 +377,8 @@ public EnsureCertificateResult EnsureDevelopmentCertificate(
}
}

DisposeCertificates(!isNewCertificate ? certificates : certificates.Append(certificate));
var returnedCertificate = certificate;
DisposeCertificates(certificates.Where(c => !ReferenceEquals(c, returnedCertificate)));

return result;
}
Expand Down
37 changes: 30 additions & 7 deletions src/EventStore.Common/DevCertificates/UnixCertificateManager.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using EventStore.Common.Utils;

Expand All @@ -24,18 +26,26 @@ protected override X509Certificate2 SaveCertificateCore(X509Certificate2 certifi
{
var export = certificate.ExportToPkcs12(string.Empty);
certificate.Dispose();
certificate = X509CertificateLoader.LoadPkcs12(export, "",
X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
Array.Clear(export, 0, export.Length);

using (var store = new X509Store(storeName, storeLocation))
try
{
certificate = X509CertificateLoader.LoadPkcs12(export, "",
X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);

using var store = new X509Store(storeName, storeLocation);
store.Open(OpenFlags.ReadWrite);
store.Add(certificate);
store.Close();
}

;
catch (Exception ex) when (IsHomeDirectoryAccessError(ex))
{
certificate.Dispose();
certificate = X509CertificateLoader.LoadPkcs12(export, "",
X509KeyStorageFlags.EphemeralKeySet | X509KeyStorageFlags.Exportable);
}
finally
{
Array.Clear(export, 0, export.Length);
}

return certificate;
}
Expand Down Expand Up @@ -67,4 +77,17 @@ protected override IList<X509Certificate2> GetCertificatesToRemove(StoreName sto
{
return ListCertificates(StoreName.My, StoreLocation.CurrentUser, isValid: false, requireExportable: false);
}

static bool IsHomeDirectoryAccessError(Exception ex)
{
for (var current = ex; current != null; current = current.InnerException)
{
if (current is UnauthorizedAccessException or DirectoryNotFoundException)
{
return true;
}
}

return false;
}
}
Loading