Skip to content

Commit

Permalink
feature: added new options 'ssl' and 'ssl_verify' to the 'connect()' …
Browse files Browse the repository at this point in the history
…method for connecting to Redis over TLS.

Signed-off-by: Thibault Charbonnier <thibaultcha@me.com>
  • Loading branch information
vinayakhulawale authored and thibaultcha committed Sep 1, 2019
1 parent 12b10cc commit 79d2421
Show file tree
Hide file tree
Showing 8 changed files with 576 additions and 5 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ install:
- git clone https://github.com/openresty/nginx-devel-utils.git
- git clone https://github.com/openresty/lua-cjson.git
- git clone https://github.com/openresty/lua-nginx-module.git ../lua-nginx-module
- git clone https://github.com/openresty/stream-lua-nginx-module.git ../stream-lua-nginx-module
- git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core
- git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache
- git clone https://github.com/openresty/echo-nginx-module.git ../echo-nginx-module
Expand All @@ -62,7 +63,7 @@ script:
- cd ..
- export PATH=$PWD/work/nginx/sbin:$PWD/nginx-devel-utils:$PATH
- export NGX_BUILD_CC=$CC
- 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
- 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
- nginx -V
- ldd `which nginx`|grep -E 'luajit|ssl|pcre'
- prove -r t
22 changes: 21 additions & 1 deletion README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -225,12 +225,32 @@ Attempts to connect to the remote host and port that the redis server is listeni

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.

An optional Lua table can be specified as the last argument to this method to specify various connect options:
The optional `options_table` argument is a Lua table holding the following keys:

* `ssl`

If set to true, then uses SSL to connect to redis (defaults to false).

* `ssl_verify`

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.

* `server_name`

Specifies the server name for the new TLS extension Server Name Indication (SNI) when connecting over SSL.

* `pool`

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

* `pool_size`

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.

* `backlog`

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.

