0

I'm trying to connect a .Net Core API to my SSL protected MongoDB. I can connect using Mongo Compass so I am confident the problem is not in my MongoDB configuration.

Everything was working fine before the certificates were added to the MongoDB and as you'll see the stack points right to the certificates. I just don't know how to correct it.

I wasn't able to find a lot of documentation on how to implement the SslSettings in the MongoDB driver so I'm piecing this together from different sources and SO questions.

The problem seems to come when my API goes to connect to Mongo. I get the following exception.

Value cannot be null. (Parameter 'source')

The stack dump points right to the X509 certificates being the missing / null value.

at System.Linq.ThrowHelper.ThrowArgumentNullException(ExceptionArgument argument)
at System.Linq.Enumerable.Cast[TResult](IEnumerable source)
at MongoDB.Driver.SslSettings.X509CertificateCollectionEqualityComparer.Equals(X509CertificateCollection lhs, X509CertificateCollection rhs)
at MongoDB.Driver.SslSettings.Equals(Object obj)
at System.Object.Equals(Object objA, Object objB)
at MongoDB.Driver.ClusterKey.Equals(Object obj)
at System.Collections.Generic.ObjectEqualityComparer`1.Equals(T x, T y)
at System.Collections.Generic.Dictionary`2.FindValue(TKey key)
at System.Collections.Generic.Dictionary`2.TryGetValue(TKey key, TValue& value)
at MongoDB.Driver.ClusterRegistry.GetOrCreateCluster(ClusterKey clusterKey)
at MongoDB.Driver.MongoClient..ctor(MongoClientSettings settings)
at MongoDB.Driver.MongoClient..ctor(String connectionString)
at Bullies.API.Endpoints.Users.UserService..ctor(IOptions`1 config, IMediator mediator) in C:\Users\...\Demo.API\Endpoints\Users\UserService.cs:line 38
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
at Demo.API.HostedServices.SubscriptionHostedService.<ExecuteAsync>d__2.MoveNext() in C:\Users\...\Demo.API\HostedServices\SubscriptionHostedService.cs:line 32
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult()
at Microsoft.Extensions.Hosting.Internal.Host.<StartAsync>d__9.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult()
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.<RunAsync>d__4.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.<RunAsync>d__4.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.Run(IHost host)
at Demo.API.Program.Main(String[] args) in C:\...\Demo.API\Program.cs:line 22

appsettings.json connection string

mongodb://user:pass@localmongo:12345/MyDb?authSource=admin&readPreference=primary&appname=demoapi&ssl=true

AccessMongoClass.cs

MongoCredential creds = MongoCredential.CreateMongoCRCredential("localmongo:12345", "user", "pass");
var clientCertificate = new List<X509Certificate>()
  { new X509Certificate("Path\\...\\mysite_com.pfx", "privateKeyPassword") };

MongoClientSettings settingz = new MongoClientSettings();
settingz.ApplicationName = "demo-api";
settingz.Credential = creds;
settingz.SslSettings = new SslSettings()
{
    CheckCertificateRevocation = false,
    //EnabledSslProtocols = System.Security.Authentication.SslProtocols.Tls12,
    ClientCertificates = clientCertificate,
    ClientCertificateSelectionCallback =
      (sender, host, certificates, certificate, issuers) => clientCertificate.ToList()[0],
    ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true
 };

MongoClient client = new MongoClient(settingz);

On the mongodb side I see this error:

"ctx":"conn7","msg":"Interrupted operation as its client disconnected","attr":{"opId":1731}}

The Mongo log is VERY long so I'm just picking out what I think is the key line. If you want to see something else I can easily add it.

I would have thought I provided the X509 certificates but clearly I'm missing something.

0

1 Answer 1

1

I assume you don't see this exception if you just create a new client and in the full stacktrace you see ClusterRegistry.GetOrCreateCluster. This logic is called when the driver is trying to reuse previously created cluster that is stored in a static storage(ClusterRegistry). In your case it looks like you create 2 mongoClients from the settings like this:

        var clientSettings1 = new MongoClientSettings()
        {
            SslSettings = new SslSettings
            {
                ClientCertificates = new X509Certificate2[] { new X509Certificate2(@"path", "pass") }
            }
        };
        // create client 1
        var clientSettings2 = new MongoClientSettings()
        {
            SslSettings = new SslSettings
            {
                ClientCertificates = null
            }
        };
        // create client 2

if so, it's a bug in the driver, try avoid setting a null in ClientCertificates for example you can assign an empty collection

Sign up to request clarification or add additional context in comments.

2 Comments

Actually I get the same error just using connection string. That is what led me to use the more verbose attempt in the OP with defining x509 certs. //MongoClient client = new(_mongoSettings.ConnectionString);
I don't think that you can configure x509 certificate just via connectionString, but the key points:1.when you create new MongoClient,effectively the driver tries to find the previously created mongoClients, if it's found, then it tries to check whether the found client has the same MongoClientSettings as the client that you're going to create. 2. For that it calls MongoClientSettings.Equals that in turns calls SslSettings.Equals which in turn calls Equals for X509CertificateCollection. And this method has a bug if the right collection is null which leads to the error you're seeing

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.