Skip to content

Commit 2485b1d

Browse files
authored
Merge pull request #1 from rgacogne/tls
Add TLS support
2 parents 980da7a + 748c53e commit 2485b1d

3 files changed

Lines changed: 65 additions & 9 deletions

File tree

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ deps:
88
luarocks install luasocket
99
luarocks install lua-cjson
1010
luarocks install uuid
11+
luarocks install luasec
1112

1213
.PHONY: test test-deps deps

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Requirements
2121
* [lua-cjson](https://github.com/mpx/lua-cjson)
2222
* [uuid](https://github.com/Tieske/uuid)
2323
* [nats](https://github.com/derekcollison/nats) or [gnatsd](https://github.com/apcera/gnatsd)
24+
* [luasec](https://github.com/lunarmodules/luasec) if TLS support is needed
2425

2526
This is a NATS Lua library for Lua 5.1, 5.2 and 5.3. The
2627
libraries are copyright by their author 2015 (see the Creators
@@ -94,6 +95,22 @@ client:set_auth(user, password)
9495
client:connect()
9596
```
9697

98+
### Basic usage: TLS connection
99+
100+
```lua
101+
local nats = require 'nats'
102+
103+
local client = nats.connect({
104+
host = '127.0.0.1',
105+
port = 4222,
106+
tls = true,
107+
tls_ca_path = '/etc/ssl/certs',
108+
})
109+
110+
-- connect to the server using TLS and validating the server certificate
111+
client:connect()
112+
```
113+
97114
Developer's Information
98115
-----------------------
99116

src/nats.lua

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ local defaults = {
3434
port = 4222,
3535
tcp_nodelay = true,
3636
path = nil,
37+
tls = false,
38+
tls_ca_path = nil,
39+
tls_ca_file = nil,
40+
tls_cert = nil,
41+
tls_key = nil,
3742
}
3843

3944
-- ### Create a properly formatted inbox subject.
@@ -76,10 +81,12 @@ local function load_methods(proto, commands)
7681
return client
7782
end
7883

79-
local function create_client(client_proto, client_socket, commands)
84+
local function create_client(client_proto, client_socket, commands, parameters)
8085
local client = load_methods(client_proto, commands)
8186
-- assign client error handler
8287
client.error = nats.error
88+
-- keep parameters around, for TLS
89+
client.parameters = parameters
8390
-- assign client network methods
8491
client.network = {
8592
socket = client_socket,
@@ -224,6 +231,26 @@ client_prototype.get_server_info = function(client)
224231
return client.information
225232
end
226233

234+
client_prototype.upgrade_to_tls = function(client)
235+
local status, luasec = pcall(require, 'ssl')
236+
if not status then
237+
nats.error('TLS is required but the luasec library is not available')
238+
end
239+
local params = {
240+
capath = client.parameters.tls_ca_path,
241+
cafile = client.parameters.tls_ca_file,
242+
certificate = client.parameters.tls_cert,
243+
key = client.parameters.tls_key,
244+
mode = "client",
245+
options = {"all", "no_sslv3"},
246+
protocol = "tlsv1_2",
247+
verify = "peer",
248+
}
249+
250+
client.network.socket = luasec.wrap(client.network.socket, params)
251+
client.network.socket:dohandshake()
252+
end
253+
227254
client_prototype.shutdown = function(client)
228255
client.network.socket:shutdown()
229256
end
@@ -265,7 +292,10 @@ local function create_connection(parameters)
265292
else
266293
if parameters.scheme then
267294
local scheme = parameters.scheme
268-
assert(scheme == 'nats' or scheme == 'tcp', 'invalid scheme: '..scheme)
295+
assert(scheme == 'nats' or scheme == 'tcp' or scheme == 'tls', 'invalid scheme: '..scheme)
296+
if scheme == 'tls' then
297+
parameters.tls = true
298+
end
269299
end
270300
perform_connection, socket = connect_tcp, require('socket').tcp
271301
end
@@ -295,7 +325,7 @@ function nats.connect(...)
295325
end
296326

297327
local socket = create_connection(merge_defaults(parameters))
298-
local client = create_client(client_prototype, socket, commands)
328+
local client = create_client(client_prototype, socket, commands, parameters)
299329

300330
return client
301331
end
@@ -308,24 +338,32 @@ end
308338

309339
function command.connect(client)
310340
local config = {
311-
lang = client.lang,
312-
version = client.version,
313-
verbose = client.verbose,
314-
pedantic = client.pedantic,
341+
lang = client.lang,
342+
version = client.version,
343+
verbose = client.verbose,
344+
pedantic = client.pedantic,
345+
tls_required = client.parameters.tls,
315346
}
316347

317348
if client.user ~= nil and client.pass ~= nil then
318349
config.user = client.user
319350
config.pass = client.pass
320351
end
321352

322-
request.raw(client, 'CONNECT '..cjson.encode(config)..'\r\n')
323-
324353
-- gather the server information
325354
local data = response.read(client)
326355
if data.action == 'INFO' then
327356
client.information = cjson.decode(data.content)
357+
if client.parameters.tls then
358+
if (client.information['tls_available'] or client.information['tls_required']) then
359+
client:upgrade_to_tls()
360+
else
361+
nats.error('TLS is required but not offered by the server')
362+
end
363+
end
328364
end
365+
366+
request.raw(client, 'CONNECT '..cjson.encode(config)..'\r\n')
329367
end
330368

331369
function command.ping(client)

0 commit comments

Comments
 (0)