[Back to TOC](#table-of-contents)

set_timeout
Expand Down
65 changes: 63 additions & 2 deletions lib/resty/redis.lua
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,76 @@ function _M.set_timeouts(self, connect_timeout, send_timeout, read_timeout)
end


function _M.connect(self, ...)
function _M.connect(self, host, port_or_opts, opts)
local sock = rawget(self, "_sock")
if not sock then
return nil, "not initialized"
end

local unix

do
local typ = type(host)
if typ ~= "string" then
error("bad argument #1 host: string expected, got " .. typ, 2)
end

if sub(host, 1, 5) == "unix:" then
unix = true
end

if unix then
typ = type(port_or_opts)
if port_or_opts ~= nil and typ ~= "table" then
error("bad argument #2 opts: nil or table expected, got " ..
typ, 2)
end

else
typ = type(port_or_opts)
if typ ~= "number" then
port_or_opts = tonumber(port_or_opts)
if port_or_opts == nil then
error("bad argument #2 port: number expected, got " ..
typ, 2)
end
end

if opts ~= nil then
typ = type(opts)
if typ ~= "table" then
error("bad argument #3 opts: nil or table expected, got " ..
typ, 2)
end
end
end

end

self._subscribed = false

return sock:connect(...)
local ok, err

if unix then
ok, err = sock:connect(host, port_or_opts)
opts = port_or_opts

else
ok, err = sock:connect(host, port_or_opts, opts)
end

if not ok then
return ok, err
end

if opts and opts.ssl then
ok, err = sock:sslhandshake(false, opts.server_name, opts.ssl_verify)
if not ok then
return ok, "failed to do ssl handshake: " .. err
end
end

return ok, err
end


Expand Down
2 changes: 1 addition & 1 deletion t/bugs.t
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ run_tests();

__DATA__

=== TEST 1: github issue #108: ngx.locaiton.capture + redis.set_keepalive
=== TEST 1: github issue #108: ngx.location.capture + redis.set_keepalive
--- http_config eval: $::HttpConfig
--- config
location /r1 {
Expand Down
17 changes: 17 additions & 0 deletions t/cert/test.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICqTCCAhICCQClDm1WkreW4jANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMC
VVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28x
EjAQBgNVBAoMCU9wZW5SZXN0eTESMBAGA1UECwwJT3BlblJlc3R5MREwDwYDVQQD
DAh0ZXN0LmNvbTEgMB4GCSqGSIb3DQEJARYRYWdlbnR6aEBnbWFpbC5jb20wIBcN
MTQwNzIxMDMyMzQ3WhgPMjE1MTA2MTMwMzIzNDdaMIGXMQswCQYDVQQGEwJVUzET
MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzESMBAG
A1UECgwJT3BlblJlc3R5MRIwEAYDVQQLDAlPcGVuUmVzdHkxETAPBgNVBAMMCHRl
c3QuY29tMSAwHgYJKoZIhvcNAQkBFhFhZ2VudHpoQGdtYWlsLmNvbTCBnzANBgkq
hkiG9w0BAQEFAAOBjQAwgYkCgYEA6P18zUvtmaKQK2xePy8ZbFwSyTLw+jW6t9eZ
aiTec8X3ibN9WemrxHzkTRikxP3cAQoITRuZiQvF4Q7DO6wMkz/b0zwfgX5uedGq
047AJP6n/mwlDOjGSNomBLoXQzo7tVe60ikEm3ZyDUqnJPJMt3hImO5XSop4MPMu
Za9WhFcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQA4OBb9bOyWB1//93nSXX1mdENZ
IQeyTK0Dd6My76lnZxnZ4hTWrvvd0b17KLDU6JnS2N5ee3ATVkojPidRLWLIhnh5
0eXrcKalbO2Ce6nShoFvQCQKXN2Txmq2vO/Mud2bHAWwJALg+qi1Iih/gVYB9sct
FLg8zFOzRlYiU+6Mmw==
-----END CERTIFICATE-----
15 changes: 15 additions & 0 deletions t/cert/test.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQDo/XzNS+2ZopArbF4/LxlsXBLJMvD6Nbq315lqJN5zxfeJs31Z
6avEfORNGKTE/dwBCghNG5mJC8XhDsM7rAyTP9vTPB+Bfm550arTjsAk/qf+bCUM
6MZI2iYEuhdDOju1V7rSKQSbdnINSqck8ky3eEiY7ldKingw8y5lr1aEVwIDAQAB
AoGBANgB66sKMga2SKN5nQdHS3LDCkevCutu1OWM5ZcbB4Kej5kC57xsf+tzPtab
emeIVGhCPOAALqB4YcT+QtMX967oM1MjcFbtH7si5oq6UYyp3i0G9Si6jIoVHz3+
8yOUaqwKbK+bRX8VS0YsHZmBsPK5ryN50iUwsU08nemoA94BAkEA9GS9Q5OPeFkM
tFxsIQ1f2FSsZAuN/1cpZgJqY+YaAN7MSPGTWyfd7nWG/Zgk3GO9/2ihh4gww+7B
To09GkmW4QJBAPQOHC2V+t2TA98+6Lj6+TYwcGEkhOENfVpH25mQ+kXgF/1Bd6rA
nosT1bdAY+SnmWXbSw6Kv5C20Em+bEX8WjcCQCSRRjhsRdVODbaW9Z7kb2jhEoJN
sEt6cTlQNzcHYPCsZYisjM3g4zYg47fiIfHQAsfKkhDDcfh/KvFj9LaQOEECQQCH
eBWYEDpSJ7rsfqT7mQQgWj7nDThdG/nK1TxGP71McBmg0Gg2dfkLRhVJRQqt74Is
kc9V4Rp4n6F6baL4Lh19AkEA6pZZer0kg3Kv9hjhaITIKUYdfIp9vYnDRWbQlBmR
atV8V9u9q2ETZvqfHpN+9Lu6NYR4yXIEIRf1bnIZ/mr9eQ==
-----END RSA PRIVATE KEY-----
211 changes: 211 additions & 0 deletions t/sanity.t
Original file line number Diff line number Diff line change
Expand Up @@ -967,3 +967,214 @@ flushall: OK
failed to blpop: timeout
--- no_error_log
[alert]



=== TEST 19: connect() bad host argument (boolean)
--- http_config eval: $::HttpConfig
--- config
location /t {
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()

red:connect(true)
}
}
--- request
GET /t
--- error_code: 500
--- ignore_response_body
--- error_log
bad argument #1 host: string expected, got boolean
--- no_error_log
[crit]



=== TEST 20: connect() bad host argument (nil)
--- http_config eval: $::HttpConfig
--- config
location /t {
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()

red:connect(nil)
}
}
--- request
GET /t
--- error_code: 500
--- ignore_response_body
--- error_log
bad argument #1 host: string expected, got nil
--- no_error_log
[crit]



