diff --git a/.gitignore b/.gitignore index da1cc2c64..9ac38b3ad 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,4 @@ sandbox.lua *.xlog *.snap package.json +*.log diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 813711dc6..577b7542b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -23,6 +23,10 @@ Added - GraphQL API to enable previously disabled instances: ``mutation { cluster { enable_servers(uuids: [...]) { } } }``. +- ``cartridge.cfg`` param ``set_cookie_hash_membership`` to set + cluster cookie hash as encryption key in membership instead of + plain cookie. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Changed ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -42,6 +46,12 @@ Fixed - Leader autoreturn doesn't try to return leadership to unhealthy leader anymore. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Deprecated +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Usage of plain cluster cookie as a membership encryption key by default. + ------------------------------------------------------------------------------- [2.10.0] - 2024-04-10 ------------------------------------------------------------------------------- diff --git a/cartridge.lua b/cartridge.lua index 1aae66959..23efa16ea 100644 --- a/cartridge.lua +++ b/cartridge.lua @@ -23,6 +23,7 @@ local http = require('http.server') local fiber = require('fiber') local socket = require('socket') local json = require('json') +local digest = require('digest') local tarantool_version = require('tarantool').version local rpc = require('cartridge.rpc') @@ -275,6 +276,34 @@ end -- env `TARANTOOL_UPLOAD_PREFIX`, -- args `--upload-prefix`) -- + +-- @tparam ?boolean opts.enable_failover_suppressing +-- Enable failover suppressing. It forces eventual failover +-- to stop in case of constant switching. +-- default: `false`, overridden by +-- env `TARANTOOL_ENABLE_FAILOVER_SUPPRESSING`, +-- args `--enable-failover-suppressing`) +-- +-- @tparam ?boolean opts.enable_synchro_mode +-- Allow to use sync spaces in Cartridge. +-- default: `false`, overridden by +-- env `TARANTOOL_ENABLE_SYNCHRO_MODE`, +-- args `--enable-synchro-mode`) +-- +-- @tparam ?boolean opts.disable_raft_on_small_clusters +-- Disable Raft Failover on small clusters (where +-- number of instances is less than 3) +-- default: `true`, overridden by +-- env `TARANTOOL_DISABLE_RAFT_ON_SMALL_CLUSTERS`, +-- args `--disable-raft-on-small-clusters`) +-- +-- @tparam ?boolean opts.set_cookie_hash_membership +-- Set cookie hash instead of full cluster cookie +-- as membership encryption key. +-- default: `false`, overridden by +-- env `TARANTOOL_SET_COOKIE_HASH_MEMBERSHIP`, +-- args `--set-cookie-hash-membership`) +-- -- @tparam ?table box_opts -- tarantool extra box.cfg options (e.g. memtx_memory), -- that may require additional tuning @@ -310,6 +339,7 @@ local function cfg(opts, box_opts) enable_sychro_mode = '?boolean', enable_synchro_mode = '?boolean', disable_raft_on_small_clusters = '?boolean', + set_cookie_hash_membership = '?boolean', transport = '?string', ssl_ciphers = '?string', @@ -645,8 +675,18 @@ local function cfg(opts, box_opts) if opts.alias == nil then opts.alias = args.instance_name end + local encryption_key = cluster_cookie.cookie() + + if opts.set_cookie_hash_membership == true then + encryption_key = digest.md5_hex(encryption_key) + else + log.warn( + 'Consider changing membership encryption key to cookie hash manually. ' .. + 'set_cookie_hash_membership will be true by default in next releases.' + ) + end - membership.set_encryption_key(cluster_cookie.cookie()) + membership.set_encryption_key(encryption_key) membership.set_payload('alias', opts.alias) local probe_uri_opts, err = argparse.get_opts({probe_uri_timeout = 'number'}) diff --git a/cartridge/argparse.lua b/cartridge/argparse.lua index 376e07abf..a2cf23c3d 100644 --- a/cartridge/argparse.lua +++ b/cartridge/argparse.lua @@ -116,6 +116,7 @@ local cluster_opts = { enable_synchro_mode = 'boolean', -- **boolean** disable_raft_on_small_clusters = 'boolean', -- **boolean** enable_failover_suppressing = 'boolean', -- **boolean** + set_cookie_hash_membership = 'boolean', -- **boolean** twophase_netbox_call_timeout = 'number', -- **number** twophase_upload_config_timeout = 'number', -- **number** diff --git a/test/integration/two_cluster_32_symbol_cookie_test.lua b/test/integration/two_cluster_32_symbol_cookie_test.lua new file mode 100644 index 000000000..df8d322cc --- /dev/null +++ b/test/integration/two_cluster_32_symbol_cookie_test.lua @@ -0,0 +1,76 @@ +local fio = require('fio') +local digest = require('digest') +local t = require('luatest') +local g = t.group() + +local helpers = require('test.helper') + +g.before_all = function() + local cookie = digest.urandom(16):hex() + + g.cluster1 = helpers.Cluster:new({ + datadir = fio.tempdir(), + server_command = helpers.entrypoint('srv_basic'), + cookie = cookie..'a', + replicasets = { + { + alias = 'master', + uuid = helpers.uuid('a'), + roles = {}, + servers = {{ + http_port = 8081, + advertise_port = 13301, + instance_uuid = helpers.uuid('a', 'a', 1) + }}, + } + }, + env = { + TARANTOOL_SET_COOKIE_HASH_MEMBERSHIP = 'true', + } + }) + + g.cluster1:start() + + g.cluster2 = helpers.Cluster:new({ + datadir = fio.tempdir(), + server_command = helpers.entrypoint('srv_basic'), + cookie = cookie..'b', + replicasets = { + { + alias = 'master', + uuid = helpers.uuid('b'), + roles = {}, + servers = {{ + http_port = 8082, + advertise_port = 13302, + instance_uuid = helpers.uuid('b', 'b', 1) + }}, + } + }, + env = { + TARANTOOL_SET_COOKIE_HASH_MEMBERSHIP = 'true', + } + }) + + g.cluster2.servers[1]:start() + -- this instance is seen by the first cluster + -- because of membership encryption key + -- ignores keys with lenght > 32 symbols + -- setting TARANTOOL_SET_COOKIE_HASH_MEMBERSHIP to true + -- fixes this bug +end + +g.after_all = function() + g.cluster1:stop() + fio.rmtree(g.cluster1.datadir) + g.cluster2:stop() + fio.rmtree(g.cluster2.datadir) +end + +function g.test_two_clusters() + local res = g.cluster1.main_server:exec(function() + local members = require('membership').members() + return members['localhost:13302'] + end) + t.assert_not(res) +end