From c6c8ae2928cf99230bcd6e951a3b628024208150 Mon Sep 17 00:00:00 2001 From: themilchenko Date: Tue, 11 Nov 2025 19:52:12 +0300 Subject: [PATCH 1/7] Revert "Release 1.9.0" This reverts commit 8d8a022b0455849122a55d013a93a06fc2d6c108. --- CHANGELOG.md | 17 ++++------------- http/version.lua | 2 +- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2043715..3ad867c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,22 +6,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] -### Added - -### Changed - -### Fixed - -## [1.9.0] - 2025-11-11 - -The release introduces a new `ssl_verify_client` option and changes default -behavior with provided `ca_file` param. Also a few bugs were fixed. - -### Added +## Added - `ssl_verify_client` option (#207). -### Fixed +## Changed + +## Fixed - Do not recreate server if it's address and port were not changed (#219). - Server doesn't change after updating parameters on config reload (#216). diff --git a/http/version.lua b/http/version.lua index d604410..c7cf31a 100644 --- a/http/version.lua +++ b/http/version.lua @@ -1,4 +1,4 @@ -- Сontains the module version. -- Requires manual update in case of release commit. -return '1.9.0' +return '1.8.0' From 6737576cb1e77a1a132f392d725d27483bf2427e Mon Sep 17 00:00:00 2001 From: themilchenko Date: Tue, 11 Nov 2025 19:52:20 +0300 Subject: [PATCH 2/7] Revert "roles: support `ssl_verify_client` option" This reverts commit 5ebd39df681390e69160960e663bef058acaf303. --- README.md | 1 - roles/httpd.lua | 1 - test/integration/httpd_role_test.lua | 56 ---------------------------- test/unit/httpd_role_test.lua | 18 --------- 4 files changed, 76 deletions(-) diff --git a/README.md b/README.md index f79aa6e..69500bc 100644 --- a/README.md +++ b/README.md @@ -604,7 +604,6 @@ roles_cfg: ssl_ciphers: "cipher1:cipher2" ssl_password: "password" ssl_password_file: "path/to/ssl/password" - ssl_verify_client: "off" ``` This role accepts a server by name from a config and creates a route to return diff --git a/roles/httpd.lua b/roles/httpd.lua index adb0402..0fae3e0 100644 --- a/roles/httpd.lua +++ b/roles/httpd.lua @@ -104,7 +104,6 @@ local function parse_params(node) ssl_password_file = node.ssl_password_file, ssl_ca_file = node.ssl_ca_file, ssl_ciphers = node.ssl_ciphers, - ssl_verify_client = node.ssl_verify_client, } end diff --git a/test/integration/httpd_role_test.lua b/test/integration/httpd_role_test.lua index cb5c43e..12f5739 100644 --- a/test/integration/httpd_role_test.lua +++ b/test/integration/httpd_role_test.lua @@ -255,59 +255,3 @@ g.test_enable_tls_on_config_reload = function(cg) local resp = http_client:get('http://localhost:13000/ping') t.assert_equals(resp.status, 444, 'response not 444') end - -g.test_ssl_verify_client = function(cg) - t.skip_if(not cg.params.use_tls, 'tls config required') - - local cfg = table.copy(tls_config) - - cfg.groups['group-001'].replicasets['replicaset-001'].roles_cfg['roles.httpd'].default - .ssl_ca_file = fio.pathjoin(ssl_data_dir, 'ca.crt') - cfg.groups['group-001'].replicasets['replicaset-001'].roles_cfg['roles.httpd'].default - .ssl_verify_client = "on" - treegen.write_file(cg.server.chdir, 'config.yaml', yaml.encode(cfg)) - local _, err = cg.server:eval("require('config'):reload()") - t.assert_not(err) - - t.assert_error_msg_contains(helpers.CONNECTION_REFUSED_ERR_MSG, function() - http_client:get('https://localhost:13000/ping', { - ca_file = fio.pathjoin(ssl_data_dir, 'ca.crt') - }) - end) - - local resp = http_client:get('https://localhost:13000/ping', { - ca_file = fio.pathjoin(ssl_data_dir, 'ca.crt'), - ssl_cert = fio.pathjoin(ssl_data_dir, 'client.crt'), - ssl_key = fio.pathjoin(ssl_data_dir, 'client.key'), - }) - t.assert_equals(resp.status, 200, 'response not 200') - t.assert_equals(resp.body, 'pong') - - cfg.groups['group-001'].replicasets['replicaset-001'].roles_cfg['roles.httpd'].default - .ssl_verify_client = "optional" - treegen.write_file(cg.server.chdir, 'config.yaml', yaml.encode(cfg)) - _, err = cg.server:eval("require('config'):reload()") - t.assert_not(err) - - t.assert_error_msg_contains(helpers.CONNECTION_REFUSED_ERR_MSG, function() - http_client:get('https://localhost:13000/ping', { - ca_file = fio.pathjoin(ssl_data_dir, 'ca.crt'), - ssl_cert = fio.pathjoin(ssl_data_dir, 'bad_client.crt'), - ssl_key = fio.pathjoin(ssl_data_dir, 'bad_client.key'), - }) - end) - - resp = http_client:get('https://localhost:13000/ping', { - ca_file = fio.pathjoin(ssl_data_dir, 'ca.crt'), - ssl_cert = fio.pathjoin(ssl_data_dir, 'client.crt'), - ssl_key = fio.pathjoin(ssl_data_dir, 'client.key'), - }) - t.assert_equals(resp.status, 200, 'response not 200') - t.assert_equals(resp.body, 'pong') -end - -g.after_test('test_ssl_verify_client', function(cg) - treegen.write_file(cg.server.chdir, 'config.yaml', yaml.encode(tls_config)) - local _, err = cg.server:eval("require('config'):reload()") - t.assert_not(err) -end) diff --git a/test/unit/httpd_role_test.lua b/test/unit/httpd_role_test.lua index 1321ea6..48cf837 100644 --- a/test/unit/httpd_role_test.lua +++ b/test/unit/httpd_role_test.lua @@ -226,24 +226,6 @@ local validation_cases = { }, }, err = "log_requests option should be a string", - }, - ["ssl_verify_client_invalid_type"] = { - cfg = { - server = { - listen = "localhost:123", - ssl_verify_client = 1, - } - }, - err = "ssl_verify_client option must be a string", - }, - ["ssl_verify_client_invalid_value"] = { - cfg = { - server = { - listen = "localhost:123", - ssl_verify_client = "unknown", - } - }, - err = '"unknown" option not exists. Available options: "on", "off", "optional"', } } From 3b1d1773f985ac45d85b174ed6e7595b049656cd Mon Sep 17 00:00:00 2001 From: themilchenko Date: Tue, 11 Nov 2025 19:52:21 +0300 Subject: [PATCH 3/7] Revert "api: support `ssl_verify_client` option" This reverts commit f8947793cf870b1334599d4fbb83cef6998a806b. --- CHANGELOG.md | 2 - README.md | 4 -- http/server.lua | 20 +----- http/sslsocket.lua | 8 --- test/helpers.lua | 10 --- test/integration/http_tls_enabled_test.lua | 67 ------------------- .../http_tls_enabled_validate_test.lua | 6 -- test/ssl_data/bad_client.crt | 32 --------- test/ssl_data/bad_client.key | 52 -------------- 9 files changed, 1 insertion(+), 200 deletions(-) delete mode 100644 test/ssl_data/bad_client.crt delete mode 100644 test/ssl_data/bad_client.key diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ad867c..bd269f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## Added -- `ssl_verify_client` option (#207). - ## Changed ## Fixed diff --git a/README.md b/README.md index 69500bc..1d90ceb 100644 --- a/README.md +++ b/README.md @@ -158,10 +158,6 @@ httpd = require('http.server').new(host, port[, { options } ]) * `ssl_ciphers` is a colon-separated list of SSL ciphers, optional; * `ssl_password` is a password for decrypting SSL private key, optional; * `ssl_password_file` is a SSL file with key for decrypting SSL private key, optional. - * `ssl_verify_client` is an option that allows to verify client. It has following values: - * `off` (default) means that no client's certs will be verified; - * `on` means that server will verify client's certs; - * `optional` means that server will verify client's certs only if it exist. ## Using routes diff --git a/http/server.lua b/http/server.lua index fa24e46..91d6a36 100644 --- a/http/server.lua +++ b/http/server.lua @@ -1296,12 +1296,6 @@ local function url_for_httpd(httpd, name, args, query) end end -local VERIFY_CLIENT_OPTS = { - off = sslsocket.SET_VERIFY_FLAGS.SSL_VERIFY_NONE, - optional = sslsocket.SET_VERIFY_FLAGS.SSL_VERIFY_PEER, - on = bit.bor(sslsocket.SET_VERIFY_FLAGS.SSL_VERIFY_PEER, sslsocket.SET_VERIFY_FLAGS.SSL_VERIFY_FAIL_IF_NO_PEER), -} - local function create_ssl_ctx(host, port, opts) local ok, ctx = pcall(sslsocket.ctx, sslsocket.tls_server_method()) if ok ~= true then @@ -1334,11 +1328,7 @@ local function create_ssl_ctx(host, port, opts) ) end - local set_verify_flag = ( - opts.ssl_verify_client and VERIFY_CLIENT_OPTS[opts.ssl_verify_client] or - VERIFY_CLIENT_OPTS.off - ) - sslsocket.ctx_set_verify(ctx, set_verify_flag) + sslsocket.ctx_set_verify(ctx, 0x00) end if opts.ssl_ciphers ~= nil then @@ -1393,12 +1383,6 @@ local function validate_ssl_opts(opts) errorf("%s option must be a string", key) end - if key == 'ssl_verify_client' then - if VERIFY_CLIENT_OPTS[value] == nil then - errorf('%q option not exists. Available options: "on", "off", "optional"', value) - end - end - if string.find(key, 'file') ~= nil and fio.path.exists(value) ~= true then errorf("file %q not exists", value) end @@ -1445,7 +1429,6 @@ local exports = { ssl_password_file = options.ssl_password_file, ssl_ca_file = options.ssl_ca_file, ssl_ciphers = options.ssl_ciphers, - ssl_verify_client = options.ssl_verify_client, }) local default = { @@ -1516,7 +1499,6 @@ local exports = { ssl_password_file = self.options.ssl_password_file, ssl_ca_file = self.options.ssl_ca_file, ssl_ciphers = self.options.ssl_ciphers, - ssl_verify_client = self.options.ssl_verify_client, }) return sslsocket.tcp_server(host, port, handler, timeout, ssl_ctx) end diff --git a/http/sslsocket.lua b/http/sslsocket.lua index 345d6ea..950196b 100644 --- a/http/sslsocket.lua +++ b/http/sslsocket.lua @@ -56,12 +56,6 @@ pcall(ffi.cdef, [[ const void *needle, size_t needlelen); ]]) -local SET_VERIFY_FLAGS = { - SSL_VERIFY_NONE = 0x00, - SSL_VERIFY_PEER = 0x01, - SSL_VERIFY_FAIL_IF_NO_PEER = 0x02, -} - local function slice_wait(timeout, starttime) if timeout == nil then return nil @@ -458,8 +452,6 @@ local function tcp_server(host, port, handler, timeout, sslctx) end return { - SET_VERIFY_FLAGS = SET_VERIFY_FLAGS, - tls_server_method = tls_server_method, ctx = ctx, diff --git a/test/helpers.lua b/test/helpers.lua index bfc6425..44936ce 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -12,16 +12,6 @@ helpers.base_host = '127.0.0.1' helpers.base_uri = ('http://%s:%s'):format(helpers.base_host, helpers.base_port) helpers.tls_uri = ('https://%s:%s'):format('localhost', helpers.base_port) -local is_tarantool1 = luatest_utils.version_ge( - luatest_utils.get_tarantool_version(), - luatest_utils.version(1, 0, 0) -) - -helpers.CONNECTION_REFUSED_ERR_MSG = "Failure when receiving data from the peer: Connection refused" -if is_tarantool1 then - helpers.CONNECTION_REFUSED_ERR_MSG = "Failure when receiving data from the peer" -end - helpers.get_testdir_path = function() local path = os.getenv('LUA_SOURCE_DIR') or './' return fio.pathjoin(path, 'test') diff --git a/test/integration/http_tls_enabled_test.lua b/test/integration/http_tls_enabled_test.lua index 478ae5e..92eebe7 100644 --- a/test/integration/http_tls_enabled_test.lua +++ b/test/integration/http_tls_enabled_test.lua @@ -145,73 +145,6 @@ local client_test_cases = { }, expected_err_msg = "curl: Problem with the local SSL certificate", }, - test_verify_client_optional_with_certs_valid = { - ssl_opts = { - ssl_verify_client = 'optional', - ssl_key_file = fio.pathjoin(ssl_data_dir, 'server.key'), - ssl_cert_file = fio.pathjoin(ssl_data_dir, 'server.crt'), - ssl_ca_file = fio.pathjoin(ssl_data_dir, 'ca.crt'), - }, - request_opts = { - ssl_cert = fio.pathjoin(ssl_data_dir, 'client.crt'), - ssl_key = fio.pathjoin(ssl_data_dir, 'client.key'), - }, - }, - test_verify_client_optional_with_certs_invalid = { - ssl_opts = { - ssl_verify_client = 'optional', - ssl_key_file = fio.pathjoin(ssl_data_dir, 'server.key'), - ssl_cert_file = fio.pathjoin(ssl_data_dir, 'server.crt'), - ssl_ca_file = fio.pathjoin(ssl_data_dir, 'ca.crt'), - }, - request_opts = { - ssl_cert = fio.pathjoin(ssl_data_dir, 'bad_client.crt'), - ssl_key = fio.pathjoin(ssl_data_dir, 'bad_client.key'), - }, - expected_err_msg = helpers.CONNECTION_REFUSED_ERR_MSG, - }, - test_verify_client_optional_withouts_certs = { - ssl_opts = { - ssl_verify_client = 'optional', - ssl_key_file = fio.pathjoin(ssl_data_dir, 'server.key'), - ssl_cert_file = fio.pathjoin(ssl_data_dir, 'server.crt'), - ssl_ca_file = fio.pathjoin(ssl_data_dir, 'ca.crt'), - }, - }, - test_verify_client_on_valid = { - ssl_opts = { - ssl_verify_client = 'on', - ssl_key_file = fio.pathjoin(ssl_data_dir, 'server.key'), - ssl_cert_file = fio.pathjoin(ssl_data_dir, 'server.crt'), - ssl_ca_file = fio.pathjoin(ssl_data_dir, 'ca.crt'), - }, - request_opts = { - ssl_cert = fio.pathjoin(ssl_data_dir, 'client.crt'), - ssl_key = fio.pathjoin(ssl_data_dir, 'client.key'), - }, - }, - test_verify_client_on_invalid = { - ssl_opts = { - ssl_verify_client = 'on', - ssl_key_file = fio.pathjoin(ssl_data_dir, 'server.key'), - ssl_cert_file = fio.pathjoin(ssl_data_dir, 'server.crt'), - ssl_ca_file = fio.pathjoin(ssl_data_dir, 'ca.crt'), - }, - request_opts = { - ssl_cert = fio.pathjoin(ssl_data_dir, 'bad_client.crt'), - ssl_key = fio.pathjoin(ssl_data_dir, 'bad_client.key'), - }, - expected_err_msg = helpers.CONNECTION_REFUSED_ERR_MSG, - }, - test_verify_client_on_certs_missing = { - ssl_opts = { - ssl_verify_client = 'on', - ssl_key_file = fio.pathjoin(ssl_data_dir, 'server.key'), - ssl_cert_file = fio.pathjoin(ssl_data_dir, 'server.crt'), - ssl_ca_file = fio.pathjoin(ssl_data_dir, 'ca.crt'), - }, - expected_err_msg = helpers.CONNECTION_REFUSED_ERR_MSG, - }, } for name, tc in pairs(client_test_cases) do diff --git a/test/integration/http_tls_enabled_validate_test.lua b/test/integration/http_tls_enabled_validate_test.lua index ea4835f..0439fa6 100644 --- a/test/integration/http_tls_enabled_validate_test.lua +++ b/test/integration/http_tls_enabled_validate_test.lua @@ -105,12 +105,6 @@ local test_cases = { }, expected_err_msg = "ssl_ciphers option must be a string", }, - ssl_verify_client_incorrect_value = { - opts = { - ssl_verify_client = "unknown", - }, - expected_err_msg = '"unknown" option not exists. Available options: "on", "off", "optional"' - }, } for name, case in pairs(test_cases) do diff --git a/test/ssl_data/bad_client.crt b/test/ssl_data/bad_client.crt deleted file mode 100644 index e097781..0000000 --- a/test/ssl_data/bad_client.crt +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFkDCCA3igAwIBAgIUdML0W9aabPXYExbeWFU4c5s7/ZYwDQYJKoZIhvcNAQEL -BQAwVTEQMA4GA1UECwwHVW5rbm93bjEQMA4GA1UECgwHVW5rbm93bjEQMA4GA1UE -BwwHVW5rbm93bjEQMA4GA1UECAwHdW5rbm93bjELMAkGA1UEBhMCQVUwIBcNMjUx -MTAxMTM1NzM4WhgPMjEyNTEwMDgxMzU3MzhaMGkxEjAQBgNVBAMMCWxvY2FsaG9z -dDEQMA4GA1UECwwHVW5rbm93bjEQMA4GA1UECgwHVW5rbm93bjEQMA4GA1UEBwwH -VW5rbm93bjEQMA4GA1UECAwHdW5rbm93bjELMAkGA1UEBhMCQVUwggIiMA0GCSqG -SIb3DQEBAQUAA4ICDwAwggIKAoICAQC9AKdee4oztHzNrfDvf0wrijKPaHCKtrwz -7vegGUiP6cQRLn9RO+OHnvZOxii/QdCtFAZHDLyhd5pwzyiJxh6iup2teOkmE9jQ -RdkFkZifJe5hzuRvZKj0Wdqe0T2bNzlNH2ejDrTD+I2n1N33pDcg7OIzm7w/FyV7 -HdXOU52cOrwcCv+5OdG8qxr7KunrD/Es5HMr3YkNeEk6PNAZeKIFHEmiIZoavfcZ -v1Ks78jRNh1/FehgM1lrCvhAF7S+u3NTKoMRutLMMNJ67ag9bwVbeYgxtXFLwFr/ -GBx+K1xnG9rsI6TiC48OoYBKSgFXmDLu27scgtIlbdlcMJBX4ElpTiLcPvo62UoC -TaImArBFaiFsO3QeG4Db6i20zXrlpTWJMDTq06Uk9zScpHGlFDLsLt3Ptk7hw9wf -kU/vMO/GAgU/ShQbTK/Cw0ZodTpAcCyyH60owx4ynBd+XgHEa3jbG2MAOsPtbgUL -OnboFUkwtwvKN+M647aD8OLQWGCgNOQM05MDe4BJnFf9yQEU0gyWQVT6n3hhgV3Z -RWZ4nrEz7qJf4ay+kvLrvP7jdELMmb2p+HATdzeiAb6jpIsmse6x/DfL96qc1j8G -H9P60W9oE0ISR+7Fy15Y+Wqov/WnrpbCIA/yw6JdjDRb+4qjY2+BmpGIwKWKND5I -C33s6oCh5wIDAQABo0IwQDAdBgNVHQ4EFgQUpiDiWDD57qUoauGYNJjODpqGr3Iw -HwYDVR0jBBgwFoAUfete7UEBQwYIC4dsESdN4ryLsZIwDQYJKoZIhvcNAQELBQAD -ggIBAGLBf4N8956edEl1o1lDyCk/NSdwk0th2LGZMh7WRLpGwh+Qwf42x5INjcBd -p8y2E6Avx5rtSDBgEqt7c1/Ug0aKlNxgu588MtBIaAy6PeyfPK1yjWE5MwSOFONn -qHcPKc92eixyZyv9BAC2PiqskFzTDAEQ78n1TBH4pgVurfoSybYOZcCy4nS6ug77 -HNNVKoGX2TQUclC4ZToywYextggZALZEL+xxNz8Xt+1ak6GLhOFfxvGJU43lY4dd -PDiueObzeFrLkHq/Tt0l9p7glV0DHW4MC3R8w5fKIUyXcXGBX+UB2ewFaiDHsWCt -NiuSbWtOkAnt6I5I8h2exbC1mWbUzUTyMV9zfYmwXJrvtN5DiBhBOXODZns1ZWck -AKgyOhma8Uey7VaDuhNbobHh7eRhkD1qqX2+OQAHX8z19vGTWmwyCqjhuNdFd1uu -PLaGrqo8e1GrdrwcZeVrSI9Cp15zi4LmLnG8YiDG5RkoIR6h3VKNmZsq8/U/7ouS -pyx5TRsY9s2v37dTJIFEmeUuG4GDW4EG2DDtJ75qXlipfCHLe4BT0cSKYCAXpJ1v -aqUYzPPmiP9q1wYfIkuNjJ3AGwPGoaCpBOMFrul8XU3Qk+kjXfVw3ZD6wR20iL86 -46UvD+VktOt6EZTOGjTdEjlyOzvRQdqqMIIiVNRx7gT8TlrN ------END CERTIFICATE----- diff --git a/test/ssl_data/bad_client.key b/test/ssl_data/bad_client.key deleted file mode 100644 index a99e586..0000000 --- a/test/ssl_data/bad_client.key +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -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 -4xz9jwcKbEzsHdwVB+OoRs47xcWsd923 ------END PRIVATE KEY----- From 542823d282dfef6c2ef3bd9bac7c9b6e3b4cddcb Mon Sep 17 00:00:00 2001 From: themilchenko Date: Wed, 5 Nov 2025 12:58:51 +0300 Subject: [PATCH 4/7] api: support `ssl_verify_client` option This patch allows to set a new `ssl_verify_client` option. It uses in pair with `ssl_ca_file` option and needs for client validation. It could have following values: * `off` (default one) means that no client's certs will be verified; * `on` means that server will verify client's certs; * `optional` means that server will verify client's certs only if it exist. This set of options was was built on top of the NGINX API (https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_verify_client). In effect, this option forces the server to work with mutual TLS. Part of #207 --- CHANGELOG.md | 2 + README.md | 4 ++ http/server.lua | 16 ++++- http/sslsocket.lua | 17 ++++- test/helpers.lua | 10 +++ test/integration/http_tls_enabled_test.lua | 67 +++++++++++++++++++ .../http_tls_enabled_validate_test.lua | 6 ++ test/ssl_data/bad_client.crt | 32 +++++++++ test/ssl_data/bad_client.key | 52 ++++++++++++++ 9 files changed, 203 insertions(+), 3 deletions(-) create mode 100644 test/ssl_data/bad_client.crt create mode 100644 test/ssl_data/bad_client.key diff --git a/CHANGELOG.md b/CHANGELOG.md index bd269f4..3ad867c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## Added +- `ssl_verify_client` option (#207). + ## Changed ## Fixed diff --git a/README.md b/README.md index 1d90ceb..69500bc 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,10 @@ httpd = require('http.server').new(host, port[, { options } ]) * `ssl_ciphers` is a colon-separated list of SSL ciphers, optional; * `ssl_password` is a password for decrypting SSL private key, optional; * `ssl_password_file` is a SSL file with key for decrypting SSL private key, optional. + * `ssl_verify_client` is an option that allows to verify client. It has following values: + * `off` (default) means that no client's certs will be verified; + * `on` means that server will verify client's certs; + * `optional` means that server will verify client's certs only if it exist. ## Using routes diff --git a/http/server.lua b/http/server.lua index 91d6a36..68757e6 100644 --- a/http/server.lua +++ b/http/server.lua @@ -1328,7 +1328,7 @@ local function create_ssl_ctx(host, port, opts) ) end - sslsocket.ctx_set_verify(ctx, 0x00) + sslsocket.ctx_set_verify(ctx, opts.ssl_verify_client) end if opts.ssl_ciphers ~= nil then @@ -1370,6 +1370,12 @@ local function httpd_start(self) return self end +local AVAILABLE_SSL_VERIFY_CLIENT_OPTS = { + off = true, + optional = true, + on = true, +} + -- validate_ssl_opts validates ssl_opts and returns true if at least ssl_cert_file -- and ssl_key_file parameters are not nil. local function validate_ssl_opts(opts) @@ -1383,6 +1389,12 @@ local function validate_ssl_opts(opts) errorf("%s option must be a string", key) end + if key == 'ssl_verify_client' then + if AVAILABLE_SSL_VERIFY_CLIENT_OPTS[value] == nil then + errorf('%q option not exists. Available options: "on", "off", "optional"', value) + end + end + if string.find(key, 'file') ~= nil and fio.path.exists(value) ~= true then errorf("file %q not exists", value) end @@ -1429,6 +1441,7 @@ local exports = { ssl_password_file = options.ssl_password_file, ssl_ca_file = options.ssl_ca_file, ssl_ciphers = options.ssl_ciphers, + ssl_verify_client = options.ssl_verify_client, }) local default = { @@ -1499,6 +1512,7 @@ local exports = { ssl_password_file = self.options.ssl_password_file, ssl_ca_file = self.options.ssl_ca_file, ssl_ciphers = self.options.ssl_ciphers, + ssl_verify_client = self.options.ssl_verify_client, }) return sslsocket.tcp_server(host, port, handler, timeout, ssl_ctx) end diff --git a/http/sslsocket.lua b/http/sslsocket.lua index 950196b..eea3226 100644 --- a/http/sslsocket.lua +++ b/http/sslsocket.lua @@ -56,6 +56,18 @@ pcall(ffi.cdef, [[ const void *needle, size_t needlelen); ]]) +local SET_VERIFY_FLAGS = { + SSL_VERIFY_NONE = 0x00, + SSL_VERIFY_PEER = 0x01, + SSL_VERIFY_FAIL_IF_NO_PEER = 0x02, +} + +local VERIFY_CLIENT_OPTS = { + off = SET_VERIFY_FLAGS.SSL_VERIFY_NONE, + optional = SET_VERIFY_FLAGS.SSL_VERIFY_PEER, + on = bit.bor(SET_VERIFY_FLAGS.SSL_VERIFY_PEER, SET_VERIFY_FLAGS.SSL_VERIFY_FAIL_IF_NO_PEER), +} + local function slice_wait(timeout, starttime) if timeout == nil then return nil @@ -162,8 +174,9 @@ local function ctx_set_cipher_list(ctx, str) return true end -local function ctx_set_verify(ctx, flags) - ffi.C.SSL_CTX_set_verify(ctx, flags, box.NULL) +local function ctx_set_verify(ctx, mode) + mode = mode or 'off' + ffi.C.SSL_CTX_set_verify(ctx, VERIFY_CLIENT_OPTS[mode], box.NULL) end local default_ctx = ctx(ffi.C.TLS_server_method()) diff --git a/test/helpers.lua b/test/helpers.lua index 44936ce..bfc6425 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -12,6 +12,16 @@ helpers.base_host = '127.0.0.1' helpers.base_uri = ('http://%s:%s'):format(helpers.base_host, helpers.base_port) helpers.tls_uri = ('https://%s:%s'):format('localhost', helpers.base_port) +local is_tarantool1 = luatest_utils.version_ge( + luatest_utils.get_tarantool_version(), + luatest_utils.version(1, 0, 0) +) + +helpers.CONNECTION_REFUSED_ERR_MSG = "Failure when receiving data from the peer: Connection refused" +if is_tarantool1 then + helpers.CONNECTION_REFUSED_ERR_MSG = "Failure when receiving data from the peer" +end + helpers.get_testdir_path = function() local path = os.getenv('LUA_SOURCE_DIR') or './' return fio.pathjoin(path, 'test') diff --git a/test/integration/http_tls_enabled_test.lua b/test/integration/http_tls_enabled_test.lua index 92eebe7..478ae5e 100644 --- a/test/integration/http_tls_enabled_test.lua +++ b/test/integration/http_tls_enabled_test.lua @@ -145,6 +145,73 @@ local client_test_cases = { }, expected_err_msg = "curl: Problem with the local SSL certificate", }, + test_verify_client_optional_with_certs_valid = { + ssl_opts = { + ssl_verify_client = 'optional', + ssl_key_file = fio.pathjoin(ssl_data_dir, 'server.key'), + ssl_cert_file = fio.pathjoin(ssl_data_dir, 'server.crt'), + ssl_ca_file = fio.pathjoin(ssl_data_dir, 'ca.crt'), + }, + request_opts = { + ssl_cert = fio.pathjoin(ssl_data_dir, 'client.crt'), + ssl_key = fio.pathjoin(ssl_data_dir, 'client.key'), + }, + }, + test_verify_client_optional_with_certs_invalid = { + ssl_opts = { + ssl_verify_client = 'optional', + ssl_key_file = fio.pathjoin(ssl_data_dir, 'server.key'), + ssl_cert_file = fio.pathjoin(ssl_data_dir, 'server.crt'), + ssl_ca_file = fio.pathjoin(ssl_data_dir, 'ca.crt'), + }, + request_opts = { + ssl_cert = fio.pathjoin(ssl_data_dir, 'bad_client.crt'), + ssl_key = fio.pathjoin(ssl_data_dir, 'bad_client.key'), + }, + expected_err_msg = helpers.CONNECTION_REFUSED_ERR_MSG, + }, + test_verify_client_optional_withouts_certs = { + ssl_opts = { + ssl_verify_client = 'optional', + ssl_key_file = fio.pathjoin(ssl_data_dir, 'server.key'), + ssl_cert_file = fio.pathjoin(ssl_data_dir, 'server.crt'), + ssl_ca_file = fio.pathjoin(ssl_data_dir, 'ca.crt'), + }, + }, + test_verify_client_on_valid = { + ssl_opts = { + ssl_verify_client = 'on', + ssl_key_file = fio.pathjoin(ssl_data_dir, 'server.key'), + ssl_cert_file = fio.pathjoin(ssl_data_dir, 'server.crt'), + ssl_ca_file = fio.pathjoin(ssl_data_dir, 'ca.crt'), + }, + request_opts = { + ssl_cert = fio.pathjoin(ssl_data_dir, 'client.crt'), + ssl_key = fio.pathjoin(ssl_data_dir, 'client.key'), + }, + }, + test_verify_client_on_invalid = { + ssl_opts = { + ssl_verify_client = 'on', + ssl_key_file = fio.pathjoin(ssl_data_dir, 'server.key'), + ssl_cert_file = fio.pathjoin(ssl_data_dir, 'server.crt'), + ssl_ca_file = fio.pathjoin(ssl_data_dir, 'ca.crt'), + }, + request_opts = { + ssl_cert = fio.pathjoin(ssl_data_dir, 'bad_client.crt'), + ssl_key = fio.pathjoin(ssl_data_dir, 'bad_client.key'), + }, + expected_err_msg = helpers.CONNECTION_REFUSED_ERR_MSG, + }, + test_verify_client_on_certs_missing = { + ssl_opts = { + ssl_verify_client = 'on', + ssl_key_file = fio.pathjoin(ssl_data_dir, 'server.key'), + ssl_cert_file = fio.pathjoin(ssl_data_dir, 'server.crt'), + ssl_ca_file = fio.pathjoin(ssl_data_dir, 'ca.crt'), + }, + expected_err_msg = helpers.CONNECTION_REFUSED_ERR_MSG, + }, } for name, tc in pairs(client_test_cases) do diff --git a/test/integration/http_tls_enabled_validate_test.lua b/test/integration/http_tls_enabled_validate_test.lua index 0439fa6..ea4835f 100644 --- a/test/integration/http_tls_enabled_validate_test.lua +++ b/test/integration/http_tls_enabled_validate_test.lua @@ -105,6 +105,12 @@ local test_cases = { }, expected_err_msg = "ssl_ciphers option must be a string", }, + ssl_verify_client_incorrect_value = { + opts = { + ssl_verify_client = "unknown", + }, + expected_err_msg = '"unknown" option not exists. Available options: "on", "off", "optional"' + }, } for name, case in pairs(test_cases) do diff --git a/test/ssl_data/bad_client.crt b/test/ssl_data/bad_client.crt new file mode 100644 index 0000000..e097781 --- /dev/null +++ b/test/ssl_data/bad_client.crt @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFkDCCA3igAwIBAgIUdML0W9aabPXYExbeWFU4c5s7/ZYwDQYJKoZIhvcNAQEL +BQAwVTEQMA4GA1UECwwHVW5rbm93bjEQMA4GA1UECgwHVW5rbm93bjEQMA4GA1UE +BwwHVW5rbm93bjEQMA4GA1UECAwHdW5rbm93bjELMAkGA1UEBhMCQVUwIBcNMjUx +MTAxMTM1NzM4WhgPMjEyNTEwMDgxMzU3MzhaMGkxEjAQBgNVBAMMCWxvY2FsaG9z +dDEQMA4GA1UECwwHVW5rbm93bjEQMA4GA1UECgwHVW5rbm93bjEQMA4GA1UEBwwH +VW5rbm93bjEQMA4GA1UECAwHdW5rbm93bjELMAkGA1UEBhMCQVUwggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQC9AKdee4oztHzNrfDvf0wrijKPaHCKtrwz +7vegGUiP6cQRLn9RO+OHnvZOxii/QdCtFAZHDLyhd5pwzyiJxh6iup2teOkmE9jQ +RdkFkZifJe5hzuRvZKj0Wdqe0T2bNzlNH2ejDrTD+I2n1N33pDcg7OIzm7w/FyV7 +HdXOU52cOrwcCv+5OdG8qxr7KunrD/Es5HMr3YkNeEk6PNAZeKIFHEmiIZoavfcZ +v1Ks78jRNh1/FehgM1lrCvhAF7S+u3NTKoMRutLMMNJ67ag9bwVbeYgxtXFLwFr/ +GBx+K1xnG9rsI6TiC48OoYBKSgFXmDLu27scgtIlbdlcMJBX4ElpTiLcPvo62UoC +TaImArBFaiFsO3QeG4Db6i20zXrlpTWJMDTq06Uk9zScpHGlFDLsLt3Ptk7hw9wf +kU/vMO/GAgU/ShQbTK/Cw0ZodTpAcCyyH60owx4ynBd+XgHEa3jbG2MAOsPtbgUL +OnboFUkwtwvKN+M647aD8OLQWGCgNOQM05MDe4BJnFf9yQEU0gyWQVT6n3hhgV3Z +RWZ4nrEz7qJf4ay+kvLrvP7jdELMmb2p+HATdzeiAb6jpIsmse6x/DfL96qc1j8G +H9P60W9oE0ISR+7Fy15Y+Wqov/WnrpbCIA/yw6JdjDRb+4qjY2+BmpGIwKWKND5I +C33s6oCh5wIDAQABo0IwQDAdBgNVHQ4EFgQUpiDiWDD57qUoauGYNJjODpqGr3Iw +HwYDVR0jBBgwFoAUfete7UEBQwYIC4dsESdN4ryLsZIwDQYJKoZIhvcNAQELBQAD +ggIBAGLBf4N8956edEl1o1lDyCk/NSdwk0th2LGZMh7WRLpGwh+Qwf42x5INjcBd +p8y2E6Avx5rtSDBgEqt7c1/Ug0aKlNxgu588MtBIaAy6PeyfPK1yjWE5MwSOFONn +qHcPKc92eixyZyv9BAC2PiqskFzTDAEQ78n1TBH4pgVurfoSybYOZcCy4nS6ug77 +HNNVKoGX2TQUclC4ZToywYextggZALZEL+xxNz8Xt+1ak6GLhOFfxvGJU43lY4dd +PDiueObzeFrLkHq/Tt0l9p7glV0DHW4MC3R8w5fKIUyXcXGBX+UB2ewFaiDHsWCt +NiuSbWtOkAnt6I5I8h2exbC1mWbUzUTyMV9zfYmwXJrvtN5DiBhBOXODZns1ZWck +AKgyOhma8Uey7VaDuhNbobHh7eRhkD1qqX2+OQAHX8z19vGTWmwyCqjhuNdFd1uu +PLaGrqo8e1GrdrwcZeVrSI9Cp15zi4LmLnG8YiDG5RkoIR6h3VKNmZsq8/U/7ouS +pyx5TRsY9s2v37dTJIFEmeUuG4GDW4EG2DDtJ75qXlipfCHLe4BT0cSKYCAXpJ1v +aqUYzPPmiP9q1wYfIkuNjJ3AGwPGoaCpBOMFrul8XU3Qk+kjXfVw3ZD6wR20iL86 +46UvD+VktOt6EZTOGjTdEjlyOzvRQdqqMIIiVNRx7gT8TlrN +-----END CERTIFICATE----- diff --git a/test/ssl_data/bad_client.key b/test/ssl_data/bad_client.key new file mode 100644 index 0000000..a99e586 --- /dev/null +++ b/test/ssl_data/bad_client.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +4xz9jwcKbEzsHdwVB+OoRs47xcWsd923 +-----END PRIVATE KEY----- From b4ad81dce69ea222a23adb9add011c2e21c9421a Mon Sep 17 00:00:00 2001 From: themilchenko Date: Wed, 5 Nov 2025 16:16:42 +0300 Subject: [PATCH 5/7] roles: support `ssl_verify_client` option Since http server supports a new `ssl_verify_client` option it is necessary to support it in role api as well. This patch introduces a new config parameter in httpd role with the same `ssl_verify_client` name. Closes #207 --- README.md | 1 + roles/httpd.lua | 1 + test/integration/httpd_role_test.lua | 56 ++++++++++++++++++++++++++++ test/unit/httpd_role_test.lua | 18 +++++++++ 4 files changed, 76 insertions(+) diff --git a/README.md b/README.md index 69500bc..f79aa6e 100644 --- a/README.md +++ b/README.md @@ -604,6 +604,7 @@ roles_cfg: ssl_ciphers: "cipher1:cipher2" ssl_password: "password" ssl_password_file: "path/to/ssl/password" + ssl_verify_client: "off" ``` This role accepts a server by name from a config and creates a route to return diff --git a/roles/httpd.lua b/roles/httpd.lua index 0fae3e0..adb0402 100644 --- a/roles/httpd.lua +++ b/roles/httpd.lua @@ -104,6 +104,7 @@ local function parse_params(node) ssl_password_file = node.ssl_password_file, ssl_ca_file = node.ssl_ca_file, ssl_ciphers = node.ssl_ciphers, + ssl_verify_client = node.ssl_verify_client, } end diff --git a/test/integration/httpd_role_test.lua b/test/integration/httpd_role_test.lua index 12f5739..cb5c43e 100644 --- a/test/integration/httpd_role_test.lua +++ b/test/integration/httpd_role_test.lua @@ -255,3 +255,59 @@ g.test_enable_tls_on_config_reload = function(cg) local resp = http_client:get('http://localhost:13000/ping') t.assert_equals(resp.status, 444, 'response not 444') end + +g.test_ssl_verify_client = function(cg) + t.skip_if(not cg.params.use_tls, 'tls config required') + + local cfg = table.copy(tls_config) + + cfg.groups['group-001'].replicasets['replicaset-001'].roles_cfg['roles.httpd'].default + .ssl_ca_file = fio.pathjoin(ssl_data_dir, 'ca.crt') + cfg.groups['group-001'].replicasets['replicaset-001'].roles_cfg['roles.httpd'].default + .ssl_verify_client = "on" + treegen.write_file(cg.server.chdir, 'config.yaml', yaml.encode(cfg)) + local _, err = cg.server:eval("require('config'):reload()") + t.assert_not(err) + + t.assert_error_msg_contains(helpers.CONNECTION_REFUSED_ERR_MSG, function() + http_client:get('https://localhost:13000/ping', { + ca_file = fio.pathjoin(ssl_data_dir, 'ca.crt') + }) + end) + + local resp = http_client:get('https://localhost:13000/ping', { + ca_file = fio.pathjoin(ssl_data_dir, 'ca.crt'), + ssl_cert = fio.pathjoin(ssl_data_dir, 'client.crt'), + ssl_key = fio.pathjoin(ssl_data_dir, 'client.key'), + }) + t.assert_equals(resp.status, 200, 'response not 200') + t.assert_equals(resp.body, 'pong') + + cfg.groups['group-001'].replicasets['replicaset-001'].roles_cfg['roles.httpd'].default + .ssl_verify_client = "optional" + treegen.write_file(cg.server.chdir, 'config.yaml', yaml.encode(cfg)) + _, err = cg.server:eval("require('config'):reload()") + t.assert_not(err) + + t.assert_error_msg_contains(helpers.CONNECTION_REFUSED_ERR_MSG, function() + http_client:get('https://localhost:13000/ping', { + ca_file = fio.pathjoin(ssl_data_dir, 'ca.crt'), + ssl_cert = fio.pathjoin(ssl_data_dir, 'bad_client.crt'), + ssl_key = fio.pathjoin(ssl_data_dir, 'bad_client.key'), + }) + end) + + resp = http_client:get('https://localhost:13000/ping', { + ca_file = fio.pathjoin(ssl_data_dir, 'ca.crt'), + ssl_cert = fio.pathjoin(ssl_data_dir, 'client.crt'), + ssl_key = fio.pathjoin(ssl_data_dir, 'client.key'), + }) + t.assert_equals(resp.status, 200, 'response not 200') + t.assert_equals(resp.body, 'pong') +end + +g.after_test('test_ssl_verify_client', function(cg) + treegen.write_file(cg.server.chdir, 'config.yaml', yaml.encode(tls_config)) + local _, err = cg.server:eval("require('config'):reload()") + t.assert_not(err) +end) diff --git a/test/unit/httpd_role_test.lua b/test/unit/httpd_role_test.lua index 48cf837..1321ea6 100644 --- a/test/unit/httpd_role_test.lua +++ b/test/unit/httpd_role_test.lua @@ -226,6 +226,24 @@ local validation_cases = { }, }, err = "log_requests option should be a string", + }, + ["ssl_verify_client_invalid_type"] = { + cfg = { + server = { + listen = "localhost:123", + ssl_verify_client = 1, + } + }, + err = "ssl_verify_client option must be a string", + }, + ["ssl_verify_client_invalid_value"] = { + cfg = { + server = { + listen = "localhost:123", + ssl_verify_client = "unknown", + } + }, + err = '"unknown" option not exists. Available options: "on", "off", "optional"', } } From 71303dd93338d64b00a45339ed2dd9de3b5f5fe8 Mon Sep 17 00:00:00 2001 From: themilchenko Date: Tue, 11 Nov 2025 20:16:35 +0300 Subject: [PATCH 6/7] test: add check on unsupported ssl Closes #202 --- .github/workflows/test.yml | 10 ++++++++-- test/helpers.lua | 13 +++++++++++++ test/integration/http_tls_enabled_test.lua | 4 ++++ test/integration/http_tls_enabled_validate_test.lua | 12 ++++++++++++ test/integration/httpd_role_test.lua | 4 ++++ test/unit/httpd_role_test.lua | 3 +++ 6 files changed, 44 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d89339e..f068f76 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,9 +12,14 @@ jobs: matrix: tarantool: ['1.10', '2.10', '2.11', '3.1', '3.2'] coveralls: [false] + static-build: [false] include: - - tarantool: '2.11' + - tarantool: '3.5' coveralls: true + static-build: false + - tarantool: '3.5' + coveralls: false + static-build: true runs-on: [ubuntu-22.04] steps: - uses: actions/checkout@master @@ -32,7 +37,8 @@ jobs: env: DEBIAN_FRONTEND: noninteractive - - name: Install Tarantool + - name: Install dynamic Tarantool + if: matrix.static-build == false run: tt install tarantool ${{ matrix.tarantool }} --dynamic - name: Cache rocks diff --git a/test/helpers.lua b/test/helpers.lua index bfc6425..4991c4b 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -148,4 +148,17 @@ helpers.tcp_connection_exists = function(host, port) return ok end +local ffi = require('ffi') +local has_tls_method = pcall(function() + return ffi.C.TLS_server_method() ~= nil +end) + +helpers.skip_if_ssl_not_enabled = function() + luatest.skip_if(not has_tls_method, 'tarantool does not support ssl') +end + +helpers.skip_if_ssl_enabled = function() + luatest.skip_if(has_tls_method, 'tarantool supports ssl') +end + return helpers diff --git a/test/integration/http_tls_enabled_test.lua b/test/integration/http_tls_enabled_test.lua index 478ae5e..9b8259f 100644 --- a/test/integration/http_tls_enabled_test.lua +++ b/test/integration/http_tls_enabled_test.lua @@ -9,6 +9,10 @@ local g = t.group('ssl') local ssl_data_dir = fio.pathjoin(helpers.get_testdir_path(), "ssl_data") +g.before_all(function() + helpers.skip_if_ssl_not_enabled() +end) + local server_test_cases = { test_key_password_missing = { ssl_opts = { diff --git a/test/integration/http_tls_enabled_validate_test.lua b/test/integration/http_tls_enabled_validate_test.lua index ea4835f..3ac649b 100644 --- a/test/integration/http_tls_enabled_validate_test.lua +++ b/test/integration/http_tls_enabled_validate_test.lua @@ -111,10 +111,22 @@ local test_cases = { }, expected_err_msg = '"unknown" option not exists. Available options: "on", "off", "optional"' }, + ssl_socket_not_supported = { + check_ssl = true, + opts = { + ssl_cert_file = fio.pathjoin(ssl_data_dir, 'server.crt'), + ssl_key_file = fio.pathjoin(ssl_data_dir, 'server.key'), + }, + expected_err_msg = 'ssl socket is not supported', + } } for name, case in pairs(test_cases) do g['test_ssl_option_' .. name] = function() + helpers.skip_if_ssl_not_enabled() + if case.check_ssl == true then + helpers.skip_if_ssl_enabled() + end t.assert_error_msg_contains(case.expected_err_msg, function() http_server.new('host', 8080, case.opts) end) diff --git a/test/integration/httpd_role_test.lua b/test/integration/httpd_role_test.lua index cb5c43e..36dc92b 100644 --- a/test/integration/httpd_role_test.lua +++ b/test/integration/httpd_role_test.lua @@ -88,6 +88,10 @@ tls_config.groups['group-001'].replicasets['replicaset-001'].roles_cfg['roles.ht tls_config.groups['group-001'].replicasets['replicaset-001'].roles_cfg['roles.httpd'].default .ssl_password_file = fio.pathjoin(ssl_data_dir, 'passwords') +g.before_all(function() + helpers.skip_if_ssl_not_enabled() +end) + g.before_each(function(cg) helpers.skip_if_not_tarantool3() diff --git a/test/unit/httpd_role_test.lua b/test/unit/httpd_role_test.lua index 1321ea6..d554660 100644 --- a/test/unit/httpd_role_test.lua +++ b/test/unit/httpd_role_test.lua @@ -254,6 +254,9 @@ for name, case in pairs(validation_cases) do ) g[test_name] = function() + if name:find('ssl_') ~= nil and case.err == nil then + helpers.skip_if_ssl_not_enabled() + end local ok, res = pcall(httpd_role.validate, case.cfg) if case.err ~= nil then From 00b95267436fbfa1bffb4bb83415beee4f57d20b Mon Sep 17 00:00:00 2001 From: themilchenko Date: Tue, 11 Nov 2025 20:38:42 +0300 Subject: [PATCH 7/7] Release 1.9.0 --- CHANGELOG.md | 17 +++++++++++++---- http/version.lua | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ad867c..537634d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,13 +6,22 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] -## Added +### Added -- `ssl_verify_client` option (#207). +### Changed + +### Fixed + +## [1.9.0] - 2025-11-12 -## Changed +The release introduces a new `ssl_verify_client` option and changes default +behavior with provided `ca_file` param. Also a few bugs were fixed. -## Fixed +### Added + +- `ssl_verify_client` option (#207). + +### Fixed - Do not recreate server if it's address and port were not changed (#219). - Server doesn't change after updating parameters on config reload (#216). diff --git a/http/version.lua b/http/version.lua index c7cf31a..d604410 100644 --- a/http/version.lua +++ b/http/version.lua @@ -1,4 +1,4 @@ -- Сontains the module version. -- Requires manual update in case of release commit. -return '1.8.0' +return '1.9.0'