Merge branch '2.2' into develop

This commit is contained in:
Markus Mäkelä
2018-10-02 10:18:09 +03:00
16 changed files with 125 additions and 135 deletions

View File

@ -18,7 +18,7 @@ cd build
cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTS=Y cmake .. -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTS=Y
make make
make test || exit 1 ctest --output-on-failure || exit 1
sudo make install sudo make install
sudo ./postinst sudo ./postinst

View File

@ -16,6 +16,8 @@ make
if [[ "$cmake_flags" =~ "BUILD_TESTS" ]] if [[ "$cmake_flags" =~ "BUILD_TESTS" ]]
then 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 # All tests must pass otherwise the build is considered a failure
ctest --output-on-failure || exit 1 ctest --output-on-failure || exit 1
fi fi

View File

@ -12,29 +12,32 @@ _GSSAPIBackendAuth_ module implements the backend authentication.
For Unix systems, the usual GSSAPI implementation is Kerberos. This is a short For Unix systems, the usual GSSAPI implementation is Kerberos. This is a short
guide on how to set up Kerberos for MaxScale. 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 first step is to configure MariaDB to use GSSAPI authentication. The MariaDB
the _kadmin_ or _kadmin.local_ tools. 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 Change the principal name to the same value you configured for the MariaDB
_maxscale_ user to acquire a ticket for it without a password being prompted. 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 ## Authenticator options
The client side GSSAPIAuth authenticator supports one option, the service The client side GSSAPIAuth authenticator supports one option, the service
@ -43,14 +46,12 @@ module has no options.
### `principal_name` ### `principal_name`
The service principal name to send to the client. This parameter is a The service principal name to send to the client. This parameter is a string
string parameter which is used by the client to request the token. 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_. This parameter *must* be the same as the principal name that the backend MariaDB
server uses.
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.
## Implementation details ## Implementation details

View File

@ -5,7 +5,7 @@
set(MAXSCALE_VERSION_MAJOR "2" CACHE STRING "Major version") set(MAXSCALE_VERSION_MAJOR "2" CACHE STRING "Major version")
set(MAXSCALE_VERSION_MINOR "2" CACHE STRING "Minor 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 # This should only be incremented if a package is rebuilt
set(MAXSCALE_BUILD_NUMBER 1 CACHE STRING "Release number") set(MAXSCALE_BUILD_NUMBER 1 CACHE STRING "Release number")

View File

@ -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) #define MXS_SO_SNDBUF_SIZE (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)
/** /**
* MXS_MAX_NW_READ_BUFFER_SIZE * MXS_MAX_NW_READ_BUFFER_SIZE

View File

@ -46,6 +46,20 @@ enum mxs_socket_type
bool utils_init(); /*< Call this first before using any other function */ bool utils_init(); /*< Call this first before using any other function */
void utils_end(); 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 * @brief Create a network socket and a socket configuration
* *

View File

@ -23,7 +23,7 @@ void get_output(TestConnections& test)
test.tprintf("MaxScale output:"); test.tprintf("MaxScale output:");
} }
output = test.maxscales->ssh_node_output(0, 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", "sudo truncate -s 0 /var/log/maxscale/maxscale.log",
true, true,
&ec); &ec);

View File

