Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ deps:
luarocks install luasocket
luarocks install lua-cjson
luarocks install uuid
luarocks install luasec

.PHONY: test test-deps deps
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Requirements
* [lua-cjson](https://github.com/mpx/lua-cjson)
* [uuid](https://github.com/Tieske/uuid)
* [nats](https://github.com/derekcollison/nats) or [gnatsd](https://github.com/apcera/gnatsd)
* [luasec](https://github.com/lunarmodules/luasec) if TLS support is needed

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

### Basic usage: TLS connection

```lua
local nats = require 'nats'

local client = nats.connect({
host = '127.0.0.1',
port = 4222,
tls = true,
tls_ca_path = '/etc/ssl/certs',
})

-- connect to the server using TLS and validating the server certificate
client:connect()
```

Developer's Information
-----------------------

Expand Down
56 changes: 47 additions & 9 deletions src/nats.lua
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ local defaults = {
port = 4222,
tcp_nodelay = true,
path = nil,
tls = false,
tls_ca_path = nil,
tls_ca_file = nil,
tls_cert = nil,
tls_key = nil,
}

-- ### Create a properly formatted inbox subject.
Expand Down Expand Up @@ -76,10 +81,12 @@ local function load_methods(proto, commands)
return client
end

local function create_client(client_proto, client_socket, commands)
local function create_client(client_proto, client_socket, commands, parameters)
local client = load_methods(client_proto, commands)
-- assign client error handler
client.error = nats.error
-- keep parameters around, for TLS
client.parameters = parameters
-- assign client network methods
client.network = {
socket = client_socket,
Expand Down Expand Up @@ -224,6 +231,26 @@ client_prototype.get_server_info = function(client)
return client.information
end

client_prototype.upgrade_to_tls = function(client)
local status, luasec = pcall(require, 'ssl')
if not status then
nats.error('TLS is required but the luasec library is not available')
end
local params = {
capath = client.parameters.tls_ca_path,
cafile = client.parameters.tls_ca_file,
certificate = client.parameters.tls_cert,
key = client.parameters.tls_key,
mode = "client",
options = {"all", "no_sslv3"},
protocol = "tlsv1_2",
verify = "peer",
}

client.network.socket = luasec.wrap(client.network.socket, params)
client.network.socket:dohandshake()
end

client_prototype.shutdown = function(client)
client.network.socket:shutdown()
end
Expand Down Expand Up @@ -265,7 +292,10 @@ local function create_connection(parameters)
else
if parameters.scheme then
local scheme = parameters.scheme
assert(scheme == 'nats' or scheme == 'tcp', 'invalid scheme: '..scheme)
assert(scheme == 'nats' or scheme == 'tcp' or scheme == 'tls', 'invalid scheme: '..scheme)
if scheme == 'tls' then
parameters.tls = true
end
end
perform_connection, socket = connect_tcp, require('socket').tcp
end
Expand Down Expand Up @@ -295,7 +325,7 @@ function nats.connect(...)
end

local socket = create_connection(merge_defaults(parameters))
local client = create_client(client_prototype, socket, commands)
local client = create_client(client_prototype, socket, commands, parameters)

return client
end
Expand All @@ -308,24 +338,32 @@ end

function command.connect(client)
local config = {
lang = client.lang,
version = client.version,
verbose = client.verbose,
pedantic = client.pedantic,
lang = client.lang,
version = client.version,
verbose = client.verbose,
pedantic = client.pedantic,
tls_required = client.parameters.tls,
}

if client.user ~= nil and client.pass ~= nil then
config.user = client.user
config.pass = client.pass
end

request.raw(client, 'CONNECT '..cjson.encode(config)..'\r\n')

-- gather the server information
local data = response.read(client)
if data.action == 'INFO' then
client.information = cjson.decode(data.content)
if client.parameters.tls then
if (client.information['tls_available'] or client.information['tls_required']) then
client:upgrade_to_tls()
else
nats.error('TLS is required but not offered by the server')
end
end
end

request.raw(client, 'CONNECT '..cjson.encode(config)..'\r\n')
end

function command.ping(client)
Expand Down