MXS-1220: Add HTTPS support

The REST API now supports encryption. The user needs to configure
certificates for the REST API before encryption is used.
This commit is contained in:
Markus Mäkelä 2017-05-02 07:12:44 +03:00
parent 4ed4773d91
commit bf44cd0d14
6 changed files with 167 additions and 32 deletions

View File

@ -172,35 +172,6 @@ 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
8080.
#### `admin_auth`
Enable HTTP admin interface authentication using HTTP Basic Access
authentication. This is not a secure method of authentication but it does add a
small layer of security. This option id disabled by default.
#### `admin_user`
The HTTP admin interface username. This is the username which is used when
_admin_auth_ is enabled. The default user for the HTTP admin interface is
`admin`.
#### `admin_password`
The HTTP admin interface password. This is the which which is used when
_admin_auth_ is enabled. The default password for the HTTP admin interface is
`mariadb`.
#### `ms_timestamp`
Enable or disable the high precision timestamps in logfiles. Enabling this adds
@ -548,6 +519,60 @@ This will log all statements that cannot be parsed completely. This may be
useful if you suspect that MariaDB MaxScale routes statements to the wrong
server (e.g. to a slave instead of to a master).
### REST API Configuration
The MaxScale REST API is an HTTP interface that provides JSON format data
intended to be consumed by monitoring appllications and visualization tools.
The following options must be defined under the `[maxscale]` section in the
configuration file.
#### `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
8080.
#### `admin_auth`
Enable HTTP admin interface authentication using HTTP Basic Access
authentication. This is not a secure method of authentication but it does add a
small layer of security. This option id disabled by default.
#### `admin_user`
The HTTP admin interface username. This is the username which is used when
_admin_auth_ is enabled. The default user for the HTTP admin interface is
`admin`.
#### `admin_password`
The HTTP admin interface password. This is the which which is used when
_admin_auth_ is enabled. The default password for the HTTP admin interface is
`mariadb`.
#### `admin_ssl_key`
The path to the TLS private key in PEM format for the admin interface.
If the `admin_ssl_key`, `admin_ssl_cert` and `admin_ssl_ca_cert` options are all
defined, the admin interface will use encrypted HTTPS instead of plain HTTP.
#### `admin_ssl_cert`
The path to the TLS public certificate in PEM format. See `admin_ssl_key`
documentation for more details.
#### `admin_ssl_ca_cert`
The path to the TLS CA certificate in PEM format. See `admin_ssl_key`
documentation for more details.
### Service
A service represents the database service that MariaDB MaxScale offers to the

View File

@ -48,6 +48,9 @@ extern const char CN_ADMIN_HOST[];
extern const char CN_ADMIN_PASSWORD[];
extern const char CN_ADMIN_PORT[];
extern const char CN_ADMIN_USER[];
extern const char CN_ADMIN_SSL_KEY[];
extern const char CN_ADMIN_SSL_CERT[];
extern const char CN_ADMIN_SSL_CA_CERT[];
extern const char CN_AUTHENTICATOR[];
extern const char CN_AUTHENTICATOR_OPTIONS[];
extern const char CN_AUTH_ALL_SERVERS[];
@ -162,6 +165,9 @@ typedef struct
char admin_host[MAX_ADMIN_HOST_LEN]; /**< Admin interface host */
uint16_t admin_port; /**< Admin interface port */
bool admin_auth; /**< Admin interface authentication */
char admin_ssl_key[PATH_MAX]; /**< Admin SSL key */
char admin_ssl_cert[PATH_MAX]; /**< Admin SSL cert */
char admin_ssl_ca_cert[PATH_MAX]; /**< Admin SSL CA cert */
} MXS_CONFIG;
/**

View File

@ -18,7 +18,9 @@
#include <climits>
#include <new>
#include <fstream>
#include <microhttpd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
@ -29,14 +31,23 @@
#include <maxscale/utils.h>
#include <maxscale/config.h>
#include <maxscale/hk_heartbeat.h>
#include <sys/stat.h>
#include "maxscale/resource.hh"
#include "maxscale/http.hh"
using std::string;
using std::ifstream;
static struct MHD_Daemon* http_daemon = NULL;
/** In-memory certificates in PEM format */
static char* admin_ssl_key = NULL;
static char* admin_ssl_cert = NULL;
static char* admin_ssl_ca_cert = NULL;
static bool using_ssl = false;
int kv_iter(void *cls,
enum MHD_ValueKind kind,
const char *key,
@ -221,6 +232,58 @@ static bool host_to_sockaddr(const char* host, uint16_t port, struct sockaddr_st
return true;
}
static char* load_cert(const char* file)
{
char* rval = NULL;
ifstream infile(file);
struct stat st;
if (stat(file, &st) == 0 &&
(rval = new (std::nothrow) char[st.st_size + 1]))
{
infile.read(rval, st.st_size);
rval[st.st_size] = '\0';
if (!infile.good())
{
MXS_ERROR("Failed to load certificate file: %s", file);
delete rval;
rval = NULL;
}
}
return rval;
}
static bool load_ssl_certificates()
{
bool rval = false;
const char* key = config_get_global_options()->admin_ssl_key;
const char* cert = config_get_global_options()->admin_ssl_cert;
const char* ca = config_get_global_options()->admin_ssl_ca_cert;
if (*key && *cert && *ca)
{
if ((admin_ssl_key = load_cert(key)) &&
(admin_ssl_cert = load_cert(cert)) &&
(admin_ssl_ca_cert = load_cert(ca)))
{
rval = true;
}
else
{
delete admin_ssl_key;
delete admin_ssl_cert;
delete admin_ssl_ca_cert;
admin_ssl_key = NULL;
admin_ssl_cert = NULL;
admin_ssl_ca_cert = NULL;
}
}
return rval;
}
bool mxs_admin_init()
{
struct sockaddr_storage addr;
@ -236,10 +299,20 @@ bool mxs_admin_init()
options |= MHD_USE_DUAL_STACK;
}
if (load_ssl_certificates())
{
using_ssl = true;
options |= MHD_USE_SSL;
}
// 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,
http_daemon = MHD_start_daemon(options, 0, NULL, NULL, handle_client, NULL,
MHD_OPTION_NOTIFY_COMPLETED, close_client, NULL,
MHD_OPTION_SOCK_ADDR, &addr,
!using_ssl ? MHD_OPTION_END :
MHD_OPTION_HTTPS_MEM_KEY, admin_ssl_key,
MHD_OPTION_HTTPS_MEM_CERT, admin_ssl_cert,
MHD_OPTION_HTTPS_MEM_TRUST, admin_ssl_cert,
MHD_OPTION_END);
}
@ -250,3 +323,8 @@ void mxs_admin_shutdown()
{
MHD_stop_daemon(http_daemon);
}
bool mxs_admin_https_enabled()
{
return using_ssl;
}

