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
4 changed files with 92 additions and 49 deletions

View File

@ -295,6 +295,16 @@ private:
*/ */
void accept_connections(); 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 * The file descriptor for accepting new connections
* *

View File

@ -167,7 +167,18 @@ struct MXS_PROTOCOL
* *
* @return JSON representation of the DCB * @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 * the MXS_PROTOCOL structure is changed. See the rules defined in modinfo.h
* that define how these numbers should change. * 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. * 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); 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) 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; 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() void Listener::accept_connections()
{ {
for (ClientConn conn = accept_one_connection(fd()); conn.fd != -1; conn = accept_one_connection(fd())) 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)) if (DCB* dcb = accept_one_dcb(conn.fd, &conn.addr, conn.host))
{ {

View File

@ -23,6 +23,7 @@
#include <algorithm> #include <algorithm>
#include <string> #include <string>
#include <vector> #include <vector>
#include <sstream>
#include <maxscale/alloc.h> #include <maxscale/alloc.h>
#include <maxscale/authenticator.hh> #include <maxscale/authenticator.hh>
@ -80,6 +81,8 @@ static spec_com_res_t handle_query_kill(DCB* dcb,
unsigned int packet_len); 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 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 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 * The module entry point routine. It is this routine that
* must populate the structure that is referred to as the * must populate the structure that is referred to as the
@ -88,12 +91,8 @@ static void parse_and_set_trx_state(MXS_SESSION* ses, GWBUF* data);
* *
* @return The module object * @return The module object
*/ */
extern "C" MXS_MODULE* MXS_CREATE_MODULE()
extern "C"
{ {
MXS_MODULE* MXS_CREATE_MODULE()
{
static MXS_PROTOCOL MyObject = static MXS_PROTOCOL MyObject =
{ {
gw_read_client_event, /* Read - EPOLLIN handler */ gw_read_client_event, /* Read - EPOLLIN handler */
@ -108,7 +107,8 @@ extern "C"
gw_default_auth, /* Default authenticator */ gw_default_auth, /* Default authenticator */
gw_connection_limit, /* Send error connection limit */ gw_connection_limit, /* Send error connection limit */
NULL, NULL,
NULL NULL,
gw_reject_connection
}; };
static MXS_MODULE info = static MXS_MODULE info =
@ -130,7 +130,6 @@ extern "C"
}; };
return &info; return &info;
}
} }
/*lint +e14 */ /*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("trx state:%s", session_trx_state_to_string(ses->trx_state));
MXS_DEBUG("autcommit:%s", session_is_autocommit(ses) ? "ON" : "OFF"); 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());
}