summaryrefslogtreecommitdiffstats
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
parent5943203a66b6b98e9619c8c2825aad517b209b1c (diff)
bearer improvements (#15342)
-rw-r--r--aclk/aclk_capas.c2
-rw-r--r--claim/claim.c1
-rw-r--r--daemon/common.c8
-rw-r--r--database/contexts/api_v2.c21
-rw-r--r--database/contexts/rrdcontext.h2
-rw-r--r--registry/registry.c13
-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
12 files changed, 142 insertions, 15 deletions
diff --git a/aclk/aclk_capas.c b/aclk/aclk_capas.c
index a81116faf6..e8d85a2b9f 100644
--- a/aclk/aclk_capas.c
+++ b/aclk/aclk_capas.c
@@ -4,7 +4,7 @@
#include "ml/ml.h"
-#define HTTP_API_V2_VERSION 5
+#define HTTP_API_V2_VERSION 6
const struct capability *aclk_get_agent_capas()
{
diff --git a/claim/claim.c b/claim/claim.c
index 6a9f75ef45..825f27bd5b 100644
--- a/claim/claim.c
+++ b/claim/claim.c
@@ -458,6 +458,7 @@ int api_v2_claim(struct web_client *w, char *url) {
if(can_be_claimed)
buffer_json_member_add_string(wb, "key_filename", netdata_random_session_id_get_filename());
+ buffer_json_agents_v2(wb, NULL, now_s, false, false);
buffer_json_finalize(wb);
return HTTP_RESP_OK;
diff --git a/daemon/common.c b/daemon/common.c
index 1fa95d0abe..d441c73b67 100644
--- a/daemon/common.c
+++ b/daemon/common.c
@@ -182,8 +182,14 @@ CLOUD_STATUS buffer_json_cloud_status(BUFFER *wb, time_t now_s) {
buffer_json_member_add_time_t(wb, "next_in", next_connect - now_s);
}
- if (status != CLOUD_STATUS_DISABLED && cloud_base_url())
+ if (cloud_base_url())
buffer_json_member_add_string(wb, "url", cloud_base_url());
+
+ char *claim_id = get_agent_claimid();
+ if(claim_id) {
+ buffer_json_member_add_string(wb, "claim_id", claim_id);
+ freez(claim_id);
+ }
}
buffer_json_object_close(wb); // cloud
diff --git a/database/contexts/api_v2.c b/database/contexts/api_v2.c
index 9c84a6fe9b..474b085047 100644
--- a/database/contexts/api_v2.c
+++ b/database/contexts/api_v2.c
@@ -956,17 +956,24 @@ void buffer_json_query_timings(BUFFER *wb, const char *key, struct query_timings
void build_info_to_json_object(BUFFER *b);
-void buffer_json_agents_array_v2(BUFFER *wb, struct query_timings *timings, time_t now_s, bool info) {
+void buffer_json_agents_v2(BUFFER *wb, struct query_timings *timings, time_t now_s, bool info, bool array) {
if(!now_s)
now_s = now_realtime_sec();
- buffer_json_member_add_array(wb, "agents");
- buffer_json_add_array_item_object(wb);
+ if(array) {
+ buffer_json_member_add_array(wb, "agents");
+ buffer_json_add_array_item_object(wb);
+ }
+ else
+ buffer_json_member_add_object(wb, "agent");
+
buffer_json_member_add_string(wb, "mg", localhost->machine_guid);
buffer_json_member_add_uuid(wb, "nd", localhost->node_id);
buffer_json_member_add_string(wb, "nm", rrdhost_hostname(localhost));
buffer_json_member_add_time_t(wb, "now", now_s);
- buffer_json_member_add_uint64(wb, "ai", 0);
+
+ if(array)
+ buffer_json_member_add_uint64(wb, "ai", 0);
if(info) {
buffer_json_member_add_object(wb, "application");
@@ -1022,7 +1029,9 @@ void buffer_json_agents_array_v2(BUFFER *wb, struct query_timings *timings, time
buffer_json_query_timings(wb, "timings", timings);
buffer_json_object_close(wb);
- buffer_json_array_close(wb);
+
+ if(array)
+ buffer_json_array_close(wb);
}
void buffer_json_cloud_timings(BUFFER *wb, const char *key, struct query_timings *timings) {
@@ -2050,7 +2059,7 @@ int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTE
version_hashes_api_v2(wb, &ctl.versions);
if (mode & CONTEXTS_V2_AGENTS)
- buffer_json_agents_array_v2(wb, &ctl.timings, ctl.now, mode & (CONTEXTS_V2_AGENTS_INFO));
+ buffer_json_agents_v2(wb, &ctl.timings, ctl.now, mode & (CONTEXTS_V2_AGENTS_INFO), true);
}
buffer_json_cloud_timings(wb, "timings", &ctl.timings);
diff --git a/database/contexts/rrdcontext.h b/database/contexts/rrdcontext.h
index 80e5f0afe3..585226df09 100644
--- a/database/contexts/rrdcontext.h
+++ b/database/contexts/rrdcontext.h
@@ -647,7 +647,7 @@ typedef enum __attribute__ ((__packed__)) {
int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTEXTS_V2_MODE mode);
RRDCONTEXT_TO_JSON_OPTIONS rrdcontext_to_json_parse_options(char *o);
-void buffer_json_agents_array_v2(BUFFER *wb, struct query_timings *timings, time_t now_s, bool info);
+void buffer_json_agents_v2(BUFFER *wb, struct query_timings *timings, time_t now_s, bool info, bool array);
void buffer_json_node_add_v2(BUFFER *wb, RRDHOST *host, size_t ni, usec_t duration_ut, bool status);
void buffer_json_query_timings(BUFFER *wb, const char *key, struct query_timings *timings);
void buffer_json_cloud_timings(BUFFER *wb, const char *key, struct query_timings *timings);
diff --git a/registry/registry.c b/registry/registry.c
index 04edb95c0d..378e5f8afd 100644
--- a/registry/registry.c
+++ b/registry/registry.c
@@ -163,6 +163,15 @@ void registry_update_cloud_base_url() {
int registry_request_hello_json(RRDHOST *host, struct web_client *w) {
registry_json_header(host, w, "hello", REGISTRY_STATUS_OK);
+ if(host->node_id)
+ buffer_json_member_add_uuid(w->response.data, "node_id", host->node_id);
+
+ char *claim_id = get_agent_claimid();
+ if(claim_id) {
+ buffer_json_member_add_string(w->response.data, "claim_id", claim_id);
+ freez(claim_id);
+ }
+
buffer_json_member_add_string(w->response.data, "registry", registry.registry_to_announce);
buffer_json_member_add_string(w->response.data, "cloud_base_url", registry.cloud_base_url);
buffer_json_member_add_boolean(w->response.data, "anonymous_statistics", netdata_anonymous_statistics_enabled);
@@ -172,6 +181,10 @@ int registry_request_hello_json(RRDHOST *host, struct web_client *w) {
dfe_start_read(rrdhost_root_index, h) {
buffer_json_add_array_item_object(w->response.data);
buffer_json_member_add_string(w->response.data, "machine_guid", h->machine_guid);
+
+ if(h->node_id)
+ buffer_json_member_add_uuid(w->response.data, "node_id", h->node_id);
+
buffer_json_member_add_string(w->response.data, "hostname", rrdhost_registry_hostname(h));
buffer_json_object_close(w->response.data);
}
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);