Skip to content

Commit 780b945

Browse files
author
Tomasz Stepniak
committed
Bug#33308536 - Default auth backend for old metadata is not valid
Issue ===== WL#13906 changed the default authentication backend for the http_auth_backend to metadata_cache which is not supported in metadata 1.0. Fix === If old metadata is used then config generated during the bootstrap is going to set up a file based authentication for the http_auth_backend. RB: 26955 Reviewed by: Jan Kneschke <[email protected]>
1 parent efe89cd commit 780b945

File tree

4 files changed

+95
-10
lines changed

4 files changed

+95
-10
lines changed

router/src/router/src/config_generator.cc

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -423,25 +423,25 @@ void ConfigGenerator::init(
423423

424424
// throws std::runtime_error, std::logic_error,
425425
connect_to_metadata_server(u, bootstrap_socket, bootstrap_options);
426-
auto schema_version = mysqlrouter::get_metadata_schema_version(mysql_.get());
426+
schema_version_ = mysqlrouter::get_metadata_schema_version(mysql_.get());
427427

428-
if (schema_version == mysqlrouter::kUpgradeInProgressMetadataVersion) {
428+
if (schema_version_ == mysqlrouter::kUpgradeInProgressMetadataVersion) {
429429
throw std::runtime_error(
430430
"Currently the cluster metadata update is in progress. Please rerun "
431431
"the bootstrap when it is finished.");
432432
}
433433

434434
if (!metadata_schema_version_is_compatible(kRequiredBootstrapSchemaVersion,
435-
schema_version)) {
435+
schema_version_)) {
436436
throw std::runtime_error(mysqlrouter::string_format(
437437
"This version of MySQL Router is not compatible with the provided "
438438
"MySQL InnoDB cluster metadata. Expected metadata version %s, "
439439
"got %s",
440440
to_string(kRequiredBootstrapSchemaVersion).c_str(),
441-
to_string(schema_version).c_str()));
441+
to_string(schema_version_).c_str()));
442442
}
443443

