summaryrefslogtreecommitdiffstats
path: root/src/web_client.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/web_client.c')
-rw-r--r--src/web_client.c155
1 files changed, 134 insertions, 21 deletions
diff --git a/src/web_client.c b/src/web_client.c
index 6ec3e11e3..e17bac922 100644
--- a/src/web_client.c
+++ b/src/web_client.c
@@ -8,6 +8,15 @@ int web_client_timeout = DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS;
int respect_web_browser_do_not_track_policy = 0;
char *web_x_frame_options = NULL;
+SIMPLE_PATTERN *web_allow_connections_from = NULL;
+SIMPLE_PATTERN *web_allow_streaming_from = NULL;
+SIMPLE_PATTERN *web_allow_netdataconf_from = NULL;
+
+// WEB_CLIENT_ACL
+SIMPLE_PATTERN *web_allow_dashboard_from = NULL;
+SIMPLE_PATTERN *web_allow_registry_from = NULL;
+SIMPLE_PATTERN *web_allow_badges_from = NULL;
+
#ifdef NETDATA_WITH_ZLIB
int web_enable_gzip = 1, web_gzip_level = 3, web_gzip_strategy = Z_DEFAULT_STRATEGY;
#endif /* NETDATA_WITH_ZLIB */
@@ -50,6 +59,31 @@ static inline int web_client_uncrock_socket(struct web_client *w) {
return 0;
}
+inline int web_client_permission_denied(struct web_client *w) {
+ w->response.data->contenttype = CT_TEXT_PLAIN;
+ buffer_flush(w->response.data);
+ buffer_strcat(w->response.data, "You are not allowed to access this resource.");
+ w->response.code = 403;
+ return 403;
+}
+
+static void log_connection(struct web_client *w, const char *msg) {
+ log_access("%llu: %d '[%s]:%s' '%s'", w->id, gettid(), w->client_ip, w->client_port, msg);
+}
+
+static void web_client_update_acl_matches(struct web_client *w) {
+ w->acl = WEB_CLIENT_ACL_NONE;
+
+ if(!web_allow_dashboard_from || simple_pattern_matches(web_allow_dashboard_from, w->client_ip))
+ w->acl |= WEB_CLIENT_ACL_DASHBOARD;
+
+ if(!web_allow_registry_from || simple_pattern_matches(web_allow_registry_from, w->client_ip))
+ w->acl |= WEB_CLIENT_ACL_REGISTRY;
+
+ if(!web_allow_badges_from || simple_pattern_matches(web_allow_badges_from, w->client_ip))
+ w->acl |= WEB_CLIENT_ACL_BADGE;
+}
+
struct web_client *web_client_create(int listener) {
struct web_client *w;
@@ -58,12 +92,25 @@ struct web_client *web_client_create(int listener) {
w->mode = WEB_CLIENT_MODE_NORMAL;
{
- w->ifd = accept_socket(listener, SOCK_NONBLOCK, w->client_ip, sizeof(w->client_ip), w->client_port, sizeof(w->client_port));
+ w->ifd = accept_socket(listener, SOCK_NONBLOCK, w->client_ip, sizeof(w->client_ip), w->client_port, sizeof(w->client_port), web_allow_connections_from);
+
+ if(unlikely(!*w->client_ip)) strcpy(w->client_ip, "-");
+ if(unlikely(!*w->client_port)) strcpy(w->client_port, "-");
+
if (w->ifd == -1) {
- error("%llu: Cannot accept new incoming connection.", w->id);
+ if(errno == EPERM)
+ log_connection(w, "ACCESS DENIED");
+ else {
+ log_connection(w, "CONNECTION FAILED");
+ error("%llu: Failed to accept new incoming connection.", w->id);
+ }
+
freez(w);
return NULL;
}
+ else
+ log_connection(w, "CONNECTED");
+
w->ofd = w->ifd;
int flag = 1;
@@ -75,6 +122,8 @@ struct web_client *web_client_create(int listener) {
error("%llu: Cannot set SO_KEEPALIVE on socket.", w->id);
}
+ web_client_update_acl_matches(w);
+
w->response.data = buffer_create(INITIAL_WEB_DATA_LENGTH);
w->response.header = buffer_create(HTTP_RESPONSE_HEADER_SIZE);
w->response.header_output = buffer_create(HTTP_RESPONSE_HEADER_SIZE);
@@ -119,18 +168,45 @@ void web_client_reset(struct web_client *w) {
// --------------------------------------------------------------------
- // access log
- log_access("%llu: (sent/all = %zu/%zu bytes %0.0f%%, prep/sent/total = %0.2f/%0.2f/%0.2f ms) %s: %d '%s'",
- w->id,
- sent, size, -((size > 0) ? ((size - sent) / (double) size * 100.0) : 0.0),
- dt_usec(&w->tv_ready, &w->tv_in) / 1000.0,
- dt_usec(&tv, &w->tv_ready) / 1000.0,
- dt_usec(&tv, &w->tv_in) / 1000.0,
- (w->mode == WEB_CLIENT_MODE_FILECOPY) ? "filecopy" : ((w->mode == WEB_CLIENT_MODE_OPTIONS)
- ? "options" : "data"),
- w->response.code,
- w->last_url
+ const char *mode;
+ switch(w->mode) {
+ case WEB_CLIENT_MODE_FILECOPY:
+ mode = "FILECOPY";
+ break;
+
+ case WEB_CLIENT_MODE_OPTIONS:
+ mode = "OPTIONS";
+ break;
+
+ case WEB_CLIENT_MODE_STREAM:
+ mode = "STREAM";
+ break;
+
+ case WEB_CLIENT_MODE_NORMAL:
+ mode = "DATA";
+ break;
+
+ default:
+ mode = "UNKNOWN";
+ break;
+ }
+
+ // access log
+ log_access("%llu: %d '[%s]:%s' '%s' (sent/all = %zu/%zu bytes %0.0f%%, prep/sent/total = %0.2f/%0.2f/%0.2f ms) %d '%s'",
+ w->id
+ , gettid()
+ , w->client_ip
+ , w->client_port
+ , mode
+ , sent
+ , size
+ , -((size > 0) ? ((size - sent) / (double) size * 100.0) : 0.0)
+ , dt_usec(&w->tv_ready, &w->tv_in) / 1000.0
+ , dt_usec(&tv, &w->tv_ready) / 1000.0
+ , dt_usec(&tv, &w->tv_in) / 1000.0
+ , w->response.code
+ , w->last_url
);
}
@@ -271,6 +347,9 @@ gid_t web_files_gid(void) {
int mysendfile(struct web_client *w, char *filename) {
debug(D_WEB_CLIENT, "%llu: Looking for file '%s/%s'", w->id, netdata_configured_web_dir, filename);
+ if(!web_client_can_access_dashboard(w))
+ return web_client_permission_denied(w);
+
// skip leading slashes
while (*filename == '/') filename++;
@@ -553,6 +632,13 @@ static inline int check_host_and_call(RRDHOST *host, struct web_client *w, char
return func(host, w, url);
}
+static inline int check_host_and_dashboard_acl_and_call(RRDHOST *host, struct web_client *w, char *url, int (*func)(RRDHOST *, struct web_client *, char *)) {
+ if(!web_client_can_access_dashboard(w))
+ return web_client_permission_denied(w);
+
+ return check_host_and_call(host, w, url, func);
+}
+
int web_client_api_request(RRDHOST *host, struct web_client *w, char *url)
{
// get the api version
@@ -1082,25 +1168,28 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
}
else if(unlikely(hash == hash_data && strcmp(tok, WEB_PATH_DATA) == 0)) { // old API "data"
debug(D_WEB_CLIENT_ACCESS, "%llu: old API data request...", w->id);
- return check_host_and_call(host, w, url, web_client_api_old_data_request_json);
+ return check_host_and_dashboard_acl_and_call(host, w, url, web_client_api_old_data_request_json);
}
else if(unlikely(hash == hash_datasource && strcmp(tok, WEB_PATH_DATASOURCE) == 0)) { // old API "datasource"
debug(D_WEB_CLIENT_ACCESS, "%llu: old API datasource request...", w->id);
- return check_host_and_call(host, w, url, web_client_api_old_data_request_jsonp);
+ return check_host_and_dashboard_acl_and_call(host, w, url, web_client_api_old_data_request_jsonp);
}
else if(unlikely(hash == hash_graph && strcmp(tok, WEB_PATH_GRAPH) == 0)) { // old API "graph"
debug(D_WEB_CLIENT_ACCESS, "%llu: old API graph request...", w->id);
- return check_host_and_call(host, w, url, web_client_api_old_graph_request);
+ return check_host_and_dashboard_acl_and_call(host, w, url, web_client_api_old_graph_request);
}
else if(unlikely(hash == hash_list && strcmp(tok, "list") == 0)) { // old API "list"
debug(D_WEB_CLIENT_ACCESS, "%llu: old API list request...", w->id);
- return check_host_and_call(host, w, url, web_client_api_old_list_request);
+ return check_host_and_dashboard_acl_and_call(host, w, url, web_client_api_old_list_request);
}
else if(unlikely(hash == hash_all_json && strcmp(tok, "all.json") == 0)) { // old API "all.json"
debug(D_WEB_CLIENT_ACCESS, "%llu: old API all.json request...", w->id);
- return check_host_and_call(host, w, url, web_client_api_old_all_json);
+ return check_host_and_dashboard_acl_and_call(host, w, url, web_client_api_old_all_json);
}
else if(unlikely(hash == hash_netdata_conf && strcmp(tok, "netdata.conf") == 0)) { // netdata.conf
+ if(unlikely(!web_client_can_access_netdataconf(w)))
+ return web_client_permission_denied(w);
+
debug(D_WEB_CLIENT_ACCESS, "%llu: generating netdata.conf ...", w->id);
w->response.data->contenttype = CT_TEXT_PLAIN;
buffer_flush(w->response.data);
@@ -1109,6 +1198,9 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
}
#ifdef NETDATA_INTERNAL_CHECKS
else if(unlikely(hash == hash_exit && strcmp(tok, "exit") == 0)) {
+ if(unlikely(!web_client_can_access_netdataconf(w)))
+ return web_client_permission_denied(w);
+
w->response.data->contenttype = CT_TEXT_PLAIN;
buffer_flush(w->response.data);
@@ -1122,6 +1214,9 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
return 200;
}
else if(unlikely(hash == hash_debug && strcmp(tok, "debug") == 0)) {
+ if(unlikely(!web_client_can_access_netdataconf(w)))
+ return web_client_permission_denied(w);
+
buffer_flush(w->response.data);
// get the name of the data to show
@@ -1159,6 +1254,9 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
return 400;
}
else if(unlikely(hash == hash_mirror && strcmp(tok, "mirror") == 0)) {
+ if(unlikely(!web_client_can_access_netdataconf(w)))
+ return web_client_permission_denied(w);
+
debug(D_WEB_CLIENT_ACCESS, "%llu: Mirroring...", w->id);
// replace the zero bytes with spaces
@@ -1189,10 +1287,20 @@ void web_client_process_request(struct web_client *w) {
case HTTP_VALIDATION_OK:
switch(w->mode) {
case WEB_CLIENT_MODE_STREAM:
+ if(unlikely(!web_client_can_access_stream(w))) {
+ web_client_permission_denied(w);
+ return;
+ }
+
w->response.code = rrdpush_receiver_thread_spawn(localhost, w, w->decoded_url);
return;
case WEB_CLIENT_MODE_OPTIONS:
+ if(unlikely(!web_client_can_access_dashboard(w) && !web_client_can_access_registry(w) && !web_client_can_access_badges(w))) {
+ web_client_permission_denied(w);
+ return;
+ }
+
w->response.data->contenttype = CT_TEXT_PLAIN;
buffer_flush(w->response.data);
buffer_strcat(w->response.data, "OK");
@@ -1201,6 +1309,11 @@ void web_client_process_request(struct web_client *w) {
case WEB_CLIENT_MODE_FILECOPY:
case WEB_CLIENT_MODE_NORMAL:
+ if(unlikely(!web_client_can_access_dashboard(w) && !web_client_can_access_registry(w) && !web_client_can_access_badges(w))) {
+ web_client_permission_denied(w);
+ return;
+ }
+
w->response.code = web_client_process_url(localhost, w, w->decoded_url);
break;
}
@@ -1607,8 +1720,6 @@ void *web_client_main(void *ptr)
int retval, timeout;
nfds_t fdmax = 0;
- log_access("%llu: %s port %s connected on thread task id %d", w->id, w->client_ip, w->client_port, gettid());
-
for(;;) {
if(unlikely(netdata_exit)) break;
@@ -1716,9 +1827,11 @@ void *web_client_main(void *ptr)
}
}
+ if(w->mode != WEB_CLIENT_MODE_STREAM)
+ log_connection(w, "DISCONNECTED");
+
web_client_reset(w);
- log_access("%llu: %s port %s disconnected from thread task id %d", w->id, w->client_ip, w->client_port, gettid());
debug(D_WEB_CLIENT, "%llu: done...", w->id);
// close the sockets/files now