feat: add TLS URL parameters

This commit is contained in:
Ben Weissmann 2022-04-19 22:01:21 -04:00
parent 4c8b4d1619
commit fae038dbdd
No known key found for this signature in database
GPG Key ID: 4B8AAAB2E30D895A
4 changed files with 119 additions and 1 deletions

View File

@ -390,6 +390,37 @@ func setupConnParams(u *url.URL, o *Options) (*Options, error) {
o.PoolTimeout = q.duration("pool_timeout") o.PoolTimeout = q.duration("pool_timeout")
o.IdleTimeout = q.duration("idle_timeout") o.IdleTimeout = q.duration("idle_timeout")
o.IdleCheckFrequency = q.duration("idle_check_frequency") o.IdleCheckFrequency = q.duration("idle_check_frequency")
if u.Scheme == "rediss" {
tlsCertPEMFile := q.string("TLSCertPEMFile")
tlsKeyPEMFile := q.string("TLSKeyPEMFile")
if (tlsCertPEMFile == "") != (tlsKeyPEMFile == "") {
return nil, fmt.Errorf("redis: TLSCertPEMFile and TLSKeyPEMFile URL parameters must be both set or both omitted")
}
if tlsCertPEMFile != "" {
cert, certLoadErr := tls.LoadX509KeyPair(tlsCertPEMFile, tlsKeyPEMFile)
if certLoadErr != nil {
return nil, fmt.Errorf("redis: Error loading X509 Key Pair: %w", certLoadErr)
}
o.TLSConfig.Certificates = []tls.Certificate{cert}
}
o.TLSConfig.MinVersion = uint16(q.int("TLSMinVersion"))
o.TLSConfig.MaxVersion = uint16(q.int("TLSMaxVersion"))
o.TLSConfig.InsecureSkipVerify = q.bool("TLSInsecureSkipVerify")
serverNameOverride := q.string("ServerName")
if serverNameOverride != "" {
// we explicitly check for this query parameter, so we don't overwrite
// the default server name (the hostname of the Redis server) if it's
// not given
o.TLSConfig.ServerName = serverNameOverride
}
}
if q.err != nil { if q.err != nil {
return nil, q.err return nil, q.err
} }

View File

@ -11,6 +11,27 @@ import (
) )
func TestParseURL(t *testing.T) { func TestParseURL(t *testing.T) {
certPem := []byte(`-----BEGIN CERTIFICATE-----
MIIBhTCCASugAwIBAgIQIRi6zePL6mKjOipn+dNuaTAKBggqhkjOPQQDAjASMRAw
DgYDVQQKEwdBY21lIENvMB4XDTE3MTAyMDE5NDMwNloXDTE4MTAyMDE5NDMwNlow
EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD0d
7VNhbWvZLWPuj/RtHFjvtJBEwOkhbN/BnnE8rnZR8+sbwnc/KhCk3FhnpHZnQz7B
5aETbbIgmuvewdjvSBSjYzBhMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr
BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1UdEQQiMCCCDmxvY2FsaG9zdDo1
NDUzgg4xMjcuMC4wLjE6NTQ1MzAKBggqhkjOPQQDAgNIADBFAiEA2zpJEPQyz6/l
Wf86aX6PepsntZv2GYlA5UpabfT2EZICICpJ5h/iI+i341gBmLiAFQOyTDT+/wQc
6MF9+Yw1Yy0t
-----END CERTIFICATE-----`)
keyPem := []byte(`-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIIrYSSNQFaA2Hwf1duRSxKtLYX5CB04fSeQ6tF1aY/PuoAoGCCqGSM49
AwEHoUQDQgAEPR3tU2Fta9ktY+6P9G0cWO+0kETA6SFs38GecTyudlHz6xvCdz8q
EKTcWGekdmdDPsHloRNtsiCa697B2O9IFA==
-----END EC PRIVATE KEY-----`)
testCert, err := tls.X509KeyPair(certPem, keyPem)
if err != nil {
t.Fatal(err)
}
cases := []struct { cases := []struct {
url string url string
o *Options // expected value o *Options // expected value
@ -30,7 +51,24 @@ func TestParseURL(t *testing.T) {
o: &Options{Addr: "12345:6379"}, o: &Options{Addr: "12345:6379"},
}, { }, {
url: "rediss://localhost:123", url: "rediss://localhost:123",
o: &Options{Addr: "localhost:123", TLSConfig: &tls.Config{ /* no deep comparison */ }}, o: &Options{Addr: "localhost:123", TLSConfig: &tls.Config{ServerName: "localhost"}},
}, {
url: "rediss://localhost:123?ServerName=abc&TLSMinVersion=1&TLSMaxVersion=3&TLSInsecureSkipVerify=true",
o: &Options{Addr: "localhost:123", TLSConfig: &tls.Config{ServerName: "abc", MinVersion: 1, MaxVersion: 3, InsecureSkipVerify: true}},
}, {
url: "rediss://localhost:123?TLSCertPEMFile=./testdata/testcert.pem&TLSKeyPEMFile=./testdata/testkey.pem",
o: &Options{Addr: "localhost:123", TLSConfig: &tls.Config{ServerName: "localhost", Certificates: []tls.Certificate{testCert}}},
}, {
url: "rediss://localhost:123?TLSCertPEMFile=./testdata/doesnotexist.pem&TLSKeyPEMFile=./testdata/testkey.pem",
o: &Options{Addr: "localhost:123", TLSConfig: &tls.Config{ServerName: "abc"}},
err: errors.New("redis: Error loading X509 Key Pair: open ./testdata/doesnotexist.pem: no such file or directory"),
}, {
url: "rediss://localhost:123?TLSCertPEMFile=./testdata/testcert.pem",
o: &Options{Addr: "localhost:123", TLSConfig: &tls.Config{ServerName: "abc"}},
err: errors.New("redis: TLSCertPEMFile and TLSKeyPEMFile URL parameters must be both set or both omitted"),
}, {
url: "rediss://localhost:123?TLSKeyPEMFile=./testdata/testkey.pem",
err: errors.New("redis: TLSCertPEMFile and TLSKeyPEMFile URL parameters must be both set or both omitted"),
}, { }, {
url: "redis://:bar@localhost:123", url: "redis://:bar@localhost:123",
o: &Options{Addr: "localhost:123", Password: "bar"}, o: &Options{Addr: "localhost:123", Password: "bar"},
@ -189,6 +227,39 @@ func comprareOptions(t *testing.T, actual, expected *Options) {
if actual.IdleCheckFrequency != expected.IdleCheckFrequency { if actual.IdleCheckFrequency != expected.IdleCheckFrequency {
t.Errorf("IdleCheckFrequency: got %v, expected %v", actual.IdleCheckFrequency, expected.IdleCheckFrequency) t.Errorf("IdleCheckFrequency: got %v, expected %v", actual.IdleCheckFrequency, expected.IdleCheckFrequency)
} }
if (actual.TLSConfig == nil) != (expected.TLSConfig == nil) {
t.Errorf("TLSConfig nil: got %v, expected %v", actual.TLSConfig == nil, expected.TLSConfig == nil)
}
if (actual.TLSConfig != nil) && (expected.TLSConfig != nil) {
if actual.TLSConfig.MinVersion != expected.TLSConfig.MinVersion {
t.Errorf("TLSConfig.MinVersion: got %v, expected %v", actual.TLSConfig.MinVersion, expected.TLSConfig.MinVersion)
}
if actual.TLSConfig.MaxVersion != expected.TLSConfig.MaxVersion {
t.Errorf("TLSConfig.MaxVersion: got %v, expected %v", actual.TLSConfig.MaxVersion, expected.TLSConfig.MaxVersion)
}
if actual.TLSConfig.ServerName != expected.TLSConfig.ServerName {
t.Errorf("TLSConfig.ServerName: got %v, expected %v", actual.TLSConfig.ServerName, expected.TLSConfig.ServerName)
}
if actual.TLSConfig.InsecureSkipVerify != expected.TLSConfig.InsecureSkipVerify {
t.Errorf("TLSConfig.InsecureSkipVerify: got %v, expected %v", actual.TLSConfig.InsecureSkipVerify, expected.TLSConfig.InsecureSkipVerify)
}
if len(actual.TLSConfig.Certificates) != len(expected.TLSConfig.Certificates) {
t.Errorf("TLSConfig.Certificates: got %v, expected %v", actual.TLSConfig.Certificates, expected.TLSConfig.Certificates)
}
for i, actualCert := range actual.TLSConfig.Certificates {
expectedCert := expected.TLSConfig.Certificates[i]
if !actualCert.Leaf.Equal(expectedCert.Leaf) {
t.Errorf("TLSConfig.Certificates[%d].Leaf: got %v, expected %v", i, actual.TLSConfig.Certificates, expected.TLSConfig.Certificates)
}
}
}
} }
// Test ReadTimeout option initialization, including special values -1 and 0. // Test ReadTimeout option initialization, including special values -1 and 0.

11
testdata/testcert.pem vendored Normal file
View File

@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE-----
MIIBhTCCASugAwIBAgIQIRi6zePL6mKjOipn+dNuaTAKBggqhkjOPQQDAjASMRAw
DgYDVQQKEwdBY21lIENvMB4XDTE3MTAyMDE5NDMwNloXDTE4MTAyMDE5NDMwNlow
EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD0d
7VNhbWvZLWPuj/RtHFjvtJBEwOkhbN/BnnE8rnZR8+sbwnc/KhCk3FhnpHZnQz7B
5aETbbIgmuvewdjvSBSjYzBhMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr
BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1UdEQQiMCCCDmxvY2FsaG9zdDo1
NDUzgg4xMjcuMC4wLjE6NTQ1MzAKBggqhkjOPQQDAgNIADBFAiEA2zpJEPQyz6/l
Wf86aX6PepsntZv2GYlA5UpabfT2EZICICpJ5h/iI+i341gBmLiAFQOyTDT+/wQc
6MF9+Yw1Yy0t
-----END CERTIFICATE-----

5
testdata/testkey.pem vendored Normal file
View File

@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIIrYSSNQFaA2Hwf1duRSxKtLYX5CB04fSeQ6tF1aY/PuoAoGCCqGSM49
AwEHoUQDQgAEPR3tU2Fta9ktY+6P9G0cWO+0kETA6SFs38GecTyudlHz6xvCdz8q
EKTcWGekdmdDPsHloRNtsiCa697B2O9IFA==
-----END EC PRIVATE KEY-----