diff options
author | Costa Tsaousis <costa@netdata.cloud> | 2023-07-11 02:28:06 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-07-11 02:28:06 +0300 |
commit | 77076d876407abc23ee7162a7fde33866c201be7 (patch) | |
tree | cc33b899c8c1d7e10caeefe561556027c22ceca8 /web | |
parent | 5943203a66b6b98e9619c8c2825aad517b209b1c (diff) |
bearer improvements (#15342)
Diffstat (limited to 'web')
-rw-r--r-- | web/api/formatters/json_wrapper.c | 2 | ||||
-rw-r--r-- | web/api/queries/weights.c | 4 | ||||
-rw-r--r-- | web/api/web_api.c | 2 | ||||
-rw-r--r-- | web/api/web_api_v2.c | 93 | ||||
-rw-r--r-- | web/server/web_client.c | 8 | ||||
-rw-r--r-- | web/server/web_client.h | 1 |
6 files changed, 104 insertions, 6 deletions
diff --git a/web/api/formatters/json_wrapper.c b/web/api/formatters/json_wrapper.c index f9d9dac6f0..6a66cbcca6 100644 --- a/web/api/formatters/json_wrapper.c +++ b/web/api/formatters/json_wrapper.c @@ -1570,7 +1570,7 @@ void rrdr_json_wrapper_end2(RRDR *r, BUFFER *wb) { } buffer_json_object_close(wb); // view - buffer_json_agents_array_v2(wb, &r->internal.qt->timings, 0, false); + buffer_json_agents_v2(wb, &r->internal.qt->timings, 0, false, true); buffer_json_cloud_timings(wb, "timings", &r->internal.qt->timings); buffer_json_finalize(wb); } diff --git a/web/api/queries/weights.c b/web/api/queries/weights.c index e569ce134c..8ffd8951af 100644 --- a/web/api/queries/weights.c +++ b/web/api/queries/weights.c @@ -936,7 +936,7 @@ static size_t registered_results_to_json_multinode_no_group_by( buffer_json_object_close(wb); //dictionaries - buffer_json_agents_array_v2(wb, &qwd->timings, 0, false); + buffer_json_agents_v2(wb, &qwd->timings, 0, false, true); buffer_json_member_add_uint64(wb, "correlated_dimensions", total_dimensions); buffer_json_member_add_uint64(wb, "total_dimensions_count", examined_dimensions); buffer_json_finalize(wb); @@ -1067,7 +1067,7 @@ static size_t registered_results_to_json_multinode_group_by( dfe_done(aw); buffer_json_array_close(wb); // result - buffer_json_agents_array_v2(wb, &qwd->timings, 0, false); + buffer_json_agents_v2(wb, &qwd->timings, 0, false, true); buffer_json_member_add_uint64(wb, "correlated_dimensions", total_dimensions); buffer_json_member_add_uint64(wb, "total_dimensions_count", examined_dimensions); buffer_json_finalize(wb); diff --git a/web/api/web_api.c b/web/api/web_api.c index ad69c0b490..4372bb8cbd 100644 --- a/web/api/web_api.c +++ b/web/api/web_api.c @@ -43,7 +43,7 @@ int web_client_api_request_vX(RRDHOST *host, struct web_client *w, char *url_pat for(int i = 0; api_commands[i].command ; i++) { if(unlikely(hash == api_commands[i].hash && !strcmp(url_path_endpoint, api_commands[i].command))) { if(unlikely(!web_client_check_acl_and_bearer(w, api_commands[i].acl))) - return web_client_permission_denied(w); + return web_client_bearer_required(w); 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 665f234ec4..c4727f75d9 100644 --- a/web/api/web_api_v2.c +++ b/web/api/web_api_v2.c @@ -8,6 +8,24 @@ struct bearer_token { time_t expires_s; }; +static void bearer_token_cleanup(void) { + static time_t attempts = 0; + + if(++attempts % 1000 != 0) + return; + + time_t now_s = now_monotonic_sec(); + + struct bearer_token *z; + dfe_start_read(netdata_authorized_bearers, z) { + if(z->expires_s < now_s) + dictionary_del(netdata_authorized_bearers, z_dfe.name); + } + dfe_done(z); + + dictionary_garbage_collect(netdata_authorized_bearers); +} + static void bearer_get_token(uuid_t *uuid) { static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER; static bool initialized = false; @@ -34,6 +52,8 @@ static void bearer_get_token(uuid_t *uuid) { z->created_s = now_monotonic_sec(); z->expires_s = z->created_s + 86400; } + + bearer_token_cleanup(); } #define HTTP_REQUEST_AUTHORIZATION_BEARER "\r\nAuthorization: Bearer " @@ -75,7 +95,36 @@ bool api_check_bearer_token(struct web_client *w) { return z && z->expires_s > now_monotonic_sec(); } +static bool verify_agent_uuids(const char *machine_guid, const char *node_id, const char *claim_id) { + if(!machine_guid || !node_id || !claim_id) + return false; + + if(strcmp(machine_guid, localhost->machine_guid) != 0) + return false; + + char *agent_claim_id = get_agent_claimid(); + if(!agent_claim_id || strcmp(claim_id, agent_claim_id) != 0) + return false; + freez(agent_claim_id); + + if(!localhost->node_id) + return false; + + char buf[UUID_STR_LEN]; + uuid_unparse_lower(*localhost->node_id, buf); + + if(strcmp(node_id, buf) != 0) + return false; + + return true; +} + int api_v2_bearer_protection(RRDHOST *host __maybe_unused, struct web_client *w __maybe_unused, char *url) { + char *machine_guid = NULL; + char *claim_id = NULL; + char *node_id = NULL; + bool protection = netdata_is_protected_by_bearer; + while (url) { char *value = strsep_skip_consecutive_separators(&url, "&"); if (!value || !*value) continue; @@ -86,12 +135,26 @@ int api_v2_bearer_protection(RRDHOST *host __maybe_unused, struct web_client *w if(!strcmp(name, "bearer_protection")) { if(!strcmp(value, "on") || !strcmp(value, "true") || !strcmp(value, "yes")) - netdata_is_protected_by_bearer = true; + protection = true; else - netdata_is_protected_by_bearer = false; + protection = false; } + else if(!strcmp(name, "machine_guid")) + machine_guid = value; + else if(!strcmp(name, "claim_id")) + claim_id = value; + else if(!strcmp(name, "node_id")) + node_id = value; + } + + if(!verify_agent_uuids(machine_guid, node_id, claim_id)) { + buffer_flush(w->response.data); + buffer_strcat(w->response.data, "The request is missing or not matching local UUIDs"); + return HTTP_RESP_BAD_REQUEST; } + netdata_is_protected_by_bearer = protection; + BUFFER *wb = w->response.data; buffer_flush(wb); buffer_json_initialize(wb, "\"", "\"", 0, true, false); @@ -102,6 +165,32 @@ int api_v2_bearer_protection(RRDHOST *host __maybe_unused, struct web_client *w } int api_v2_bearer_token(RRDHOST *host __maybe_unused, struct web_client *w __maybe_unused, char *url __maybe_unused) { + char *machine_guid = NULL; + char *claim_id = NULL; + char *node_id = NULL; + + while(url) { + char *value = strsep_skip_consecutive_separators(&url, "&"); + if (!value || !*value) continue; + + char *name = strsep_skip_consecutive_separators(&value, "="); + if (!name || !*name) continue; + if (!value || !*value) continue; + + if(!strcmp(name, "machine_guid")) + machine_guid = value; + else if(!strcmp(name, "claim_id")) + claim_id = value; + else if(!strcmp(name, "node_id")) + node_id = value; + } + + if(!verify_agent_uuids(machine_guid, node_id, claim_id)) { + buffer_flush(w->response.data); + buffer_strcat(w->response.data, "The request is missing or not matching local UUIDs"); + return HTTP_RESP_BAD_REQUEST; + } + uuid_t uuid; bearer_get_token(&uuid); diff --git a/web/server/web_client.c b/web/server/web_client.c index 982bbd0164..ec075ad7ff 100644 --- a/web/server/web_client.c +++ b/web/server/web_client.c @@ -18,6 +18,14 @@ inline int web_client_permission_denied(struct web_client *w) { return HTTP_RESP_FORBIDDEN; } +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; +} + static inline int bad_request_multiple_dashboard_versions(struct web_client *w) { w->response.data->content_type = CT_TEXT_PLAIN; buffer_flush(w->response.data); diff --git a/web/server/web_client.h b/web/server/web_client.h index 7a74bffe42..68fcbfa31d 100644 --- a/web/server/web_client.h +++ b/web/server/web_client.h @@ -199,6 +199,7 @@ struct web_client { }; int web_client_permission_denied(struct web_client *w); +int web_client_bearer_required(struct web_client *w); ssize_t web_client_send(struct web_client *w); ssize_t web_client_receive(struct web_client *w); |