@ -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` 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 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 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) # User name and Password for Master/Slave replication setup (should have all PRIVILEGES)

View File

@ -45,8 +45,9 @@ int main(int argc, char** argv)
test.try_query(test.repl->nodes[0], "%s", "GRANT ALL ON *.* TO 'mxs1743'@'%'"); test.try_query(test.repl->nodes[0], "%s", "GRANT ALL ON *.* TO 'mxs1743'@'%'");
test.tprintf("Syncing slaves"); test.tprintf("Syncing slaves");
test.set_timeout(60); test.stop_timeout();
test.repl->sync_slaves(); test.repl->sync_slaves();
test.set_timeout(60);
test.tprintf("Opening new connections to verify readconnroute works"); test.tprintf("Opening new connections to verify readconnroute works");

View File

@ -35,16 +35,21 @@ int main(int argc, char* argv[])
test.stop_timeout(); test.stop_timeout();
test.repl->connect(); test.repl->connect();
test.repl->sync_slaves(); test.repl->sync_slaves();
test.repl->disconnect();
test.set_timeout(60); test.set_timeout(60);
test.maxscales->connect();
check_val(test.maxscales->conn_rwsplit[0], test); check_val(test.maxscales->conn_rwsplit[0], test);
check_val(test.maxscales->conn_master[0], test); check_val(test.maxscales->conn_master[0], test);
check_val(test.maxscales->conn_slave[0], test); check_val(test.maxscales->conn_slave[0], test);
test.maxscales->disconnect();
nodes->connect();
for (int i = 0; i < test.repl->N; i++) for (int i = 0; i < test.repl->N; i++)
{ {
check_val(nodes->nodes[i], test); check_val(nodes->nodes[i], test);
} }
nodes->disconnect();
return test.global_result; return test.global_result;
} }

View File

@ -226,6 +226,13 @@ void mxb_log_set_throttling(const MXB_LOG_THROTTLING* throttling);
*/ */
void mxb_log_get_throttling(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. * Log a message of a particular priority.
* *

View File

@ -20,6 +20,7 @@
#include <cmath> #include <cmath>
#include <cstring> #include <cstring>
#include <string> #include <string>
#include <cstdio>
#include <mutex> #include <mutex>
#include <unordered_map> #include <unordered_map>
@ -384,6 +385,7 @@ struct this_unit
bool do_highprecision; // Can change during the lifetime of log_manager. bool do_highprecision; // Can change during the lifetime of log_manager.
bool do_syslog; // 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 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. MXB_LOG_THROTTLING throttling; // Can change during the lifetime of log_manager.
std::unique_ptr<mxb::Logger> sLogger; std::unique_ptr<mxb::Logger> sLogger;
std::unique_ptr<MessageRegistry> sMessage_registry; std::unique_ptr<MessageRegistry> sMessage_registry;
@ -394,6 +396,7 @@ struct this_unit
false, // do_highprecision false, // do_highprecision
true, // do_syslog true, // do_syslog
true, // do_maxlog true, // do_maxlog
false, // redirect_stdout
DEFAULT_LOG_THROTTLING, // throttling DEFAULT_LOG_THROTTLING, // throttling
}; };
@ -486,6 +489,13 @@ bool mxb_log_init(const char* ident,
case MXB_LOG_TARGET_FS: case MXB_LOG_TARGET_FS:
case MXB_LOG_TARGET_DEFAULT: case MXB_LOG_TARGET_DEFAULT:
this_unit.sLogger = mxb::FileLogger::create(filepath); 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; break;
case MXB_LOG_TARGET_STDOUT: case MXB_LOG_TARGET_STDOUT:
@ -598,9 +608,23 @@ void mxb_log_get_throttling(MXB_LOG_THROTTLING* throttling)
*throttling = this_unit.throttling; *throttling = this_unit.throttling;
} }
void mxs_log_redirect_stdout(bool redirect)
{
this_unit.redirect_stdout = redirect;
}
bool mxb_log_rotate() 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() const char* mxb_log_get_filename()

View File

@ -2405,21 +2405,7 @@ DCB* dcb_accept(DCB* dcb)
{ {
dcb->stats.n_accepts++; dcb->stats.n_accepts++;
/* set nonblocking */ configure_network_socket(c_sock, client_conn.ss_family);
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);
client_dcb = dcb_alloc(DCB_ROLE_CLIENT_HANDLER, dcb->listener); client_dcb = dcb_alloc(DCB_ROLE_CLIENT_HANDLER, dcb->listener);

View File

@ -347,12 +347,15 @@ static void sigterm_handler(int i)
int n_shutdowns = maxscale_shutdown(); int n_shutdowns = maxscale_shutdown();
if (n_shutdowns == 1) if (n_shutdowns == 1)
{
if (!daemon_mode)
{ {
if (write(STDERR_FILENO, shutdown_msg, sizeof(shutdown_msg) - 1) == -1) if (write(STDERR_FILENO, shutdown_msg, sizeof(shutdown_msg) - 1) == -1)
{ {
printf("Failed to write shutdown message!\n"); printf("Failed to write shutdown message!\n");
} }
} }
}
else else
{ {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@ -365,19 +368,25 @@ static void sigint_handler(int i)
int n_shutdowns = maxscale_shutdown(); int n_shutdowns = maxscale_shutdown();
if (n_shutdowns == 1) if (n_shutdowns == 1)
{
if (!daemon_mode)
{ {
if (write(STDERR_FILENO, shutdown_msg, sizeof(shutdown_msg) - 1) == -1) if (write(STDERR_FILENO, shutdown_msg, sizeof(shutdown_msg) - 1) == -1)
{ {
printf("Failed to write shutdown message!\n"); printf("Failed to write shutdown message!\n");
} }
} }
}
else if (n_shutdowns == 2) else if (n_shutdowns == 2)
{
if (!daemon_mode)
{ {
if (write(STDERR_FILENO, patience_msg, sizeof(patience_msg) - 1) == -1) if (write(STDERR_FILENO, patience_msg, sizeof(patience_msg) - 1) == -1)
{ {
printf("Failed to write shutdown message!\n"); printf("Failed to write shutdown message!\n");
} }
} }
}
else else
{ {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@ -1919,6 +1928,11 @@ int main(int argc, char** argv)
mxs_log_finish(); mxs_log_finish();
} }
if (cnf->log_target != MXB_LOG_TARGET_STDOUT && daemon_mode)
{
mxs_log_redirect_stdout(true);
}
if (!init_log()) if (!init_log())
{ {
rc = MAXSCALE_BADCONFIG; rc = MAXSCALE_BADCONFIG;

View File

@ -983,17 +983,18 @@ void utils_end()
replace_values_re = NULL; 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 sndbufsize = MXS_SO_SNDBUF_SIZE;
int rcvbufsize = MXS_BACKEND_SO_RCVBUF; int rcvbufsize = MXS_SO_RCVBUF_SIZE;
int one = 1; int one = 1;
if (setsockopt(so, SOL_SOCKET, SO_SNDBUF, &sndbufsize, sizeof(sndbufsize)) != 0 if (setsockopt(so, SOL_SOCKET, SO_SNDBUF, &sndbufsize, sizeof(sndbufsize)) != 0
|| setsockopt(so, SOL_SOCKET, SO_RCVBUF, &rcvbufsize, sizeof(rcvbufsize)) != 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)); MXS_ERROR("Failed to set socket option: %d, %s.", errno, mxs_strerror(errno));
mxb_assert(!true);
return false; return false;
} }
@ -1065,7 +1066,7 @@ int open_network_socket(enum mxs_socket_type type,
freeaddrinfo(ai); 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))) || (type == MXS_SOCKET_LISTENER && !configure_listener_socket(so)))
{ {
close(so); close(so);

View File

@ -59,56 +59,19 @@ void gssapi_backend_auth_free(void* data)
static bool send_new_auth_token(DCB* dcb) static bool send_new_auth_token(DCB* dcb)
{ {
bool rval = false; 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; 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 */ // This function actually just forwards the client's token to the backend server
target.value = auth->principal_name;
target.length = auth->principal_name_len + 1;
/** Convert the name into GSSAPI format */
major = gss_import_name(&minor, &target, GSS_C_NT_USER_NAME, &princ);
if (GSS_ERROR(major))
{
report_error(major, minor);
}
/** 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 (buffer)
{ {
uint8_t* data = (uint8_t*)GWBUF_DATA(buffer); uint8_t* data = (uint8_t*)GWBUF_DATA(buffer);
gw_mysql_set_byte3(data, out.length); gw_mysql_set_byte3(data, ses->auth_token_len);
data += 3; data += 3;
*data++ = ++auth->sequence; *data++ = ++auth->sequence;
memcpy(data, out.value, out.length); memcpy(data, ses->auth_token, ses->auth_token_len);
if (dcb_write(dcb, buffer)) if (dcb_write(dcb, buffer))
{ {
@ -116,21 +79,6 @@ static bool send_new_auth_token(DCB* dcb)
} }
} }
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);
}
}
return rval; return rval;
} }