Skip to content

Commit fae038d

Browse files
committed
feat: add TLS URL parameters
1 parent 4c8b4d1 commit fae038d

File tree

4 files changed

+119
-1
lines changed

4 files changed

+119
-1
lines changed

options.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,37 @@ func setupConnParams(u *url.URL, o *Options) (*Options, error) {
390390
o.PoolTimeout = q.duration("pool_timeout")
391391
o.IdleTimeout = q.duration("idle_timeout")
392392
o.IdleCheckFrequency = q.duration("idle_check_frequency")
393+
394+
if u.Scheme == "rediss" {
395+
tlsCertPEMFile := q.string("TLSCertPEMFile")
396+
tlsKeyPEMFile := q.string("TLSKeyPEMFile")
397+
398+
if (tlsCertPEMFile == "") != (tlsKeyPEMFile == "") {
399+
return nil, fmt.Errorf("redis: TLSCertPEMFile and TLSKeyPEMFile URL parameters must be both set or both omitted")
400+
}
401+
402+
if tlsCertPEMFile != "" {
403+
cert, certLoadErr := tls.LoadX509KeyPair(tlsCertPEMFile, tlsKeyPEMFile)
404+
if certLoadErr != nil {
405+
return nil, fmt.Errorf("redis: Error loading X509 Key Pair: %w", certLoadErr)
406+
}
407+
408+
o.TLSConfig.Certificates = []tls.Certificate{cert}
409+
}
410+
411+
o.TLSConfig.MinVersion = uint16(q.int("TLSMinVersion"))
412+
o.TLSConfig.MaxVersion = uint16(q.int("TLSMaxVersion"))
413+
o.TLSConfig.InsecureSkipVerify = q.bool("TLSInsecureSkipVerify")
414+
415+
serverNameOverride := q.string("ServerName")
416+
if serverNameOverride != "" {
417+
// we explicitly check for this query parameter, so we don't overwrite
418+
// the default server name (the hostname of the Redis server) if it's
419+
// not given
420+
o.TLSConfig.ServerName = serverNameOverride
421+
}
422+
}
423+
393424
if q.err != nil {
394425
return nil, q.err
395426
}

options_test.go

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,27 @@ import (
1111
)
1212

1313
func TestParseURL(t *testing.T) {
14+
certPem := []byte(`-----BEGIN CERTIFICATE-----
15+
MIIBhTCCASugAwIBAgIQIRi6zePL6mKjOipn+dNuaTAKBggqhkjOPQQDAjASMRAw
16+
DgYDVQQKEwdBY21lIENvMB4XDTE3MTAyMDE5NDMwNloXDTE4MTAyMDE5NDMwNlow
17+
EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD0d
18+
7VNhbWvZLWPuj/RtHFjvtJBEwOkhbN/BnnE8rnZR8+sbwnc/KhCk3FhnpHZnQz7B
19+
5aETbbIgmuvewdjvSBSjYzBhMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr
20+
BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1UdEQQiMCCCDmxvY2FsaG9zdDo1
21+
NDUzgg4xMjcuMC4wLjE6NTQ1MzAKBggqhkjOPQQDAgNIADBFAiEA2zpJEPQyz6/l
22+
Wf86aX6PepsntZv2GYlA5UpabfT2EZICICpJ5h/iI+i341gBmLiAFQOyTDT+/wQc
23+
6MF9+Yw1Yy0t
24+
-----END CERTIFICATE-----`)
25+
keyPem := []byte(`-----BEGIN EC PRIVATE KEY-----
26+
MHcCAQEEIIrYSSNQFaA2Hwf1duRSxKtLYX5CB04fSeQ6tF1aY/PuoAoGCCqGSM49
27+
AwEHoUQDQgAEPR3tU2Fta9ktY+6P9G0cWO+0kETA6SFs38GecTyudlHz6xvCdz8q
28+
EKTcWGekdmdDPsHloRNtsiCa697B2O9IFA==
29+
-----END EC PRIVATE KEY-----`)
30+
testCert, err := tls.X509KeyPair(certPem, keyPem)
31+
if err != nil {
32+
t.Fatal(err)
33+
}
34+
1435
cases := []struct {
1536
url string
1637
o *Options // expected value
@@ -30,7 +51,24 @@ func TestParseURL(t *testing.T) {
3051
o: &Options{Addr: "12345:6379"},
3152
}, {
3253
url: "rediss://localhost:123",
33-
o: &Options{Addr: "localhost:123", TLSConfig: &tls.Config{ /* no deep comparison */ }},
54+
o: &Options{Addr: "localhost:123", TLSConfig: &tls.Config{ServerName: "localhost"}},
55+
}, {
56+
url: "rediss://localhost:123?ServerName=abc&TLSMinVersion=1&TLSMaxVersion=3&TLSInsecureSkipVerify=true",
57+
o: &Options{Addr: "localhost:123", TLSConfig: &tls.Config{ServerName: "abc", MinVersion: 1, MaxVersion: 3, InsecureSkipVerify: true}},
58+
}, {
59+
url: "rediss://localhost:123?TLSCertPEMFile=./testdata/testcert.pem&TLSKeyPEMFile=./testdata/testkey.pem",
60+
o: &Options{Addr: "localhost:123", TLSConfig: &tls.Config{ServerName: "localhost", Certificates: []tls.Certificate{testCert}}},
61+
}, {
62+
url: "rediss://localhost:123?TLSCertPEMFile=./testdata/doesnotexist.pem&TLSKeyPEMFile=./testdata/testkey.pem",
63+
o: &Options{Addr: "localhost:123", TLSConfig: &tls.Config{ServerName: "abc"}},
64+
err: errors.New("redis: Error loading X509 Key Pair: open ./testdata/doesnotexist.pem: no such file or directory"),
65+
}, {
66+
url: "rediss://localhost:123?TLSCertPEMFile=./testdata/testcert.pem",
67+
o: &Options{Addr: "localhost:123", TLSConfig: &tls.Config{ServerName: "abc"}},
68+
err: errors.New("redis: TLSCertPEMFile and TLSKeyPEMFile URL parameters must be both set or both omitted"),
69+
}, {
70+
url: "rediss://localhost:123?TLSKeyPEMFile=./testdata/testkey.pem",
71+
err: errors.New("redis: TLSCertPEMFile and TLSKeyPEMFile URL parameters must be both set or both omitted"),
3472
}, {
3573
url: "redis://:bar@localhost:123",
3674
o: &Options{Addr: "localhost:123", Password: "bar"},
@@ -189,6 +227,39 @@ func comprareOptions(t *testing.T, actual, expected *Options) {
189227
if actual.IdleCheckFrequency != expected.IdleCheckFrequency {
190228
t.Errorf("IdleCheckFrequency: got %v, expected %v", actual.IdleCheckFrequency, expected.IdleCheckFrequency)
191229
}
230+
231+
if (actual.TLSConfig == nil) != (expected.TLSConfig == nil) {
232+
t.Errorf("TLSConfig nil: got %v, expected %v", actual.TLSConfig == nil, expected.TLSConfig == nil)
233+
}
234+
235+
if (actual.TLSConfig != nil) && (expected.TLSConfig != nil) {
236+
if actual.TLSConfig.MinVersion != expected.TLSConfig.MinVersion {
237+
t.Errorf("TLSConfig.MinVersion: got %v, expected %v", actual.TLSConfig.MinVersion, expected.TLSConfig.MinVersion)
238+
}
239+
240+
if actual.TLSConfig.MaxVersion != expected.TLSConfig.MaxVersion {
241+
t.Errorf("TLSConfig.MaxVersion: got %v, expected %v", actual.TLSConfig.MaxVersion, expected.TLSConfig.MaxVersion)
242+
}
243+
244+
if actual.TLSConfig.ServerName != expected.TLSConfig.ServerName {
245+
t.Errorf("TLSConfig.ServerName: got %v, expected %v", actual.TLSConfig.ServerName, expected.TLSConfig.ServerName)
246+
}
247+
248+
if actual.TLSConfig.InsecureSkipVerify != expected.TLSConfig.InsecureSkipVerify {
249+
t.Errorf("TLSConfig.InsecureSkipVerify: got %v, expected %v", actual.TLSConfig.InsecureSkipVerify, expected.TLSConfig.InsecureSkipVerify)
250+
}
251+
252+
if len(actual.TLSConfig.Certificates) != len(expected.TLSConfig.Certificates) {
253+
t.Errorf("TLSConfig.Certificates: got %v, expected %v", actual.TLSConfig.Certificates, expected.TLSConfig.Certificates)
254+
}
255+
256+
for i, actualCert := range actual.TLSConfig.Certificates {
257+
expectedCert := expected.TLSConfig.Certificates[i]
258+
if !actualCert.Leaf.Equal(expectedCert.Leaf) {
259+
t.Errorf("TLSConfig.Certificates[%d].Leaf: got %v, expected %v", i, actual.TLSConfig.Certificates, expected.TLSConfig.Certificates)
260+
}
261+
}
262+
}
192263
}
193264

194265
// Test ReadTimeout option initialization, including special values -1 and 0.

testdata/testcert.pem

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIBhTCCASugAwIBAgIQIRi6zePL6mKjOipn+dNuaTAKBggqhkjOPQQDAjASMRAw
3+
DgYDVQQKEwdBY21lIENvMB4XDTE3MTAyMDE5NDMwNloXDTE4MTAyMDE5NDMwNlow
4+
EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD0d
5+
7VNhbWvZLWPuj/RtHFjvtJBEwOkhbN/BnnE8rnZR8+sbwnc/KhCk3FhnpHZnQz7B
6+
5aETbbIgmuvewdjvSBSjYzBhMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr
7+
BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1UdEQQiMCCCDmxvY2FsaG9zdDo1
8+
NDUzgg4xMjcuMC4wLjE6NTQ1MzAKBggqhkjOPQQDAgNIADBFAiEA2zpJEPQyz6/l
9+
Wf86aX6PepsntZv2GYlA5UpabfT2EZICICpJ5h/iI+i341gBmLiAFQOyTDT+/wQc
10+
6MF9+Yw1Yy0t
11+
-----END CERTIFICATE-----

testdata/testkey.pem

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-----BEGIN EC PRIVATE KEY-----
2+
MHcCAQEEIIrYSSNQFaA2Hwf1duRSxKtLYX5CB04fSeQ6tF1aY/PuoAoGCCqGSM49
3+
AwEHoUQDQgAEPR3tU2Fta9ktY+6P9G0cWO+0kETA6SFs38GecTyudlHz6xvCdz8q
4+
EKTcWGekdmdDPsHloRNtsiCa697B2O9IFA==
5+
-----END EC PRIVATE KEY-----

0 commit comments

Comments
 (0)