@cryptotaxi247 / netdata-1 / commits / b61ddad5e

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