Add proxy protocol test
The test creates a user with only the client ip as allowed host and then uses that client to log in.
This commit is contained in:
@ -533,6 +533,9 @@ add_test_executable(mxs951_utfmb4.cpp mxs951_utfmb4 replication LABELS REPL_BACK
|
||||
# Execute given SQL through readwritesplit (with temporary tables usage)
|
||||
add_test_executable(mxs957.cpp mxs957 replication LABELS readwritesplit REPL_BACKEND)
|
||||
|
||||
# Proxy protocol test
|
||||
add_test_executable(proxy_protocol.cpp proxy_protocol proxy_protocol LABELS MySQLAuth MySQLProtocol)
|
||||
|
||||
# Regression case for the bug "Defunct processes after maxscale have executed script during failover"
|
||||
add_test_executable(mxs1045.cpp mxs1045 mxs1045 LABELS maxscale REPL_BACKEND)
|
||||
|
||||
|
@ -0,0 +1,71 @@
|
||||
[maxscale]
|
||||
threads=###threads###
|
||||
|
||||
[MySQL-Monitor]
|
||||
type=monitor
|
||||
module=mysqlmon
|
||||
servers= server1
|
||||
user=maxskysql
|
||||
passwd= skysql
|
||||
monitor_interval=1000
|
||||
detect_standalone_master=true
|
||||
failcount=1
|
||||
allow_cluster_recovery=true
|
||||
|
||||
[RW Split Router]
|
||||
type=service
|
||||
router= readwritesplit
|
||||
servers=server1
|
||||
user=maxskysql
|
||||
passwd=skysql
|
||||
|
||||
[Read Connection Router Slave]
|
||||
type=service
|
||||
router=readconnroute
|
||||
router_options= slave
|
||||
servers=server1
|
||||
user=maxskysql
|
||||
passwd=skysql
|
||||
|
||||
[Read Connection Router Master]
|
||||
type=service
|
||||
router=readconnroute
|
||||
router_options=master
|
||||
servers=server1
|
||||
user=maxskysql
|
||||
passwd=skysql
|
||||
|
||||
[RW Split Listener]
|
||||
type=listener
|
||||
service=RW Split Router
|
||||
protocol=MySQLClient
|
||||
port=4006
|
||||
|
||||
[Read Connection Listener Slave]
|
||||
type=listener
|
||||
service=Read Connection Router Slave
|
||||
protocol=MySQLClient
|
||||
port=4009
|
||||
|
||||
[Read Connection Listener Master]
|
||||
type=listener
|
||||
service=Read Connection Router Master
|
||||
protocol=MySQLClient
|
||||
port=4008
|
||||
|
||||
[CLI]
|
||||
type=service
|
||||
router=cli
|
||||
|
||||
[CLI Listener]
|
||||
type=listener
|
||||
service=CLI
|
||||
protocol=maxscaled
|
||||
socket=default
|
||||
|
||||
[server1]
|
||||
type=server
|
||||
address=###node_server_IP_1###
|
||||
port=###node_server_port_1###
|
||||
protocol=MySQLBackend
|
||||
proxy_protocol=1
|
184
maxscale-system-test/proxy_protocol.cpp
Normal file
184
maxscale-system-test/proxy_protocol.cpp
Normal file
@ -0,0 +1,184 @@
|
||||
/**
|
||||
* @file proxy_protocol.cpp proxy protocol test
|
||||
*
|
||||
* Proxy protocol is enabled in MaxScale config. Enable it on the server, then create a user which has only
|
||||
* the client ip in its allowed hosts. Check that user can log in directly to server and through MaxScale.
|
||||
*/
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include "testconnections.h"
|
||||
|
||||
using std::string;
|
||||
using std::cout;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
TestConnections::require_repl_version("10.3.1"); // Proxy protocol support is rather new.
|
||||
TestConnections test(argc, argv);
|
||||
test.repl->connect();
|
||||
|
||||
const string maxscale_ip = test.maxscales->IP[0];
|
||||
const int maxscale_port = test.maxscales->rwsplit_port[0];
|
||||
|
||||
// Router sessions shouldn't work, since MaxScale is sending the proxy header even when
|
||||
// server is not expecting it. The connection to MaxScale is created but queries will fail.
|
||||
auto adminconn = test.maxscales->open_rwsplit_connection(0);
|
||||
test.expect(adminconn != NULL, "Connection to MaxScale with user %s failed when success was expected.",
|
||||
test.maxscales->user_name);
|
||||
if (adminconn)
|
||||
{
|
||||
test.expect(execute_query(adminconn, "SELECT 1") != 0,
|
||||
"Query with user %s succeeded when failure was expected.", test.maxscales->user_name);
|
||||
mysql_close(adminconn);
|
||||
adminconn = NULL;
|
||||
}
|
||||
|
||||
bool server_proxy_setting = false;
|
||||
// Activate proxy protocol on the server now, otherwise router sessions won't work.
|
||||
if (test.global_result == 0)
|
||||
{
|
||||
cout << "Setting up proxy protocol on server1.\n";
|
||||
// Configure proxy protocol on the server.
|
||||
test.repl->stop_node(0);
|
||||
test.repl->stash_server_settings(0);
|
||||
|
||||
string proxy_setting = string("proxy_protocol_networks = ") + maxscale_ip;
|
||||
test.repl->add_server_setting(0, proxy_setting.c_str());
|
||||
test.repl->add_server_setting(0, "skip-name-resolve=1"); // To disable server hostname resolution.
|
||||
test.repl->start_node(0, (char *) "");
|
||||
cout << "Proxy protocol set.\n";
|
||||
test.maxscales->wait_for_monitor(2); // Wait for server to start and be detected
|
||||
test.repl->connect();
|
||||
server_proxy_setting = true;
|
||||
}
|
||||
|
||||
// Check what is the client ip.
|
||||
string client_ip;
|
||||
if (test.global_result == 0)
|
||||
{
|
||||
int bufsize = 512;
|
||||
char client_userhost[bufsize];
|
||||
// Send the user query directly to backend.
|
||||
if (find_field(test.repl->nodes[0], "SELECT USER();", "USER()", client_userhost) == 0)
|
||||
{
|
||||
client_ip = strstr(client_userhost, "@") + 1;
|
||||
cout << "Client IP is " << client_ip << "\n";
|
||||
cout << "MaxScale IP is " << maxscale_ip << " and port is " << maxscale_port << "\n";
|
||||
cout << "Server IP is " << test.repl->IP[0] << "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
test.expect(false, "Could not read client ip.");
|
||||
}
|
||||
}
|
||||
|
||||
const string username = "proxy_user";
|
||||
const string userpass = "proxy_pwd";
|
||||
if (test.global_result == 0)
|
||||
{
|
||||
adminconn = test.maxscales->open_rwsplit_connection(0);
|
||||
test.expect(adminconn, "MaxScale connection failed.");
|
||||
if (adminconn)
|
||||
{
|
||||
// Remove any existing conflicting user names, should not exist.
|
||||
cout << "Removing any leftover users, these queries may fail.\n";
|
||||
execute_query(adminconn, "DROP USER IF EXISTS '%s'@'%%'", username.c_str());
|
||||
execute_query(adminconn, "DROP USER IF EXISTS '%s'@'%s'", username.c_str(), maxscale_ip.c_str());
|
||||
execute_query(adminconn, "DROP USER IF EXISTS '%s'@'%s'", username.c_str(), client_ip.c_str());
|
||||
mysql_close(adminconn);
|
||||
adminconn = NULL;
|
||||
|
||||
// Try to connect through MaxScale using the proxy-user, it shouldn't work.
|
||||
auto testconn = open_conn(maxscale_port, maxscale_ip, username, userpass);
|
||||
if (testconn)
|
||||
{
|
||||
test.expect(execute_query(testconn, "SELECT 1") != 0,
|
||||
"Query with user %s succeeded when failure was expected.", username.c_str());
|
||||
mysql_close(testconn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (test.global_result == 0)
|
||||
{
|
||||
adminconn = test.maxscales->open_rwsplit_connection(0);
|
||||
test.expect(adminconn, "MaxScale connection failed.");
|
||||
if (adminconn)
|
||||
{
|
||||
// Create a test table and the proxy user.
|
||||
cout << "Creating user '"<< username << "' \n";
|
||||
test.try_query(adminconn, "CREATE OR REPLACE TABLE test.t1(id INT)");
|
||||
test.try_query(adminconn, "CREATE USER '%s'@'%s' identified by '%s'",
|
||||
username.c_str(), client_ip.c_str(), userpass.c_str());
|
||||
test.try_query(adminconn, "GRANT SELECT,INSERT ON test.t1 TO '%s'@'%s'", username.c_str(), client_ip.c_str());
|
||||
test.try_query(adminconn, "FLUSH PRIVILEGES;");
|
||||
if (test.global_result == 0)
|
||||
{
|
||||
cout << "User created\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "User creation or related query failed.\n";
|
||||
}
|
||||
mysql_close(adminconn);
|
||||
}
|
||||
}
|
||||
|
||||
// Try the user by connecting directly to the server, it should work.
|
||||
auto testconn = open_conn(test.repl->port[0], test.repl->IP[0], username, userpass);
|
||||
test.expect(testconn != NULL, "Connection to server1 failed when success was expected.");
|
||||
if (testconn)
|
||||
{
|
||||
mysql_close(testconn);
|
||||
testconn = NULL;
|
||||
}
|
||||
|
||||
if (test.global_result == 0)
|
||||
{
|
||||
// The test user should be able to login also through MaxScale.
|
||||
testconn = open_conn(maxscale_port, maxscale_ip, username, userpass);
|
||||
test.expect(testconn != NULL, "Connection to MaxScale failed when success was expected.");
|
||||
if (testconn)
|
||||
{
|
||||
// Try some queries to ensure it's working.
|
||||
test.try_query(testconn, "INSERT INTO test.t1 VALUES (232);");
|
||||
test.try_query(testconn, "INSERT INTO test.t1 VALUES (323);");
|
||||
int expected_rows = 2;
|
||||
int found_rows = execute_query_count_rows(testconn, "SELECT * FROM test.t1;");
|
||||
test.expect(found_rows == expected_rows, "Unexpected query results.");
|
||||
mysql_close(testconn);
|
||||
if (test.global_result == 0)
|
||||
{
|
||||
cout << "Results were as expected, test successful.\n";
|
||||
}
|
||||
}
|
||||
// Use the superuser to remove the test user.
|
||||
adminconn = test.maxscales->open_rwsplit_connection(0);
|
||||
test.expect(adminconn, "MaxScale connection failed.");
|
||||
if (adminconn)
|
||||
{
|
||||
cout << "Removing test user.\n";
|
||||
test.try_query(adminconn, "DROP TABLE IF EXISTS test.t1");
|
||||
test.try_query(adminconn, "DROP USER '%s'@'%s'", username.c_str(), client_ip.c_str());
|
||||
mysql_close(adminconn);
|
||||
adminconn = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (server_proxy_setting)
|
||||
{
|
||||
// Restore server settings.
|
||||
cout << "Removing proxy setting from server1.\n";
|
||||
test.repl->stop_node(0);
|
||||
test.repl->restore_server_settings(0);
|
||||
test.repl->start_node(0, (char *) "");
|
||||
test.maxscales->wait_for_monitor(2);
|
||||
server_proxy_setting = false;
|
||||
}
|
||||
|
||||
test.repl->disconnect();
|
||||
return test.global_result;
|
||||
}
|
||||
|
Reference in New Issue
Block a user