diff options
author | mkaruza <mario.karuza@galeracluster.com> | 2021-11-22 13:04:40 +0100 |
---|---|---|
committer | Jan Lindström <jan.lindstrom@mariadb.com> | 2021-12-14 11:32:50 +0200 |
commit | f68aa1e4511106c81f60e8fa37896276d2267e77 (patch) | |
tree | 93ac8dfe0d0e862ddef071804594fe62d39707bf | |
parent | ccb345e2a3616590ea741830ded1b7f645639de0 (diff) | |
download | mariadb-git-preview-10.8-MDEV-27246-galera-whitelist.tar.gz |
MDEV-27246 Implement a method to add IPs to allowlist for Galera Cluster node addresses that can make SST/IST requestspreview-10.8-MDEV-27246-galera-whitelist
Reviewed-by: Jan Lindström <jan.lindstrom@mariadb.com>
25 files changed, 489 insertions, 40 deletions
diff --git a/mysql-test/include/galera_variables_ok.inc b/mysql-test/include/galera_variables_ok.inc index 82c5174bc14..c9a54724c17 100644 --- a/mysql-test/include/galera_variables_ok.inc +++ b/mysql-test/include/galera_variables_ok.inc @@ -1,6 +1,6 @@ --disable_query_log ---let $galera_variables_ok = `SELECT COUNT(*) = 49 FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'wsrep%'` +--let $galera_variables_ok = `SELECT COUNT(*) = 50 FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'wsrep%'` --if (!$galera_variables_ok) { --skip Galera number of variables has changed! diff --git a/mysql-test/include/galera_variables_ok_debug.inc b/mysql-test/include/galera_variables_ok_debug.inc index c9a54724c17..e420b3af6c3 100644 --- a/mysql-test/include/galera_variables_ok_debug.inc +++ b/mysql-test/include/galera_variables_ok_debug.inc @@ -1,6 +1,6 @@ --disable_query_log ---let $galera_variables_ok = `SELECT COUNT(*) = 50 FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'wsrep%'` +--let $galera_variables_ok = `SELECT COUNT(*) = 51 FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'wsrep%'` --if (!$galera_variables_ok) { --skip Galera number of variables has changed! diff --git a/mysql-test/suite/galera/r/galera_defaults.result b/mysql-test/suite/galera/r/galera_defaults.result index d6bbcbae680..738e474560f 100644 --- a/mysql-test/suite/galera/r/galera_defaults.result +++ b/mysql-test/suite/galera/r/galera_defaults.result @@ -1,9 +1,6 @@ connection node_2; connection node_1; # Correct Galera library found -SELECT COUNT(*) `expect 50` FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'wsrep_%'; -expect 50 -49 SELECT VARIABLE_NAME, VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'wsrep_%' @@ -20,6 +17,7 @@ AND VARIABLE_NAME NOT IN ( ) ORDER BY VARIABLE_NAME; VARIABLE_NAME VARIABLE_VALUE +WSREP_ALLOWLIST WSREP_AUTO_INCREMENT_CONTROL ON WSREP_CAUSAL_READS ON WSREP_CERTIFICATION_RULES strict diff --git a/mysql-test/suite/galera/t/galera_defaults.test b/mysql-test/suite/galera/t/galera_defaults.test index 6b76473d6a6..ff08151327a 100644 --- a/mysql-test/suite/galera/t/galera_defaults.test +++ b/mysql-test/suite/galera/t/galera_defaults.test @@ -13,13 +13,11 @@ --source include/force_restart.inc # Make sure that the test is operating on the right version of galera library. ---let $galera_version=26.4.7 +--let $galera_version=26.4.11 source ../wsrep/include/check_galera_version.inc; # Global Variables -SELECT COUNT(*) `expect 50` FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'wsrep_%'; - SELECT VARIABLE_NAME, VARIABLE_VALUE FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'wsrep_%' diff --git a/mysql-test/suite/galera_3nodes/r/galera_allowlist.result b/mysql-test/suite/galera_3nodes/r/galera_allowlist.result new file mode 100644 index 00000000000..471444d8c08 --- /dev/null +++ b/mysql-test/suite/galera_3nodes/r/galera_allowlist.result @@ -0,0 +1,35 @@ +connection node_2; +connection node_1; +SELECT COUNT(*) = 3 FROM mysql.wsrep_allowlist; +COUNT(*) = 3 +1 +connection node_2; +SELECT COUNT(*) = 3 FROM mysql.wsrep_allowlist; +COUNT(*) = 3 +1 +connection node_3; +SET @@global.wsrep_desync = 1; +SET SESSION wsrep_sync_wait = 0; +SET GLOBAL wsrep_provider_options = 'gmcast.isolate=1'; +connection node_1; +DELETE FROM mysql.wsrep_allowlist WHERE ip LIKE '127.0.0.3'; +SELECT COUNT(*) = 2 FROM mysql.wsrep_allowlist; +COUNT(*) = 2 +1 +connection node_2; +SELECT COUNT(*) = 2 FROM mysql.wsrep_allowlist; +COUNT(*) = 2 +1 +connection node_3; +SET GLOBAL wsrep_provider_options = 'gmcast.isolate=0'; +SET @@global.wsrep_desync = 0; +connection node_1; +INSERT INTO mysql.wsrep_allowlist(ip) VALUES ('127.0.0.3'); +connection node_3; +# restart +connection node_1; +CALL mtr.add_suppression('WSREP: Connection not allowed'); +connection node_2; +CALL mtr.add_suppression('WSREP: Connection not allowed'); +connection node_3; +CALL mtr.add_suppression('WSREP: Ignoring lack of quorum'); diff --git a/mysql-test/suite/galera_3nodes/t/galera_allowlist.cnf b/mysql-test/suite/galera_3nodes/t/galera_allowlist.cnf new file mode 100644 index 00000000000..62f24c172af --- /dev/null +++ b/mysql-test/suite/galera_3nodes/t/galera_allowlist.cnf @@ -0,0 +1,26 @@ +!include ../galera_3nodes.cnf + +[mysqld] +wsrep_sst_method=rsync + +[mysqld.1] +wsrep_allowlist="127.0.0.1,127.0.0.2,127.0.0.3" + +[mysqld.2] +wsrep_provider_options='repl.causal_read_timeout=PT90S;base_port=@mysqld.2.#galera_port;gmcast.listen_addr=127.0.0.2;evs.suspect_timeout=PT10S;evs.inactive_timeout=PT30S;evs.install_timeout=PT15S' + +# Variable is only used on bootstrap node, so this will be ignored +wsrep_allowlist="127.0.0.1,127.0.0.2,127.0.0.3,127.0.0.4,127.0.0.5" + +wsrep_node_address=127.0.0.2 +wsrep_sst_receive_address=127.0.0.2:@mysqld.2.#sst_port +wsrep_node_incoming_address=127.0.0.2:@mysqld.2.port +wsrep_sst_receive_address='127.0.0.2:@mysqld.2.#sst_port' + +[mysqld.3] +wsrep_provider_options='repl.causal_read_timeout=PT90S;base_port=@mysqld.3.#galera_port;gmcast.listen_addr=127.0.0.3;evs.suspect_timeout=PT10S;evs.inactive_timeout=PT30S;evs.install_timeout=PT15S;pc.ignore_quorum=TRUE;pc.wait_prim=FALSE' + +wsrep_node_address=127.0.0.3 +wsrep_sst_receive_address=127.0.0.3:@mysqld.3.#sst_port +wsrep_node_incoming_address=127.0.0.3:@mysqld.3.port +wsrep_sst_receive_address='127.0.0.3:@mysqld.3.#sst_port'
\ No newline at end of file diff --git a/mysql-test/suite/galera_3nodes/t/galera_allowlist.test b/mysql-test/suite/galera_3nodes/t/galera_allowlist.test new file mode 100644 index 00000000000..74fff61c4f8 --- /dev/null +++ b/mysql-test/suite/galera_3nodes/t/galera_allowlist.test @@ -0,0 +1,66 @@ +--source include/galera_cluster.inc +--source include/have_innodb.inc + +# Check that `wsrep_allowlist` variable is loaded +SELECT COUNT(*) = 3 FROM mysql.wsrep_allowlist; + +--connection node_2 +# Check that non-bootstrap nodes doesn't populate `mysql.wsrep_allowlist` +SELECT COUNT(*) = 3 FROM mysql.wsrep_allowlist; + +--let $galera_connection_name = node_3 +--let $galera_server_number = 3 +--source include/galera_connect.inc + +--connection node_3 +# Desync and disconnect node 3 from the PC: +SET @@global.wsrep_desync = 1; +SET SESSION wsrep_sync_wait = 0; +SET GLOBAL wsrep_provider_options = 'gmcast.isolate=1'; + +--connection node_1 +# Wait until node 3 disappears from the PC: +--let $wait_condition = SELECT VARIABLE_VALUE = 2 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'; +--source include/wait_condition.inc + +# Delete node ip (127.0.0.3) from allowlist +DELETE FROM mysql.wsrep_allowlist WHERE ip LIKE '127.0.0.3'; + +SELECT COUNT(*) = 2 FROM mysql.wsrep_allowlist; + +--connection node_2 +SELECT COUNT(*) = 2 FROM mysql.wsrep_allowlist; + +--connection node_3 +# Reconnect node 2 to the PC: +SET GLOBAL wsrep_provider_options = 'gmcast.isolate=0'; + +# We should reach Primary with cluster size = 1 because of `pc.ignore_quorum=TRUE and pc.wait_prim=FALSE` used in configuration +--let $wait_condition = SELECT VARIABLE_VALUE = 1 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'; +--source include/wait_condition.inc + +# Resync should pass: +SET @@global.wsrep_desync = 0; + +# Shutdown node +--source include/shutdown_mysqld.inc + +--connection node_1 +# Allow node 3 could be reconnected to cluster +INSERT INTO mysql.wsrep_allowlist(ip) VALUES ('127.0.0.3'); + +--connection node_3 +--source include/start_mysqld.inc +--source include/wait_until_connected_again.inc + +--connection node_1 +--let $wait_condition = SELECT VARIABLE_VALUE = 3 FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'wsrep_cluster_size'; +--source include/wait_condition.inc + +CALL mtr.add_suppression('WSREP: Connection not allowed'); + +--connection node_2 +CALL mtr.add_suppression('WSREP: Connection not allowed'); + +--connection node_3 +CALL mtr.add_suppression('WSREP: Ignoring lack of quorum'); diff --git a/mysql-test/suite/sys_vars/r/sysvars_wsrep.result b/mysql-test/suite/sys_vars/r/sysvars_wsrep.result index bc1f49195b5..f047216bec3 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_wsrep.result +++ b/mysql-test/suite/sys_vars/r/sysvars_wsrep.result @@ -1,6 +1,21 @@ select * from information_schema.system_variables where variable_name like 'wsrep%' order by variable_name; +VARIABLE_NAME WSREP_ALLOWLIST +SESSION_VALUE NULL +GLOBAL_VALUE +GLOBAL_VALUE_ORIGIN COMPILE-TIME +DEFAULT_VALUE +VARIABLE_SCOPE GLOBAL +VARIABLE_TYPE VARCHAR +VARIABLE_COMMENT Allowed IP addresses split by comma delimiter +NUMERIC_MIN_VALUE NULL +NUMERIC_MAX_VALUE NULL +NUMERIC_BLOCK_SIZE NULL +ENUM_VALUE_LIST NULL +READ_ONLY YES +COMMAND_LINE_ARGUMENT REQUIRED +GLOBAL_VALUE_PATH NULL VARIABLE_NAME WSREP_AUTO_INCREMENT_CONTROL SESSION_VALUE NULL GLOBAL_VALUE ON diff --git a/mysql-test/suite/wsrep/r/variables.result b/mysql-test/suite/wsrep/r/variables.result index 8e1af35fd62..cb2cfc82627 100644 --- a/mysql-test/suite/wsrep/r/variables.result +++ b/mysql-test/suite/wsrep/r/variables.result @@ -88,6 +88,7 @@ wsrep_thread_count 2 # variables SELECT VARIABLE_NAME FROM INFORMATION_SCHEMA.SESSION_VARIABLES WHERE VARIABLE_NAME LIKE "wsrep%" ORDER BY VARIABLE_NAME; VARIABLE_NAME +WSREP_ALLOWLIST WSREP_AUTO_INCREMENT_CONTROL WSREP_CAUSAL_READS WSREP_CERTIFICATION_RULES diff --git a/mysql-test/suite/wsrep/r/variables_debug.result b/mysql-test/suite/wsrep/r/variables_debug.result index 0690f540a37..f13aefec4b3 100644 --- a/mysql-test/suite/wsrep/r/variables_debug.result +++ b/mysql-test/suite/wsrep/r/variables_debug.result @@ -89,6 +89,7 @@ wsrep_thread_count 2 # variables SELECT VARIABLE_NAME FROM INFORMATION_SCHEMA.SESSION_VARIABLES WHERE VARIABLE_NAME LIKE "wsrep%" ORDER BY VARIABLE_NAME; VARIABLE_NAME +WSREP_ALLOWLIST WSREP_AUTO_INCREMENT_CONTROL WSREP_CAUSAL_READS WSREP_CERTIFICATION_RULES diff --git a/mysql-test/suite/wsrep/t/variables.test b/mysql-test/suite/wsrep/t/variables.test index e40ac7b8772..c28638e78f1 100644 --- a/mysql-test/suite/wsrep/t/variables.test +++ b/mysql-test/suite/wsrep/t/variables.test @@ -3,7 +3,7 @@ --source include/have_innodb.inc --source include/galera_no_debug_sync.inc ---let $galera_version=26.4.9 +--let $galera_version=26.4.11 source include/check_galera_version.inc; source include/galera_variables_ok.inc; diff --git a/mysql-test/suite/wsrep/t/variables_debug.test b/mysql-test/suite/wsrep/t/variables_debug.test index 29747e48f18..5e90d61c84e 100644 --- a/mysql-test/suite/wsrep/t/variables_debug.test +++ b/mysql-test/suite/wsrep/t/variables_debug.test @@ -5,7 +5,7 @@ --source include/have_debug_sync.inc --source include/galera_have_debug_sync.inc ---let $galera_version=26.4.9 +--let $galera_version=26.4.11 source include/check_galera_version.inc; source include/galera_variables_ok.inc; diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index 972cc0b736c..b9accff02a1 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -22,6 +22,7 @@ IF(WITH_WSREP AND NOT EMBEDDED_LIBRARY) wsrep_server_service.cc wsrep_storage_service.cc wsrep_server_state.cc + wsrep_allowlist_service.cc wsrep_utils.cc wsrep_xid.cc wsrep_check_opts.cc diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index e18ca6392d8..5216a9dd5c8 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -6157,6 +6157,12 @@ static Sys_var_charptr Sys_wsrep_patch_version( READ_ONLY GLOBAL_VAR(wsrep_patch_version_ptr), CMD_LINE_HELP_ONLY, DEFAULT(WSREP_PATCH_VERSION)); + +static Sys_var_charptr Sys_wsrep_allowlist( + "wsrep_allowlist", "Allowed IP addresses split by comma delimiter", + READ_ONLY GLOBAL_VAR(wsrep_allowlist), CMD_LINE(REQUIRED_ARG), + DEFAULT("")); + #endif /* WITH_WSREP */ static bool fix_host_cache_size(sys_var *, THD *, enum_var_type) diff --git a/sql/wsrep_allowlist_service.cc b/sql/wsrep_allowlist_service.cc new file mode 100644 index 00000000000..22b7edfd23b --- /dev/null +++ b/sql/wsrep_allowlist_service.cc @@ -0,0 +1,54 @@ +/* Copyright 2021 Codership Oy <info@codership.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "wsrep_allowlist_service.h" + +#include "my_global.h" +#include "wsrep_mysqld.h" +#include "wsrep_priv.h" + +#include <algorithm> +#include <memory> +#include <vector> + +class Wsrep_allowlist_service : public wsrep::allowlist_service +{ +public: + bool allowlist_cb(wsrep::allowlist_service::allowlist_key key, + const wsrep::const_buffer& value) WSREP_NOEXCEPT override; +}; + +bool Wsrep_allowlist_service::allowlist_cb ( + wsrep::allowlist_service::allowlist_key key, + const wsrep::const_buffer& value) + WSREP_NOEXCEPT +{ + std::string string_value(value.data()); + return (wsrep_schema->allowlist_check(key, string_value)); +} + +std::unique_ptr<wsrep::allowlist_service> entrypoint; + +wsrep::allowlist_service* wsrep_allowlist_service_init() +{ + entrypoint = std::unique_ptr<wsrep::allowlist_service>(new Wsrep_allowlist_service); + return entrypoint.get(); +} + +void wsrep_allowlist_service_deinit() +{ + entrypoint.reset(); +} + diff --git a/sql/wsrep_allowlist_service.h b/sql/wsrep_allowlist_service.h new file mode 100644 index 00000000000..2d96139b5c6 --- /dev/null +++ b/sql/wsrep_allowlist_service.h @@ -0,0 +1,29 @@ +/* Copyright 2021 Codership Oy <info@codership.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/* + Implementation of wsrep provider threads instrumentation. + */ + +#ifndef WSREP_PROVIDER_ALLOWLIST_H +#define WSREP_PROVIDER_ALLOWLIST_H + +#include "wsrep/allowlist_service.hpp" + +wsrep::allowlist_service* wsrep_allowlist_service_init(); + +void wsrep_allowlist_service_deinit(); + +#endif /* WSREP_PROVIDER_ALLOWLIST_H */ diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index d61494e31be..5d387dde2d2 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -82,6 +82,7 @@ const char *wsrep_start_position; const char *wsrep_data_home_dir; const char *wsrep_dbug_option; const char *wsrep_notify_cmd; +const char *wsrep_allowlist; ulong wsrep_debug; // Debug level logging my_bool wsrep_convert_LOCK_to_trx; // Convert locking sessions to trx @@ -453,6 +454,16 @@ void wsrep_init_schema() WSREP_ERROR("Failed to init wsrep schema"); unireg_abort(1); } + // If we are bootstraping new cluster we should + // populate allowlist from variable + if (wsrep_new_cluster) + { + std::vector<std::string> ip_allowlist; + if (wsrep_split_allowlist(ip_allowlist)) + { + wsrep_schema->store_allowlist(ip_allowlist); + } + } } } @@ -862,10 +873,14 @@ int wsrep_init() if (!wsrep_data_home_dir || strlen(wsrep_data_home_dir) == 0) wsrep_data_home_dir= mysql_real_data_home; - if (Wsrep_server_state::instance().load_provider(wsrep_provider, - wsrep_provider_options)) + Wsrep_server_state::init_provider_services(); + if (Wsrep_server_state::instance().load_provider( + wsrep_provider, + wsrep_provider_options, + Wsrep_server_state::instance().provider_services())) { WSREP_ERROR("Failed to load provider"); + Wsrep_server_state::deinit_provider_services(); return 1; } @@ -879,6 +894,7 @@ int wsrep_init() "supports streaming replication.", wsrep_provider, global_system_variables.wsrep_trx_fragment_size); Wsrep_server_state::instance().unload_provider(); + Wsrep_server_state::deinit_provider_services(); return 1; } @@ -994,6 +1010,8 @@ void wsrep_deinit(bool free_options) WSREP_DEBUG("wsrep_deinit"); Wsrep_server_state::instance().unload_provider(); + Wsrep_server_state::deinit_provider_services(); + provider_name[0]= '\0'; provider_version[0]= '\0'; provider_vendor[0]= '\0'; @@ -1140,6 +1158,16 @@ bool wsrep_start_replication(const char *wsrep_cluster_address) DBUG_ASSERT(wsrep_cluster_address[0]); + // --wsrep-new-cluster flag is not used, checking wsrep_cluster_address + // it should match gcomm:// only to be considered as bootstrap node. + // This logic is used in galera. + if (!wsrep_new_cluster && + (strlen(wsrep_cluster_address) == 8) && + !strncmp(wsrep_cluster_address, "gcomm://", 8)) + { + wsrep_new_cluster= true; + } + bool const bootstrap(TRUE == wsrep_new_cluster); WSREP_INFO("Start replication"); @@ -1675,6 +1703,34 @@ bool wsrep_reload_ssl() } } +bool wsrep_split_allowlist(std::vector<std::string>& allowlist) +{ + if (!wsrep_allowlist || 0 == strlen(wsrep_allowlist)) + { + return false; + } + std::istringstream ss{wsrep_allowlist}; + std::string token; + while (std::getline(ss, token, ',')) + { + if (!token.empty()) + { + struct sockaddr_in sa_4; + struct sockaddr_in6 sa_6; + if ((inet_pton(AF_INET, token.c_str(), &(sa_4.sin_addr)) != 0) || + (inet_pton(AF_INET6, token.c_str(), &(sa_6.sin6_addr)) != 0)) + { + allowlist.push_back(token); + } + else + { + WSREP_WARN("Invalid IP address %s provided in `wsrep_allowlist` variable", token.c_str()); + } + } + } + return allowlist.size(); +} + /*! * @param db Database string * @param table Table string @@ -3205,20 +3261,6 @@ void wsrep_wait_appliers_close(THD *thd) */ } -void -wsrep_last_committed_id(wsrep_gtid_t* gtid) -{ - wsrep::gtid ret= Wsrep_server_state::instance().last_committed_gtid(); - memcpy(gtid->uuid.data, ret.id().data(), sizeof(gtid->uuid.data)); - gtid->seqno= ret.seqno().get(); -} - -void -wsrep_node_uuid(wsrep_uuid_t& uuid) -{ - uuid= node_uuid; -} - int wsrep_must_ignore_error(THD* thd) { const int error= thd->get_stmt_da()->sql_errno(); diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h index 73eff87e42e..735b63cda9a 100644 --- a/sql/wsrep_mysqld.h +++ b/sql/wsrep_mysqld.h @@ -79,6 +79,7 @@ extern const char* wsrep_start_position; extern ulong wsrep_max_ws_size; extern ulong wsrep_max_ws_rows; extern const char* wsrep_notify_cmd; +extern const char* wsrep_allowlist; extern my_bool wsrep_certify_nonPK; extern long int wsrep_protocol_version; extern ulong wsrep_forced_binlog_format; @@ -227,11 +228,11 @@ extern bool wsrep_must_sync_wait (THD* thd, uint mask= WSREP_SYNC_WAIT_BEFORE_RE extern bool wsrep_sync_wait (THD* thd, uint mask= WSREP_SYNC_WAIT_BEFORE_READ); extern enum wsrep::provider::status wsrep_sync_wait_upto (THD* thd, wsrep_gtid_t* upto, int timeout); -extern void wsrep_last_committed_id (wsrep_gtid_t* gtid); extern int wsrep_check_opts(); extern void wsrep_prepend_PATH (const char* path); extern bool wsrep_append_fk_parent_table(THD* thd, TABLE_LIST* table, wsrep::key_array* keys); extern bool wsrep_reload_ssl(); +extern bool wsrep_split_allowlist(std::vector<std::string>& allowlist); /* Other global variables */ extern wsrep_seqno_t wsrep_locked_seqno; @@ -407,7 +408,6 @@ bool wsrep_node_is_synced(); void wsrep_init_SR(); void wsrep_verify_SE_checkpoint(const wsrep_uuid_t& uuid, wsrep_seqno_t seqno); int wsrep_replay_from_SR_store(THD*, const wsrep_trx_meta_t&); -void wsrep_node_uuid(wsrep_uuid_t&); class Log_event; int wsrep_ignored_error_code(Log_event* ev, int error); diff --git a/sql/wsrep_schema.cc b/sql/wsrep_schema.cc index 48077040f88..a15d5f91e7e 100644 --- a/sql/wsrep_schema.cc +++ b/sql/wsrep_schema.cc @@ -38,6 +38,7 @@ #define WSREP_STREAMING_TABLE "wsrep_streaming_log" #define WSREP_CLUSTER_TABLE "wsrep_cluster" #define WSREP_MEMBERS_TABLE "wsrep_cluster_members" +#define WSREP_ALLOWLIST_TABLE "wsrep_allowlist" const char* wsrep_sr_table_name_full= WSREP_SCHEMA "/" WSREP_STREAMING_TABLE; @@ -45,6 +46,7 @@ static const std::string wsrep_schema_str= WSREP_SCHEMA; static const std::string sr_table_str= WSREP_STREAMING_TABLE; static const std::string cluster_table_str= WSREP_CLUSTER_TABLE; static const std::string members_table_str= WSREP_MEMBERS_TABLE; +static const std::string allowlist_table_str= WSREP_ALLOWLIST_TABLE; static const std::string create_cluster_table_str= "CREATE TABLE IF NOT EXISTS " + wsrep_schema_str + "." + cluster_table_str + @@ -90,6 +92,13 @@ static const std::string create_frag_table_str= "PRIMARY KEY (node_uuid, trx_id, seqno)" ") ENGINE=InnoDB STATS_PERSISTENT=0"; +static const std::string create_allowlist_table_str= + "CREATE TABLE IF NOT EXISTS " + wsrep_schema_str + "." + allowlist_table_str + + "(" + "ip CHAR(64) NOT NULL," + "PRIMARY KEY (ip)" + ") ENGINE=InnoDB STATS_PERSISTENT=0"; + static const std::string delete_from_cluster_table= "DELETE FROM " + wsrep_schema_str + "." + cluster_table_str; @@ -439,11 +448,18 @@ static int insert(TABLE* table) { } if ((error= table->file->ha_write_row(table->record[0]))) { - WSREP_ERROR("Error writing into %s.%s: %d", - table->s->db.str, - table->s->table_name.str, - error); - ret= 1; + if (error == HA_ERR_FOUND_DUPP_KEY) { + WSREP_WARN("Duplicate key found when writing into %s.%s", + table->s->db.str, + table->s->table_name.str); + ret= HA_ERR_FOUND_DUPP_KEY; + } else { + WSREP_ERROR("Error writing into %s.%s: %d", + table->s->db.str, + table->s->table_name.str, + error); + ret= 1; + } } DBUG_RETURN(ret); @@ -682,6 +698,8 @@ static void wsrep_init_thd_for_schema(THD *thd) wsrep_store_threadvars(thd); } +static bool wsrep_schema_ready= false; + int Wsrep_schema::init() { DBUG_ENTER("Wsrep_schema::init()"); @@ -717,12 +735,16 @@ int Wsrep_schema::init() alter_members_table.size()) || Wsrep_schema_impl::execute_SQL(thd, alter_frag_table.c_str(), - alter_frag_table.size())) + alter_frag_table.size()) || + Wsrep_schema_impl::execute_SQL(thd, + create_allowlist_table_str.c_str(), + create_allowlist_table_str.size())) { ret= 1; } else { + wsrep_schema_ready= true; ret= 0; } @@ -1493,3 +1515,121 @@ int Wsrep_schema::recover_sr_transactions(THD *orig_thd) out: DBUG_RETURN(ret); } + +void Wsrep_schema::store_allowlist(std::vector<std::string>& ip_allowlist) +{ + THD* thd= new THD(next_thread_id()); + if (!thd) + { + WSREP_ERROR("Unable to get thd"); + return; + } + thd->thread_stack= (char*)&thd; + wsrep_init_thd_for_schema(thd); + TABLE* allowlist_table= 0; + int error; + Wsrep_schema_impl::init_stmt(thd); + if (Wsrep_schema_impl::open_for_write(thd, allowlist_table_str.c_str(), + &allowlist_table)) + { + WSREP_ERROR("Failed to open mysql.wsrep_allowlist table"); + goto out; + } + for (size_t i= 0; i < ip_allowlist.size(); ++i) + { + Wsrep_schema_impl::store(allowlist_table, 0, ip_allowlist[i]); + if ((error= Wsrep_schema_impl::insert(allowlist_table))) + { + if (error == HA_ERR_FOUND_DUPP_KEY) + { + WSREP_WARN("Duplicate entry (%s) found in `wsrep_allowlist` list", ip_allowlist[i].c_str()); + } + else + { + WSREP_ERROR("Failed to write mysql.wsrep_allowlist table: %d", error); + goto out; + } + } + } + Wsrep_schema_impl::finish_stmt(thd); +out: + delete thd; +} + +bool Wsrep_schema::allowlist_check(Wsrep_allowlist_key key, + const std::string& value) +{ + // We don't have wsrep schema initialized at this point + if (wsrep_schema_ready == false) + { + return true; + } + my_thread_init(); + THD *thd = new THD(next_thread_id()); + if (!thd) + { + my_thread_end(); + WSREP_ERROR("Unable to get thd"); + return false; + } + thd->thread_stack= (char*)&thd; + int error; + TABLE *allowlist_table= 0; + bool match_found_or_empty= false; + bool table_have_rows= false; + char row[64]= { 0, }; + wsrep_init_thd_for_schema(thd); + + /* + * Read allowlist table + */ + Wsrep_schema_impl::init_stmt(thd); + if (Wsrep_schema_impl::open_for_read(thd, + allowlist_table_str.c_str(), + &allowlist_table) || + Wsrep_schema_impl::init_for_scan(allowlist_table)) + + { + goto out; + } + while (true) + { + if ((error= Wsrep_schema_impl::next_record(allowlist_table)) == 0) + { + if (Wsrep_schema_impl::scan(allowlist_table, 0, row, sizeof(row))) + { + goto out; + } + table_have_rows= true; + if (!value.compare(row)) + { + match_found_or_empty= true; + break; + } + } + else if (error == HA_ERR_END_OF_FILE) + { + if (!table_have_rows) + { + WSREP_DEBUG("allowlist table empty, allowing all connections."); + // If table is empty we are allowing all connections + match_found_or_empty= true; + } + break; + } + else + { + goto out; + } + } + if (Wsrep_schema_impl::end_scan(allowlist_table)) + { + goto out; + } + Wsrep_schema_impl::finish_stmt(thd); + (void)trans_commit(thd); +out: + delete thd; + my_thread_end(); + return match_found_or_empty; +} diff --git a/sql/wsrep_schema.h b/sql/wsrep_schema.h index 36e23998d19..943fe8759c0 100644 --- a/sql/wsrep_schema.h +++ b/sql/wsrep_schema.h @@ -133,6 +133,22 @@ class Wsrep_schema */ int recover_sr_transactions(THD* orig_thd); + /** + Store allowlist ip on bootstrap from `wsrep_allowlist` variable + */ + void store_allowlist(std::vector<std::string>& ip_allowlist); + + /** + Scan white list table against accepted connection. Allow if ip + is found in table or if table is empty. + + @param key Which allowlist column to compare + @param value Value to be checked against allowlist + + @return True if found or empty table, false on not found + */ + bool allowlist_check(Wsrep_allowlist_key key, const std::string& val); + private: /* Non-copyable */ Wsrep_schema(const Wsrep_schema&); diff --git a/sql/wsrep_server_state.cc b/sql/wsrep_server_state.cc index ebc4efaabe5..30029c73152 100644 --- a/sql/wsrep_server_state.cc +++ b/sql/wsrep_server_state.cc @@ -16,6 +16,7 @@ #include "my_global.h" #include "wsrep_api.h" #include "wsrep_server_state.h" +#include "wsrep_allowlist_service.h" #include "wsrep_binlog.h" /* init/deinit group commit */ mysql_mutex_t LOCK_wsrep_server_state; @@ -26,6 +27,8 @@ PSI_mutex_key key_LOCK_wsrep_server_state; PSI_cond_key key_COND_wsrep_server_state; #endif +wsrep::provider::services Wsrep_server_state::m_provider_services; + Wsrep_server_state::Wsrep_server_state(const std::string& name, const std::string& incoming_address, const std::string& address, @@ -74,7 +77,6 @@ void Wsrep_server_state::init_once(const std::string& name, void Wsrep_server_state::destroy() { - if (m_instance) { delete m_instance; @@ -83,3 +85,16 @@ void Wsrep_server_state::destroy() mysql_cond_destroy(&COND_wsrep_server_state); } } + +void Wsrep_server_state::init_provider_services() +{ + m_provider_services.allowlist_service= wsrep_allowlist_service_init(); +} + +void Wsrep_server_state::deinit_provider_services() +{ + if (m_provider_services.allowlist_service) + wsrep_allowlist_service_deinit(); + m_provider_services= wsrep::provider::services(); +} + diff --git a/sql/wsrep_server_state.h b/sql/wsrep_server_state.h index 1ef937300f6..8759f7a9d84 100644 --- a/sql/wsrep_server_state.h +++ b/sql/wsrep_server_state.h @@ -55,6 +55,14 @@ public: { return (get_provider().capabilities() & capability); } + + static void init_provider_services(); + static void deinit_provider_services(); + + static const wsrep::provider::services& provider_services() + { + return m_provider_services; + } private: Wsrep_server_state(const std::string& name, @@ -67,6 +75,7 @@ private: Wsrep_mutex m_mutex; Wsrep_condition_variable m_cond; Wsrep_server_service m_service; + static wsrep::provider::services m_provider_services; static Wsrep_server_state* m_instance; }; diff --git a/sql/wsrep_sst.cc b/sql/wsrep_sst.cc index 2adc893c131..786d8b9bbf5 100644 --- a/sql/wsrep_sst.cc +++ b/sql/wsrep_sst.cc @@ -1898,11 +1898,6 @@ int wsrep_sst_donate(const std::string& msg, const wsrep::gtid& current_gtid, const bool bypass) { - /* This will be reset when sync callback is called. - * Should we set wsrep_ready to FALSE here too? */ - - wsrep_config_state->set(wsrep::server_state::s_donor); - const char* method= msg.data(); size_t method_len= strlen (method); diff --git a/sql/wsrep_types.h b/sql/wsrep_types.h index 9da00e305a7..cd53ab95d0c 100644 --- a/sql/wsrep_types.h +++ b/sql/wsrep_types.h @@ -21,9 +21,11 @@ #include "wsrep/seqno.hpp" #include "wsrep/view.hpp" +#include "wsrep/allowlist_service.hpp" typedef wsrep::id Wsrep_id; typedef wsrep::seqno Wsrep_seqno; typedef wsrep::view Wsrep_view; +typedef enum wsrep::allowlist_service::allowlist_key Wsrep_allowlist_key; #endif /* WSREP_TYPES_H */ diff --git a/wsrep-lib b/wsrep-lib -Subproject 22921e7082ddfb45222f21a585aa8b877e62aa8 +Subproject 13442a04d8bdeb71ab10e6d59577748224a7092 |