go-redis icon indicating copy to clipboard operation
go-redis copied to clipboard

Connecting to 3rd Party Redis Databases how to indicate tls

Open gofiliate opened this issue 4 years ago • 4 comments

Trying to connect to a redis database that's managed by DigitalOcean, but when pinging the connection I receive an EOF. DigitalOcean says

"This error occurs when the command does not contain the --tls flag, or is otherwise unable to encrypt the connection. To troubleshoot this issue, make sure your connection string includes the --tls flag. If it does, check that your local computer's certificate store is up to date."

I'm trying to go through the documentation but I cannot find any way to setup a connection with the tls flags. Can someone point me in the right direction please?

gofiliate avatar Apr 22 '20 11:04 gofiliate

This may solve the problem.

cert, err := tls.LoadX509KeyPair("fullchain.pem", "privkey.pem")
if err != nil {
    log.Println(err)
    return
}
client = redis.NewClient(&redis.Options{
    Addr:      "127.0.0.1:6379",
    TLSConfig: &tls.Config{
        Certificates: []tls.Certificate{
            cert,
        },
    },
})

kallydev avatar May 01 '20 19:05 kallydev

Thanks for the reply kallydav,

I was wondering if supplying the TLSConfig to the NewClient call would work. But what stopped me looking into it the question, what certificate to use? As DO don't supply a certificate for the connection, from their documentation about this issue they only say "To troubleshoot this issue, make sure your connection string includes the --tls flag."

gofiliate avatar May 04 '20 11:05 gofiliate

You can try using ClientAuthType.

https://github.com/golang/go/blob/53f27474a455a4a82e8c0f8ef4cee9a37b51ff98/src/crypto/tls/common.go#L248-L254

// VerifyPeerCertificate can be used to replace and customize certificate
// verification. This example shows a VerifyPeerCertificate implementation
// that will be approximately equivalent to what crypto/tls does normally.

config := &tls.Config{
    // Set InsecureSkipVerify to skip the default validation we are
    // replacing. This will not disable VerifyPeerCertificate.
    InsecureSkipVerify: true,

    // While packages like net/http will implicitly set ServerName, the
    // VerifyPeerCertificate callback can't access that value, so it has to be set
    // explicitly here or in VerifyPeerCertificate on the client side. If in
    // an http.Transport DialTLS callback, this can be obtained by passing
    // the addr argument to net.SplitHostPort.
    ServerName: "example.com",

    // On the server side, set ClientAuth to require client certificates (or
    // VerifyPeerCertificate will run anyway and panic accessing certs[0])
    // but not verify them with the default verifier.
    ClientAuth: tls.RequireAnyClientCert,
}

But I did not have a test environment, so this is not necessarily the correct answer.

kallydev avatar May 04 '20 12:05 kallydev

We were facing the same issue, using tls.Config fixed the problem. 🙏

Fardinak avatar Feb 01 '21 16:02 Fardinak

I'm having the same issue with a redis db on render, using v9 of go-redis and render has version 6.2.5 of redis.

Url given to me by render: rediss://username:[email protected]:6379 I've tried options, _ := redis.ParseURL("rediss://username:[email protected]:6379")

And various variants of

	options := &redis.Options{
		Addr:     "frankfurt-redis.render.com:6379",
		Username: "username",
		Password: "password",
		TLSConfig: &tls.Config{
			InsecureSkipVerify: true,
			ClientAuth: tls.RequireAnyClientCert,
			ServerName: "frankfurt-redis.render.com",
		},
	}

Everything gives me EOF error, I can successfully connect to the server with CLI using --tls, and with TablePlus

I also tried the node-redis client and that works as expected with this

import { createClient } from 'redis';

const client = createClient({
  url:'rediss://username:[email protected]:6379',
});

AdnanCukur avatar Jul 23 '23 17:07 AdnanCukur

This works for me:

func getRedisClient() (*redis.Client, error) {
    pool := x509.NewCertPool()

    // Note: redisCert here is just a string containing the CA file provided to me by Redis Labs.
    b := []byte(redisCert)
    pool.AppendCertsFromPEM(b)
    client := redis.NewClient(&redis.Options{
        Addr:     os.Getenv("REDIS_HOST"),
        Password: os.Getenv("REDIS_PASSWORD"),
        Protocol: 2,
        TLSConfig: &tls.Config{
            ClientCAs:          pool,
            InsecureSkipVerify: true,
            MinVersion:         tls.VersionTLS12,
        },
    })
    status := client.Ping(ctx)
    if status.Err() == nil {
        lg.Debug("#S23 successfully connected with TLS Client")
        return client.WithTimeout(time.Second*5), nil
    }

    return client, status.Err()
}

ShawnMilo avatar Jul 23 '23 17:07 ShawnMilo

Just like the previous guy mentioned about Digital Ocean not providing a certificate, Render doesnt provide a certificate either

AdnanCukur avatar Jul 24 '23 13:07 AdnanCukur

Fwiw, I found that, when connecting to a Digital Ocean instance of Redis, passing an empty tls.Config struct is enough to get the client to use TLS. I think this indicates to the client that it should use TLS. For example:

	rdc := redis.NewClient(&redis.Options{
		Addr:      addr,
		Password:  password, 
		Username:  username, 
		DB:        0,  
		TLSConfig: &tls.Config{},  // Empty struct does the trick 
	})

This works when connecting to a private/vpc instance.

patrickward avatar Sep 29 '23 23:09 patrickward