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);