redis / go-redis

Redis Go client
https://redis.uptrace.dev
BSD 2-Clause "Simplified" License
20.11k stars 2.37k forks source link

Connecting to 3rd Party Redis Databases how to indicate tls #1306

Open gofiliate opened 4 years ago

gofiliate commented 4 years ago

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?

kallydev commented 4 years ago

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,
        },
    },
})
gofiliate commented 4 years ago

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."

kallydev commented 4 years ago

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.

Fardinak commented 3 years ago

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

AdnanCukur commented 1 year ago

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:password@frankfurt-redis.render.com:6379 I've tried options, _ := redis.ParseURL("rediss://username:password@frankfurt-redis.render.com: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:password@frankfurt-redis.render.com:6379',
});
ShawnMilo commented 1 year ago

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()
}
AdnanCukur commented 1 year ago

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

patrickward commented 1 year ago

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.

salamer commented 2 months ago

any progress? same issue, anytime, it return 'EOF'

    opt := &redis.Options{
        Addr:     addr,
        Username: "default",
        Password: password,
        TLSConfig: &tls.Config{
            InsecureSkipVerify: true,
            ServerName:         "example.com",
            ClientAuth:         tls.RequireAnyClientCert,
        },
    }