MXS-2414: Send error when host is blocked

If a connection attempt is not accepted due to the host being blocked, the
protocol can now return an error message that is sent to the client. Only
mariadb_client implements this as it is the only one who calls the auth
failure methods in the first place.
This commit is contained in:
Markus Mäkelä 2019-04-26 11:32:35 +03:00
parent db0e491ace
commit 6caa8e55b0
No known key found for this signature in database
GPG Key ID: 72D48FCE664F7B19
4 changed files with 92 additions and 49 deletions

View File

@ -295,6 +295,16 @@ private:
*/
void accept_connections();
/**
* Reject a client connection
*
* Writes an error message to the fd if the protocol supports it and then closes it.
*
* @param fd The file descriptor to close
* @param host The host where the connection originated from
*/
void reject_connection(int fd, const char* host);
/**
* The file descriptor for accepting new connections
*

View File

@ -167,7 +167,18 @@ struct MXS_PROTOCOL
*
* @return JSON representation of the DCB
*/
json_t* (*diagnostics_json)(DCB * dcb);
json_t* (* diagnostics_json)(DCB* dcb);
/**
* Get rejection message
*
* The protocol should return an error indicating that access to MaxScale has been temporarily suspended.
*
* @param host The host that is blocked
*
* @return A buffer containing the error message
*/
GWBUF* (* reject)(const char* host);
};
/**
@ -175,7 +186,7 @@ struct MXS_PROTOCOL
* the MXS_PROTOCOL structure is changed. See the rules defined in modinfo.h
* that define how these numbers should change.
*/
#define MXS_PROTOCOL_VERSION {2, 0, 0}
#define MXS_PROTOCOL_VERSION {2, 1, 0}
/**
* Specifies capabilities specific for protocol.

View File

@ -1022,12 +1022,6 @@ static ClientConn accept_one_connection(int fd)
}
configure_network_socket(conn.fd, conn.addr.ss_family);
if (rate_limit.is_blocked(conn.host))
{
close(conn.fd);
conn.fd = -1;
}
}
else if (errno != EAGAIN && errno != EWOULDBLOCK)
{
@ -1211,11 +1205,33 @@ uint32_t Listener::poll_handler(MXB_POLL_DATA* data, MXB_WORKER* worker, uint32_
return MXB_POLL_ACCEPT;
}
void Listener::reject_connection(int fd, const char* host)
{
if (m_proto_func.reject)
{
if (GWBUF* buf = m_proto_func.reject(host))
{
for (auto b = buf; b; b = b->next)
{
write(fd, GWBUF_DATA(b), GWBUF_LENGTH(b));
}
gwbuf_free(buf);
}
}
close(fd);
}
void Listener::accept_connections()
{
for (ClientConn conn = accept_one_connection(fd()); conn.fd != -1; conn = accept_one_connection(fd()))
{
if (type() == Type::UNIQUE_TCP)
if (rate_limit.is_blocked(conn.host))
{
reject_connection(conn.fd, conn.host);
}
else if (type() == Type::UNIQUE_TCP)
{
if (DCB* dcb = accept_one_dcb(conn.fd, &conn.addr, conn.host))
{

View File

@ -23,6 +23,7 @@
#include <algorithm>
#include <string>
#include <vector>
#include <sstream>
#include <maxscale/alloc.h>
#include <maxscale/authenticator.hh>
@ -78,8 +79,10 @@ static spec_com_res_t handle_query_kill(DCB* dcb,
spec_com_res_t current,
bool is_complete,
unsigned int packet_len);
static bool parse_kill_query(char* query, uint64_t* thread_id_out, kill_type_t* kt_out, std::string* user);
static void parse_and_set_trx_state(MXS_SESSION* ses, GWBUF* data);
static bool parse_kill_query(char* query, uint64_t* thread_id_out, kill_type_t* kt_out, std::string* user);
static void parse_and_set_trx_state(MXS_SESSION* ses, GWBUF* data);
static GWBUF* gw_reject_connection(const char* host);
/**
* The module entry point routine. It is this routine that
* must populate the structure that is referred to as the
@ -88,49 +91,45 @@ static void parse_and_set_trx_state(MXS_SESSION* ses, GWBUF* data);
*
* @return The module object
*/
extern "C"
extern "C" MXS_MODULE* MXS_CREATE_MODULE()
{
MXS_MODULE* MXS_CREATE_MODULE()
static MXS_PROTOCOL MyObject =
{
static MXS_PROTOCOL MyObject =
{
gw_read_client_event, /* Read - EPOLLIN handler */
gw_MySQLWrite_client, /* Write - data from gateway */
gw_write_client_event, /* WriteReady - EPOLLOUT handler */
gw_error_client_event, /* Error - EPOLLERR handler */
gw_client_hangup_event, /* HangUp - EPOLLHUP handler */
gw_MySQLAccept, /* Accept */
NULL, /* Connect */
gw_client_close, /* Close */
NULL, /* Authentication */
gw_default_auth, /* Default authenticator */
gw_connection_limit, /* Send error connection limit */
NULL,
NULL
};
gw_read_client_event, /* Read - EPOLLIN handler */
gw_MySQLWrite_client, /* Write - data from gateway */
gw_write_client_event, /* WriteReady - EPOLLOUT handler */
gw_error_client_event, /* Error - EPOLLERR handler */
gw_client_hangup_event, /* HangUp - EPOLLHUP handler */
gw_MySQLAccept, /* Accept */
NULL, /* Connect */
gw_client_close, /* Close */
NULL, /* Authentication */
gw_default_auth, /* Default authenticator */
gw_connection_limit, /* Send error connection limit */
NULL,
NULL,
gw_reject_connection
};
static MXS_MODULE info =
static MXS_MODULE info =
{
MXS_MODULE_API_PROTOCOL,
MXS_MODULE_GA,
MXS_PROTOCOL_VERSION,
"The client to MaxScale MySQL protocol implementation",
"V1.1.0",
MXS_NO_MODULE_CAPABILITIES,
&MyObject,
process_init,
process_finish,
thread_init,
thread_finish,
{
MXS_MODULE_API_PROTOCOL,
MXS_MODULE_GA,
MXS_PROTOCOL_VERSION,
"The client to MaxScale MySQL protocol implementation",
"V1.1.0",
MXS_NO_MODULE_CAPABILITIES,
&MyObject,
process_init,
process_finish,
thread_init,
thread_finish,
{
{MXS_END_MODULE_PARAMS}
}
};
{MXS_END_MODULE_PARAMS}
}
};
return &info;
}
return &info;
}
/*lint +e14 */
@ -2122,3 +2121,10 @@ static void parse_and_set_trx_state(MXS_SESSION* ses, GWBUF* data)
MXS_DEBUG("trx state:%s", session_trx_state_to_string(ses->trx_state));
MXS_DEBUG("autcommit:%s", session_is_autocommit(ses) ? "ON" : "OFF");
}
static GWBUF* gw_reject_connection(const char* host)
{
std::stringstream ss;
ss << "Host '" << host << "' is temporarily blocked due to too many authentication failures.";
return modutil_create_mysql_err_msg(0, 0, 1129, "HY000", ss.str().c_str());
}