@cryptotaxi247 / netdata-1 / commits / 77076d876

bearer improvements (#15342)

Costa Tsaousis committed Jul 11, 2023 at 02:28 UTC 77076d876407abc23ee7162a7fde33866c201be7
12 files changed +142 -15
aclk/aclk_capas.c
+1 -1
@@ -4,7 +4,7 @@
4
5 #include "ml/ml.h"
6
7 -#define HTTP_API_V2_VERSION 5
7 +#define HTTP_API_V2_VERSION 6
8
9 const struct capability *aclk_get_agent_capas()
10 {
claim/claim.c
+1
@@ -458,6 +458,7 @@ int api_v2_claim(struct web_client *w, char *url) {
458 if(can_be_claimed)
459 buffer_json_member_add_string(wb, "key_filename", netdata_random_session_id_get_filename());
460
461 + buffer_json_agents_v2(wb, NULL, now_s, false, false);
462 buffer_json_finalize(wb);
463
464 return HTTP_RESP_OK;
daemon/common.c
+7 -1
@@ -182,8 +182,14 @@ CLOUD_STATUS buffer_json_cloud_status(BUFFER *wb, time_t now_s) {
182 buffer_json_member_add_time_t(wb, "next_in", next_connect - now_s);
183 }
184
185 - if (status != CLOUD_STATUS_DISABLED && cloud_base_url())
185 + if (cloud_base_url())
186 buffer_json_member_add_string(wb, "url", cloud_base_url());
187 +
188 + char *claim_id = get_agent_claimid();
189 + if(claim_id) {
190 + buffer_json_member_add_string(wb, "claim_id", claim_id);
191 + freez(claim_id);
192 + }
193 }
194 buffer_json_object_close(wb); // cloud
195
database/contexts/api_v2.c
+15 -6
@@ -956,17 +956,24 @@ void buffer_json_query_timings(BUFFER *wb, const char *key, struct query_timings
956
957 void build_info_to_json_object(BUFFER *b);
958
959 -void buffer_json_agents_array_v2(BUFFER *wb, struct query_timings *timings, time_t now_s, bool info) {
959 +void buffer_json_agents_v2(BUFFER *wb, struct query_timings *timings, time_t now_s, bool info, bool array) {
960 if(!now_s)
961 now_s = now_realtime_sec();
962
963 - buffer_json_member_add_array(wb, "agents");
964 - buffer_json_add_array_item_object(wb);
963 + if(array) {
964 + buffer_json_member_add_array(wb, "agents");
965 + buffer_json_add_array_item_object(wb);
966 + }
967 + else
968 + buffer_json_member_add_object(wb, "agent");
969 +
970 buffer_json_member_add_string(wb, "mg", localhost->machine_guid);
971 buffer_json_member_add_uuid(wb, "nd", localhost->node_id);
972 buffer_json_member_add_string(wb, "nm", rrdhost_hostname(localhost));
973 buffer_json_member_add_time_t(wb, "now", now_s);
969 - buffer_json_member_add_uint64(wb, "ai", 0);
974 +
975 + if(array)
976 + buffer_json_member_add_uint64(wb, "ai", 0);
977
978 if(info) {
979 buffer_json_member_add_object(wb, "application");
@@ -1022,7 +1029,9 @@ void buffer_json_agents_array_v2(BUFFER *wb, struct query_timings *timings, time
1029 buffer_json_query_timings(wb, "timings", timings);
1030
1031 buffer_json_object_close(wb);
1025 - buffer_json_array_close(wb);
1032 +
1033 + if(array)
1034 + buffer_json_array_close(wb);
1035 }
1036
1037 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
2059 version_hashes_api_v2(wb, &ctl.versions);
2060
2061 if (mode & CONTEXTS_V2_AGENTS)
2053 - buffer_json_agents_array_v2(wb, &ctl.timings, ctl.now, mode & (CONTEXTS_V2_AGENTS_INFO));
2062 + buffer_json_agents_v2(wb, &ctl.timings, ctl.now, mode & (CONTEXTS_V2_AGENTS_INFO), true);
2063 }
2064
2065 buffer_json_cloud_timings(wb, "timings", &ctl.timings);
database/contexts/rrdcontext.h
+1 -1
@@ -647,7 +647,7 @@ typedef enum __attribute__ ((__packed__)) {
647 int rrdcontext_to_json_v2(BUFFER *wb, struct api_v2_contexts_request *req, CONTEXTS_V2_MODE mode);
648
649 RRDCONTEXT_TO_JSON_OPTIONS rrdcontext_to_json_parse_options(char *o);
650 -void buffer_json_agents_array_v2(BUFFER *wb, struct query_timings *timings, time_t now_s, bool info);
650 +void buffer_json_agents_v2(BUFFER *wb, struct query_timings *timings, time_t now_s, bool info, bool array);
651 void buffer_json_node_add_v2(BUFFER *wb, RRDHOST *host, size_t ni, usec_t duration_ut, bool status);
652 void buffer_json_query_timings(BUFFER *wb, const char *key, struct query_timings *timings);
653 void buffer_json_cloud_timings(BUFFER *wb, const char *key, struct query_timings *timings);
registry/registry.c
+13
@@ -163,6 +163,15 @@ void registry_update_cloud_base_url() {
163 int registry_request_hello_json(RRDHOST *host, struct web_client *w) {
164 registry_json_header(host, w, "hello", REGISTRY_STATUS_OK);
165
166 + if(host->node_id)
167 + buffer_json_member_add_uuid(w->response.data, "node_id", host->node_id);
168 +
169 + char *claim_id = get_agent_claimid();
170 + if(claim_id) {
171 + buffer_json_member_add_string(w->response.data, "claim_id", claim_id);
172 + freez(claim_id);
173 + }
174 +
175 buffer_json_member_add_string(w->response.data, "registry", registry.registry_to_announce);
176 buffer_json_member_add_string(w->response.data, "cloud_base_url", registry.cloud_base_url);
177 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) {
181 dfe_start_read(rrdhost_root_index, h) {
182 buffer_json_add_array_item_object(w->response.data);
183 buffer_json_member_add_string(w->response.data, "machine_guid", h->machine_guid);
184 +
185 + if(h->node_id)
186 + buffer_json_member_add_uuid(w->response.data, "node_id", h->node_id);
187 +
188 buffer_json_member_add_string(w->response.data, "hostname", rrdhost_registry_hostname(h));
189 buffer_json_object_close(w->response.data);
190 }
web/api/formatters/json_wrapper.c
+1 -1
@@ -1570,7 +1570,7 @@ void rrdr_json_wrapper_end2(RRDR *r, BUFFER *wb) {
1570 }
1571 buffer_json_object_close(wb); // view
1572
1573 - buffer_json_agents_array_v2(wb, &r->internal.qt->timings, 0, false);
1573 + buffer_json_agents_v2(wb, &r->internal.qt->timings, 0, false, true);
1574 buffer_json_cloud_timings(wb, "timings", &r->internal.qt->timings);
1575 buffer_json_finalize(wb);
1576 }
web/api/queries/weights.c
+2 -2
@@ -936,7 +936,7 @@ static size_t registered_results_to_json_multinode_no_group_by(
936
937 buffer_json_object_close(wb); //dictionaries
938
939 - buffer_json_agents_array_v2(wb, &qwd->timings, 0, false);
939 + buffer_json_agents_v2(wb, &qwd->timings, 0, false, true);
940 buffer_json_member_add_uint64(wb, "correlated_dimensions", total_dimensions);
941 buffer_json_member_add_uint64(wb, "total_dimensions_count", examined_dimensions);
942 buffer_json_finalize(wb);
@@ -1067,7 +1067,7 @@ static size_t registered_results_to_json_multinode_group_by(
1067 dfe_done(aw);
1068 buffer_json_array_close(wb); // result
1069
1070 - buffer_json_agents_array_v2(wb, &qwd->timings, 0, false);
1070 + buffer_json_agents_v2(wb, &qwd->timings, 0, false, true);
1071 buffer_json_member_add_uint64(wb, "correlated_dimensions", total_dimensions);
1072 buffer_json_member_add_uint64(wb, "total_dimensions_count", examined_dimensions);
1073 buffer_json_finalize(wb);
web/api/web_api.c
+1 -1
@@ -43,7 +43,7 @@ int web_client_api_request_vX(RRDHOST *host, struct web_client *w, char *url_pat
43 for(int i = 0; api_commands[i].command ; i++) {
44 if(unlikely(hash == api_commands[i].hash && !strcmp(url_path_endpoint, api_commands[i].command))) {
45 if(unlikely(!web_client_check_acl_and_bearer(w, api_commands[i].acl)))
46 - return web_client_permission_denied(w);
46 + return web_client_bearer_required(w);
47
48 char *query_string = (char *)buffer_tostring(w->url_query_string_decoded);
49
web/api/web_api_v2.c
+91 -2
@@ -8,6 +8,24 @@ struct bearer_token {
8 time_t expires_s;
9 };
10
11 +static void bearer_token_cleanup(void) {
12 + static time_t attempts = 0;
13 +
14 + if(++attempts % 1000 != 0)
15 + return;
16 +
17 + time_t now_s = now_monotonic_sec();
18 +
19 + struct bearer_token *z;
20 + dfe_start_read(netdata_authorized_bearers, z) {
21 + if(z->expires_s < now_s)
22 + dictionary_del(netdata_authorized_bearers, z_dfe.name);
23 + }
24 + dfe_done(z);
25 +
26 + dictionary_garbage_collect(netdata_authorized_bearers);
27 +}
28 +
29 static void bearer_get_token(uuid_t *uuid) {
30 static SPINLOCK spinlock = NETDATA_SPINLOCK_INITIALIZER;
31 static bool initialized = false;
@@ -34,6 +52,8 @@ static void bearer_get_token(uuid_t *uuid) {
52 z->created_s = now_monotonic_sec();
53 z->expires_s = z->created_s + 86400;
54 }
55 +
56 + bearer_token_cleanup();
57 }
58
59 #define HTTP_REQUEST_AUTHORIZATION_BEARER "\r\nAuthorization: Bearer "
@@ -75,7 +95,36 @@ bool api_check_bearer_token(struct web_client *w) {
95 return z && z->expires_s > now_monotonic_sec();
96 }
97
98 +static bool verify_agent_uuids(const char *machine_guid, const char *node_id, const char *claim_id) {
99 + if(!machine_guid || !node_id || !claim_id)
100 + return false;
101 +
102 + if(strcmp(machine_guid, localhost->machine_guid) != 0)
103 + return false;
104 +
105 + char *agent_claim_id = get_agent_claimid();
106 + if(!agent_claim_id || strcmp(claim_id, agent_claim_id) != 0)
107 + return false;
108 + freez(agent_claim_id);
109 +
110 + if(!localhost->node_id)
111 + return false;
112 +
113 + char buf[UUID_STR_LEN];
114 + uuid_unparse_lower(*localhost->node_id, buf);
115 +
116 + if(strcmp(node_id, buf) != 0)
117 + return false;
118 +
119 + return true;
120 +}
121 +
122 int api_v2_bearer_protection(RRDHOST *host __maybe_unused, struct web_client *w __maybe_unused, char *url) {
123 + char *machine_guid = NULL;
124 + char *claim_id = NULL;
125 + char *node_id = NULL;
126 + bool protection = netdata_is_protected_by_bearer;
127 +
128 while (url) {
129 char *value = strsep_skip_consecutive_separators(&url, "&");
130 if (!value || !*value) continue;
@@ -86,12 +135,26 @@ int api_v2_bearer_protection(RRDHOST *host __maybe_unused, struct web_client *w
135
136 if(!strcmp(name, "bearer_protection")) {
137 if(!strcmp(value, "on") || !strcmp(value, "true") || !strcmp(value, "yes"))
89 - netdata_is_protected_by_bearer = true;
138 + protection = true;
139 else
91 - netdata_is_protected_by_bearer = false;
140 + protection = false;
141 }
142 + else if(!strcmp(name, "machine_guid"))
143 + machine_guid = value;
144 + else if(!strcmp(name, "claim_id"))
145 + claim_id = value;
146 + else if(!strcmp(name, "node_id"))
147 + node_id = value;
148 + }
149 +
150 + if(!verify_agent_uuids(machine_guid, node_id, claim_id)) {
151 + buffer_flush(w->response.data);
152 + buffer_strcat(w->response.data, "The request is missing or not matching local UUIDs");
153 + return HTTP_RESP_BAD_REQUEST;
154 }
155
156 + netdata_is_protected_by_bearer = protection;
157 +
158 BUFFER *wb = w->response.data;
159 buffer_flush(wb);
160 buffer_json_initialize(wb, "\"", "\"", 0, true, false);
@@ -102,6 +165,32 @@ int api_v2_bearer_protection(RRDHOST *host __maybe_unused, struct web_client *w
165 }
166
167 int api_v2_bearer_token(RRDHOST *host __maybe_unused, struct web_client *w __maybe_unused, char *url __maybe_unused) {
168 + char *machine_guid = NULL;
169 + char *claim_id = NULL;
170 + char *node_id = NULL;
171 +
172 + while(url) {
173 + char *value = strsep_skip_consecutive_separators(&url, "&");
174 + if (!value || !*value) continue;
175 +
176 + char *name = strsep_skip_consecutive_separators(&value, "=");
177 + if (!name || !*name) continue;
178 + if (!value || !*value) continue;
179 +
180 + if(!strcmp(name, "machine_guid"))
181 + machine_guid = value;
182 + else if(!strcmp(name, "claim_id"))
183 + claim_id = value;
184 + else if(!strcmp(name, "node_id"))
185 + node_id = value;
186 + }
187 +
188 + if(!verify_agent_uuids(machine_guid, node_id, claim_id)) {
189 + buffer_flush(w->response.data);
190 + buffer_strcat(w->response.data, "The request is missing or not matching local UUIDs");
191 + return HTTP_RESP_BAD_REQUEST;
192 + }
193 +
194 uuid_t uuid;
195 bearer_get_token(&uuid);
196
web/server/web_client.c
+8
@@ -18,6 +18,14 @@ inline int web_client_permission_denied(struct web_client *w) {
18 return HTTP_RESP_FORBIDDEN;
19 }
20
21 +inline int web_client_bearer_required(struct web_client *w) {
22 + w->response.data->content_type = CT_TEXT_PLAIN;
23 + buffer_flush(w->response.data);
24 + buffer_strcat(w->response.data, "An authorization bearer is required to access the resource.");
25 + w->response.code = HTTP_RESP_UNAUTHORIZED;
26 + return HTTP_RESP_UNAUTHORIZED;
27 +}
28 +
29 static inline int bad_request_multiple_dashboard_versions(struct web_client *w) {
30 w->response.data->content_type = CT_TEXT_PLAIN;
31 buffer_flush(w->response.data);
web/server/web_client.h
+1
@@ -199,6 +199,7 @@ struct web_client {
199 };
200
201 int web_client_permission_denied(struct web_client *w);
202 +int web_client_bearer_required(struct web_client *w);
203
204 ssize_t web_client_send(struct web_client *w);
205 ssize_t web_client_receive(struct web_client *w);