Skip to content

Commit 4a064fb

Browse files
committed
roles: add tls support
Since we support TLS in this module, we need to add it into roles too. After the patch TLS params was added. It can be configurated like the following: ```yaml server_name: listen: "localhost:3013" ssl_key_file: "path/to/key/file" ssl_cert_file: "path/to/key/file" ssl_ca_file: "path/to/key/file" ssl_ciphers: "cipher1:cipher2" ssl_password: "password" ssl_password_file: "path/to/ssl/password" ``` Closes #199
1 parent 5bfaaf8 commit 4a064fb

File tree

7 files changed

+178
-13
lines changed

7 files changed

+178
-13
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
99
### Added
1010

1111
- SSL support (#35).
12+
- SSL support into roles (#199).
1213

1314
### Changed
1415

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,22 @@ end
581581
return M
582582
```
583583

584+
To enable TLS, provide the following params into roles config (for proper work
585+
it's enough to provide only `ssl_key_file` and `ssl_cert_file`):
586+
587+
```yaml
588+
roles_cfg:
589+
roles.httpd:
590+
default:
591+
listen: 8081
592+
ssl_key_file: "path/to/key/file"
593+
ssl_cert_file: "path/to/key/file"
594+
ssl_ca_file: "path/to/key/file"
595+
ssl_ciphers: "cipher1:cipher2"
596+
ssl_password: "password"
597+
ssl_password_file: "path/to/ssl/password"
598+
```
599+
584600
This role accepts a server by name from a config and creates a route to return
585601
`Hello, world!` to every request by this route.
586602

http/server.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1404,6 +1404,8 @@ local exports = {
14041404
_VERSION = require('http.version'),
14051405
DETACHED = DETACHED,
14061406

1407+
-- Since TLS support this function uses in roles's validate section to check
1408+
-- TLS options.
14071409
new = function(host, port, options)
14081410
if options == nil then
14091411
options = {}

roles/httpd.lua

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,28 @@ local function parse_listen(listen)
7272
return host, port, nil
7373
end
7474

75+
-- parse_params returns table with set options from config to pass
76+
-- it into new() function.
77+
local function parse_params(node)
78+
return {
79+
ssl_cert_file = node.ssl_cert_file,
80+
ssl_key_file = node.ssl_key_file,
81+
ssl_password = node.ssl_password,
82+
ssl_password_file = node.ssl_password_file,
83+
ssl_ca_file = node.ssl_ca_file,
84+
ssl_ciphers = node.ssl_ciphers,
85+
}
86+
end
87+
7588
local function apply_http(name, node)
7689
local host, port, err = parse_listen(node.listen)
7790
if err ~= nil then
7891
error("failed to parse URI: " .. err)
7992
end
8093

8194
if servers[name] == nil then
82-
local httpd = http_server.new(host, port)
95+
local httpd = http_server.new(host, port, parse_params(node))
96+
8397
httpd:start()
8498
servers[name] = {
8599
httpd = httpd,
@@ -99,10 +113,15 @@ M.validate = function(conf)
99113
error("name of the server must be a string")
100114
end
101115

102-
local _, _, err = parse_listen(node.listen)
116+
local host, port, err = parse_listen(node.listen)
103117
if err ~= nil then
104118
error("failed to parse http 'listen' param: " .. err)
105119
end
120+
121+
local ok, err = pcall(http_server.new, host, port, parse_params(node))
122+
if not ok then
123+
error("failed to parse params in " .. name .. " server: " .. tostring(err))
124+
end
106125
end
107126
end
108127

test/integration/httpd_role_test.lua

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,15 @@ local treegen = require('luatest.treegen')
33
local server = require('luatest.server')
44
local fun = require('fun')
55
local yaml = require('yaml')
6+
local fio = require('fio')
7+
local http_client = require('http.client').new()
8+
69

710
local helpers = require('test.helpers')
811

9-
local g = t.group()
12+
local g = t.group(nil, t.helpers.matrix({use_tls = {true, false}}))
13+
14+
local ssl_data_dir = fio.abspath(fio.pathjoin(helpers.get_testdir_path(), "ssl_data"))
1015

1116
local config = {
1217
credentials = {
@@ -55,29 +60,58 @@ local config = {
5560
},
5661
}
5762

58-
g.before_each(function()
63+
local tls_config = table.deepcopy(config)
64+
tls_config.groups['group-001'].replicasets['replicaset-001'].roles_cfg['roles.httpd'].default
65+
.ssl_cert_file = fio.pathjoin(ssl_data_dir, 'server.crt')
66+
67+
tls_config.groups['group-001'].replicasets['replicaset-001'].roles_cfg['roles.httpd'].default
68+
.ssl_key_file = fio.pathjoin(ssl_data_dir, 'server.enc.key')
69+
70+
tls_config.groups['group-001'].replicasets['replicaset-001'].roles_cfg['roles.httpd'].default
71+
.ssl_password_file = fio.pathjoin(ssl_data_dir, 'passwords')
72+
73+
g.before_each(function(cg)
5974
helpers.skip_if_not_tarantool3()
6075

6176
local dir = treegen.prepare_directory({}, {})
6277

78+
local cfg = config
79+
if cg.params.use_tls then
80+
cfg = tls_config
81+
end
82+
6383
local config_file = treegen.write_file(dir, 'config.yaml',
64-
yaml.encode(config))
84+
yaml.encode(cfg))
6585
local opts = {config_file = config_file, chdir = dir}
66-
g.server = server:new(fun.chain(opts, {alias = 'instance-001'}):tomap())
67-
helpers.update_lua_env_variables(g.server)
86+
cg.server = server:new(fun.chain(opts, {alias = 'instance-001'}):tomap())
87+
helpers.update_lua_env_variables(cg.server)
6888

69-
g.server:start()
89+
cg.server:start()
7090
end)
7191

72-
g.after_each(function()
73-
g.server:stop()
92+
g.after_each(function(cg)
93+
helpers.teardown(cg.server)
7494
end)
7595

76-
g.test_httpd_role_usage = function()
77-
t.assert_equals(g.server:eval(
96+
g.test_httpd_role_usage = function(cg)
97+
if cg.params.use_tls then
98+
local resp = http_client:get('https://localhost:13000/ping', {
99+
ca_file = fio.pathjoin(ssl_data_dir, 'ca.crt')
100+
})
101+
t.assert_equals(resp.status, 200, 'response not 200')
102+
t.assert_equals(resp.body, 'pong')
103+
end
104+
105+
-- We can use https only for one endpoind due to we haven't publish separate
106+
-- certificates for it.
107+
local resp = http_client:get('http://localhost:13001/ping')
108+
t.assert_equals(resp.status, 200, 'response not 200')
109+
t.assert_equals(resp.body, 'pong')
110+
111+
t.assert_equals(cg.server:eval(
78112
'return require("test.mocks.mock_role").get_server_port(1)'
79113
), 13000)
80-
t.assert_equals(g.server:eval(
114+
t.assert_equals(cg.server:eval(
81115
'return require("test.mocks.mock_role").get_server_port(2)'
82116
), 13001)
83117
end

test/mocks/mock_role.lua

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ M.validate = function() end
77
M.apply = function(conf)
88
for _, server in pairs(conf) do
99
servers[server.id] = require('roles.httpd').get_server(server.name)
10+
11+
servers[server.id]:route({
12+
path = '/ping',
13+
}, function(tx)
14+
return tx:render({text = 'pong'})
15+
end)
1016
end
1117
end
1218

test/unit/httpd_role_test.lua

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ local t = require('luatest')
22
local g = t.group()
33

44
local httpd_role = require('roles.httpd')
5+
local helpers = require('test.helpers')
6+
local fio = require('fio')
7+
8+
local ssl_data_dir = fio.abspath(fio.pathjoin(helpers.get_testdir_path(), "ssl_data"))
59

610
g.after_each(function()
711
httpd_role.stop()
@@ -122,6 +126,89 @@ local validation_cases = {
122126
},
123127
err = "failed to parse http 'listen' param: URI query component is not supported",
124128
},
129+
["ssl_ok_minimal"] = {
130+
cfg = {
131+
server = {
132+
listen = "localhost:123",
133+
ssl_key_file = fio.pathjoin(ssl_data_dir, 'server.key'),
134+
ssl_cert_file = fio.pathjoin(ssl_data_dir, 'server.crt')
135+
},
136+
},
137+
},
138+
["ssl_ok_full"] = {
139+
cfg = {
140+
server = {
141+
listen = 123,
142+
ssl_key_file = fio.pathjoin(ssl_data_dir, 'server.key'),
143+
ssl_cert_file = fio.pathjoin(ssl_data_dir, 'server.crt'),
144+
ssl_ca_file = fio.pathjoin(ssl_data_dir, 'ca.crt'),
145+
ssl_ciphers = "ECDHE-RSA-AES256-GCM-SHA384",
146+
},
147+
},
148+
},
149+
["ssl_key_file_not_string"] = {
150+
cfg = {
151+
server = {
152+
listen = "localhost:123",
153+
ssl_key_file = 123,
154+
},
155+
},
156+
err = "ssl_key_file option must be a string",
157+
},
158+
["ssl_cert_file_not_string"] = {
159+
cfg = {
160+
server = {
161+
listen = "localhost:123",
162+
ssl_cert_file = 123,
163+
},
164+
},
165+
err = "ssl_cert_file option must be a string",
166+
},
167+
["ssl_password_not_string"] = {
168+
cfg = {
169+
server = {
170+
listen = "localhost:123",
171+
ssl_password = 123,
172+
},
173+
},
174+
err = "ssl_password option must be a string",
175+
},
176+
["ssl_password_file_not_string"] = {
177+
cfg = {
178+
server = {
179+
listen = "localhost:123",
180+
ssl_password_file = 123,
181+
},
182+
},
183+
err = "ssl_password_file option must be a string",
184+
},
185+
["ssl_ca_file_not_string"] = {
186+
cfg = {
187+
server = {
188+
listen = "localhost:123",
189+
ssl_ca_file = 123,
190+
},
191+
},
192+
err = "ssl_ca_file option must be a string",
193+
},
194+
["ssl_ciphers_not_string"] = {
195+
cfg = {
196+
server = {
197+
listen = "localhost:123",
198+
ssl_ciphers = 123,
199+
},
200+
},
201+
err = "ssl_ciphers option must be a string",
202+
},
203+
["ssl_key_and_cert_must_exist"] = {
204+
cfg = {
205+
server = {
206+
listen = "localhost:123",
207+
ssl_ciphers = 'cipher1:cipher2',
208+
},
209+
},
210+
err = "ssl_key_file and ssl_cert_file must be set to enable TLS",
211+
},
125212
}
126213

127214
for name, case in pairs(validation_cases) do

0 commit comments

Comments
 (0)