View File

@ -57,6 +57,9 @@ 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";
const char CN_ADMIN_SSL_KEY[] = "admin_ssl_key";
const char CN_ADMIN_SSL_CERT[] = "admin_ssl_cert";
const char CN_ADMIN_SSL_CA_CERT[] = "admin_ssl_ca_cert";
const char CN_AUTHENTICATOR[] = "authenticator";
const char CN_AUTHENTICATOR_OPTIONS[] = "authenticator_options";
const char CN_AUTH_ALL_SERVERS[] = "auth_all_servers";
@ -1525,6 +1528,18 @@ handle_global_item(const char *name, const char *value)
{
strcpy(gateway.admin_host, value);
}
else if (strcmp(name, CN_ADMIN_SSL_KEY) == 0)
{
strcpy(gateway.admin_ssl_key, value);
}
else if (strcmp(name, CN_ADMIN_SSL_CERT) == 0)
{
strcpy(gateway.admin_ssl_cert, value);
}
else if (strcmp(name, CN_ADMIN_SSL_CA_CERT) == 0)
{
strcpy(gateway.admin_ssl_ca_cert, value);
}
else if (strcmp(name, CN_ADMIN_AUTH) == 0)
{
gateway.admin_auth = config_truth_value(value);
@ -1754,6 +1769,9 @@ global_defaults()
strcpy(gateway.admin_host, DEFAULT_ADMIN_HOST);
strcpy(gateway.admin_user, INET_DEFAULT_USERNAME);
strcpy(gateway.admin_password, INET_DEFAULT_PASSWORD);
gateway.admin_ssl_key[0] = '\0';
gateway.admin_ssl_cert[0] = '\0';
gateway.admin_ssl_ca_cert[0] = '\0';
if (version_string != NULL)
{

View File

@ -12,6 +12,7 @@
*/
#include "maxscale/httprequest.hh"
#include "maxscale/admin.hh"
#include <ctype.h>
#include <string.h>
@ -98,8 +99,8 @@ HttpRequest::HttpRequest(struct MHD_Connection *connection, string url, string m
m_connection(connection)
{
process_uri(url, m_resource_parts);
// TODO: Add https support
m_hostname = HttpRequest::HTTP_PREFIX;
m_hostname = mxs_admin_https_enabled() ? HttpRequest::HTTPS_PREFIX : HttpRequest::HTTP_PREFIX;
m_hostname += get_header(HTTP_HOST_HEADER);
}

View File

@ -68,3 +68,10 @@ bool mxs_admin_init();
* @brief Shutdown the administrative interface
*/
void mxs_admin_shutdown();
/**
* @brief Check if admin interface uses HTTPS protocol
*
* @return True if HTTPS is enabled
*/
bool mxs_admin_https_enabled();