databaseamazon-web-servicescassandranosqlamazon-keyspaces

Cannot connect to Amazon Keyspaces with cqlsh


I am having trouble connecting to Amazon Keyspaces, both with my application code and cqlsh:

cqlsh cassandra.eu-west-2.amazonaws.com 9142 -u "xxxxxxxxxxxxxxx" -p "xxxxxxxxxxxxxxxxxxxxxx" --ssl

Connection error: ('Unable to connect to any servers', {'3.10.201.209': error(1, u"Tried connecting to [('3.10.201.209', 9142)]. Last error: [SSL] internal error (_ssl.c:727)")})

What is particularly confusing is that my setup worked in the past.

My cqlshrc:

[connection]
port = 9142
factory = cqlshlib.ssl.ssl_transport_factory

[ssl]
validate = true
certfile = /home/abc/.cassandra/AmazonRootCA1.pem

I fetched the certificate like this:

wget -c https://www.amazontrust.com/repository/AmazonRootCA1.pem

DNS seems fine:

nslookup cassandra.eu-west-2.amazonaws.com
Server:     8.8.8.8
Address:    8.8.8.8#53

Non-authoritative answer:
Name:   cassandra.eu-west-2.amazonaws.com
Address: 3.10.201.209

I recently upgraded to Ubuntu 20.04 from 18.04, which may be causing issues.

Update: Yes, it probably changed the default SSL protocol


Solution

  • I figured it out for cqlsh; you need to set the SSL version:

    [connection]
    port = 9142
    factory = cqlshlib.ssl.ssl_transport_factory
    
    [cql]
    version = 3.4.4
    
    [ssl]
    validate = true
    certfile = /home/abc/.cassandra/AmazonRootCA1.pem
    version = TLSv1_2
    

    The fix for .NET solution is similar; you must set the SslProtocols correctly.

    Here is an F# script that works:

    #load "../.paket/load/netcoreapp3.1/CassandraCSharpDriver.fsx"
    
    open System
    open System.Net.Security
    open System.Security
    open System.Security.Authentication
    open System.Security.Cryptography
    open System.Security.Cryptography.X509Certificates
    open Cassandra
    
    let private getEnvVar (name : string) =
      let x = Environment.GetEnvironmentVariable name
      if String.IsNullOrWhiteSpace x
      then
        failwithf "The environment variable %s must be set" name
      else
        x
    
    let region = getEnvVar "AWS_REGION"
    
    let keyspace = getEnvVar "AWS_KEYSPACES_KEYSPACE"
    let keyspacesUsername = getEnvVar "AWS_KEYSPACES_USERNAME"
    let keyspacesPassword = getEnvVar "AWS_KEYSPACES_PASSWORD"
    
    async {
      let certCollection = X509Certificate2Collection ()
      use cert = new X509Certificate2 (@"./AmazonRootCA1.pem", "amazon")
    
      certCollection.Add (cert) |> ignore
    
      let sslOptions =
        SSLOptions
          (
            SslProtocols.Tls12,
            true,
            (fun sender certificate chain sslPolicyErrors ->
              if sslPolicyErrors = SslPolicyErrors.None
              then
                true
              else
                printfn "Cassandra node SSL certificate validation error(s): {%A}" sslPolicyErrors
                false)
          )
        |> (fun x -> x.SetCertificateCollection(certCollection))
    
      let contactPoints = [| sprintf "cassandra.%s.amazonaws.com" region |]
    
      let cluster =
        Cluster.Builder()
          .AddContactPoints(contactPoints)
          .WithPort(9142)
          .WithAuthProvider(PlainTextAuthProvider (keyspacesUsername, keyspacesPassword))
          .WithSSL(sslOptions)
          .Build()
    
      use! cassandra =
        cluster.ConnectAsync keyspace
        |> Async.AwaitTask
    
      printfn "Connected. "
    }
    |> Async.RunSynchronously
    

    It should be easy to translate to C# :)