Skip to content

Commit

Permalink
refactor: implem factory and generic callback for tls mongo
Browse files Browse the repository at this point in the history
  • Loading branch information
Nico-dl05 committed Dec 31, 2024
1 parent fdabc79 commit 1e657f8
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 60 deletions.
71 changes: 11 additions & 60 deletions Adaptors/MongoDB/src/ServiceCollectionExt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@

using System;
using System.IO;
using System.Linq;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;

Expand All @@ -44,6 +42,8 @@
using MongoDB.Driver.Core.Configuration;
using MongoDB.Driver.Core.Extensions.DiagnosticSources;

using static ArmoniK.Core.Utils.CertificateValidator;

namespace ArmoniK.Core.Adapters.MongoDB;

public static class ServiceCollectionExt
Expand Down Expand Up @@ -183,72 +183,23 @@ public static IServiceCollection AddMongoClient(this IServiceCollection services
{
if (!File.Exists(mongoOptions.CAFile))
{
throw new FileNotFoundException("CA certificate file not found",
logger.LogError("CA certificate Mongo file not found at {path}",
mongoOptions.CAFile);
throw new FileNotFoundException("CA certificate Mongo file not found",
mongoOptions.CAFile);
}

// Load the CA certificate
var authority = new X509Certificate2(mongoOptions.CAFile);
logger.LogInformation("CA certificate loaded: {authority}",
authority);
// SSL Parameters configuration
var (validationCallback, authority) = CertificateValidatorFactory.CreateCallback(mongoOptions.CAFile,
logger);

settings.SslSettings = new SslSettings
{
ClientCertificates = new X509Certificate2Collection(authority),
EnabledSslProtocols = SslProtocols.Tls12,
ServerCertificateValidationCallback = (sender,
certificate,
certChain,
sslPolicyErrors) =>

{
// If there is any error other than untrusted root or partial chain, fail the validation
if ((sslPolicyErrors & ~SslPolicyErrors.RemoteCertificateChainErrors) != 0)
{
logger.LogDebug("SSL validation failed with errors: {sslPolicyErrors}",
sslPolicyErrors);
return false;
}

if (certificate == null)
{
logger.LogDebug("Certificate is null!");
return false;
}

if (certChain == null)
{
logger.LogDebug("Certificate chain is null!");
return false;
}

// If there is any error other than untrusted root or partial chain, fail the validation
if (certChain.ChainStatus.Any(status
=> status.Status is not X509ChainStatusFlags.UntrustedRoot and
not X509ChainStatusFlags.PartialChain))
{
logger.LogDebug("SSL validation failed with chain status: {chainStatus}",
certChain.ChainStatus);
return false;
}

var cert = new X509Certificate2(certificate);
certChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
certChain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;

certChain.ChainPolicy.ExtraStore.Add(authority);
if (!certChain.Build(cert))
{
return false;
}

return certChain.ChainElements.Any(x => x.Certificate.Thumbprint == authority.Thumbprint);
;
},
ClientCertificates = new X509Certificate2Collection(authority),
EnabledSslProtocols = SslProtocols.Tls12,
ServerCertificateValidationCallback = validationCallback
};
}


settings.ClusterConfigurator = cb =>
{
//cb.Subscribe<CommandStartedEvent>(e => logger.LogTrace("{CommandName} - {Command}",
Expand Down
126 changes: 126 additions & 0 deletions Utils/src/ServerCertificateValidator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// This file is part of the ArmoniK project
//
// Copyright (C) ANEO, 2021-2024. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY, without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

using System.IO;
using System.Linq;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

using Microsoft.Extensions.Logging;


namespace ArmoniK.Core.Utils;

public static class CertificateValidator
{
public static class CertificateValidatorFactory
{
public static (RemoteCertificateValidationCallback, X509Certificate2) CreateCallback(string caFilePath,
ILogger logger)
{
var content = File.ReadAllText(caFilePath);
var authority = X509Certificate2.CreateFromPem(content);
logger.LogInformation("Loaded CA certificate from file {path}",
caFilePath);
var callback = ValidationCallback(logger,
authority);
return (callback, authority);
}
}

public static RemoteCertificateValidationCallback ValidationCallback(ILogger logger,
X509Certificate2 authority)
{
return (sender,
certificate,
chain,
sslPolicyErrors) =>
{
if (certificate == null || chain == null)
{
logger.LogWarning("Certificate or certificate chain is null");
return false;
}

return ValidateServerCertificate(sender,
certificate,
chain,
sslPolicyErrors,
authority,
logger);
};
}

public static bool ValidateServerCertificate(object sender,
X509Certificate certificate,
X509Chain certChain,
SslPolicyErrors sslPolicyErrors,
X509Certificate2 authority,
ILogger logger)
{
// If there is any error other than untrusted root or partial chain, fail the validation
if ((sslPolicyErrors & ~SslPolicyErrors.RemoteCertificateChainErrors) != 0)
{
logger.LogDebug("SSL validation failed with errors: {sslPolicyErrors}",
sslPolicyErrors);
return false;
}

if (certificate == null)
{
logger.LogDebug("Certificate is null!");
return false;
}

if (certChain == null)
{
logger.LogDebug("Certificate chain is null!");
return false;
}

// If there is any error other than untrusted root or partial chain, fail the validation
if (certChain.ChainStatus.Any(status => status.Status is not X509ChainStatusFlags.UntrustedRoot and not X509ChainStatusFlags.PartialChain))
{
logger.LogDebug("SSL validation failed with chain status: {chainStatus}",
certChain.ChainStatus);
return false;
}

var cert = new X509Certificate2(certificate);
certChain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
certChain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;

certChain.ChainPolicy.ExtraStore.Add(authority);
if (!certChain.Build(cert))
{
return false;
}

var isTrusted = certChain.ChainElements.Any(x => x.Certificate.Thumbprint == authority.Thumbprint);
if (isTrusted)
{
logger.LogInformation("SSL validation succeeded");
}
else
{
logger.LogInformation("SSL validation failed with errors: {sslPolicyErrors}",
sslPolicyErrors);
}

return isTrusted;
}
}

0 comments on commit 1e657f8

Please sign in to comment.