From 03b22ad6ccb537d8cf67e04a7811e37628bd6483 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 2 Sep 2024 16:17:10 +0200 Subject: [PATCH 1/8] Add TLS support --- src/nats.lua | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/nats.lua b/src/nats.lua index fe9236e..114ccc8 100644 --- a/src/nats.lua +++ b/src/nats.lua @@ -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. @@ -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, @@ -224,6 +231,23 @@ client_prototype.get_server_info = function(client) return client.information end +client_prototype.upgrade_to_tls = function(client) + local luasec = require('ssl') + 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 @@ -295,7 +319,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 @@ -319,13 +343,16 @@ function command.connect(client) 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 and (client.information['tls_available'] or client.information['tls_required']) then + client:upgrade_to_tls() + end end + + request.raw(client, 'CONNECT '..cjson.encode(config)..'\r\n') end function command.ping(client) From 5a82215a09785df734ac0a497f5f889f3e308fb2 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 2 Sep 2024 16:57:48 +0200 Subject: [PATCH 2/8] Error if TLS support is requested but lua-sec is not available --- src/nats.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/nats.lua b/src/nats.lua index 114ccc8..3ae8c25 100644 --- a/src/nats.lua +++ b/src/nats.lua @@ -232,7 +232,11 @@ client_prototype.get_server_info = function(client) end client_prototype.upgrade_to_tls = function(client) - local luasec = require('ssl') + local status, luasec = pcall(require, 'ssl') + if not status then + nats.error('TLS is required but the luasec library is not available') + return + end local params = { capath = client.parameters.tls_ca_path, cafile = client.parameters.tls_ca_file, From 238e6f8630ae4bab3acc3c16aa6e12ebda857613 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 2 Sep 2024 16:58:36 +0200 Subject: [PATCH 3/8] Accept a 'tls' scheme --- src/nats.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/nats.lua b/src/nats.lua index 3ae8c25..918095c 100644 --- a/src/nats.lua +++ b/src/nats.lua @@ -293,7 +293,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 From d52524893d4285ccf095b3b004d4f272c53994cf Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 2 Sep 2024 16:59:07 +0200 Subject: [PATCH 4/8] Properly advertise TLS as required when enabled --- src/nats.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/nats.lua b/src/nats.lua index 918095c..b8fc495 100644 --- a/src/nats.lua +++ b/src/nats.lua @@ -339,10 +339,11 @@ 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 From db70910052aefa7eee747d0772ca817fc49c5a37 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 2 Sep 2024 16:59:56 +0200 Subject: [PATCH 5/8] Error when TLS is requested but not supported by the server --- src/nats.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/nats.lua b/src/nats.lua index b8fc495..8b1a9f8 100644 --- a/src/nats.lua +++ b/src/nats.lua @@ -355,8 +355,12 @@ function command.connect(client) local data = response.read(client) if data.action == 'INFO' then client.information = cjson.decode(data.content) - if client.parameters.tls and (client.information['tls_available'] or client.information['tls_required']) then - client:upgrade_to_tls() + 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 From 741b3c33856e539ab5bf3ef2dd90ee08e4ef853f Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Tue, 3 Sep 2024 09:55:22 +0200 Subject: [PATCH 6/8] Document TLS support --- Makefile | 1 + README.md | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/Makefile b/Makefile index 5a4270b..3cb15eb 100644 --- a/Makefile +++ b/Makefile @@ -8,5 +8,6 @@ deps: luarocks install luasocket luarocks install lua-cjson luarocks install uuid + luarocks install luasec .PHONY: test test-deps deps diff --git a/README.md b/README.md index e3e9820..5a7c465 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 ----------------------- From 606d02dc3867914977fd32b436bbab64278e3f60 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Fri, 27 Sep 2024 14:15:18 +0200 Subject: [PATCH 7/8] Fix a consistency nit in the configuration init --- src/nats.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nats.lua b/src/nats.lua index 8b1a9f8..3aa300e 100644 --- a/src/nats.lua +++ b/src/nats.lua @@ -38,7 +38,7 @@ local defaults = { tls_ca_path = nil, tls_ca_file = nil, tls_cert = nil, - tls_key = nil + tls_key = nil, } -- ### Create a properly formatted inbox subject. From 748c53e33cfba6cc028720c7c306065c85124155 Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Fri, 27 Sep 2024 14:15:29 +0200 Subject: [PATCH 8/8] Remove useless `return` spotted by Habbie --- src/nats.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/src/nats.lua b/src/nats.lua index 3aa300e..1113c82 100644 --- a/src/nats.lua +++ b/src/nats.lua @@ -235,7 +235,6 @@ 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') - return end local params = { capath = client.parameters.tls_ca_path,