简体   繁体   中英

Cannot Connect to AWS Database using TLS with Server CA Validation

AWS documentation states that to connect to my DocumentDB Cluster, I need to use a query string that ends like so ?ssl_ca_certs=rds-combined-ca-bundle.pem&replicaSet=rs0 . It is a root certificate chain that my Client should validate. I should not need a Client Certificate .

Using the MongoDB C# driver and this specific query, with the .pem file in the same directory, I cannot establish the connection. If I use the same .pem file and query string from the Mongo Shell, I can correctly connect to my database. It only doesn't work from my .net core application, that also runs on AWS.

By removing TLS from the Cluster and removing the ssl_ca_certs option from the query, I can connect correctly to my Cluster.

I thought I could convert my .pem file to a .pfx using openssl , but I have to give the .pfx a password and MongoDB documentation states that

It is imperative that when loading a certificate with a password, the PrivateKey property not be null. If the property is null, it means that your certificate does not contain the private key and will not be passed to the server.

How can I use the .pem file provided by Amazon AWS to connect to my database using the C# MongoDB driver?

###Connection to Document DB with simple .Net console Application with SSL.

->First of all, enable SSL on your Document DB cluster by setting the parameter tls to 'enabled'. Make sure to reboot the writer node of your cluster to reboot the whole cluster in order to apply the parameter group changes. By default TLS is enabled wench you launch a new Doc DB cluster.

->Set up SSL certificate on your environment:

1)Download the PKCS#7 SSL certificate on your source windows machine from the below link:

https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.p7b

2)Click on Start menu, click Run and type mmc

3)In MMC, File->Add/Remove Snap-in.

4)Select Certificates from the list of snap-ins and click Add.

5)Trusted CA certificates should go in the Local Computer store, so choose the 'Computer Account' radio button, click next and then choose 'Local Computer'. Click Next and then Finish.

6)Now from the left hand pane(under Console Root, you will see 'Certificates' option. Click on it.

7)A list will appear, right click on 'Trusted Root Certification Authorities' then choose All Tasks->Import

8)In the window that opens, click on Next, browse for the certificate (.p7b) file downloaded in Step 1(If you can't find it, from the file type drop down, select All Files), and then Continue to click on Next and finally Finish. Then Save the configuration.

->Then wrote the below code:

---------------------------------------------------

using MongoDB.Bson;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
namespace FirstDocDB
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var connectionString = "mongodb://pulkit:password@ClusterID:27017/?ssl=true&sslVerifyCertificate=true&replicaSet=rs0";
            var client = new MongoClient(connectionString);
            var database = client.GetDatabase("test");
            var collection = database.GetCollection("stuff");
            var document = collection.Find(new BsonDocument()).FirstOrDefault();
            Console.WriteLine(document.ToString());
        }
    }
}

---------------------------------------------------

->And after build and run, I was successfully able to get the document in the collection named “stuff” as output: { "_id" : ObjectId("5c5a63b10cf861158c1d241c"), "hello" : "world" }

Thus, After following the above steps, I was successfully able to connect to Document DB using Mongo driver for .Net.

I had similar issue, had a ticket open with AWS and it was resolved with similar steps as Pulkit Agarwal's answer.

Main change was connection string, even after adding the certificate to local store i was still using the querystring as "?ssl_ca_certs=rds-combined-ca-bundle.pem&replicaSet=rs0" which needs to be changed to "?ssl=true&sslVerifyCertificate=true&replicaSet=rs0"

Here are examples on how to programmatically connect to Amazon DocumentDB with C# (and other drivers) with both TLS enabled/disabled.

https://docs.aws.amazon.com/documentdb/latest/developerguide/connect.html

Here is another way. However I found that by using SSL with the C# Mongo Driver doesn't do connection Pooling and opened a new connection for each call. You can reduce the active connections by including MaxConnectionIdleTime but it's still not ideal if your application creates a lot of connections.

    var connectionString = "username:password@cluster_endpoint:27017/?replicaSet=rs0";
    var clientSettings = MongoClientSettings.FromUrl(new MongoUrl("mongodb://" + connectionString));
    var certificatePath = "ssl\rds-combined-ca-bundle.pem";

    var pem = System.IO.File.ReadAllText(AppDomain.CurrentDomain.BaseDirectory + certificatePath);
    byte[] certBuffer = GetBytesFromPEM(pem, "CERTIFICATE");

    clientSettings.UseSsl = true;
    clientSettings.SslSettings = new SslSettings()
    {
        ClientCertificates = new List<X509Certificate2>()
        {
            new X509Certificate2(certBuffer)
        },
        EnabledSslProtocols = System.Security.Authentication.SslProtocols.Default,
        CheckCertificateRevocation = true
        };

    clientSettings.VerifySslCertificate = true;

    clientSettings.SslSettings.ClientCertificateSelectionCallback = (sender, host, certificates, certificate, issuers) => clientSettings.SslSettings.ClientCertificates.ToList()[0];
    clientSettings.SslSettings.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;

    clientSettings.MaxConnectionIdleTime = new TimeSpan(0, 0, 30);

    _client = new MongoClient(clientSettings);
    _database = _client.GetDatabase(db.ToString());

Worth adding that currently, MongoDB C# Driver does not support PEM certificates. So anything referencing a PEM certificate will fail with System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.

AWS Developer Guide suggests using P7B certificates instead which can be downloaded from here: https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.p7b

This has worked for us.

On Kubernetes and Windows, we needed to add rds-combined-ca-bundlee.p7b to local trust store as shown in AWS C# example and do NOT reference it in the connection string.

On Mac, I have struggled with adding the P7B certificate to the Keystore programmatically because of the access denied problem. Will update the answer if I manage to resolve it.


Last thing worth to mention, the answer provided by Kenny Dickie essentially switches off the certificate validation and makes the setup insecure. This line of code clientSettings.SslSettings.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true; will always return true .

Try adding the RDS CA file into your C# trust store.

            X509Store store = new X509Store(StoreName.Root);
            X509Certificate2 ca = new X509Certificate2(<path_to_rds-combined-ca-bundle.pem>);
            try {
                store.Open(OpenFlags.ReadWrite);
                store.Add(ca);
            } catch (Exception ex) {
                Console.WriteLine("Root certificate import failed: " + ex.Message);
                throw;
            } finally {
                store.Close();
            }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM