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
make
make test || exit 1
ctest --output-on-failure || exit 1
sudo make install
sudo ./postinst

View File

@ -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

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
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

View File

@ -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")

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)
/**
* 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

View File

@ -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
*

View File

@ -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);

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`
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)

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.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");

View File

@ -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;
}

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);
/**
* 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.
*

View File

@ -20,6 +20,7 @@
#include <cmath>
#include <cstring>
#include <string>
#include <cstdio>
#include <mutex>
#include <unordered_map>
@ -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<mxb::Logger> sLogger;
std::unique_ptr<MessageRegistry> 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()

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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;
}
}