Skip to content

Commit ecc5084

Browse files
committed
feature: added new boolean-value options "ssl" and "ssl_verify" to the connect() method connecting to MySQL via SSL.
1 parent ada4920 commit ecc5084

File tree

3 files changed

+210
-11
lines changed

3 files changed

+210
-11
lines changed

‎README.markdown‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,18 @@ The `options` argument is a Lua table holding the following keys:
205205
*`max_packet_size`
206206

207207
the upper limit for the reply packets sent from the MySQL server (default to 1MB).
208+
*`ssl`
209+
210+
If set to `true`, then uses SSL to connect to MySQL (default to `false`). If the MySQL
211+
server does not have SSL support
212+
(or just disabled), the error string "ssl disabled on server" will be returned.
213+
*`ssl_verify`
214+
215+
If set to `true`, then verifies the validity of the server SSL certificate (default to `false`).
216+
Note that you need to configure the [lua_ssl_trusted_certificate](https://github.com/openresty/lua-nginx-module#lua_ssl_trusted_certificate)
217+
to specify the CA certificate used by your MySQL server. You may also
218+
need to configure [lua_ssl_verify_depth](https://github.com/openresty/lua-nginx-module#lua_ssl_verify_depth)
219+
accordingly.
208220
*`pool`
209221

210222
the name for the MySQL connection pool. if omitted, an ambiguous pool name will be generated automatically with the string template `user:database:host:port` or `user:database:path`. (this option was first introduced in `v0.08`.)

‎lib/resty/mysql.lua‎

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ local tcp = ngx.socket.tcp
77
localstrbyte=string.byte
88
localstrchar=string.char
99
localstrfind=string.find
10+
localformat=string.format
1011
localstrrep=string.rep
1112
localnull=ngx.null
1213
localband=bit.band
@@ -23,6 +24,14 @@ local error = error
2324
localtonumber=tonumber
2425

2526

27+
ifnotngx.config
28+
ornotngx.config.ngx_lua_version
29+
orngx.config.ngx_lua_version<9011
30+
then
31+
error("ngx_lua 0.9.11+ required")
32+
end
33+
34+
2635
localok, new_tab=pcall(require, "table.new")
2736
ifnotokthen
2837
new_tab=function (narr, nrec) return{} end
@@ -38,6 +47,7 @@ local STATE_CONNECTED = 1
3847
localSTATE_COMMAND_SENT=2
3948

4049
localCOM_QUERY=0x03
50+
localCLIENT_SSL=0x0800
4151

4252
localSERVER_MORE_RESULTS_EXISTS=8
4353

@@ -136,7 +146,7 @@ local function _dump(data)
136146
locallen=#data
137147
localbytes=new_tab(len, 0)
138148
fori=1, lendo
139-
bytes[i] =strbyte(data, i)
149+
bytes[i] =format("%x", strbyte(data, i))
140150
end
141151
returnconcat(bytes, "")
142152
end
@@ -175,11 +185,13 @@ local function _send_packet(self, req, size)
175185

176186
self.packet_no=self.packet_no+1
177187

178-
--print("packet no: ", self.packet_no)
188+
--print("packet no: ", self.packet_no)
179189

180190
localpacket=_set_byte3(size) ..strchar(self.packet_no) ..req
181191

182-
--print("sending packet...")
192+
-- print("sending packet: ", _dump(packet))
193+
194+
-- print("sending packet... of size " .. #packet)
183195

184196
returnsock:send(packet)
185197
end
@@ -562,9 +574,10 @@ function _M.connect(self, opts)
562574
pos=pos+9-- skip filler
563575

564576
-- two lower bytes
565-
self._server_capabilities, pos=_get_byte2(packet, pos)
577+
localcapabilities-- server capabilities
578+
capabilities, pos=_get_byte2(packet, pos)
566579

567-
--print("server capabilities: ", self._server_capabilities)
580+
--print(format("server capabilities: %#x", capabilities))
568581

569582
self._server_lang=strbyte(packet, pos)
570583
pos=pos+1
@@ -578,10 +591,9 @@ function _M.connect(self, opts)
578591
localmore_capabilities
579592
more_capabilities, pos=_get_byte2(packet, pos)
580593

581-
self._server_capabilities=bor(self._server_capabilities,
582-
lshift(more_capabilities, 16))
594+
capabilities=bor(capabilities, lshift(more_capabilities, 16))
583595

584-
--print("server capabilities: ", self._server_capabilities)
596+
--print("server capabilities: ", capabilities)
585597

586598
-- local len = strbyte(packet, pos)
587599
locallen=21-8-1
@@ -598,13 +610,38 @@ function _M.connect(self, opts)
598610
scramble=scramble..scramble_part2
599611
--print("scramble: ", _dump(scramble))
600612

613+
localclient_flags=0x3f7cf;
614+
615+
localssl_verify=opts.ssl_verify
616+
localuse_ssl=opts.sslorssl_verify
617+
618+
ifuse_sslthen
619+
ifband(capabilities, CLIENT_SSL) ==0then
620+
returnnil, "ssl disabled on server"
621+
end
622+
623+
-- send a SSL Request Packet
624+
localreq=_set_byte4(bor(client_flags, CLIENT_SSL))
625+
.._set_byte4(self._max_packet_size)
626+
.."\0" -- TODO: add support for charset encoding
627+
..strrep("\0", 23)
628+
629+
localpacket_len=4+4+1+23
630+
localbytes, err=_send_packet(self, req, packet_len)
631+
ifnotbytesthen
632+
returnnil, "failed to send client authentication packet: " ..err
633+
end
634+
635+
localok, err=sock:sslhandshake(false, nil, ssl_verify)
636+
ifnotokthen
637+
returnnil, "failed to do ssl handshake: " .. (error"")
638+
end
639+
end
640+
601641
localpassword=opts.passwordor""
602642

603643
localtoken=_compute_token(password, scramble)
604644

605-
-- local client_flags = self._server_capabilities
606-
localclient_flags=260047;
607-
608645
--print("token: ", _dump(token))
609646

610647
localreq=_set_byte4(client_flags)

‎t/ssl.t‎

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# vim:set ft= ts=4 sw=4 et:
2+
3+
useTest::Nginx::Socket::Lua;
4+
use Cwd qw(cwd);
5+
6+
repeat_each(2);
7+
8+
plantests => repeat_each() * (3* blocks());
9+
10+
my$pwd= cwd();
11+
12+
our$HttpConfig=qq{
13+
resolver \$TEST_NGINX_RESOLVER;
14+
lua_package_path "$pwd/lib/?.lua;$pwd/t/lib/?.lua;"
15+
lua_package_cpath "/usr/local/openresty-debug/lualib/?.so;/usr/local/openresty/lualib/?.so;"
16+
};
17+
18+
$ENV{TEST_NGINX_RESOLVER} ='8.8.8.8';
19+
$ENV{TEST_NGINX_MYSQL_PORT} ||=3306;
20+
$ENV{TEST_NGINX_MYSQL_HOST} ||='127.0.0.1';
21+
$ENV{TEST_NGINX_MYSQL_PATH} ||='/var/run/mysql/mysql.sock';
22+
23+
#log_level 'warn'
24+
25+
no_long_string();
26+
no_shuffle();
27+
check_accum_error_log();
28+
29+
run_tests();
30+
31+
__DATA__
32+
33+
=== TEST 1: send query w/o result set
34+
--- http_config eval: $::HttpConfig
35+
--- config
36+
location /t{
37+
content_by_lua '
38+
local mysql = require "resty.mysql"
39+
local db = mysql:new()
40+
41+
db:set_timeout(1000) -- 1 sec
42+
43+
local ok, err, errno, sqlstate = db:connect({
44+
host = "$TEST_NGINX_MYSQL_HOST",
45+
port = $TEST_NGINX_MYSQL_PORT,
46+
database = "ngx_test",
47+
user = "ngx_test",
48+
password = "ngx_test",
49+
ssl = true,
50+
})
51+
52+
if not ok then
53+
ngx.say("failed to connect: ", err, ": ", errno, " ", sqlstate)
54+
return
55+
end
56+
57+
ngx.say("connected to mysql ", db:server_ver(), ".")
58+
59+
local bytes, err = db:send_query("drop table if exists cats")
60+
if not bytes then
61+
ngx.say("failed to send query: ", err)
62+
end
63+
64+
ngx.say("sent ", bytes, " bytes.")
65+
66+
local res, err, errno, sqlstate = db:read_result()
67+
if not res then
68+
ngx.say("bad result: ", err, ": ", errno, ": ", sqlstate, ".")
69+
end
70+
71+
local ljson = require "ljson"
72+
ngx.say("result: ", ljson.encode(res))
73+
74+
local ok, err = db:close()
75+
if not ok then
76+
ngx.say("failed to close: ", err)
77+
return
78+
end
79+
';
80+
}
81+
--- request
82+
GET /t
83+
--- response_body_like chop
84+
^connected to mysql \d\.\S+\.
85+
sent 30bytes\.
86+
result: \{"affected_rows":0,"insert_id":0,"server_status":2,"warning_count":[01]\}$
87+
--- no_error_log
88+
[error]
89+
90+
91+
92+
=== TEST 2: send query w/o result set (verify)
93+
--- http_config eval: $::HttpConfig
94+
--- config
95+
lua_ssl_trusted_certificate /etc/test.crt; # assuming used by the MySQL server
96+
location /t{
97+
content_by_lua '
98+
local mysql = require "resty.mysql"
99+
local db = mysql:new()
100+
101+
db:set_timeout(1000) -- 1 sec
102+
103+
local ok, err, errno, sqlstate = db:connect({
104+
host = "$TEST_NGINX_MYSQL_HOST",
105+
port = $TEST_NGINX_MYSQL_PORT,
106+
database = "ngx_test",
107+
user = "ngx_test",
108+
password = "ngx_test",
109+
ssl = true,
110+
ssl_verify = true,
111+
})
112+
113+
if not ok then
114+
ngx.say("failed to connect: ", err, ": ", errno, " ", sqlstate)
115+
return
116+
end
117+
118+
ngx.say("connected to mysql ", db:server_ver(), ".")
119+
120+
local bytes, err = db:send_query("drop table if exists cats")
121+
if not bytes then
122+
ngx.say("failed to send query: ", err)
123+
end
124+
125+
ngx.say("sent ", bytes, " bytes.")
126+
127+
local res, err, errno, sqlstate = db:read_result()
128+
if not res then
129+
ngx.say("bad result: ", err, ": ", errno, ": ", sqlstate, ".")
130+
end
131+
132+
local ljson = require "ljson"
133+
ngx.say("result: ", ljson.encode(res))
134+
135+
local ok, err = db:close()
136+
if not ok then
137+
ngx.say("failed to close: ", err)
138+
return
139+
end
140+
';
141+
}
142+
--- request
143+
GET /t
144+
--- response_body_like chop
145+
^connected to mysql \d\.\S+\.
146+
sent 30bytes\.
147+
result: \{"affected_rows":0,"insert_id":0,"server_status":2,"warning_count":[01]\}$
148+
--- no_error_log
149+
[error]
150+

0 commit comments

Comments
(0)