diff options
Diffstat (limited to 'src/web_client.c')
-rw-r--r-- | src/web_client.c | 155 |
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 |