Skip to content

Commit 79d2421

Browse files
vinayakhulawalethibaultcha
authored andcommitted
feature: added new options 'ssl' and 'ssl_verify' to the 'connect()' method for connecting to Redis over TLS.
Signed-off-by: Thibault Charbonnier <[email protected]>
1 parent 12b10cc commit 79d2421

File tree

8 files changed

+576
-5
lines changed

8 files changed

+576
-5
lines changed

.travis.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ install:
4242
- git clone https://github.com/openresty/nginx-devel-utils.git
4343
- git clone https://github.com/openresty/lua-cjson.git
4444
- git clone https://github.com/openresty/lua-nginx-module.git ../lua-nginx-module
45+
- git clone https://github.com/openresty/stream-lua-nginx-module.git ../stream-lua-nginx-module
4546
- git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core
4647
- git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache
4748
- git clone https://github.com/openresty/echo-nginx-module.git ../echo-nginx-module
@@ -62,7 +63,7 @@ script:
6263
- cd ..
6364
- export PATH=$PWD/work/nginx/sbin:$PWD/nginx-devel-utils:$PATH
6465
- export NGX_BUILD_CC=$CC
65-
- ngx-build $NGINX_VERSION --with-ipv6 --with-http_realip_module --with-http_ssl_module --add-module=../echo-nginx-module --add-module=../lua-nginx-module --with-debug
66+
- ngx-build $NGINX_VERSION --with-ipv6 --with-http_realip_module --with-http_ssl_module --add-module=../echo-nginx-module --add-module=../lua-nginx-module --add-module=../stream-lua-nginx-module --with-stream --with-stream_ssl_module --with-debug
6667
- nginx -V
6768
- ldd `which nginx`|grep -E 'luajit|ssl|pcre'
6869
- prove -r t

README.markdown

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,12 +225,32 @@ Attempts to connect to the remote host and port that the redis server is listeni
225225

226226
Before actually resolving the host name and connecting to the remote backend, this method will always look up the connection pool for matched idle connections created by previous calls of this method.
227227

228-
An optional Lua table can be specified as the last argument to this method to specify various connect options:
228+
The optional `options_table` argument is a Lua table holding the following keys:
229+
230+
* `ssl`
231+
232+
If set to true, then uses SSL to connect to redis (defaults to false).
233+
234+
* `ssl_verify`
235+
236+
If set to true, then verifies the validity of the server SSL certificate (defaults to false). Note that you need to configure the lua_ssl_trusted_certificate to specify the CA (or server) certificate used by your redis server. You may also need to configure lua_ssl_verify_depth accordingly.
237+
238+
* `server_name`
239+
240+
Specifies the server name for the new TLS extension Server Name Indication (SNI) when connecting over SSL.
229241

230242
* `pool`
231243

232244
Specifies a custom name for the connection pool being used. If omitted, then the connection pool name will be generated from the string template `<host>:<port>` or `<unix-socket-path>`.
233245

