diff options
author | Costa Tsaousis <costa@netdata.cloud> | 2023-07-12 21:08:44 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-07-12 21:08:44 +0300 |
commit | b61ddad5e631fe0fedd11ff835a642a42d5d71b8 (patch) | |
tree | 8a4e439af433cf83271b1d0be16d0a4a388255b4 | |
parent | 1fb7aeddcc5a85d458bcbd3a20d7b0b2939e3cfe (diff) |
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
-rw-r--r-- | health/health.c | 14 | ||||
-rwxr-xr-x | health/notifications/alarm-notify.sh.in | 31 | ||||
-rw-r--r-- | registry/registry.c | 48 | ||||
-rw-r--r-- | registry/registry.h | 2 | ||||
-rw-r--r-- | registry/registry_init.c | 5 | ||||
-rw-r--r-- | registry/registry_internals.c | 63 | ||||
-rw-r--r-- | registry/registry_internals.h | 2 | ||||
-rw-r--r-- | registry/registry_machine.c | 1 | ||||
-rw-r--r-- | web/api/web_api_v1.c | 10 |
9 files changed, 84 insertions, 92 deletions
diff --git a/health/health.c b/health/health.c index 4ae7e99974..c39677e250 100644 --- a/health/health.c +++ b/health/health.c @@ -81,8 +81,9 @@ static bool prepare_command(BUFFER *wb, const char *crit_alarms, const char *classification, const char *edit_command, - const char *machine_guid) -{ + const char *machine_guid, + uuid_t *transition_id +) { char buf[8192]; size_t n = 8192 - 1; @@ -188,6 +189,12 @@ static bool prepare_command(BUFFER *wb, return false; buffer_sprintf(wb, " '%s'", buf); + char tr_id[UUID_STR_LEN]; + uuid_unparse_lower(*transition_id, tr_id); + if (!sanitize_command_argument_string(buf, tr_id, n)) + return false; + buffer_sprintf(wb, " '%s'", buf); + return true; } @@ -575,7 +582,8 @@ static inline void health_alarm_execute(RRDHOST *host, ALARM_ENTRY *ae) { buffer_tostring(crit_alarms), ae->classification?ae_classification(ae):"Unknown", edit_command, - host != localhost ? host->machine_guid:""); + host->machine_guid, + &ae->transition_id); const char *command_to_run = buffer_tostring(wb); if (ok) { diff --git a/health/notifications/alarm-notify.sh.in b/health/notifications/alarm-notify.sh.in index 1867cf818f..3cff33db93 100755 --- a/health/notifications/alarm-notify.sh.in +++ b/health/notifications/alarm-notify.sh.in @@ -3,7 +3,7 @@ # netdata # real-time performance and health monitoring, done right! -# (C) 2017 Costa Tsaousis <costa@tsaousis.gr> +# (C) 2023 Netdata Inc. # SPDX-License-Identifier: GPL-3.0-or-later # # Script to send alarm notifications for netdata @@ -246,7 +246,8 @@ else total_crit_alarms="${26}" # List of alarms in critical state classification="${27}" # The class field from .conf files edit_command_line="${28}" # The command to edit the alarm, with the line number - child_machine_guid="${29}" # If populated, the notification is sent for a child + child_machine_guid="${29}" # the machine_guid of the child + transition_id="${30}" # the transition_id of the alert fi # ----------------------------------------------------------------------------- @@ -2488,31 +2489,17 @@ urlencode "${value_string}" >/dev/null url_value_string="${REPLY}" 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}" -GOTOCLOUD=0 -if [ "${NETDATA_REGISTRY_URL}" == "https://registry.my-netdata.io" ]; then - if [ -z "${NETDATA_REGISTRY_UNIQUE_ID}" ]; then - if [ -f "@registrydir_POST@/netdata.public.unique.id" ]; then - NETDATA_REGISTRY_UNIQUE_ID="$(cat "@registrydir_POST@/netdata.public.unique.id")" - fi - fi - if [ -n "${NETDATA_REGISTRY_UNIQUE_ID}" ]; then - GOTOCLOUD=1 - fi -fi - -if [ ${GOTOCLOUD} -eq 0 ]; then - goto_url="${NETDATA_REGISTRY_URL}/goto-host-from-alarm.html?${redirect_params}" -else - # 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 - # Re-allow alarm redirection, for alarms 2.0, new template - if [ -z "${child_machine_guid}" ]; then - goto_url="${NETDATA_REGISTRY_CLOUD_BASE_URL}/alarms/redirect?agentId=${NETDATA_REGISTRY_UNIQUE_ID}&${redirect_params}" +if [ -z "${NETDATA_REGISTRY_UNIQUE_ID}" ]; then + if [ -f "@registrydir_POST@/netdata.public.unique.id" ]; then + NETDATA_REGISTRY_UNIQUE_ID="$(cat "@registrydir_POST@/netdata.public.unique.id")" else - goto_url="${NETDATA_REGISTRY_CLOUD_BASE_URL}/alarms/redirect?agentId=${NETDATA_REGISTRY_UNIQUE_ID}&childId=${child_machine_guid}&${redirect_params}" + error "failed to identify this agent via its NETDATA_REGISTRY_UNIQUE_ID." fi fi +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}" + # the severity of the alarm severity="${status}" diff --git a/registry/registry.c b/registry/registry.c index b8935d4e99..743b1e8a4e 100644 --- a/registry/registry.c +++ b/registry/registry.c @@ -33,11 +33,13 @@ static void registry_set_cookie(struct web_client *w, const char *guid) { strftime(e_date, sizeof(e_date), "%a, %d %b %Y %H:%M:%S %Z", etm); buffer_sprintf(w->response.header, "Set-Cookie: " NETDATA_REGISTRY_COOKIE_NAME "=%s; Expires=%s\r\n", guid, e_date); + buffer_sprintf(w->response.header, "Set-Cookie: " NETDATA_REGISTRY_COOKIE_NAME "=%s; SameSite=Strict; Expires=%s\r\n", guid, e_date); if(registry.enable_cookies_samesite_secure) buffer_sprintf(w->response.header, "Set-Cookie: " NETDATA_REGISTRY_COOKIE_NAME "=%s; Expires=%s; SameSite=None; Secure\r\n", guid, e_date); if(registry.registry_domain && *registry.registry_domain) { buffer_sprintf(w->response.header, "Set-Cookie: " NETDATA_REGISTRY_COOKIE_NAME "=%s; Expires=%s; Domain=%s\r\n", guid, e_date, registry.registry_domain); + 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); if(registry.enable_cookies_samesite_secure) 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); } @@ -166,16 +168,26 @@ int registry_request_hello_json(RRDHOST *host, struct web_client *w) { 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_object(w->response.data, "agent"); + { + buffer_json_member_add_string(w->response.data, "machine_guid", localhost->machine_guid); + + if(localhost->node_id) + buffer_json_member_add_uuid(w->response.data, "node_id", localhost->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_boolean(w->response.data, "bearer_protection", netdata_is_protected_by_bearer); } + buffer_json_object_close(w->response.data); 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); - buffer_json_member_add_boolean(w->response.data, "bearer_protection", netdata_is_protected_by_bearer); buffer_json_member_add_array(w->response.data, "nodes"); RRDHOST *h; @@ -296,19 +308,19 @@ int registry_request_delete_json(RRDHOST *host, struct web_client *w, char *pers // public SEARCH request // the main method for searching the URLs of a netdata -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) { +int registry_request_search_json(RRDHOST *host, struct web_client *w, char *person_guid, char *request_machine) { if(!registry.enabled) return registry_json_disabled(host, w, "search"); - if(!registry_is_valid_url(url)) { - buffer_flush(w->response.data); - buffer_strcat(w->response.data, "Invalid URL given in the request"); - return HTTP_RESP_BAD_REQUEST; + if(!person_guid || !person_guid[0]) { + registry_json_header(host, w, "search", REGISTRY_STATUS_FAILED); + registry_json_footer(w); + return HTTP_RESP_PRECOND_FAIL; } registry_lock(); - REGISTRY_MACHINE *m = registry_request_machine(person_guid, machine_guid, url, request_machine, when); + REGISTRY_MACHINE *m = registry_request_machine(person_guid, request_machine); if(!m) { registry_json_header(host, w, "search", REGISTRY_STATUS_FAILED); registry_json_footer(w); @@ -339,6 +351,12 @@ int registry_request_switch_json(RRDHOST *host, struct web_client *w, char *pers if(!registry.enabled) return registry_json_disabled(host, w, "switch"); + if(!person_guid || !person_guid[0]) { + buffer_flush(w->response.data); + buffer_strcat(w->response.data, "Who are you? Person GUID is missing"); + return HTTP_RESP_PRECOND_FAIL; + } + if(!registry_is_valid_url(url)) { buffer_flush(w->response.data); buffer_strcat(w->response.data, "Invalid URL given in the request"); @@ -494,16 +512,16 @@ void registry_statistics(void) { } struct aral_statistics *p_aral_stats = aral_statistics(registry.persons_aral); - rrddim_set(stm, "persons", (collected_number)p_aral_stats->structures.allocated_bytes + (collected_number)p_aral_stats->malloc.allocated_bytes); + 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); struct aral_statistics *m_aral_stats = aral_statistics(registry.machines_aral); - rrddim_set(stm, "machines", (collected_number)m_aral_stats->structures.allocated_bytes + (collected_number)m_aral_stats->malloc.allocated_bytes); + 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); struct aral_statistics *pu_aral_stats = aral_statistics(registry.person_urls_aral); - rrddim_set(stm, "persons_urls", (collected_number)pu_aral_stats->structures.allocated_bytes + (collected_number)pu_aral_stats->malloc.allocated_bytes); + 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); struct aral_statistics *mu_aral_stats = aral_statistics(registry.machine_urls_aral); - rrddim_set(stm, "machines_urls", (collected_number)mu_aral_stats->structures.allocated_bytes + (collected_number)mu_aral_stats->malloc.allocated_bytes); + 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); rrdset_done(stm); } diff --git a/registry/registry.h b/registry/registry.h index 6aa3f0a118..a572991c7e 100644 --- a/registry/registry.h +++ b/registry/registry.h @@ -64,7 +64,7 @@ void registry_free(void); // HTTP requests handled by the registry int registry_request_access_json(RRDHOST *host, struct web_client *w, char *person_guid, char *machine_guid, char *url, char *name, time_t when); 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); -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); +int registry_request_search_json(RRDHOST *host, struct web_client *w, char *person_guid, char *request_machine); 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); int registry_request_hello_json(RRDHOST *host, struct web_client *w); diff --git a/registry/registry_init.c b/registry/registry_init.c index bfe0950a33..79523e2581 100644 --- a/registry/registry_init.c +++ b/registry/registry_init.c @@ -202,7 +202,7 @@ static int machine_delete_callback(const DICTIONARY_ITEM *item __maybe_unused, v count++; } - freez(m); + aral_freez(registry.machines_aral, m); return count + 1; } @@ -219,13 +219,14 @@ static int registry_person_del_callback(const DICTIONARY_ITEM *item __maybe_unus //dictionary_del(registry.persons, p->guid); netdata_log_debug(D_REGISTRY, "Registry: freeing person '%s'", p->guid); - freez(p); + aral_freez(registry.persons_aral, p); return 1; } void registry_free(void) { if(!registry.enabled) return; + registry.enabled = false; netdata_log_debug(D_REGISTRY, "Registry: destroying persons dictionary"); dictionary_walkthrough_read(registry.persons, registry_person_del_callback, NULL); diff --git a/registry/registry_internals.c b/registry/registry_internals.c index 8d2f13807b..135c67e05b 100644 --- a/registry/registry_internals.c +++ b/registry/registry_internals.c @@ -209,63 +209,38 @@ REGISTRY_PERSON *registry_request_delete(const char *person_guid, char *machine_ } -// a structure to pass to the dictionary_walkthrough_read() callback handler -struct machine_request_callback_data { - REGISTRY_MACHINE *find_this_machine; - REGISTRY_PERSON_URL *result; -}; - -// the callback function -// this will be run for every PERSON_URL of this PERSON -static int machine_request_callback(void *entry, void *data) { - REGISTRY_PERSON_URL *mypu = (REGISTRY_PERSON_URL *)entry; - struct machine_request_callback_data *myrdata = (struct machine_request_callback_data *)data; - - if(mypu->machine == myrdata->find_this_machine) { - myrdata->result = mypu; - return -1; // this will also stop the walk through - } - - return 0; // continue -} - -REGISTRY_MACHINE *registry_request_machine(const char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when) { - (void)when; - +REGISTRY_MACHINE *registry_request_machine(const char *person_guid, char *request_machine) { + char pbuf[GUID_LEN + 1]; char mbuf[GUID_LEN + 1]; - REGISTRY_PERSON *p = NULL; - REGISTRY_MACHINE *m = NULL; - REGISTRY_PERSON_URL *pu = registry_verify_request(person_guid, machine_guid, url, &p, &m); - if(!pu || !p || !m) return NULL; + // make sure the person GUID is valid + if(regenerate_guid(person_guid, pbuf) == -1) { + netdata_log_info("REGISTRY: %s(): invalid person GUID '%s'", __FUNCTION__ , person_guid); + return NULL; + } + person_guid = pbuf; - // make sure the machine GUID is valid + // make sure the person GUID is valid if(regenerate_guid(request_machine, mbuf) == -1) { - 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); + netdata_log_info("REGISTRY: %s(): invalid search machine GUID '%s'", __FUNCTION__ , request_machine); return NULL; } request_machine = mbuf; - // make sure the machine exists - m = registry_machine_find(request_machine); - if(!m) { - 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); - return NULL; - } + REGISTRY_PERSON *p = registry_person_find(person_guid); + if(!p) return NULL; + + REGISTRY_MACHINE *m = registry_machine_find(request_machine); + if(!m) return NULL; // Verify the user has in the past accessed this machine // We will walk through the PERSON_URLs to find the machine // linking to our machine - // a structure to pass to the dictionary_walkthrough_read() callback handler - struct machine_request_callback_data rdata = { m, NULL }; - - // request a walk through on the dictionary - for(pu = p->person_urls; pu ;pu = pu->next) - machine_request_callback(pu, &rdata); - - if(rdata.result) - return m; + // make sure the user has access + for(REGISTRY_PERSON_URL *pu = p->person_urls; pu ;pu = pu->next) + if(pu->machine == m) + return m; return NULL; } diff --git a/registry/registry_internals.h b/registry/registry_internals.h index 9578dc90f1..c64fe7e93b 100644 --- a/registry/registry_internals.h +++ b/registry/registry_internals.h @@ -72,7 +72,7 @@ extern struct registry registry; // REGISTRY LOW-LEVEL REQUESTS (in registry-internals.c) REGISTRY_PERSON *registry_request_access(const char *person_guid, char *machine_guid, char *url, char *name, time_t when); REGISTRY_PERSON *registry_request_delete(const char *person_guid, char *machine_guid, char *url, char *delete_url, time_t when); -REGISTRY_MACHINE *registry_request_machine(const char *person_guid, char *machine_guid, char *url, char *request_machine, time_t when); +REGISTRY_MACHINE *registry_request_machine(const char *person_guid, char *request_machine); // REGISTRY LOG (in registry_log.c) void registry_log(char action, REGISTRY_PERSON *p, REGISTRY_MACHINE *m, STRING *u, const char *name); diff --git a/registry/registry_machine.c b/registry/registry_machine.c index d09633550a..a94fb8ea47 100644 --- a/registry/registry_machine.c +++ b/registry/registry_machine.c @@ -55,6 +55,7 @@ REGISTRY_MACHINE *registry_machine_allocate(const char *machine_guid, time_t whe m->first_t = m->last_t = (uint32_t)when; m->usages = 0; + m->links = 0; registry.machines_count++; diff --git a/web/api/web_api_v1.c b/web/api/web_api_v1.c index f6ebfd5ade..f3d263b2ac 100644 --- a/web/api/web_api_v1.c +++ b/web/api/web_api_v1.c @@ -900,7 +900,7 @@ cleanup: // /api/v1/registry?action=delete&machine=${machine_guid}&name=${hostname}&url=${url}&delete_url=${delete_url} // // Search for the URLs of a machine: -// /api/v1/registry?action=search&machine=${machine_guid}&name=${hostname}&url=${url}&for=${machine_guid} +// /api/v1/registry?action=search&for=${machine_guid} // // Impersonate: // /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 * return web_client_permission_denied(w); } + buffer_no_cacheable(w->response.data); + switch(action) { case 'A': 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 * return registry_request_delete_json(host, w, person_guid, machine_guid, machine_url, delete_url, now_realtime_sec()); case 'S': - if(unlikely(!machine_guid || !machine_url || !search_machine_guid)) { - 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"); + if(unlikely(!search_machine_guid)) { + netdata_log_error("Invalid registry request - search requires these parameters: for ('%s')", search_machine_guid?search_machine_guid:"UNSET"); buffer_flush(w->response.data); buffer_strcat(w->response.data, "Invalid registry Search request."); return HTTP_RESP_BAD_REQUEST; } web_client_enable_tracking_required(w); - return registry_request_search_json(host, w, person_guid, machine_guid, machine_url, search_machine_guid, now_realtime_sec()); + return registry_request_search_json(host, w, person_guid, search_machine_guid); case 'W': if(unlikely(!machine_guid || !machine_url || !to_person_guid)) { |