Skip to content

Commit 4a84262

Browse files
committed
api: add SSL support
It wasn't SSL support. After the patch it was added several options to configure SSL, use one of them to enable it: * `ssl_cert_file` is a path to the SSL cert file; * `ssl_key_file` is a path to the SSL key file; * `ssl_ca_file` is a path to the SSL CA file; * `ssl_ciphers` is a colon-separated list of SSL ciphers; * `ssl_password` is a password for decrypting SSL private key; * `ssl_password_file` is a SSL file with key for decrypting SSL private key. Closes #35
1 parent 0e7af5a commit 4a84262

19 files changed

+1323
-3
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
88

99
### Added
1010

11+
- SSL support (#35).
12+
1113
### Changed
1214

1315
### Fixed

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,13 @@ httpd = require('http.server').new(host, port[, { options } ])
151151
* `idle_timeout` - maximum amount of time an idle (keep-alive) connection will
152152
remain idle before closing. When the idle timeout is exceeded, HTTP server
153153
closes the keepalive connection. Default value: 0 seconds (disabled).
154+
* TLS options (to enable it, provide at least one of the following parameters):
155+
* `ssl_cert_file` is a path to the SSL cert file, mandatory;
156+
* `ssl_key_file` is a path to the SSL key file, mandatory;
157+
* `ssl_ca_file` is a path to the SSL CA file, optional;
158+
* `ssl_ciphers` is a colon-separated list of SSL ciphers, optional;
159+
* `ssl_password` is a password for decrypting SSL private key, optional;
160+
* `ssl_password_file` is a SSL file with key for decrypting SSL private key, optional.
154161

155162
## Using routes
156163

http/server.lua

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
-- http.server
22

33
local lib = require('http.lib')
4+
local sslsocket_supported, sslsocket = pcall(require, 'http.sslsocket')
45

56
local fio = require('fio')
67
local require = require
@@ -1295,6 +1296,54 @@ local function url_for_httpd(httpd, name, args, query)
12951296
end
12961297
end
12971298

1299+
local function create_ssl_ctx(host, port, opts)
1300+
local ok, ctx = pcall(sslsocket.ctx, sslsocket.tls_server_method())
1301+
if ok ~= true then
1302+
error(ctx)
1303+
end
1304+
1305+
local rc = sslsocket.ctx_use_private_key_file(ctx, opts.ssl_key_file,
1306+
opts.ssl_password, opts.ssl_password_file)
1307+
if rc == false then
1308+
errorf(
1309+
"Can't start server on %s:%s: %s %s",
1310+
host, port, 'Private key is invalid or password mismatch', opts.ssl_key_file
1311+
)
1312+
end
1313+
1314+
rc = sslsocket.ctx_use_certificate_file(ctx, opts.ssl_cert_file)
1315+
if rc == false then
1316+
errorf(
1317+
"Can't start server on %s:%s: %s %s",
1318+
host, port, 'Certificate is invalid', opts.ssl_cert_file
1319+
)
1320+
end
1321+
1322+
if opts.ssl_ca_file ~= nil then
1323+
rc = sslsocket.ctx_load_verify_locations(ctx, opts.ssl_ca_file)
1324+
if rc == false then
1325+
errorf(
1326+
"Can't start server on %s:%s: %s",
1327+
host, port, 'CA file is invalid'
1328+
)
1329+
end
1330+
1331+
sslsocket.ctx_set_verify(ctx, 0x01 + 0x02)
1332+
end
1333+
1334+
if opts.ssl_ciphers ~= nil then
1335+
rc = sslsocket.ctx_set_cipher_list(ctx, opts.ssl_ciphers)
1336+
if rc == false then
1337+
errorf(
1338+
"Can't start server on %s:%s: %s",
1339+
host, port, 'Ciphers are invalid'
1340+
)
1341+
end
1342+
end
1343+
1344+
return ctx
1345+
end
1346+
12981347
local function httpd_start(self)
12991348
if type(self) ~= 'table' then
13001349
error("httpd: usage: httpd:start()")
@@ -1321,6 +1370,36 @@ local function httpd_start(self)
13211370
return self
13221371
end
13231372

1373+
-- validate_ssl_opts validates ssl_opts and returns true if at least ssl_cert_file
1374+
-- and ssl_key_file parameters are not nil.
1375+
local function validate_ssl_opts(opts)
1376+
local is_tls_enabled = false
1377+
1378+
for key, value in pairs(opts) do
1379+
if value ~= nil then
1380+
is_tls_enabled = true
1381+
1382+
if type(value) ~= 'string' then
1383+
errorf("%s option must be a string", key)
1384+
end
1385+
1386+
if string.find(key, 'file') ~= nil and fio.path.exists(value) ~= true then
1387+
errorf("file %q not exists", value)
1388+
end
1389+
end
1390+
end
1391+
1392+
if is_tls_enabled and (opts.ssl_key_file == nil or opts.ssl_cert_file == nil) then
1393+
error("ssl_key_file and ssl_cert_file must be set to enable TLS")
1394+
end
1395+
1396+
if not sslsocket_supported then
1397+
error("ssl socket is not supported")
1398+
end
1399+
1400+
return is_tls_enabled
1401+
end
1402+
13241403
local exports = {
13251404
_VERSION = require('http.version'),
13261405
DETACHED = DETACHED,
@@ -1341,6 +1420,15 @@ local exports = {
13411420
error('Option idle_timeout must be a number.')
13421421
end
13431422

1423+
local is_tls_enabled = validate_ssl_opts({
1424+
ssl_cert_file = options.ssl_cert_file,
1425+
ssl_key_file = options.ssl_key_file,
1426+
ssl_password = options.ssl_password,
1427+
ssl_password_file = options.ssl_password_file,
1428+
ssl_ca_file = options.ssl_ca_file,
1429+
ssl_ciphers = options.ssl_ciphers,
1430+
})
1431+
13441432
local default = {
13451433
max_header_size = 4096,
13461434
header_timeout = 100,
@@ -1363,7 +1451,8 @@ local exports = {
13631451
is_run = false,
13641452
stop = httpd_stop,
13651453
start = httpd_start,
1366-
options = extend(default, options, true),
1454+
use_tls = is_tls_enabled,
1455+
options = extend(default, options, false),
13671456

13681457
routes = { },
13691458
iroutes = { },
@@ -1399,6 +1488,20 @@ local exports = {
13991488
}
14001489
}
14011490

1491+
if self.use_tls then
1492+
self.tcp_server_f = function(host, port, handler, timeout)
1493+
local ssl_ctx = create_ssl_ctx(host, port, {
1494+
ssl_cert_file = self.options.ssl_cert_file,
1495+
ssl_key_file = self.options.ssl_key_file,
1496+
ssl_password = self.options.ssl_password,
1497+
ssl_password_file = self.options.ssl_password_file,
1498+
ssl_ca_file = self.options.ssl_ca_file,
1499+
ssl_ciphers = self.options.ssl_ciphers,
1500+
})
1501+
return sslsocket.tcp_server(host, port, handler, timeout, ssl_ctx)
1502+
end
1503+
end
1504+
14021505
return self
14031506
end,
14041507

0 commit comments

Comments
 (0)