Accessing Amazon Keyspaces via .NET client fails due to RemoteCertificateNameMismatch

0

When following Using a Cassandra .NET Core client driver to access Amazon Keyspaces programmatically - Amazon Keyspaces (for Apache Cassandra) I get a RemoteCertificateNameMismatch which causes the connection to fail.

I used the cassandra.eu-north-1.amazonaws.com endpoint.

The problem seems to originate from the fact that not all nodes in the cluster have an IP that resolve to a hostname that matches the Subject Alternative Name (SAN) of the certificate attached to the cassandra.eu-north-1.amazonaws.com endpoint.

Downloading the certificate from https://cassandra.eu-north-1.amazonaws.com shows 2 DNS entries in the SAN:

  • DNS Name: *.cassandra.eu-north-1.vpce.amazonaws.com
  • DNS Name: cassandra.eu-north-1.amazonaws.com

When using the code snipped from the link above, we can connect to the cluster in about 7/10 cases while it fails in about 3/10 cases. When we are succesfully connected to the cluster, we can see all the nodes by calling Cluster.AllHosts() and see the IP addresses of the 10 nodes.

IPReverse lookup DNS name
13.49.40.78ec2-13-49-40-78.eu-north-1.compute.amazonaws.com
13.49.40.86cassandra.eu-north-1.amazonaws.com
13.49.40.84cassandra.eu-north-1.amazonaws.com
13.49.40.85cassandra.eu-north-1.amazonaws.com
13.49.40.90cassandra.eu-north-1.amazonaws.com
13.49.40.88cassandra.eu-north-1.amazonaws.com
13.49.40.89cassandra.eu-north-1.amazonaws.com
13.49.40.75ec2-13-49-40-75.eu-north-1.compute.amazonaws.com
13.49.40.77ec2-13-49-40-77.eu-north-1.compute.amazonaws.com
13.49.40.80cassandra.eu-north-1.amazonaws.com

The problem is the DataStax C# Cassandra driver internally seems to be using all IP addresses, while validating the TLS certificates by doing a reverse DNS lookup (docs). This gives the above SslPolicyError RemoteCertificateNameMismatch when the original endpoint cassandra.eu-north-1.amazonaws.com resolved to one of the three IP addresses that do not resolve back to that hostname.

The problem would be fixed if all IP addresses of the nodes resolve back to the original hostname: cassandra.eu-north-1.amazonaws.com.

Below a small reproduction snippet. You will need to excute this program multiple times untill you get one of the bad IPs. If so it will print to console that it's now expecting the connection to fail. Note that this can take more than 10 tries before it fails as it is random which IP you get.

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

namespace AmazonKeyspacesMinimalReproductionSnippet
{
    internal class Program
    {
        static string ContactPoint => "cassandra.eu-north-1.amazonaws.com" ;

        static void Main(string[] args)
        {
            X509Certificate2Collection certCollection = new X509Certificate2Collection();
            X509Certificate2 amazoncert = new X509Certificate2(@"path_to_file\sf-class2-root.crt");
            certCollection.Add(amazoncert);

            var clusterBuilder = Cluster.Builder()
                .AddContactPoint(ContactPoint)
                .WithPort(9142)
                .WithAuthProvider(new PlainTextAuthProvider(
                    "ServiceUserName", "ServicePassword"));

            var sslOptions = new SSLOptions().SetCertificateCollection(certCollection);
            clusterBuilder = clusterBuilder.WithSSL(sslOptions);
            var cluster = clusterBuilder.Build();

            IPAddress address = cluster.AllHosts().First().Address.Address;
            IPHostEntry entry = Dns.GetHostEntry(address);
            Console.WriteLine($"Working with ip {address} which has hostname {entry.HostName}");

            if(entry.HostName == ContactPoint)
                Console.WriteLine("Connection expected to succeed.");
            else
                Console.WriteLine("Connection expected to fail.");

            try
            {
                cluster.Connect();
                Console.WriteLine("Successful Connection");
            }
            catch (NoHostAvailableException noHostException)
            {
                foreach (var endpoint in noHostException.Errors.Keys)
                {
                    Console.WriteLine($"Failed connecting to {endpoint} because exception: " + noHostException.Errors[endpoint]);
                }
            }
            Console.ReadKey();
        }
    }
}

RobbeDG
asked a year ago174 views
No Answers

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions