diff --git a/registry/registry.c b/registry/registry.c index 0393389ea3..a2a8dd7ecb 100644 --- a/registry/registry.c +++ b/registry/registry.c @@ -192,6 +192,7 @@ int registry_request_hello_json(RRDHOST *host, struct web_client *w) { buffer_json_member_add_string(w->response.data, "registry", registry.registry_to_announce); buffer_json_member_add_boolean(w->response.data, "anonymous_statistics", netdata_anonymous_statistics_enabled); + buffer_json_member_add_boolean(w->response.data, "X-Netdata-Auth", true); buffer_json_member_add_array(w->response.data, "nodes"); RRDHOST *h; diff --git a/web/api/web_api.c b/web/api/web_api.c index 7a4704bd58..96ab003900 100644 --- a/web/api/web_api.c +++ b/web/api/web_api.c @@ -5,27 +5,27 @@ bool netdata_is_protected_by_bearer = false; // this is controlled by cloud, at the point the agent logs in - this should also be saved to /var/lib/netdata DICTIONARY *netdata_authorized_bearers = NULL; -static bool web_client_check_acl_and_bearer(struct web_client *w, WEB_CLIENT_ACL endpoint_acl) { +static short int web_client_check_acl_and_bearer(struct web_client *w, WEB_CLIENT_ACL endpoint_acl) { if(endpoint_acl == WEB_CLIENT_ACL_NONE || (endpoint_acl & WEB_CLIENT_ACL_NOCHECK)) // the endpoint is totally public - return true; + return HTTP_RESP_OK; bool acl_allows = w->acl & endpoint_acl; if(!acl_allows) // the channel we received the request from (w->acl) is not compatible with the endpoint - return false; + return HTTP_RESP_FORBIDDEN; if(!netdata_is_protected_by_bearer && !(endpoint_acl & WEB_CLIENT_ACL_BEARER_REQUIRED)) // bearer protection is not enabled and is not required by the endpoint - return true; + return HTTP_RESP_OK; if(!(endpoint_acl & (WEB_CLIENT_ACL_BEARER_REQUIRED|WEB_CLIENT_ACL_BEARER_OPTIONAL))) // endpoint does not require a bearer - return true; + return HTTP_RESP_OK; if((w->acl & (WEB_CLIENT_ACL_ACLK|WEB_CLIENT_ACL_WEBRTC))) // the request is coming from ACLK or WEBRTC (authorized already), - return true; + return HTTP_RESP_OK; // at this point we need a bearer to serve the request // either because: @@ -37,11 +37,11 @@ static bool web_client_check_acl_and_bearer(struct web_client *w, WEB_CLIENT_ACL BEARER_STATUS t = api_check_bearer_token(w); if(t == BEARER_STATUS_AVAILABLE_AND_VALIDATED) // we have a valid bearer on the request - return true; + return HTTP_RESP_OK; netdata_log_info("BEARER: bearer is required for request: code %d", t); - return false; + return HTTP_RESP_PRECOND_FAIL; } int web_client_api_request_vX(RRDHOST *host, struct web_client *w, char *url_path_endpoint, struct web_api_command *api_commands) { @@ -73,8 +73,19 @@ int web_client_api_request_vX(RRDHOST *host, struct web_client *w, char *url_pat return HTTP_RESP_BAD_REQUEST; } - if(unlikely(!web_client_check_acl_and_bearer(w, api_commands[i].acl))) - return web_client_permission_denied(w); + short int code = web_client_check_acl_and_bearer(w, api_commands[i].acl); + if(code != HTTP_RESP_OK) { + if(code == HTTP_RESP_FORBIDDEN) + return web_client_permission_denied(w); + + if(code == HTTP_RESP_PRECOND_FAIL) + return web_client_bearer_required(w); + + buffer_flush(w->response.data); + buffer_sprintf(w->response.data, "Failed with code %d", code); + w->response.code = code; + return code; + } char *query_string = (char *)buffer_tostring(w->url_query_string_decoded); diff --git a/web/api/web_api_v2.c b/web/api/web_api_v2.c index 5921db1682..af4b60ceb5 100644 --- a/web/api/web_api_v2.c +++ b/web/api/web_api_v2.c @@ -53,16 +53,27 @@ static time_t bearer_get_token(uuid_t *uuid) { } #define HTTP_REQUEST_AUTHORIZATION_BEARER "\r\nAuthorization: Bearer " +#define HTTP_REQUEST_X_NETDATA_AUTH_BEARER "\r\nX-Netdata-Auth: Bearer " BEARER_STATUS extract_bearer_token_from_request(struct web_client *w, char *dst, size_t dst_len) { const char *req = buffer_tostring(w->response.data); size_t req_len = buffer_strlen(w->response.data); - const char *bearer = strcasestr(req, HTTP_REQUEST_AUTHORIZATION_BEARER); + const char *bearer = NULL; + const char *bearer_end = NULL; - if(!bearer) + bearer = strcasestr(req, HTTP_REQUEST_X_NETDATA_AUTH_BEARER); + if(bearer) + bearer_end = bearer + sizeof(HTTP_REQUEST_X_NETDATA_AUTH_BEARER) - 1; + else { + bearer = strcasestr(req, HTTP_REQUEST_AUTHORIZATION_BEARER); + if(bearer) + bearer_end = bearer + sizeof(HTTP_REQUEST_AUTHORIZATION_BEARER) - 1; + } + + if(!bearer || !bearer_end) return BEARER_STATUS_NO_BEARER_IN_HEADERS; - const char *token_start = bearer + sizeof(HTTP_REQUEST_AUTHORIZATION_BEARER) - 1; + const char *token_start = bearer_end; while(isspace(*token_start)) token_start++; diff --git a/web/server/web_client.c b/web/server/web_client.c index 92d97e8a80..456de68928 100644 --- a/web/server/web_client.c +++ b/web/server/web_client.c @@ -22,8 +22,8 @@ inline int web_client_bearer_required(struct web_client *w) { w->response.data->content_type = CT_TEXT_PLAIN; buffer_flush(w->response.data); buffer_strcat(w->response.data, "An authorization bearer is required to access the resource."); - w->response.code = HTTP_RESP_UNAUTHORIZED; - return HTTP_RESP_UNAUTHORIZED; + w->response.code = HTTP_RESP_PRECOND_FAIL; + return HTTP_RESP_PRECOND_FAIL; } static inline int bad_request_multiple_dashboard_versions(struct web_client *w) {