444-
metadata_ = mysqlrouter::create_metadata(schema_version, mysql_.get(),
444+
metadata_ = mysqlrouter::create_metadata(schema_version_, mysql_.get(),
445445
bootstrap_options);
446446

447447
// at this point we know the cluster type so let's do additional verifications
@@ -2020,6 +2020,42 @@ static void save_initial_dynamic_state(
20202020
mdc_dynamic_state.save(state_stream);
20212021
}
20222022

2023+
/**
2024+
* Add proper authentication backend section to the config based on the
2025+
* metadata version. If needed it creates an empty authentication password
2026+
* file used in the config.
2027+
*
2028+
* @param[in] datadir - path of a router data directory
2029+
* @param[in] auth_backend_name - authentication backend section name
2030+
* @param[in] schema_version - metadata schema version
2031+
*
2032+
* @return http_auth_backend config section string
2033+
*/
2034+
static std::string create_http_auth_backend_section(
2035+
const mysql_harness::Path &datadir,
2036+
const std::string_view auth_backend_name,
2037+
const mysqlrouter::MetadataSchemaVersion schema_version) {
2038+
if (metadata_schema_version_is_compatible(kNewMetadataVersion,
2039+
schema_version)) {
2040+
return mysql_harness::ConfigBuilder::build_section(
2041+
std::string{"http_auth_backend:"}.append(auth_backend_name),
2042+
{{"backend", "metadata_cache"}});
2043+
} else {
2044+
const auto auth_backend_passwd_file =
2045+
datadir.join("auth_backend_passwd_file").str();
2046+
const auto open_res = open_ofstream(auth_backend_passwd_file);
2047+
if (!open_res) {
2048+
log_warning("Cannot create file '%s': %s",
2049+
auth_backend_passwd_file.c_str(),
2050+
open_res.error().message().c_str());
2051+
}
2052+
2053+
return mysql_harness::ConfigBuilder::build_section(
2054+
std::string{"http_auth_backend:"}.append(auth_backend_name),
2055+
{{"backend", "file"}, {"filename", auth_backend_passwd_file}});
2056+
}
2057+
}
2058+
20232059
/*static*/ std::string ConfigGenerator::gen_metadata_cache_routing_section(
20242060
bool is_classic, bool is_writable, const Options::Endpoint endpoint,
20252061
const Options &options, const std::string &metadata_key) {
@@ -2203,9 +2239,8 @@ std::string ConfigGenerator::generate_config_for_rest(
22032239
config << mysql_harness::ConfigBuilder::build_section("rest_api", {});
22042240

22052241
config << "\n\n";
2206-
config << mysql_harness::ConfigBuilder::build_section(
2207-
"http_auth_backend:" + auth_backend_name,
2208-
{{"backend", "metadata_cache"}});
2242+
config << create_http_auth_backend_section(datadir_path, auth_backend_name,
2243+
schema_version_);
22092244

22102245
config << "\n\n";
22112246
config << mysql_harness::ConfigBuilder::build_section(

router/src/router/src/config_generator.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636

3737
#include "auto_cleaner.h"
3838
#include "mysql/harness/filesystem.h"
39+
#include "mysqlrouter/cluster_metadata.h"
3940
#include "mysqlrouter/datatypes.h"
4041
#include "mysqlrouter/keyring_info.h"
4142
#include "mysqlrouter/mysql_session.h"
@@ -617,6 +618,8 @@ class ConfigGenerator {
617618
SysUserOperationsBase *sys_user_operations_;
618619
#endif
619620

621+
mysqlrouter::MetadataSchemaVersion schema_version_;
622+
620623
#ifdef FRIEND_TEST
621624
FRIEND_TEST(::ConfigGeneratorTest, fetch_bootstrap_servers_one);
622625
FRIEND_TEST(::ConfigGeneratorTest, fetch_bootstrap_servers_three);

router/tests/component/test_bootstrap.cc

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2024,6 +2024,52 @@ TEST_F(RouterBootstrapTest, BootstrapRouterDuplicateEntry) {
20242024
EXPECT_FALSE(router.expect_output("Could not delete file .*", true, 0ms));
20252025
}
20262026

2027+
TEST_F(RouterBootstrapTest, CheckAuthBackendWhenOldMetadata) {
2028+
TempDirectory bootstrap_directory;
2029+
const auto server_port = port_pool_.get_next_available();
2030+
const auto http_port = port_pool_.get_next_available();
2031+
const std::string json_stmts =
2032+
get_data_dir().join("bootstrap_gr_v1.js").str();
2033+
2034+
// launch mock server that is our metadata server for the bootstrap
2035+
auto &server_mock = launch_mysql_server_mock(json_stmts, server_port,
2036+
EXIT_SUCCESS, false, http_port);
2037+
2038+
set_mock_bootstrap_data(http_port, "test", {{"localhost", server_port}},
2039+
{1, 0, 0}, "cluster-specific-id");
2040+
2041+
const auto base_listening_port = port_pool_.get_next_available();
2042+
std::vector<std::string> bootsrtap_params{
2043+
"--bootstrap=127.0.0.1:" + std::to_string(server_port), "-d",
2044+
bootstrap_directory.name(),
2045+
"--conf-base-port=" + std::to_string(base_listening_port)};
2046+
2047+
// launch the router in bootstrap mode
2048+
auto &router = launch_router_for_bootstrap(bootsrtap_params, EXIT_SUCCESS,
2049+
/*disable rest*/ false);
2050+
2051+
// add login hook
2052+
router.register_response("Please enter MySQL password for root: ",
2053+
kRootPassword + "\n"s);
2054+
2055+
check_exit_code(router, EXIT_SUCCESS);
2056+
2057+
const std::string conf_file =
2058+
bootstrap_directory.name() + "/mysqlrouter.conf";
2059+
2060+
// check if valid authentication backend option was added to the config file
2061+
auto conf_file_content = get_file_output(conf_file);
2062+
auto conf_lines = mysql_harness::split_string(conf_file_content, '\n');
2063+
const auto passwd_file = mysql_harness::Path{
2064+
bootstrap_directory.name() + "/data/auth_backend_passwd_file"};
2065+
EXPECT_THAT(conf_lines,
2066+
::testing::IsSupersetOf(
2067+
{::testing::ContainsRegex("backend=file"),
2068+
::testing::ContainsRegex(std::string{"filename=.*"} +
2069+
passwd_file.str())}));
2070+
ASSERT_TRUE(passwd_file.exists());
2071+
}
2072+
20272073
int main(int argc, char *argv[]) {
20282074
init_windows_sockets();
20292075
ProcessManager::set_origin(Path(argv[0]).dirname());

router/tests/helpers/router_component_test.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,9 @@ class RouterComponentBootstrapTest : virtual public RouterComponentTest {
167167
const std::vector<std::tuple<ProcessWrapper &, unsigned int>> &T);
168168

169169
ProcessWrapper &launch_router_for_bootstrap(
170-
std::vector<std::string> params, int expected_exit_code = EXIT_SUCCESS) {
171-
params.push_back("--disable-rest");
170+
std::vector<std::string> params, int expected_exit_code = EXIT_SUCCESS,
171+
const bool disable_rest = true) {
172+
if (disable_rest) params.push_back("--disable-rest");
172173
return ProcessManager::launch_router(
173174
params, expected_exit_code, /*catch_stderr=*/true, /*with_sudo=*/false,
174175
/*wait_for_notify_ready=*/std::chrono::seconds(-1));

0 commit comments

Comments
 (0)