-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support rediss:// urls #208
Conversation
if u.Scheme == "rediss" { | ||
// insert a default DlialTLS at position 0, so we don't override any | ||
// user provided *tls.Config elsewhere in options | ||
t := DialTLS(&tls.Config{InsecureSkipVerify: true}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you need InsecureSkipVerify?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suspect it's the sanest option as most setups I've seen in the wild currently make fake certs with the intent to just encrypt the connection (vs. also ensuring you are connecting to what you think you are).
Users who need more security can still DialURL(url, DialTLS(&tls.Config{<better security options>})
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer that the feature be secure by default and that apps use the option to make it less secure. Does Heroku work without InsecureSkipVerify?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. This because we use self signed certs and even if we didn't we would have to restart stunnel (breaking connections) to rotate the cert. Most customers find this objectionable. We've done the same thing with postgres for years.
With that said, if you are set on not having InsecureSkipVerify set then I can make that change. I may follow up later with a pr to add it as a url encoded option as well.
b3dc585
to
cde0485
Compare
rediss:// is a valid scheme based on IANA : https://www.iana.org/assignments/uri-schemes/prov/rediss We (Heroku) would like to move secure redis connections forward, but have this chicken and egg problem with some drivers. Drivers for other languages already support rediss://
A couple of more issues:
Because it's possible dial using a TLS connection today using the |
What I don't like about pushing the options to the query params (that is where I started) is that:
u, err := url.Parse("rediss://localhost:1234/0")
if err != nil {
return err
}
v := u.Query()
v.Set("skip-verify","true")
u.RawQuery = v.Encode()
u.String() // rediss://localhost:1234/0?skip-verify=true If you are set on query params though I'll do it. |
OK, the query parameter is a bad idea. Add |
Based on feedback in the PR. Also, default to a more secure connection by setting ServerName by default.
Okay, how about now. FWIW: I still think InsecureSkipVerify:true is better given the state of probably all redis installations behind an stunnel. But I have no numbers except for our own setups to point to. |
if tlsConfig == nil { | ||
// https://golang.org/pkg/crypto/tls/#Client | ||
// At this point we don't know the ServerName. | ||
tlsConfig = &tls.Config{InsecureSkipVerify: true} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are some other alternatives here:
(1)
if do.tlsConfig == nil {
return nil, errors.New("a valid *tls.Config is required")
}
(2)
if do.tlsConfig == nil {
if do.serverName == "" {
return nil, errors.New("a valid tlsConfig or serverName is required")
}
tlsConfig = &tls.Config{ServerName: do.serverName}
}
The later requires adding a serverName
option to dialOptions.
TBH: I don't like either, but nor do I like InsecureSkipVerify:true (but see previous comments on why I think it's sanest atm).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess I could also set a different dialer instead that is a wrapper around tls.Dial like so:
DialNetDial(func(network, addr string) (net.Conn, error) {
tls.Dial(network, addr, do.tlsConfig)
})
That actually may make more sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And nope, that doesn't work because I won't be able to override things w/o doing this:
c, err := DialURL("rediss://localhost:1234", DialNetDial(func(network, address string) (net.Conn, error) {
tls.Dial(network, address, &tls.Config{InsecureSkipVerify: true})
})
Which is a lot more verbose and then what's the point in all of this.
* Make this explicitly about redigo. * Add support for changing the redis:// url to rediss://. This will be usable once https://github.com/garyburd/redigo/pull/208 lands. * Remove our own url parser as current versions of redigo already supply one.
Set the host name unless specified in the TLS config. Close underlying network connection when returning an error.
Copy cloneTLSConfig from the standard libary. The comment for cloneTLSConfig describes why the clone function is needed. DialURL sets TLS flag only. Do not scribble on caller's slice.
Heroku users can use the function like this:
I think it's fine to add a shorter dial option:
|
Making those changes, but.... I'm pretty sure that the manual VerifyHostname call isn't required as Handshake() calls clientHandshake, which makes a clientHandshakeState and when not resuming a connection will call the clientHandshakeState's doFullHandshake(), which if !InsecureSkipVerify then the x509 cert's Verify() method is called, which calls the cert's VerifyHostname method. This is (AFAICT) the same thing that the config's VerifyHostname method does. Oddly enough the net/http code also calls VerifyHostname manually casting doubt on what I found, so I'm just not sure ATM, so I'll leave in the what appears to be an extra manual VerifyHostname and ask around. |
The net/http code is the inspiration for the snippet in my previous comment. Now that you explain it, it looks like the VerifyHostname is not required. Thank you for investigating this. |
Based on PR feedback.
How's this? |
It looks good. I'd like to add a test, but the test requires some work (setup localhost tunnel using crypto.tls package) . I'll look at doing that later. Did you test the PR with your service? |
I did some manual (not super comprehensive) testing https://github.com/freeformz/testrediss/blob/master/redigo/main.go |
PS: If you choose to test something on Heroku, then note that the hobby plan does not support SSL and that the urls returned for premium-* plans are not rediss://, hence this function for now: https://github.com/freeformz/testrediss/blob/master/test.go#L9 |
Thank you for implementing this feature! |
Thanks for being patient. ;-) |
rediss:// is a valid scheme based on IANA :
https://www.iana.org/assignments/uri-schemes/prov/rediss
We (Heroku) would like to move secure redis connections forward, but
have this chicken and egg problem with some drivers. Drivers for other
languages already support rediss://