=== TEST 21: connect() bad port argument (nil)
--- http_config eval: $::HttpConfig
--- config
location /t {
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()

red:connect("127.0.0.1", nil)
}
}
--- request
GET /t
--- error_code: 500
--- ignore_response_body
--- error_log
bad argument #2 port: number expected, got nil
--- no_error_log
[crit]



=== TEST 22: connect() bad port argument (boolean)
--- http_config eval: $::HttpConfig
--- config
location /t {
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()

red:connect("127.0.0.1", true)
}
}
--- request
GET /t
--- error_code: 500
--- ignore_response_body
--- error_log
bad argument #2 port: number expected, got boolean
--- no_error_log
[crit]



=== TEST 23: connect() bad port argument (string)
--- http_config eval: $::HttpConfig
--- config
location /t {
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()

red:connect("127.0.0.1", "foo")
}
}
--- request
GET /t
--- error_code: 500
--- ignore_response_body
--- error_log
bad argument #2 port: number expected, got string
--- no_error_log
[crit]



=== TEST 24: connect() accepts port argument as string
--- http_config eval: $::HttpConfig
--- config
location /t {
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()

red:set_timeout(1000) -- 1 sec

local ok, err = red:connect("127.0.0.1", tostring($TEST_NGINX_REDIS_PORT))
if not ok then
ngx.say("failed to connect: ", err)
return
end

ngx.say("ok")
}
}
--- request
GET /t
--- response_body
ok
--- no_error_log
[error]



=== TEST 25: connect() bad opts argument
--- http_config eval: $::HttpConfig
--- config
location /t {
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()

red:connect("127.0.0.1", $TEST_NGINX_REDIS_PORT, true)
}
}
--- request
GET /t
--- error_code: 500
--- ignore_response_body
--- error_log
bad argument #3 opts: nil or table expected, got boolean
--- no_error_log
[crit]



=== TEST 26: connect() bad opts argument for unix sockets
--- http_config eval: $::HttpConfig
--- config
location /t {
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()

red:connect("unix:", true)
}
}
--- request
GET /t
--- error_code: 500
--- ignore_response_body
--- error_log
bad argument #2 opts: nil or table expected, got boolean
--- no_error_log
[crit]



=== TEST 27: connect() unix socket arguments when 'host' starts with 'unix:'
--- http_config eval: $::HttpConfig
--- config
location /t {
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()

local pok, perr = pcall(red.connect, red, "unix:", true)
if not pok then
ngx.say(perr)
end

local pok, perr = pcall(red.connect, red, "_unix:", true)
if not pok then
ngx.say(perr)
end
}
}
--- request
GET /t
--- response_body
bad argument #2 opts: nil or table expected, got boolean
bad argument #2 port: number expected, got boolean
--- no_error_log
[error]
Loading

0 comments on commit 79d2421

Please sign in to comment.