diff --git a/.travis/build_maxscale.sh b/.travis/build_maxscale.sh index 153ccb6e3..94adeb3e0 100644 --- a/.travis/build_maxscale.sh +++ b/.travis/build_maxscale.sh @@ -18,7 +18,7 @@ cd build cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTS=Y make -make test || exit 1 +ctest --output-on-failure || exit 1 sudo make install sudo ./postinst diff --git a/BUILD/build_deb_local.sh b/BUILD/build_deb_local.sh index 0640673ef..1c8c078de 100755 --- a/BUILD/build_deb_local.sh +++ b/BUILD/build_deb_local.sh @@ -16,6 +16,8 @@ make if [[ "$cmake_flags" =~ "BUILD_TESTS" ]] then + # We don't care about memory leaks in the tests (e.g. servers are never freed) + export ASAN_OPTIONS=detect_leaks=0 # All tests must pass otherwise the build is considered a failure ctest --output-on-failure || exit 1 fi diff --git a/Documentation/Authenticators/GSSAPI-Authenticator.md b/Documentation/Authenticators/GSSAPI-Authenticator.md index ce434d5b7..ab8765587 100644 --- a/Documentation/Authenticators/GSSAPI-Authenticator.md +++ b/Documentation/Authenticators/GSSAPI-Authenticator.md @@ -12,29 +12,32 @@ _GSSAPIBackendAuth_ module implements the backend authentication. For Unix systems, the usual GSSAPI implementation is Kerberos. This is a short guide on how to set up Kerberos for MaxScale. -The first step is to create a new principal for MaxScale. This can be done with -the _kadmin_ or _kadmin.local_ tools. +The first step is to configure MariaDB to use GSSAPI authentication. The MariaDB +documentation for the +[GSSAPI Authentication Plugin](https://mariadb.com/kb/en/mariadb/gssapi-authentication-plugin/) +is a good example on how to set it up. + +The next step is to copy the keytab file from the server where MariaDB is +installed to the server where MaxScale is located. The keytab file must be +placed in the configured default location which almost always is +`/etc/krb5.keytab`. + +To take GSSAPI authentication into use, add the following to the listener. ``` -kadmin.local -q "addprinc -nokey mariadb/example.com@EXAMPLE.COM" +authenticator=GSSAPIAuth +authenticator_options=principal_name=mariadb/localhost.localdomain@EXAMPLE.COM ``` -The `-nokey` option will make the principal a passwordless one. This allows the -_maxscale_ user to acquire a ticket for it without a password being prompted. +Change the principal name to the same value you configured for the MariaDB +server. -The next step is to export this principal into the Kerberos keytab file. +After the listeners are configured, add the following to all servers that use GSSAPI users. ``` -kadmin.local -q "ktadd -k /etc/krb5.keytab -norandkey mariadb/example.com@EXAMPLE.COM" +authenticator=GSSAPIBackendAuth ``` -This adds the _mariadb/example.com@EXAMPLE.COM_ principal into the keytab -file. The `-norandkey` option tells that the password we defined earlier, -i.e. no password at all, should be used instead of a random password. - -The MariaDB documentation for the [GSSAPI Authentication Plugin](https://mariadb.com/kb/en/mariadb/gssapi-authentication-plugin/) -is a good example on how to set up a new principal for the MariaDB server. - ## Authenticator options The client side GSSAPIAuth authenticator supports one option, the service @@ -43,14 +46,12 @@ module has no options. ### `principal_name` -The service principal name to send to the client. This parameter is a -string parameter which is used by the client to request the token. +The service principal name to send to the client. This parameter is a string +parameter which is used by the client to request the token. The default value +for this option is _mariadb/localhost.localdomain_. -The default value for this option is _mariadb/localhost.localdomain_. - -The parameter must be a valid GSSAPI principal name -e.g. `styx/pluto@EXAMPLE.COM`. The principal name can also be defined -without the realm part in which case the default realm will be used. +This parameter *must* be the same as the principal name that the backend MariaDB +server uses. ## Implementation details diff --git a/VERSION22.cmake b/VERSION22.cmake index 074273e32..2090185d0 100644 --- a/VERSION22.cmake +++ b/VERSION22.cmake @@ -5,7 +5,7 @@ set(MAXSCALE_VERSION_MAJOR "2" CACHE STRING "Major version") set(MAXSCALE_VERSION_MINOR "2" CACHE STRING "Minor version") -set(MAXSCALE_VERSION_PATCH "15" CACHE STRING "Patch version") +set(MAXSCALE_VERSION_PATCH "16" CACHE STRING "Patch version") # This should only be incremented if a package is rebuilt set(MAXSCALE_BUILD_NUMBER 1 CACHE STRING "Release number") diff --git a/include/maxscale/limits.h b/include/maxscale/limits.h index e253f5d59..994ee5dac 100644 --- a/include/maxscale/limits.h +++ b/include/maxscale/limits.h @@ -24,32 +24,18 @@ MXS_BEGIN_DECLS /** - * MXS_BACKEND_SO_RCVBUF + * MXS_SO_RCVBUF * - * The value used when setting SO_RCVBUF of backend sockets. + * The size of the network input buffer. */ -#define MXS_BACKEND_SO_RCVBUF (128 * 1024) +#define MXS_SO_RCVBUF_SIZE (128 * 1024) /** - * MXS_BACKEND_SO_SNDBUF + * MXS_SO_SNDBUF * - * The value used when setting SO_SNDBUF of backend sockets. + * The size of the network output buffer. */ -#define MXS_BACKEND_SO_SNDBUF (128 * 1024) - -/** - * MXS_CLIENT_SO_RCVBUF - * - * The value used when setting SO_RCVBUF of client sockets. - */ -#define MXS_CLIENT_SO_RCVBUF (128 * 1024) - -/** - * MXS_CLIENT_SO_SNDBUF - * - * The value used when setting SO_SNDBUF of client sockets. - */ -#define MXS_CLIENT_SO_SNDBUF (128 * 1024) +#define MXS_SO_SNDBUF_SIZE (128 * 1024) /** * MXS_MAX_NW_READ_BUFFER_SIZE diff --git a/include/maxscale/utils.h b/include/maxscale/utils.h index f7e5ab78a..98bf8cfa8 100644 --- a/include/maxscale/utils.h +++ b/include/maxscale/utils.h @@ -46,6 +46,20 @@ enum mxs_socket_type bool utils_init(); /*< Call this first before using any other function */ void utils_end(); +/** + * Configure network socket options + * + * This is a helper function for setting various socket options that are always wanted for all types + * of connections. It sets the socket into nonblocking mode, configures sndbuf and rcvbuf sizes + * and sets TCP_NODELAY (no Nagle algorithm). + * + * @param so Socket to configure + * @param type Socket type + * + * @return True if configuration was successful + */ +bool configure_network_socket(int so, int type); + /** * @brief Create a network socket and a socket configuration * diff --git a/maxscale-system-test/fail_switch_rejoin_common.cpp b/maxscale-system-test/fail_switch_rejoin_common.cpp index 37feb68e8..71a27f79c 100644 --- a/maxscale-system-test/fail_switch_rejoin_common.cpp +++ b/maxscale-system-test/fail_switch_rejoin_common.cpp @@ -23,7 +23,7 @@ void get_output(TestConnections& test) test.tprintf("MaxScale output:"); } output = test.maxscales->ssh_node_output(0, - "cat /var/log/maxscale/maxscale.log && " + "cat /var/log/maxscale/maxscale.log | tee -a /var/log/maxscale/maxscale_backup.log && " "sudo truncate -s 0 /var/log/maxscale/maxscale.log", true, &ec); diff --git a/maxscale-system-test/mdbci/set_env.sh b/maxscale-system-test/mdbci/set_env.sh index 75950f7fb..46893015d 100644 --- a/maxscale-system-test/mdbci/set_env.sh +++ b/maxscale-system-test/mdbci/set_env.sh @@ -23,6 +23,7 @@ export node_N=`cat "$MDBCI_VM_PATH/$config_name"_network_config | grep node | gr export maxscale_N=`cat "$MDBCI_VM_PATH/$config_name"_network_config | grep maxscale | grep network | wc -l` sed "s/^/export /g" "$MDBCI_VM_PATH/$config_name"_network_config > "$curr_dir"/"$config_name"_network_config_export source "$curr_dir"/"$config_name"_network_config_export +rm "$curr_dir"/"$config_name"_network_config_export # User name and Password for Master/Slave replication setup (should have all PRIVILEGES) diff --git a/maxscale-system-test/mxs1743_rconn_bitmask.cpp b/maxscale-system-test/mxs1743_rconn_bitmask.cpp index 0149218ea..c54759016 100644 --- a/maxscale-system-test/mxs1743_rconn_bitmask.cpp +++ b/maxscale-system-test/mxs1743_rconn_bitmask.cpp @@ -45,8 +45,9 @@ int main(int argc, char** argv) test.try_query(test.repl->nodes[0], "%s", "GRANT ALL ON *.* TO 'mxs1743'@'%'"); test.tprintf("Syncing slaves"); - test.set_timeout(60); + test.stop_timeout(); test.repl->sync_slaves(); + test.set_timeout(60); test.tprintf("Opening new connections to verify readconnroute works"); diff --git a/maxscale-system-test/mxs682_cyrillic.cpp b/maxscale-system-test/mxs682_cyrillic.cpp index ca893cb1a..550364c8b 100644 --- a/maxscale-system-test/mxs682_cyrillic.cpp +++ b/maxscale-system-test/mxs682_cyrillic.cpp @@ -35,16 +35,21 @@ int main(int argc, char* argv[]) test.stop_timeout(); test.repl->connect(); test.repl->sync_slaves(); + test.repl->disconnect(); test.set_timeout(60); + test.maxscales->connect(); check_val(test.maxscales->conn_rwsplit[0], test); check_val(test.maxscales->conn_master[0], test); check_val(test.maxscales->conn_slave[0], test); + test.maxscales->disconnect(); + nodes->connect(); for (int i = 0; i < test.repl->N; i++) { check_val(nodes->nodes[i], test); } + nodes->disconnect(); return test.global_result; } diff --git a/maxutils/maxbase/include/maxbase/log.h b/maxutils/maxbase/include/maxbase/log.h index c65451c44..f5c6d3dbe 100644 --- a/maxutils/maxbase/include/maxbase/log.h +++ b/maxutils/maxbase/include/maxbase/log.h @@ -226,6 +226,13 @@ void mxb_log_set_throttling(const MXB_LOG_THROTTLING* throttling); */ void mxb_log_get_throttling(MXB_LOG_THROTTLING* throttling); +/** + * Redirect stdout to the log file + * + * @param redirect Whether to redirect the output to the log file + */ +void mxs_log_redirect_stdout(bool redirect); + /** * Log a message of a particular priority. * diff --git a/maxutils/maxbase/src/log.cc b/maxutils/maxbase/src/log.cc index 77d5ea7ce..7fa0ef233 100644 --- a/maxutils/maxbase/src/log.cc +++ b/maxutils/maxbase/src/log.cc @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -384,6 +385,7 @@ struct this_unit bool do_highprecision; // Can change during the lifetime of log_manager. bool do_syslog; // Can change during the lifetime of log_manager. bool do_maxlog; // Can change during the lifetime of log_manager. + bool redirect_stdout; MXB_LOG_THROTTLING throttling; // Can change during the lifetime of log_manager. std::unique_ptr sLogger; std::unique_ptr sMessage_registry; @@ -394,6 +396,7 @@ struct this_unit false, // do_highprecision true, // do_syslog true, // do_maxlog + false, // redirect_stdout DEFAULT_LOG_THROTTLING, // throttling }; @@ -486,6 +489,13 @@ bool mxb_log_init(const char* ident, case MXB_LOG_TARGET_FS: case MXB_LOG_TARGET_DEFAULT: this_unit.sLogger = mxb::FileLogger::create(filepath); + + if (this_unit.sLogger && this_unit.redirect_stdout) + { + // Redirect stdout and stderr to the log file + freopen(this_unit.sLogger->filename(), "a", stdout); + freopen(this_unit.sLogger->filename(), "a", stderr); + } break; case MXB_LOG_TARGET_STDOUT: @@ -598,9 +608,23 @@ void mxb_log_get_throttling(MXB_LOG_THROTTLING* throttling) *throttling = this_unit.throttling; } +void mxs_log_redirect_stdout(bool redirect) +{ + this_unit.redirect_stdout = redirect; +} + bool mxb_log_rotate() { - return this_unit.sLogger->rotate(); + bool rval = this_unit.sLogger->rotate(); + + if (this_unit.redirect_stdout && rval) + { + // Redirect stdout and stderr to the log file + freopen(this_unit.sLogger->filename(), "a", stdout); + freopen(this_unit.sLogger->filename(), "a", stderr); + } + + return rval; } const char* mxb_log_get_filename() diff --git a/server/core/dcb.cc b/server/core/dcb.cc index eda575dcd..6bf44ac8e 100644 --- a/server/core/dcb.cc +++ b/server/core/dcb.cc @@ -2405,21 +2405,7 @@ DCB* dcb_accept(DCB* dcb) { dcb->stats.n_accepts++; - /* set nonblocking */ - sendbuf = MXS_CLIENT_SO_SNDBUF; - - if (setsockopt(c_sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, optlen) != 0) - { - MXS_ERROR("Failed to set socket options: %d, %s", errno, mxs_strerror(errno)); - } - - sendbuf = MXS_CLIENT_SO_RCVBUF; - - if (setsockopt(c_sock, SOL_SOCKET, SO_RCVBUF, &sendbuf, optlen) != 0) - { - MXS_ERROR("Failed to set socket options: %d, %s", errno, mxs_strerror(errno)); - } - setnonblocking(c_sock); + configure_network_socket(c_sock, client_conn.ss_family); client_dcb = dcb_alloc(DCB_ROLE_CLIENT_HANDLER, dcb->listener); diff --git a/server/core/gateway.cc b/server/core/gateway.cc index 5c079db23..e0559f84e 100644 --- a/server/core/gateway.cc +++ b/server/core/gateway.cc @@ -348,9 +348,12 @@ static void sigterm_handler(int i) if (n_shutdowns == 1) { - if (write(STDERR_FILENO, shutdown_msg, sizeof(shutdown_msg) - 1) == -1) + if (!daemon_mode) { - printf("Failed to write shutdown message!\n"); + if (write(STDERR_FILENO, shutdown_msg, sizeof(shutdown_msg) - 1) == -1) + { + printf("Failed to write shutdown message!\n"); + } } } else @@ -366,16 +369,22 @@ static void sigint_handler(int i) if (n_shutdowns == 1) { - if (write(STDERR_FILENO, shutdown_msg, sizeof(shutdown_msg) - 1) == -1) + if (!daemon_mode) { - printf("Failed to write shutdown message!\n"); + if (write(STDERR_FILENO, shutdown_msg, sizeof(shutdown_msg) - 1) == -1) + { + printf("Failed to write shutdown message!\n"); + } } } else if (n_shutdowns == 2) { - if (write(STDERR_FILENO, patience_msg, sizeof(patience_msg) - 1) == -1) + if (!daemon_mode) { - printf("Failed to write shutdown message!\n"); + if (write(STDERR_FILENO, patience_msg, sizeof(patience_msg) - 1) == -1) + { + printf("Failed to write shutdown message!\n"); + } } } else @@ -1919,6 +1928,11 @@ int main(int argc, char** argv) mxs_log_finish(); } + if (cnf->log_target != MXB_LOG_TARGET_STDOUT && daemon_mode) + { + mxs_log_redirect_stdout(true); + } + if (!init_log()) { rc = MAXSCALE_BADCONFIG; diff --git a/server/core/utils.cc b/server/core/utils.cc index 862087a6b..f78b2d527 100644 --- a/server/core/utils.cc +++ b/server/core/utils.cc @@ -983,17 +983,18 @@ void utils_end() replace_values_re = NULL; } -static bool configure_network_socket(int so) +bool configure_network_socket(int so, int type) { - int sndbufsize = MXS_BACKEND_SO_SNDBUF; - int rcvbufsize = MXS_BACKEND_SO_RCVBUF; + int sndbufsize = MXS_SO_SNDBUF_SIZE; + int rcvbufsize = MXS_SO_RCVBUF_SIZE; int one = 1; if (setsockopt(so, SOL_SOCKET, SO_SNDBUF, &sndbufsize, sizeof(sndbufsize)) != 0 || setsockopt(so, SOL_SOCKET, SO_RCVBUF, &rcvbufsize, sizeof(rcvbufsize)) != 0 - || setsockopt(so, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) != 0) + || (type != AF_UNIX && setsockopt(so, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)) != 0)) { MXS_ERROR("Failed to set socket option: %d, %s.", errno, mxs_strerror(errno)); + mxb_assert(!true); return false; } @@ -1065,7 +1066,7 @@ int open_network_socket(enum mxs_socket_type type, freeaddrinfo(ai); - if ((type == MXS_SOCKET_NETWORK && !configure_network_socket(so)) + if ((type == MXS_SOCKET_NETWORK && !configure_network_socket(so, addr->ss_family)) || (type == MXS_SOCKET_LISTENER && !configure_listener_socket(so))) { close(so); diff --git a/server/modules/authenticator/GSSAPI/GSSAPIBackendAuth/gssapi_backend_auth.cc b/server/modules/authenticator/GSSAPI/GSSAPIBackendAuth/gssapi_backend_auth.cc index 122b703b2..4bf8ead69 100644 --- a/server/modules/authenticator/GSSAPI/GSSAPIBackendAuth/gssapi_backend_auth.cc +++ b/server/modules/authenticator/GSSAPI/GSSAPIBackendAuth/gssapi_backend_auth.cc @@ -59,75 +59,23 @@ void gssapi_backend_auth_free(void* data) static bool send_new_auth_token(DCB* dcb) { bool rval = false; - OM_uint32 major = 0, minor = 0; - gss_ctx_id_t handle = NULL; - gss_buffer_desc in = {0, 0}; - gss_buffer_desc out = {0, 0}; - gss_buffer_desc target = {0, 0}; - gss_name_t princ = GSS_C_NO_NAME; gssapi_auth_t* auth = (gssapi_auth_t*)dcb->authenticator_data; + MYSQL_session* ses = (MYSQL_session*)dcb->session->client_dcb->data; + GWBUF* buffer = gwbuf_alloc(MYSQL_HEADER_LEN + ses->auth_token_len); - /** The service principal name is sent by the backend server */ - target.value = auth->principal_name; - target.length = auth->principal_name_len + 1; + // This function actually just forwards the client's token to the backend server - /** Convert the name into GSSAPI format */ - major = gss_import_name(&minor, &target, GSS_C_NT_USER_NAME, &princ); - - if (GSS_ERROR(major)) + if (buffer) { - report_error(major, minor); - } + uint8_t* data = (uint8_t*)GWBUF_DATA(buffer); + gw_mysql_set_byte3(data, ses->auth_token_len); + data += 3; + *data++ = ++auth->sequence; + memcpy(data, ses->auth_token, ses->auth_token_len); - /** Request the token for the service */ - major = gss_init_sec_context(&minor, - GSS_C_NO_CREDENTIAL, - &handle, - princ, - GSS_C_NO_OID, - 0, - 0, - GSS_C_NO_CHANNEL_BINDINGS, - &in, - NULL, - &out, - 0, - 0); - if (GSS_ERROR(major)) - { - report_error(major, minor); - } - else - { - /** We successfully requested the token, send it to the backend server */ - GWBUF* buffer = gwbuf_alloc(MYSQL_HEADER_LEN + out.length); - - if (buffer) + if (dcb_write(dcb, buffer)) { - uint8_t* data = (uint8_t*)GWBUF_DATA(buffer); - gw_mysql_set_byte3(data, out.length); - data += 3; - *data++ = ++auth->sequence; - memcpy(data, out.value, out.length); - - if (dcb_write(dcb, buffer)) - { - rval = true; - } - } - - major = gss_delete_sec_context(&minor, &handle, &in); - - if (GSS_ERROR(major)) - { - report_error(major, minor); - } - - major = gss_release_name(&minor, &princ); - - if (GSS_ERROR(major)) - { - report_error(major, minor); + rval = true; } }