summaryrefslogtreecommitdiffstats
path: root/web
diff options
context:
space:
mode:
authorCosta Tsaousis <costa@netdata.cloud>2023-07-11 02:28:06 +0300
committerGitHub <noreply@github.com>2023-07-11 02:28:06 +0300
commit77076d876407abc23ee7162a7fde33866c201be7 (patch)
treecc33b899c8c1d7e10caeefe561556027c22ceca8 /web
parent5943203a66b6b98e9619c8c2825aad517b209b1c (diff)
bearer improvements (#15342)
Diffstat (limited to 'web')
-rw-r--r--web/api/formatters/json_wrapper.c2
-rw-r--r--web/api/queries/weights.c4
-rw-r--r--web/api/web_api.c2
-rw-r--r--web/api/web_api_v2.c93
-rw-r--r--web/server/web_client.c8
-rw-r--r--web/server/web_client.h1
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);