agent alert notifications redirect (#15350)
* agent alert notifications redirect * set the same cookies with SameSite: Strict * registry search now requires only "for" parameter * registry responses are not cacheable * fix typo and add more error checking * registry memory when mmap is used * fix free with aral
Costa Tsaousis committed
Jul 12, 2023 at 21:08 UTC
b61ddad5e631fe0fedd11ff835a642a42d5d71b8
9 files changed
+84
-92
health/health.c
+11
-3
@@ -81,8 +81,9 @@ static bool prepare_command(BUFFER *wb,
81
const char *crit_alarms,
82
const char *classification,
83
const char *edit_command,
84
- const char *machine_guid)
85
-{
84
+ const char *machine_guid,
85
+ uuid_t *transition_id
86
+) {
87
char buf[8192];
88
size_t n = 8192 - 1;
89
@@ -188,6 +189,12 @@ static bool prepare_command(BUFFER *wb,
189
return false;
190
buffer_sprintf(wb, " '%s'", buf);
191
192
+ char tr_id[UUID_STR_LEN];
193
+ uuid_unparse_lower(*transition_id, tr_id);
194
+ if (!sanitize_command_argument_string(buf, tr_id, n))
195
+ return false;
196
+ buffer_sprintf(wb, " '%s'", buf);
197
+
198
return true;
199
}
200
@@ -575,7 +582,8 @@ static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) {
582
buffer_tostring(crit_alarms),
583
ae->classification?ae_classification(ae):"Unknown",
584
edit_command,
578
- host != localhost ? host->machine_guid:"");
585
+ host->machine_guid,
586
+ &ae->transition_id);
587
588
const char *command_to_run = buffer_tostring(wb);
589
if (ok) {
health/notifications/alarm-notify.sh.in
+9
-22
@@ -3,7 +3,7 @@
3
4
# netdata
5
# real-time performance and health monitoring, done right!
6
-# (C) 2017 Costa Tsaousis <costa@tsaousis.gr>
6
+# (C) 2023 Netdata Inc.
7
# SPDX-License-Identifier: GPL-3.0-or-later
8
#
9
# Script to send alarm notifications for netdata
@@ -246,7 +246,8 @@ else
246
total_crit_alarms="${26}" # List of alarms in critical state
247
classification="${27}" # The class field from .conf files
248
edit_command_line="${28}" # The command to edit the alarm, with the line number
249
- child_machine_guid="${29}" # If populated, the notification is sent for a child
249
+ child_machine_guid="${29}" # the machine_guid of the child
250
+ transition_id="${30}" # the transition_id of the alert
251
fi
252
253
# -----------------------------------------------------------------------------
@@ -2488,31 +2489,17 @@ urlencode "${value_string}" >/dev/null
2489
url_value_string="${REPLY}"
2490
2491
redirect_params="host=${url_host}&chart=${url_chart}&family=${url_family}&alarm=${url_name}&alarm_unique_id=${unique_id}&alarm_id=${alarm_id}&alarm_event_id=${event_id}&alarm_when=${when}&alarm_status=${status}&alarm_chart=${chart}&alarm_value=${url_value_string}"
2491
-GOTOCLOUD=0
2492
2493
-if [ "${NETDATA_REGISTRY_URL}" == "https://registry.my-netdata.io" ]; then
2494
- if [ -z "${NETDATA_REGISTRY_UNIQUE_ID}" ]; then
2495
- if [ -f "@registrydir_POST@/netdata.public.unique.id" ]; then
2496
- NETDATA_REGISTRY_UNIQUE_ID="$(cat "@registrydir_POST@/netdata.public.unique.id")"
2497
- fi
2498
- fi
2499
- if [ -n "${NETDATA_REGISTRY_UNIQUE_ID}" ]; then
2500
- GOTOCLOUD=1
2501
- fi
2502
-fi
2503
-
2504
-if [ ${GOTOCLOUD} -eq 0 ]; then
2505
- goto_url="${NETDATA_REGISTRY_URL}/goto-host-from-alarm.html?${redirect_params}"
2506
-else
2507
- # Temporarily disable alarm redirection, as the cloud endpoint no longer exists. This functionality will be restored after discussion on #9487. For now, just lead to netdata.cloud
2508
- # Re-allow alarm redirection, for alarms 2.0, new template
2509
- if [ -z "${child_machine_guid}" ]; then
2510
- goto_url="${NETDATA_REGISTRY_CLOUD_BASE_URL}/alarms/redirect?agentId=${NETDATA_REGISTRY_UNIQUE_ID}&${redirect_params}"
2493
+if [ -z "${NETDATA_REGISTRY_UNIQUE_ID}" ]; then
2494
+ if [ -f "@registrydir_POST@/netdata.public.unique.id" ]; then
2495
+ NETDATA_REGISTRY_UNIQUE_ID="$(cat "@registrydir_POST@/netdata.public.unique.id")"
2496
else
2512
- goto_url="${NETDATA_REGISTRY_CLOUD_BASE_URL}/alarms/redirect?agentId=${NETDATA_REGISTRY_UNIQUE_ID}&childId=${child_machine_guid}&${redirect_params}"
2497
+ error "failed to identify this agent via its NETDATA_REGISTRY_UNIQUE_ID."
2498
fi
2499
fi
2500
2501
+goto_url="${NETDATA_REGISTRY_URL}/registry-alert-redirect.html?agent_machine_guid=${NETDATA_REGISTRY_UNIQUE_ID}&host_machine_guid=${child_machine_guid}&transition_id=${transition_id}&${redirect_params}"
2502
+
2503
# the severity of the alarm
2504
severity="${status}"
2505
registry/registry.c
+33
-15
@@ -33,11 +33,13 @@ static void registry_set_cookie(struct web_client *w, const char *guid) {
33
strftime(e_date, sizeof(e_date), "%a, %d %b %Y %H:%M:%S %Z", etm);
34
35
buffer_sprintf(w->response.header, "Set-Cookie: " NETDATA_REGISTRY_COOKIE_NAME "=%s; Expires=%s\r\n", guid, e_date);
36
+ buffer_sprintf(w->response.header, "Set-Cookie: " NETDATA_REGISTRY_COOKIE_NAME "=%s; SameSite=Strict; Expires=%s\r\n", guid, e_date);
37
if(registry.enable_cookies_samesite_secure)
38
buffer_sprintf(w->response.header, "Set-Cookie: " NETDATA_REGISTRY_COOKIE_NAME "=%s; Expires=%s; SameSite=None; Secure\r\n", guid, e_date);
39
40
if(registry.registry_domain && *registry.registry_domain) {
41
buffer_sprintf(w->response.header, "Set-Cookie: " NETDATA_REGISTRY_COOKIE_NAME "=%s; Expires=%s; Domain=%s\r\n", guid, e_date, registry.registry_domain);
42
+ buffer_sprintf(w->response.header, "Set-Cookie: " NETDATA_REGISTRY_COOKIE_NAME "=%s; Expires=%s; Domain=%s; SameSite=Strict\r\n", guid, e_date, registry.registry_domain);
43
if(registry.enable_cookies_samesite_secure)
44
buffer_sprintf(w->response.header, "Set-Cookie: " NETDATA_REGISTRY_COOKIE_NAME "=%s; Expires=%s; Domain=%s; SameSite=None; Secure\r\n", guid, e_date, registry.registry_domain);
45
}
@@ -166,16 +168,26 @@ int registry_request_hello_json(RRDHOST *host, struct web_client *w) {
168
if(host->node_id)
169
buffer_json_member_add_uuid(w->response.data, "node_id", host->node_id);
170
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);
171
+ buffer_json_member_add_object(w->response.data, "agent");
172
+ {
173
+ buffer_json_member_add_string(w->response.data, "machine_guid", localhost->machine_guid);
174
+
175
+ if(localhost->node_id)
176
+ buffer_json_member_add_uuid(w->response.data, "node_id", localhost->node_id);
177
+
178
+ char *claim_id = get_agent_claimid();
179
+ if (claim_id) {
180
+ buffer_json_member_add_string(w->response.data, "claim_id", claim_id);
181
+ freez(claim_id);
182
+ }
183
+
184
+ buffer_json_member_add_boolean(w->response.data, "bearer_protection", netdata_is_protected_by_bearer);
185
}
186
+ buffer_json_object_close(w->response.data);
187
188
buffer_json_member_add_string(w->response.data, "registry", registry.registry_to_announce);
189
buffer_json_member_add_string(w->response.data, "cloud_base_url", registry.cloud_base_url);
190
buffer_json_member_add_boolean(w->response.data, "anonymous_statistics", netdata_anonymous_statistics_enabled);
178
- buffer_json_member_add_boolean(w->response.data, "bearer_protection", netdata_is_protected_by_bearer);
191
192
buffer_json_member_add_array(w->response.data, "nodes");
193
RRDHOST *h;
@@ -296,19 +308,19 @@ int registry_request_delete_json(RRDHOST *host, struct web_client *w, char *pers
308
// public SEARCH request
309
310
// the main method for searching the URLs of a netdata
299
-int registry_request_search_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when) {
311
+int registry_request_search_json(RRDHOST *host, struct web_client *w, char *person_guid, char *request_machine) {
312
if(!registry.enabled)
313
return registry_json_disabled(host, w, "search");
314
303
- if(!registry_is_valid_url(url)) {
304
- buffer_flush(w->response.data);
305
- buffer_strcat(w->response.data, "Invalid URL given in the request");
306
- return HTTP_RESP_BAD_REQUEST;
315
+ if(!person_guid || !person_guid[0]) {
316
+ registry_json_header(host, w, "search", REGISTRY_STATUS_FAILED);
317
+ registry_json_footer(w);
318
+ return HTTP_RESP_PRECOND_FAIL;
319
}
320
321
registry_lock();
322
311
- REGISTRY_MACHINE *m = registry_request_machine(person_guid, machine_guid, url, request_machine, when);
323
+ REGISTRY_MACHINE *m = registry_request_machine(person_guid, request_machine);
324
if(!m) {
325
registry_json_header(host, w, "search", REGISTRY_STATUS_FAILED);
326
registry_json_footer(w);
@@ -339,6 +351,12 @@ int registry_request_switch_json(RRDHOST *host, struct web_client *w, char *pers
351
if(!registry.enabled)
352
return registry_json_disabled(host, w, "switch");
353
354
+ if(!person_guid || !person_guid[0]) {
355
+ buffer_flush(w->response.data);
356
+ buffer_strcat(w->response.data, "Who are you? Person GUID is missing");
357
+ return HTTP_RESP_PRECOND_FAIL;
358
+ }
359
+
360
if(!registry_is_valid_url(url)) {
361
buffer_flush(w->response.data);
362
buffer_strcat(w->response.data, "Invalid URL given in the request");
@@ -494,16 +512,16 @@ void registry_statistics(void) {
512
}
513
514
struct aral_statistics *p_aral_stats = aral_statistics(registry.persons_aral);
497
- rrddim_set(stm, "persons", (collected_number)p_aral_stats->structures.allocated_bytes + (collected_number)p_aral_stats->malloc.allocated_bytes);
515
+ rrddim_set(stm, "persons", (collected_number)p_aral_stats->structures.allocated_bytes + (collected_number)p_aral_stats->malloc.allocated_bytes + (collected_number)p_aral_stats->mmap.allocated_bytes);
516
517
struct aral_statistics *m_aral_stats = aral_statistics(registry.machines_aral);
500
- rrddim_set(stm, "machines", (collected_number)m_aral_stats->structures.allocated_bytes + (collected_number)m_aral_stats->malloc.allocated_bytes);
518
+ rrddim_set(stm, "machines", (collected_number)m_aral_stats->structures.allocated_bytes + (collected_number)m_aral_stats->malloc.allocated_bytes + (collected_number)m_aral_stats->mmap.allocated_bytes);
519
520
struct aral_statistics *pu_aral_stats = aral_statistics(registry.person_urls_aral);
503
- rrddim_set(stm, "persons_urls", (collected_number)pu_aral_stats->structures.allocated_bytes + (collected_number)pu_aral_stats->malloc.allocated_bytes);
521
+ rrddim_set(stm, "persons_urls", (collected_number)pu_aral_stats->structures.allocated_bytes + (collected_number)pu_aral_stats->malloc.allocated_bytes + (collected_number)pu_aral_stats->mmap.allocated_bytes);
522
523
struct aral_statistics *mu_aral_stats = aral_statistics(registry.machine_urls_aral);
506
- rrddim_set(stm, "machines_urls", (collected_number)mu_aral_stats->structures.allocated_bytes + (collected_number)mu_aral_stats->malloc.allocated_bytes);
524
+ rrddim_set(stm, "machines_urls", (collected_number)mu_aral_stats->structures.allocated_bytes + (collected_number)mu_aral_stats->malloc.allocated_bytes + (collected_number)mu_aral_stats->mmap.allocated_bytes);
525
526
rrdset_done(stm);
527
}
registry/registry.h
+1
-1
@@ -64,7 +64,7 @@ void registry_free(void);
64
// HTTP requests handled by the registry
65
int registry_request_access_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *name, time_t when);
66
int registry_request_delete_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when);
67
-int registry_request_search_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when);
67
+int registry_request_search_json(RRDHOST *host, struct web_client *w, char *person_guid, char *request_machine);
68
int registry_request_switch_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *new_person_guid, time_t when);
69
int registry_request_hello_json(RRDHOST *host, struct web_client *w);
70
registry/registry_init.c
+3
-2
@@ -202,7 +202,7 @@ static int machine_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, v
202
count++;
203
}
204
205
- freez(m);
205
+ aral_freez(registry.machines_aral, m);
206
207
return count + 1;
208
}
@@ -219,13 +219,14 @@ static int registry_person_del_callback(const DICTIONARY_ITEM *item __maybe_unus
219
//dictionary_del(registry.persons, p->guid);
220
221
netdata_log_debug(D_REGISTRY, "Registry: freeing person '%s'", p->guid);
222
- freez(p);
222
+ aral_freez(registry.persons_aral, p);
223
224
return 1;
225
}
226
227
void registry_free(void) {
228
if(!registry.enabled) return;
229
+ registry.enabled = false;
230
231
netdata_log_debug(D_REGISTRY, "Registry: destroying persons dictionary");
232
dictionary_walkthrough_read(registry.persons, registry_person_del_callback, NULL);
registry/registry_internals.c
+19
-44
@@ -209,63 +209,38 @@ REGISTRY_PERSON *registry_request_delete(const char *person_guid, char *machine_
209
}
210
211
212
-// a structure to pass to the dictionary_walkthrough_read() callback handler
213
-struct machine_request_callback_data {
214
- REGISTRY_MACHINE *find_this_machine;
215
- REGISTRY_PERSON_URL *result;
216
-};
217
-
218
-// the callback function
219
-// this will be run for every PERSON_URL of this PERSON
220
-static int machine_request_callback(void *entry, void *data) {
221
- REGISTRY_PERSON_URL *mypu = (REGISTRY_PERSON_URL *)entry;
222
- struct machine_request_callback_data *myrdata = (struct machine_request_callback_data *)data;
223
-
224
- if(mypu->machine == myrdata->find_this_machine) {
225
- myrdata->result = mypu;
226
- return -1; // this will also stop the walk through
227
- }
228
-
229
- return 0; // continue
230
-}
231
-
232
-REGISTRY_MACHINE *registry_request_machine(const char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when) {
233
- (void)when;
234
-
212
+REGISTRY_MACHINE *registry_request_machine(const char *person_guid, char *request_machine) {
213
+ char pbuf[GUID_LEN + 1];
214
char mbuf[GUID_LEN + 1];
215
237
- REGISTRY_PERSON *p = NULL;
238
- REGISTRY_MACHINE *m = NULL;
239
- REGISTRY_PERSON_URL *pu = registry_verify_request(person_guid, machine_guid, url, &p, &m);
240
- if(!pu || !p || !m) return NULL;
216
+ // make sure the person GUID is valid
217
+ if(regenerate_guid(person_guid, pbuf) == -1) {
218
+ netdata_log_info("REGISTRY: %s(): invalid person GUID '%s'", __FUNCTION__ , person_guid);
219
+ return NULL;
220
+ }
221
+ person_guid = pbuf;
222
242
- // make sure the machine GUID is valid
223
+ // make sure the person GUID is valid
224
if(regenerate_guid(request_machine, mbuf) == -1) {
244
- netdata_log_info("Registry Machine URLs request: invalid machine GUID, person: '%s', machine '%s', url '%s', request machine '%s'", p->guid, m->guid, string2str(pu->url), request_machine);
225
+ netdata_log_info("REGISTRY: %s(): invalid search machine GUID '%s'", __FUNCTION__ , request_machine);
226
return NULL;
227
}
228
request_machine = mbuf;
229
249
- // make sure the machine exists
250
- m = registry_machine_find(request_machine);
251
- if(!m) {
252
- netdata_log_info("Registry Machine URLs request: machine not found, person: '%s', machine '%s', url '%s', request machine '%s'", p->guid, machine_guid, string2str(pu->url), request_machine);
253
- return NULL;
254
- }
230
+ REGISTRY_PERSON *p = registry_person_find(person_guid);
231
+ if(!p) return NULL;
232
+
233
+ REGISTRY_MACHINE *m = registry_machine_find(request_machine);
234
+ if(!m) return NULL;
235
236
// Verify the user has in the past accessed this machine
237
// We will walk through the PERSON_URLs to find the machine
238
// linking to our machine
239
260
- // a structure to pass to the dictionary_walkthrough_read() callback handler
261
- struct machine_request_callback_data rdata = { m, NULL };
262
-
263
- // request a walk through on the dictionary
264
- for(pu = p->person_urls; pu ;pu = pu->next)
265
- machine_request_callback(pu, &rdata);
266
-
267
- if(rdata.result)
268
- return m;
240
+ // make sure the user has access
241
+ for(REGISTRY_PERSON_URL *pu = p->person_urls; pu ;pu = pu->next)
242
+ if(pu->machine == m)
243
+ return m;
244
245
return NULL;
246
}
registry/registry_internals.h
+1
-1
@@ -72,7 +72,7 @@ extern struct registry registry;
72
// REGISTRY LOW-LEVEL REQUESTS (in registry-internals.c)
73
REGISTRY_PERSON *registry_request_access(const char *person_guid, char *machine_guid, char *url, char *name, time_t when);
74
REGISTRY_PERSON *registry_request_delete(const char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when);
75
-REGISTRY_MACHINE *registry_request_machine(const char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when);
75
+REGISTRY_MACHINE *registry_request_machine(const char *person_guid, char *request_machine);
76
77
// REGISTRY LOG (in registry_log.c)
78
void registry_log(char action, REGISTRY_PERSON *p, REGISTRY_MACHINE *m, STRING *u, const char *name);
registry/registry_machine.c
+1
@@ -55,6 +55,7 @@ REGISTRY_MACHINE *registry_machine_allocate(const char *machine_guid, time_t whe
55
56
m->first_t = m->last_t = (uint32_t)when;
57
m->usages = 0;
58
+ m->links = 0;
59
60
registry.machines_count++;
61
web/api/web_api_v1.c
+6
-4
@@ -900,7 +900,7 @@ cleanup:
900
// /api/v1/registry?action=delete&machine=${machine_guid}&name=${hostname}&url=${url}&delete_url=${delete_url}
901
//
902
// Search for the URLs of a machine:
903
-// /api/v1/registry?action=search&machine=${machine_guid}&name=${hostname}&url=${url}&for=${machine_guid}
903
+// /api/v1/registry?action=search&for=${machine_guid}
904
//
905
// Impersonate:
906
// /api/v1/registry?action=switch&machine=${machine_guid}&name=${hostname}&url=${url}&to=${new_person_guid}
@@ -1026,6 +1026,8 @@ inline int web_client_api_request_v1_registry(RRDHOST *host, struct web_client *
1026
return web_client_permission_denied(w);
1027
}
1028
1029
+ buffer_no_cacheable(w->response.data);
1030
+
1031
switch(action) {
1032
case 'A':
1033
if(unlikely(!machine_guid || !machine_url || !url_name)) {
@@ -1050,15 +1052,15 @@ inline int web_client_api_request_v1_registry(RRDHOST *host, struct web_client *
1052
return registry_request_delete_json(host, w, person_guid, machine_guid, machine_url, delete_url, now_realtime_sec());
1053
1054
case 'S':
1053
- if(unlikely(!machine_guid || !machine_url || !search_machine_guid)) {
1054
- netdata_log_error("Invalid registry request - search requires these parameters: machine ('%s'), url ('%s'), for ('%s')", machine_guid?machine_guid:"UNSET", machine_url?machine_url:"UNSET", search_machine_guid?search_machine_guid:"UNSET");
1055
+ if(unlikely(!search_machine_guid)) {
1056
+ netdata_log_error("Invalid registry request - search requires these parameters: for ('%s')", search_machine_guid?search_machine_guid:"UNSET");
1057
buffer_flush(w->response.data);
1058
buffer_strcat(w->response.data, "Invalid registry Search request.");
1059
return HTTP_RESP_BAD_REQUEST;
1060
}
1061
1062
web_client_enable_tracking_required(w);
1061
- return registry_request_search_json(host, w, person_guid, machine_guid, machine_url, search_machine_guid, now_realtime_sec());
1063
+ return registry_request_search_json(host, w, person_guid, search_machine_guid);
1064
1065
case 'W':
1066
if(unlikely(!machine_guid || !machine_url || !to_person_guid)) {