Allow socket and address/port to be used with maxadmin
It's now possible to use both a Unix domain socket and host/port when connecting with MaxAdmin to MaxScale. By default MaxAdmin will attempt to use the default Unix domain socket, but if host and/or port has been specified, then an inet socket will be used. maxscaled will authenticate the connection attempt differently depending on whether a Unix domain socket is used or not. If a Unix domain socket is used, then the Linux user id will be used for the authorization, otherwise the 1.4.3 username/password handshake will be performed. adminusers has now been extended so that there is one set of functions for local users (connecting locally over a Unix socket) and one set of functions for remote users (connecting locally or remotely over an Inet socket). The local users are stored in the new .../maxscale-users and the remote users in .../passwd. That is, the old users of a 1.4 installation will work as such in 2.0. One difference is that there will be *no* default remote user. That is, remote users will always have to be added manually using a local user. The implementation is shared; the local and remote alternatives use common functions to which the hashtable and filename to be used are forwarded. The commands "[add|remove] user" behave now exactly like they did in 1.4.3, and also all existing users work out of the box. In addition there is now the commands "[enable|disable] account" using which Linux accounts can be enabled for MaxAdmin usage.
This commit is contained in:
@ -65,6 +65,8 @@ MODULE_INFO info =
|
||||
|
||||
static char *version_str = "V2.0.0";
|
||||
|
||||
#define GETPWUID_BUF_LEN 255
|
||||
|
||||
static int maxscaled_read_event(DCB* dcb);
|
||||
static int maxscaled_write_event(DCB *dcb);
|
||||
static int maxscaled_write(DCB *dcb, GWBUF *queue);
|
||||
@ -75,6 +77,98 @@ static int maxscaled_close(DCB *dcb);
|
||||
static int maxscaled_listen(DCB *dcb, char *config);
|
||||
static char *mxsd_default_auth();
|
||||
|
||||
static bool authenticate_unix_socket(MAXSCALED *protocol, DCB *dcb)
|
||||
{
|
||||
bool authenticated = false;
|
||||
|
||||
struct ucred ucred;
|
||||
socklen_t len = sizeof(struct ucred);
|
||||
|
||||
/* Get UNIX client credentials from socket*/
|
||||
if (getsockopt(dcb->fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == 0)
|
||||
{
|
||||
struct passwd pw_entry;
|
||||
struct passwd *pw_tmp;
|
||||
char buf[GETPWUID_BUF_LEN];
|
||||
|
||||
/* Fetch username from UID */
|
||||
if (getpwuid_r(ucred.uid, &pw_entry, buf, sizeof(buf), &pw_tmp) == 0)
|
||||
{
|
||||
GWBUF *username;
|
||||
|
||||
/* Set user in protocol */
|
||||
protocol->username = strdup(pw_entry.pw_name);
|
||||
|
||||
username = gwbuf_alloc(strlen(protocol->username) + 1);
|
||||
|
||||
strcpy(GWBUF_DATA(username), protocol->username);
|
||||
|
||||
/* Authenticate the user */
|
||||
if (dcb->authfunc.extract(dcb, username) == 0 &&
|
||||
dcb->authfunc.authenticate(dcb) == 0)
|
||||
{
|
||||
dcb_printf(dcb, MAXADMIN_AUTH_SUCCESS_REPLY);
|
||||
protocol->state = MAXSCALED_STATE_DATA;
|
||||
dcb->user = strdup(protocol->username);
|
||||
}
|
||||
else
|
||||
{
|
||||
dcb_printf(dcb, MAXADMIN_AUTH_FAILED_REPLY);
|
||||
}
|
||||
|
||||
authenticated = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Failed to get UNIX user %ld details for 'MaxScale Admin'",
|
||||
(unsigned long)ucred.uid);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Failed to get UNIX domain socket credentials for 'MaxScale Admin'.");
|
||||
}
|
||||
|
||||
return authenticated;
|
||||
}
|
||||
|
||||
|
||||
static bool authenticate_inet_socket(MAXSCALED *protocol, DCB *dcb)
|
||||
{
|
||||
dcb_printf(dcb, MAXADMIN_AUTH_USER_PROMPT);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool authenticate_socket(MAXSCALED *protocol, DCB *dcb)
|
||||
{
|
||||
bool authenticated = false;
|
||||
|
||||
struct sockaddr address;
|
||||
socklen_t address_len = sizeof(address);
|
||||
|
||||
if (getsockname(dcb->fd, &address, &address_len) == 0)
|
||||
{
|
||||
if (address.sa_family == AF_UNIX)
|
||||
{
|
||||
authenticated = authenticate_unix_socket(protocol, dcb);
|
||||
}
|
||||
else
|
||||
{
|
||||
authenticated = authenticate_inet_socket(protocol, dcb);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
char errbuf[STRERROR_BUFLEN];
|
||||
|
||||
MXS_ERROR("Could not get socket family of client connection: %s",
|
||||
strerror_r(errno, errbuf, sizeof(errbuf)));
|
||||
}
|
||||
|
||||
return authenticated;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The "module object" for the maxscaled protocol module.
|
||||
*/
|
||||
@ -160,10 +254,41 @@ static int maxscaled_read_event(DCB* dcb)
|
||||
{
|
||||
if (GWBUF_LENGTH(head))
|
||||
{
|
||||
if (maxscaled->state == MAXSCALED_STATE_DATA)
|
||||
switch (maxscaled->state)
|
||||
{
|
||||
SESSION_ROUTE_QUERY(dcb->session, head);
|
||||
dcb_printf(dcb, "OK");
|
||||
case MAXSCALED_STATE_LOGIN:
|
||||
{
|
||||
maxscaled->username = strndup(GWBUF_DATA(head), GWBUF_LENGTH(head));
|
||||
maxscaled->state = MAXSCALED_STATE_PASSWD;
|
||||
dcb_printf(dcb, MAXADMIN_AUTH_PASSWORD_PROMPT);
|
||||
gwbuf_free(head);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAXSCALED_STATE_PASSWD:
|
||||
{
|
||||
char *password = strndup(GWBUF_DATA(head), GWBUF_LENGTH(head));
|
||||
if (admin_verify_inet_user(maxscaled->username, password))
|
||||
{
|
||||
dcb_printf(dcb, MAXADMIN_AUTH_SUCCESS_REPLY);
|
||||
maxscaled->state = MAXSCALED_STATE_DATA;
|
||||
}
|
||||
else
|
||||
{
|
||||
dcb_printf(dcb, MAXADMIN_AUTH_FAILED_REPLY);
|
||||
maxscaled->state = MAXSCALED_STATE_LOGIN;
|
||||
}
|
||||
gwbuf_free(head);
|
||||
free(password);
|
||||
}
|
||||
break;
|
||||
|
||||
case MAXSCALED_STATE_DATA:
|
||||
{
|
||||
SESSION_ROUTE_QUERY(dcb->session, head);
|
||||
dcb_printf(dcb, "OK");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -240,9 +365,9 @@ static int maxscaled_accept(DCB *listener)
|
||||
|
||||
while ((client_dcb = dcb_accept(listener, &MyObject)) != NULL)
|
||||
{
|
||||
MAXSCALED *maxscaled_protocol = NULL;
|
||||
MAXSCALED *maxscaled_protocol = (MAXSCALED *)calloc(1, sizeof(MAXSCALED));
|
||||
|
||||
if ((maxscaled_protocol = (MAXSCALED *)calloc(1, sizeof(MAXSCALED))) == NULL)
|
||||
if (!maxscaled_protocol)
|
||||
{
|
||||
dcb_close(client_dcb);
|
||||
continue;
|
||||
@ -251,52 +376,14 @@ static int maxscaled_accept(DCB *listener)
|
||||
maxscaled_protocol->username = NULL;
|
||||
maxscaled_protocol->state = MAXSCALED_STATE_LOGIN;
|
||||
|
||||
/* Get UNIX client credentials from socket*/
|
||||
if (getsockopt(client_dcb->fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1)
|
||||
bool authenticated = false;
|
||||
|
||||
if (!authenticate_socket(maxscaled_protocol, client_dcb))
|
||||
{
|
||||
MXS_ERROR("Failed to get UNIX socket credentials for 'MaxScale Admin'");
|
||||
dcb_close(client_dcb);
|
||||
free(maxscaled_protocol);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct passwd pw_entry;
|
||||
struct passwd *pw_tmp;
|
||||
char buf[MAXADMIN_GETPWUID_BUF_LEN];
|
||||
|
||||
/* Fetch username from UID */
|
||||
if (!getpwuid_r(ucred.uid, &pw_entry, buf, sizeof(buf), &pw_tmp))
|
||||
{
|
||||
GWBUF *username;
|
||||
|
||||
/* Set user in protocol */
|
||||
maxscaled_protocol->username = strdup(pw_entry.pw_name);
|
||||
|
||||
username = gwbuf_alloc(strlen(maxscaled_protocol->username) + 1);
|
||||
|
||||
strcpy(GWBUF_DATA(username), maxscaled_protocol->username);
|
||||
|
||||
/* Authenticate the user */
|
||||
if (client_dcb->authfunc.extract(client_dcb, username) == 0 &&
|
||||
client_dcb->authfunc.authenticate(client_dcb) == 0)
|
||||
{
|
||||
dcb_printf(client_dcb, "OK----");
|
||||
maxscaled_protocol->state = MAXSCALED_STATE_DATA;
|
||||
client_dcb->user = strdup(maxscaled_protocol->username);
|
||||
}
|
||||
else
|
||||
{
|
||||
dcb_printf(client_dcb, "FAILED");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Failed to get UNIX user %ld details for 'MaxScale Admin'",
|
||||
(unsigned long)ucred.uid);
|
||||
dcb_close(client_dcb);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
spinlock_init(&maxscaled_protocol->lock);
|
||||
client_dcb->protocol = (void *)maxscaled_protocol;
|
||||
@ -358,27 +445,7 @@ static int maxscaled_listen(DCB *listener, char *config)
|
||||
}
|
||||
else
|
||||
{
|
||||
const char* colon = strchr(config, ':');
|
||||
ss_dassert(colon);
|
||||
const char* port = colon + 1;
|
||||
|
||||
if (*port == '0')
|
||||
{
|
||||
// It seems "socket=socket-path" has been specified.
|
||||
|
||||
// The colon etc. will be stripped away in dcb_listen_create_socket_unix.
|
||||
socket_path = config;
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_WARNING("The 'maxscaled' protocol can only be used with a Unix domain socket, but "
|
||||
"it seems to have been configured with an address and port: %s. "
|
||||
"Using the default socket path instead: %s. "
|
||||
"Remove all 'address' and 'port' entries from maxscaled protocol "
|
||||
"listeners and replace with 'socket=default' or 'socket=path-to-socket'.",
|
||||
config, MAXADMIN_DEFAULT_SOCKET);
|
||||
socket_path = MAXADMIN_DEFAULT_SOCKET;
|
||||
}
|
||||
socket_path = config;
|
||||
}
|
||||
|
||||
return (dcb_listen(listener, socket_path, "MaxScale Admin") < 0) ? 0 : 1;
|
||||
|
@ -202,7 +202,7 @@ static int telnetd_read_event(DCB* dcb)
|
||||
{
|
||||
*t = 0;
|
||||
}
|
||||
if (admin_remote_verify(telnetd->username, password))
|
||||
if (admin_verify_inet_user(telnetd->username, password))
|
||||
{
|
||||
telnetd_echo(dcb, 1);
|
||||
telnetd->state = TELNETD_STATE_DATA;
|
||||
|
Reference in New Issue
Block a user