MXS-1220: Allow binding to a specific address

The admin interface can now bind to a specific network interface.
This commit is contained in:
Markus Mäkelä 2017-05-02 06:05:15 +03:00
parent af2a4e792f
commit 4ed4773d91
5 changed files with 89 additions and 15 deletions

View File

@ -172,6 +172,12 @@ write or modify the data in the backend server. The default is 2 seconds.
auth_write_timeout=10
```
#### `admin_host`
The network interface where the HTTP admin interface listens on. The default
value is the IPv6 address `::` which listens on all available network
interfaces.
#### `admin_port`
The port where the HTTP admin interface listens on. The default value is port

View File

@ -28,10 +28,12 @@ MXS_BEGIN_DECLS
/** Default port where the REST API listens */
#define DEFAULT_ADMIN_HTTP_PORT 8989
#define DEFAULT_ADMIN_HOST "::"
#define _RELEASE_STR_LENGTH 256 /**< release len */
#define MAX_ADMIN_USER_LEN 1024
#define MAX_ADMIN_PW_LEN 1024
#define MAX_ADMIN_HOST_LEN 1024
/**
* Common configuration parameters names
@ -42,6 +44,7 @@ MXS_BEGIN_DECLS
*/
extern const char CN_ADDRESS[];
extern const char CN_ADMIN_AUTH[];
extern const char CN_ADMIN_HOST[];
extern const char CN_ADMIN_PASSWORD[];
extern const char CN_ADMIN_PORT[];
extern const char CN_ADMIN_USER[];
@ -156,6 +159,7 @@ typedef struct
char* qc_args; /**< Arguments for the query classifier */
char admin_user[MAX_ADMIN_USER_LEN]; /**< Admin interface user */
char admin_password[MAX_ADMIN_PW_LEN]; /**< Admin interface password */
char admin_host[MAX_ADMIN_HOST_LEN]; /**< Admin interface host */
uint16_t admin_port; /**< Admin interface port */
bool admin_auth; /**< Admin interface authentication */
} MXS_CONFIG;

View File

@ -11,11 +11,17 @@
* Public License.
*/
/**
* @file The embedded HTTP protocol administrative interface
*/
#include "maxscale/admin.hh"
#include <climits>
#include <new>
#include <microhttpd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <maxscale/atomic.h>
#include <maxscale/debug.h>
@ -180,16 +186,64 @@ int handle_client(void *cls,
return client->process(url, method, upload_data, upload_data_size);
}
static bool host_to_sockaddr(const char* host, uint16_t port, struct sockaddr_storage* addr)
{
struct addrinfo *ai = NULL, hint = {};
int rc;
hint.ai_socktype = SOCK_STREAM;
hint.ai_family = AF_UNSPEC;
hint.ai_flags = AI_ALL;
if ((rc = getaddrinfo(host, NULL, &hint, &ai)) != 0)
{
MXS_ERROR("Failed to obtain address for host %s: %s", host, gai_strerror(rc));
return false;
}
/* Take the first one */
if (ai)
{
memcpy(addr, ai->ai_addr, ai->ai_addrlen);
if (addr->ss_family == AF_INET)
{
struct sockaddr_in *ip = (struct sockaddr_in*)addr;
ip->sin_port = htons(port);
}
else if (addr->ss_family == AF_INET6)
{
struct sockaddr_in6 *ip = (struct sockaddr_in6*)addr;
ip->sin6_port = htons(port);
}
}
freeaddrinfo(ai);
return true;
}
bool mxs_admin_init()
{
http_daemon = MHD_start_daemon(MHD_USE_EPOLL_INTERNALLY_LINUX_ONLY | MHD_USE_DUAL_STACK,
config_get_global_options()->admin_port,
NULL, NULL,
handle_client, NULL,
MHD_OPTION_NOTIFY_COMPLETED, close_client, NULL,
MHD_OPTION_END);
return http_daemon != NULL;
struct sockaddr_storage addr;
if (host_to_sockaddr(config_get_global_options()->admin_host,
config_get_global_options()->admin_port,
&addr))
{
int options = MHD_USE_EPOLL_INTERNALLY_LINUX_ONLY;
if (addr.ss_family == AF_INET6)
{
options |= MHD_USE_DUAL_STACK;
}
// The port argument is ignored and the port in the struct sockaddr is used instead
http_daemon = MHD_start_daemon(options, 0, NULL, NULL, handle_client, NULL,
MHD_OPTION_NOTIFY_COMPLETED, close_client, NULL,
MHD_OPTION_SOCK_ADDR, &addr,
MHD_OPTION_END);
}
return http_daemon != NULL;
}
void mxs_admin_shutdown()

View File

@ -53,6 +53,7 @@ using std::string;
const char CN_ADDRESS[] = "address";
const char CN_ADMIN_AUTH[] = "admin_auth";
const char CN_ADMIN_HOST[] = "admin_host";
const char CN_ADMIN_PASSWORD[] = "admin_password";
const char CN_ADMIN_PORT[] = "admin_port";
const char CN_ADMIN_USER[] = "admin_user";
@ -1520,6 +1521,10 @@ handle_global_item(const char *name, const char *value)
{
gateway.admin_port = atoi(value);
}
else if (strcmp(name, CN_ADMIN_HOST) == 0)
{
strcpy(gateway.admin_host, value);
}
else if (strcmp(name, CN_ADMIN_AUTH) == 0)
{
gateway.admin_auth = config_truth_value(value);
@ -1746,6 +1751,7 @@ global_defaults()
gateway.skip_permission_checks = false;
gateway.admin_port = DEFAULT_ADMIN_HTTP_PORT;
gateway.admin_auth = false;
strcpy(gateway.admin_host, DEFAULT_ADMIN_HOST);
strcpy(gateway.admin_user, INET_DEFAULT_USERNAME);
strcpy(gateway.admin_password, INET_DEFAULT_PASSWORD);

View File

@ -1982,6 +1982,18 @@ int main(int argc, char **argv)
}
}
if (mxs_admin_init())
{
MXS_NOTICE("Started REST API on [%s]:%u", cnf->admin_host, cnf->admin_port);
}
else
{
const char* logerr = "Failed to initialize admin interface";
print_log_n_stderr(true, true, logerr, logerr, 0);
rc = MAXSCALE_INTERNALERROR;
goto return_main;
}
MXS_NOTICE("MaxScale started with %d server threads.", config_threadcount());
/**
* Successful start, notify the parent process that it can exit.
@ -1992,14 +2004,6 @@ int main(int argc, char **argv)
write_child_exit_code(daemon_pipe[1], rc);
}
if (!mxs_admin_init())
{
const char* logerr = "Failed to initialize admin interface";
print_log_n_stderr(true, true, logerr, logerr, 0);
rc = MAXSCALE_INTERNALERROR;
goto return_main;
}
/*<
* Run worker 0 in the main thread.
*/