246+
* `pool_size`
247+
248+
Specifies the size of the connection pool. If omitted and no `backlog` option was provided, no pool will be created. If omitted but `backlog` was provided, the pool will be created with a default size equal to the value of the [lua_socket_pool_size](https://github.com/openresty/lua-nginx-module#lua_socket_pool_size) directive. The connection pool holds up to `pool_size` alive connections ready to be reused by subsequent calls to [connect](#connect), but note that there is no upper limit to the total number of opened connections outside of the pool. If you need to restrict the total number of opened connections, specify the `backlog` option. When the connection pool would exceed its size limit, the least recently used (kept-alive) connection already in the pool will be closed to make room for the current connection. Note that the cosocket connection pool is per Nginx worker process rather than per Nginx server instance, so the size limit specified here also applies to every single Nginx worker process. Also note that the size of the connection pool cannot be changed once it has been created. Note that at least [ngx_lua 0.10.14](https://github.com/openresty/lua-nginx-module/tags) is required to use this options.
249+
250+
* `backlog`
251+
252+
If specified, this module will limit the total number of opened connections for this pool. No more connections than `pool_size` can be opened for this pool at any time. If the connection pool is full, subsequent connect operations will be queued into a queue equal to this option's value (the "backlog" queue). If the number of queued connect operations is equal to `backlog`, subsequent connect operations will fail and return nil plus the error string `"too many waiting connect operations"`. The queued connect operations will be resumed once the number of connections in the pool is less than `pool_size`. The queued connect operation will abort once they have been queued for more than `connect_timeout`, controlled by [set_timeout](#set_timeout), and will return nil plus the error string "timeout". Note that at least [ngx_lua 0.10.14](https://github.com/openresty/lua-nginx-module/tags) is required to use this options.
253+
234254
[Back to TOC](#table-of-contents)
235255

236256
set_timeout

lib/resty/redis.lua

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,76 @@ function _M.set_timeouts(self, connect_timeout, send_timeout, read_timeout)
8787
end
8888

8989

90-
function _M.connect(self, ...)
90+
function _M.connect(self, host, port_or_opts, opts)
9191
local sock = rawget(self, "_sock")
9292
if not sock then
9393
return nil, "not initialized"
9494
end
9595

96+
local unix
97+
98+
do
99+
local typ = type(host)
100+
if typ ~= "string" then
101+
error("bad argument #1 host: string expected, got " .. typ, 2)
102+
end
103+
104+
if sub(host, 1, 5) == "unix:" then
105+
unix = true
106+
end
107+
108+
if unix then
109+
typ = type(port_or_opts)
110+
if port_or_opts ~= nil and typ ~= "table" then
111+
error("bad argument #2 opts: nil or table expected, got " ..
112+
typ, 2)
113+
end
114+
115+
else
116+
typ = type(port_or_opts)
117+
if typ ~= "number" then
118+
port_or_opts = tonumber(port_or_opts)
119+
if port_or_opts == nil then
120+
error("bad argument #2 port: number expected, got " ..
121+
typ, 2)
122+
end
123+
end
124+
125+
if opts ~= nil then
126+
typ = type(opts)
127+
if typ ~= "table" then
128+
error("bad argument #3 opts: nil or table expected, got " ..
129+
typ, 2)
130+
end
131+
end
132+
end
133+
134+
end
135+
96136
self._subscribed = false
97137

98-
return sock:connect(...)
138+
local ok, err
139+
140+
if unix then
141+
ok, err = sock:connect(host, port_or_opts)
142+
opts = port_or_opts
143+
144+
else
145+
ok, err = sock:connect(host, port_or_opts, opts)
146+
end
147+
148+
if not ok then
149+
return ok, err
150+
end
151+
152+
if opts and opts.ssl then
153+
ok, err = sock:sslhandshake(false, opts.server_name, opts.ssl_verify)
154+
if not ok then
155+
return ok, "failed to do ssl handshake: " .. err
156+
end
157+
end
158+
159+
return ok, err
99160
end
100161

101162

t/bugs.t

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ run_tests();
2626

2727
__DATA__
2828

29-
=== TEST 1: github issue #108: ngx.locaiton.capture + redis.set_keepalive
29+
=== TEST 1: github issue #108: ngx.location.capture + redis.set_keepalive
3030
--- http_config eval: $::HttpConfig
3131
--- config
3232
location /r1 {

t/cert/test.crt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIICqTCCAhICCQClDm1WkreW4jANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMC
3+
VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28x
4+
EjAQBgNVBAoMCU9wZW5SZXN0eTESMBAGA1UECwwJT3BlblJlc3R5MREwDwYDVQQD
5+
DAh0ZXN0LmNvbTEgMB4GCSqGSIb3DQEJARYRYWdlbnR6aEBnbWFpbC5jb20wIBcN
6+
MTQwNzIxMDMyMzQ3WhgPMjE1MTA2MTMwMzIzNDdaMIGXMQswCQYDVQQGEwJVUzET
7+
MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzESMBAG
8+
A1UECgwJT3BlblJlc3R5MRIwEAYDVQQLDAlPcGVuUmVzdHkxETAPBgNVBAMMCHRl
9+
c3QuY29tMSAwHgYJKoZIhvcNAQkBFhFhZ2VudHpoQGdtYWlsLmNvbTCBnzANBgkq
10+
hkiG9w0BAQEFAAOBjQAwgYkCgYEA6P18zUvtmaKQK2xePy8ZbFwSyTLw+jW6t9eZ
11+
aiTec8X3ibN9WemrxHzkTRikxP3cAQoITRuZiQvF4Q7DO6wMkz/b0zwfgX5uedGq
12+
047AJP6n/mwlDOjGSNomBLoXQzo7tVe60ikEm3ZyDUqnJPJMt3hImO5XSop4MPMu
13+
Za9WhFcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQA4OBb9bOyWB1//93nSXX1mdENZ
14+
IQeyTK0Dd6My76lnZxnZ4hTWrvvd0b17KLDU6JnS2N5ee3ATVkojPidRLWLIhnh5
15+
0eXrcKalbO2Ce6nShoFvQCQKXN2Txmq2vO/Mud2bHAWwJALg+qi1Iih/gVYB9sct
16+
FLg8zFOzRlYiU+6Mmw==
17+
-----END CERTIFICATE-----

t/cert/test.key

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-----BEGIN RSA PRIVATE KEY-----
2+
MIICXgIBAAKBgQDo/XzNS+2ZopArbF4/LxlsXBLJMvD6Nbq315lqJN5zxfeJs31Z
3+
6avEfORNGKTE/dwBCghNG5mJC8XhDsM7rAyTP9vTPB+Bfm550arTjsAk/qf+bCUM
4+
6MZI2iYEuhdDOju1V7rSKQSbdnINSqck8ky3eEiY7ldKingw8y5lr1aEVwIDAQAB
5+
AoGBANgB66sKMga2SKN5nQdHS3LDCkevCutu1OWM5ZcbB4Kej5kC57xsf+tzPtab
6+
emeIVGhCPOAALqB4YcT+QtMX967oM1MjcFbtH7si5oq6UYyp3i0G9Si6jIoVHz3+
7+
8yOUaqwKbK+bRX8VS0YsHZmBsPK5ryN50iUwsU08nemoA94BAkEA9GS9Q5OPeFkM
8+
tFxsIQ1f2FSsZAuN/1cpZgJqY+YaAN7MSPGTWyfd7nWG/Zgk3GO9/2ihh4gww+7B
9+
To09GkmW4QJBAPQOHC2V+t2TA98+6Lj6+TYwcGEkhOENfVpH25mQ+kXgF/1Bd6rA
10+
nosT1bdAY+SnmWXbSw6Kv5C20Em+bEX8WjcCQCSRRjhsRdVODbaW9Z7kb2jhEoJN
11+
sEt6cTlQNzcHYPCsZYisjM3g4zYg47fiIfHQAsfKkhDDcfh/KvFj9LaQOEECQQCH
12+
eBWYEDpSJ7rsfqT7mQQgWj7nDThdG/nK1TxGP71McBmg0Gg2dfkLRhVJRQqt74Is
13+
kc9V4Rp4n6F6baL4Lh19AkEA6pZZer0kg3Kv9hjhaITIKUYdfIp9vYnDRWbQlBmR
14+
atV8V9u9q2ETZvqfHpN+9Lu6NYR4yXIEIRf1bnIZ/mr9eQ==
15+
-----END RSA PRIVATE KEY-----

t/sanity.t

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -967,3 +967,214 @@ flushall: OK
967967
failed to blpop: timeout
968968
--- no_error_log
969969
[alert]
970+
971+
972+
973+
=== TEST 19: connect() bad host argument (boolean)
974+
--- http_config eval: $::HttpConfig
975+
--- config
976+
location /t {
977+
content_by_lua_block {
978+
local redis = require "resty.redis"
979+
local red = redis:new()
980+
981+
red:connect(true)
982+
}
983+
}
984+
--- request
985+
GET /t
986+
--- error_code: 500
987+
--- ignore_response_body
988+
--- error_log
989+
bad argument #1 host: string expected, got boolean
990+
--- no_error_log
991+
[crit]
992+
993+
994+
995+
=== TEST 20: connect() bad host argument (nil)
996+
--- http_config eval: $::HttpConfig
997+
--- config
998+
location /t {
999+
content_by_lua_block {
1000+
local redis = require "resty.redis"
1001+
local red = redis:new()
1002+
1003+
red:connect(nil)
1004+
}
1005+
}
1006+
--- request
1007+
GET /t
1008+
--- error_code: 500
1009+
--- ignore_response_body
1010+
--- error_log
1011+
bad argument #1 host: string expected, got nil
1012+
--- no_error_log
1013+
[crit]
1014+
1015+
1016+
1017+
=== TEST 21: connect() bad port argument (nil)
1018+
--- http_config eval: $::HttpConfig
1019+
--- config
1020+
location /t {
1021+
content_by_lua_block {
1022+
local redis = require "resty.redis"
1023+
local red = redis:new()
1024+
1025+
red:connect("127.0.0.1", nil)
1026+
}
1027+
}
1028+
--- request
1029+
GET /t
1030+
--- error_code: 500
1031+
--- ignore_response_body
1032+
--- error_log
1033+
bad argument #2 port: number expected, got nil
1034+
--- no_error_log
1035+
[crit]
1036+
1037+
1038+
1039+
=== TEST 22: connect() bad port argument (boolean)
1040+
--- http_config eval: $::HttpConfig
1041+
--- config
1042+
location /t {
1043+
content_by_lua_block {
1044+
local redis = require "resty.redis"
1045+
local red = redis:new()
1046+
1047+
red:connect("127.0.0.1", true)
1048+
}
1049+
}
1050+
--- request
1051+
GET /t
1052+
--- error_code: 500
1053+
--- ignore_response_body
1054+
--- error_log
1055+
bad argument #2 port: number expected, got boolean
1056+
--- no_error_log
1057+
[crit]
1058+
1059+
1060+
1061+
=== TEST 23: connect() bad port argument (string)
1062+
--- http_config eval: $::HttpConfig
1063+
--- config
1064+
location /t {
1065+
content_by_lua_block {
1066+
local redis = require "resty.redis"
1067+
local red = redis:new()
1068+
1069+
red:connect("127.0.0.1", "foo")
1070+
}
1071+
}
1072+
--- request
1073+
GET /t
1074+
--- error_code: 500
1075+
--- ignore_response_body
1076+
--- error_log
1077+
bad argument #2 port: number expected, got string
1078+
--- no_error_log
1079+
[crit]
1080+
1081+
1082+
1083+
=== TEST 24: connect() accepts port argument as string
1084+
--- http_config eval: $::HttpConfig
1085+
--- config
1086+
location /t {
1087+
content_by_lua_block {
1088+
local redis = require "resty.redis"
1089+
local red = redis:new()
1090+
1091+
red:set_timeout(1000) -- 1 sec
1092+
1093+
local ok, err = red:connect("127.0.0.1", tostring($TEST_NGINX_REDIS_PORT))
1094+
if not ok then
1095+
ngx.say("failed to connect: ", err)
1096+
return
1097+
end
1098+
1099+
ngx.say("ok")
1100+
}
1101+
}
1102+
--- request
1103+
GET /t
1104+
--- response_body
1105+
ok
1106+
--- no_error_log
1107+
[error]
1108+
1109+
1110+
1111+
=== TEST 25: connect() bad opts argument
1112+
--- http_config eval: $::HttpConfig
1113+
--- config
1114+
location /t {
1115+
content_by_lua_block {
1116+
local redis = require "resty.redis"
1117+
local red = redis:new()
1118+
1119+
red:connect("127.0.0.1", $TEST_NGINX_REDIS_PORT, true)
1120+
}
1121+
}
1122+
--- request
1123+
GET /t
1124+
--- error_code: 500
1125+
--- ignore_response_body
1126+
--- error_log
1127+
bad argument #3 opts: nil or table expected, got boolean
1128+
--- no_error_log
1129+
[crit]
1130+
1131+
1132+
1133+
=== TEST 26: connect() bad opts argument for unix sockets
1134+
--- http_config eval: $::HttpConfig
1135+
--- config
1136+
location /t {
1137+
content_by_lua_block {
1138+
local redis = require "resty.redis"
1139+
local red = redis:new()
1140+
1141+
red:connect("unix:", true)
1142+
}
1143+
}
1144+
--- request
1145+
GET /t
1146+
--- error_code: 500
1147+
--- ignore_response_body
1148+
--- error_log
1149+
bad argument #2 opts: nil or table expected, got boolean
1150+
--- no_error_log
1151+
[crit]
1152+
1153+
1154+
1155+
=== TEST 27: connect() unix socket arguments when 'host' starts with 'unix:'
1156+
--- http_config eval: $::HttpConfig
1157+
--- config
1158+
location /t {
1159+
content_by_lua_block {
1160+
local redis = require "resty.redis"
1161+
local red = redis:new()
1162+
1163+
local pok, perr = pcall(red.connect, red, "unix:", true)
1164+
if not pok then
1165+
ngx.say(perr)
1166+
end
1167+
1168+
local pok, perr = pcall(red.connect, red, "_unix:", true)
1169+
if not pok then
1170+
ngx.say(perr)
1171+
end
1172+
}
1173+
}
1174+
--- request
1175+
GET /t
1176+
--- response_body
1177+
bad argument #2 opts: nil or table expected, got boolean
1178+
bad argument #2 port: number expected, got boolean
1179+
--- no_error_log
1180+
[error]

0 commit comments

Comments
 (0)