|
@@ -42,16 +42,16 @@ char *get_agent_claimid()
|
|
|
}
|
|
|
|
|
|
#define CLAIMING_COMMAND_LENGTH 16384
|
|
|
-#define CLAIMING_PROXY_LENGTH CLAIMING_COMMAND_LENGTH/4
|
|
|
+#define CLAIMING_PROXY_LENGTH (CLAIMING_COMMAND_LENGTH/4)
|
|
|
|
|
|
extern struct registry registry;
|
|
|
|
|
|
/* rrd_init() and post_conf_load() must have been called before this function */
|
|
|
-void claim_agent(char *claiming_arguments)
|
|
|
+CLAIM_AGENT_RESPONSE claim_agent(const char *claiming_arguments, bool force, const char **msg)
|
|
|
{
|
|
|
- if (!netdata_cloud_enabled) {
|
|
|
+ if (!force || !netdata_cloud_enabled) {
|
|
|
error("Refusing to claim agent -> cloud functionality has been disabled");
|
|
|
- return;
|
|
|
+ return CLAIM_AGENT_CLOUD_DISABLED;
|
|
|
}
|
|
|
|
|
|
#ifndef DISABLE_CLOUD
|
|
@@ -62,8 +62,11 @@ void claim_agent(char *claiming_arguments)
|
|
|
|
|
|
// This is guaranteed to be set early in main via post_conf_load()
|
|
|
char *cloud_base_url = appconfig_get(&cloud_config, CONFIG_SECTION_GLOBAL, "cloud base url", NULL);
|
|
|
- if (cloud_base_url == NULL)
|
|
|
- fatal("Do not move the cloud base url out of post_conf_load!!");
|
|
|
+ if (cloud_base_url == NULL) {
|
|
|
+ internal_fatal(true, "Do not move the cloud base url out of post_conf_load!!");
|
|
|
+ return CLAIM_AGENT_NO_CLOUD_URL;
|
|
|
+ }
|
|
|
+
|
|
|
const char *proxy_str;
|
|
|
ACLK_PROXY_TYPE proxy_type;
|
|
|
char proxy_flag[CLAIMING_PROXY_LENGTH] = "-noproxy";
|
|
@@ -76,7 +79,6 @@ void claim_agent(char *claiming_arguments)
|
|
|
snprintfz(command_buffer,
|
|
|
CLAIMING_COMMAND_LENGTH,
|
|
|
"exec netdata-claim.sh %s -hostname=%s -id=%s -url=%s -noreload %s",
|
|
|
-
|
|
|
proxy_flag,
|
|
|
netdata_configured_hostname,
|
|
|
localhost->machine_guid,
|
|
@@ -87,7 +89,7 @@ void claim_agent(char *claiming_arguments)
|
|
|
fp_child_output = netdata_popen(command_buffer, &command_pid, &fp_child_input);
|
|
|
if(!fp_child_output) {
|
|
|
error("Cannot popen(\"%s\").", command_buffer);
|
|
|
- return;
|
|
|
+ return CLAIM_AGENT_CANNOT_EXECUTE_CLAIM_SCRIPT;
|
|
|
}
|
|
|
netdata_log_info("Waiting for claiming command to finish.");
|
|
|
while (fgets(command_buffer, CLAIMING_COMMAND_LENGTH, fp_child_output) != NULL) {;}
|
|
@@ -95,25 +97,31 @@ void claim_agent(char *claiming_arguments)
|
|
|
netdata_log_info("Agent claiming command returned with code %d", exit_code);
|
|
|
if (0 == exit_code) {
|
|
|
load_claiming_state();
|
|
|
- return;
|
|
|
+ return CLAIM_AGENT_OK;
|
|
|
}
|
|
|
if (exit_code < 0) {
|
|
|
error("Agent claiming command failed to complete its run.");
|
|
|
- return;
|
|
|
+ return CLAIM_AGENT_CLAIM_SCRIPT_FAILED;
|
|
|
}
|
|
|
errno = 0;
|
|
|
unsigned maximum_known_exit_code = sizeof(claiming_errors) / sizeof(claiming_errors[0]) - 1;
|
|
|
|
|
|
if ((unsigned)exit_code > maximum_known_exit_code) {
|
|
|
error("Agent failed to be claimed with an unknown error.");
|
|
|
- return;
|
|
|
+ return CLAIM_AGENT_CLAIM_SCRIPT_RETURNED_INVALID_CODE;
|
|
|
}
|
|
|
+
|
|
|
error("Agent failed to be claimed with the following error message:");
|
|
|
error("\"%s\"", claiming_errors[exit_code]);
|
|
|
+
|
|
|
+ if(msg) *msg = claiming_errors[exit_code];
|
|
|
+
|
|
|
#else
|
|
|
UNUSED(claiming_arguments);
|
|
|
UNUSED(claiming_errors);
|
|
|
#endif
|
|
|
+
|
|
|
+ return CLAIM_AGENT_FAILED_WITH_MESSAGE;
|
|
|
}
|
|
|
|
|
|
#ifdef ENABLE_ACLK
|
|
@@ -181,7 +189,7 @@ void load_claiming_state(void)
|
|
|
freez(claimed_id);
|
|
|
|
|
|
netdata_log_info("File '%s' was found. Setting state to AGENT_CLAIMED.", filename);
|
|
|
- netdata_cloud_enabled = appconfig_get_boolean(&cloud_config, CONFIG_SECTION_GLOBAL, "enabled", 1);
|
|
|
+ netdata_cloud_enabled = appconfig_get_boolean_ondemand(&cloud_config, CONFIG_SECTION_GLOBAL, "enabled", netdata_cloud_enabled);
|
|
|
#endif
|
|
|
}
|
|
|
|
|
@@ -193,6 +201,10 @@ struct config cloud_config = { .first_section = NULL,
|
|
|
|
|
|
void load_cloud_conf(int silent)
|
|
|
{
|
|
|
+ char *nd_disable_cloud = getenv("NETDATA_DISABLE_CLOUD");
|
|
|
+ if (nd_disable_cloud && !strncmp(nd_disable_cloud, "1", 1))
|
|
|
+ netdata_cloud_enabled = CONFIG_BOOLEAN_NO;
|
|
|
+
|
|
|
char *filename;
|
|
|
errno = 0;
|
|
|
|
|
@@ -201,8 +213,254 @@ void load_cloud_conf(int silent)
|
|
|
filename = strdupz_path_subpath(netdata_configured_varlib_dir, "cloud.d/cloud.conf");
|
|
|
|
|
|
ret = appconfig_load(&cloud_config, filename, 1, NULL);
|
|
|
- if(!ret && !silent) {
|
|
|
+ if(!ret && !silent)
|
|
|
netdata_log_info("CONFIG: cannot load cloud config '%s'. Running with internal defaults.", filename);
|
|
|
- }
|
|
|
+
|
|
|
freez(filename);
|
|
|
+
|
|
|
+ // --------------------------------------------------------------------
|
|
|
+ // Check if the cloud is enabled
|
|
|
+
|
|
|
+#if defined( DISABLE_CLOUD ) || !defined( ENABLE_ACLK )
|
|
|
+ netdata_cloud_enabled = CONFIG_BOOLEAN_NO;
|
|
|
+#else
|
|
|
+ netdata_cloud_enabled = appconfig_get_boolean_ondemand(&cloud_config, CONFIG_SECTION_GLOBAL, "enabled", netdata_cloud_enabled);
|
|
|
+#endif
|
|
|
+
|
|
|
+ // This must be set before any point in the code that accesses it. Do not move it from this function.
|
|
|
+ appconfig_get(&cloud_config, CONFIG_SECTION_GLOBAL, "cloud base url", DEFAULT_CLOUD_BASE_URL);
|
|
|
+}
|
|
|
+
|
|
|
+static char *netdata_random_session_id_filename = NULL;
|
|
|
+static uuid_t netdata_random_session_id = { 0 };
|
|
|
+
|
|
|
+bool netdata_random_session_id_generate(void) {
|
|
|
+ static char guid[UUID_STR_LEN] = "";
|
|
|
+
|
|
|
+ uuid_generate_random(netdata_random_session_id);
|
|
|
+ uuid_unparse_lower(netdata_random_session_id, guid);
|
|
|
+
|
|
|
+ char filename[FILENAME_MAX + 1];
|
|
|
+ snprintfz(filename, FILENAME_MAX, "%s/netdata_random_session_id", netdata_configured_varlib_dir);
|
|
|
+
|
|
|
+ bool ret = true;
|
|
|
+
|
|
|
+ (void)unlink(filename);
|
|
|
+
|
|
|
+ // save it
|
|
|
+ int fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 640);
|
|
|
+ if(fd == -1) {
|
|
|
+ error("Cannot create random session id file '%s'.", filename);
|
|
|
+ ret = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(write(fd, guid, UUID_STR_LEN - 1) != UUID_STR_LEN - 1) {
|
|
|
+ error("Cannot write the random session id file '%s'.", filename);
|
|
|
+ ret = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ ssize_t rc = write(fd, "\n", 1);
|
|
|
+ (void)rc;
|
|
|
+
|
|
|
+ close(fd);
|
|
|
+
|
|
|
+ if(ret && (!netdata_random_session_id_filename || strcmp(netdata_random_session_id_filename, filename) != 0)) {
|
|
|
+ freez(netdata_random_session_id_filename);
|
|
|
+ netdata_random_session_id_filename = strdupz(filename);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+const char *netdata_random_session_id_get_filename(void) {
|
|
|
+ if(!netdata_random_session_id_filename)
|
|
|
+ netdata_random_session_id_generate();
|
|
|
+
|
|
|
+ return netdata_random_session_id_filename;
|
|
|
+}
|
|
|
+
|
|
|
+bool netdata_random_session_id_matches(const char *guid) {
|
|
|
+ if(uuid_is_null(netdata_random_session_id))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ uuid_t uuid;
|
|
|
+
|
|
|
+ if(uuid_parse(guid, uuid))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ if(uuid_compare(netdata_random_session_id, uuid) == 0)
|
|
|
+ return true;
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+static bool check_claim_param(const char *s) {
|
|
|
+ if(!s || !*s) return true;
|
|
|
+
|
|
|
+ do {
|
|
|
+ if(isalnum(*s) || *s == '.' || *s == ',' || *s == '-' || *s == ':' || *s == '/' || *s == '_')
|
|
|
+ ;
|
|
|
+ else
|
|
|
+ return false;
|
|
|
+
|
|
|
+ } while(*++s);
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+void claim_reload_all(void) {
|
|
|
+ error_log_limit_unlimited();
|
|
|
+ load_claiming_state();
|
|
|
+ registry_update_cloud_base_url();
|
|
|
+ rrdpush_send_claimed_id(localhost);
|
|
|
+ error_log_limit_reset();
|
|
|
+}
|
|
|
+
|
|
|
+int api_v2_claim(struct web_client *w, char *url) {
|
|
|
+ char *key = NULL;
|
|
|
+ char *token = NULL;
|
|
|
+ char *rooms = NULL;
|
|
|
+ char *base_url = NULL;
|
|
|
+
|
|
|
+ while (url) {
|
|
|
+ char *value = strsep_skip_consecutive_separators(&url, "&");
|
|
|
+ if (!value || !*value) continue;
|
|
|
+
|
|
|
+ char *name = strsep_skip_consecutive_separators(&value, "=");
|
|
|
+ if (!name || !*name) continue;
|
|
|
+ if (!value || !*value) continue;
|
|
|
+
|
|
|
+ if(!strcmp(name, "key"))
|
|
|
+ key = value;
|
|
|
+ else if(!strcmp(name, "token"))
|
|
|
+ token = value;
|
|
|
+ else if(!strcmp(name, "rooms"))
|
|
|
+ rooms = value;
|
|
|
+ else if(!strcmp(name, "url"))
|
|
|
+ base_url = value;
|
|
|
+ }
|
|
|
+
|
|
|
+ BUFFER *wb = w->response.data;
|
|
|
+ buffer_flush(wb);
|
|
|
+ buffer_json_initialize(wb, "\"", "\"", 0, true, false);
|
|
|
+
|
|
|
+ time_t now_s = now_realtime_sec();
|
|
|
+ CLOUD_STATUS status = buffer_json_cloud_status(wb, now_s);
|
|
|
+
|
|
|
+ bool can_be_claimed = false;
|
|
|
+ switch(status) {
|
|
|
+ case CLOUD_STATUS_AVAILABLE:
|
|
|
+ case CLOUD_STATUS_DISABLED:
|
|
|
+ case CLOUD_STATUS_OFFLINE:
|
|
|
+ can_be_claimed = true;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case CLOUD_STATUS_UNAVAILABLE:
|
|
|
+ case CLOUD_STATUS_BANNED:
|
|
|
+ case CLOUD_STATUS_ONLINE:
|
|
|
+ can_be_claimed = false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ buffer_json_member_add_boolean(wb, "can_be_claimed", can_be_claimed);
|
|
|
+
|
|
|
+ if(can_be_claimed && key) {
|
|
|
+ if(!netdata_random_session_id_matches(key)) {
|
|
|
+ buffer_reset(wb);
|
|
|
+ buffer_strcat(wb, "invalid key");
|
|
|
+ netdata_random_session_id_generate(); // generate a new key, to avoid an attack to find it
|
|
|
+ return HTTP_RESP_FORBIDDEN;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(!token || !base_url || !check_claim_param(token) || !check_claim_param(base_url) || (rooms && !check_claim_param(rooms))) {
|
|
|
+ buffer_reset(wb);
|
|
|
+ buffer_strcat(wb, "invalid parameters");
|
|
|
+ netdata_random_session_id_generate(); // generate a new key, to avoid an attack to find it
|
|
|
+ return HTTP_RESP_BAD_REQUEST;
|
|
|
+ }
|
|
|
+
|
|
|
+ netdata_random_session_id_generate(); // generate a new key, to avoid an attack to find it
|
|
|
+
|
|
|
+ netdata_cloud_enabled = CONFIG_BOOLEAN_AUTO;
|
|
|
+ appconfig_set_boolean(&cloud_config, CONFIG_SECTION_GLOBAL, "enabled", CONFIG_BOOLEAN_AUTO);
|
|
|
+ appconfig_set(&cloud_config, CONFIG_SECTION_GLOBAL, "cloud base url", base_url);
|
|
|
+
|
|
|
+ uuid_t claimed_id;
|
|
|
+ uuid_generate_random(claimed_id);
|
|
|
+ char claimed_id_str[UUID_STR_LEN];
|
|
|
+ uuid_unparse_lower(claimed_id, claimed_id_str);
|
|
|
+
|
|
|
+ BUFFER *t = buffer_create(1024, NULL);
|
|
|
+ if(rooms)
|
|
|
+ buffer_sprintf(t, "-id=%s -token=%s -rooms=%s", claimed_id_str, token, rooms);
|
|
|
+ else
|
|
|
+ buffer_sprintf(t, "-id=%s -token=%s", claimed_id_str, token);
|
|
|
+
|
|
|
+ bool success = false;
|
|
|
+ const char *msg = NULL;
|
|
|
+ CLAIM_AGENT_RESPONSE rc = claim_agent(buffer_tostring(t), true, &msg);
|
|
|
+ switch(rc) {
|
|
|
+ case CLAIM_AGENT_OK:
|
|
|
+ msg = "ok";
|
|
|
+ success = true;
|
|
|
+ can_be_claimed = false;
|
|
|
+ claim_reload_all();
|
|
|
+ {
|
|
|
+ int ms = 0;
|
|
|
+ do {
|
|
|
+ status = cloud_status();
|
|
|
+ if (status == CLOUD_STATUS_ONLINE)
|
|
|
+ break;
|
|
|
+
|
|
|
+ sleep_usec(100 * USEC_PER_MS);
|
|
|
+ ms += 100;
|
|
|
+ } while (ms < 5000);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case CLAIM_AGENT_NO_CLOUD_URL:
|
|
|
+ msg = "No Netdata Cloud URL.";
|
|
|
+ break;
|
|
|
+
|
|
|
+ case CLAIM_AGENT_CLAIM_SCRIPT_FAILED:
|
|
|
+ msg = "Claiming script failed.";
|
|
|
+ break;
|
|
|
+
|
|
|
+ case CLAIM_AGENT_CLOUD_DISABLED:
|
|
|
+ msg = "Netdata Cloud is disabled on this agent.";
|
|
|
+ break;
|
|
|
+
|
|
|
+ case CLAIM_AGENT_CANNOT_EXECUTE_CLAIM_SCRIPT:
|
|
|
+ msg = "Failed to execute claiming script.";
|
|
|
+ break;
|
|
|
+
|
|
|
+ case CLAIM_AGENT_CLAIM_SCRIPT_RETURNED_INVALID_CODE:
|
|
|
+ msg = "Claiming script returned invalid code.";
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ case CLAIM_AGENT_FAILED_WITH_MESSAGE:
|
|
|
+ if(!msg)
|
|
|
+ msg = "Unknown error";
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ // our status may have changed
|
|
|
+ // refresh the status in our output
|
|
|
+ buffer_flush(wb);
|
|
|
+ buffer_json_initialize(wb, "\"", "\"", 0, true, false);
|
|
|
+ now_s = now_realtime_sec();
|
|
|
+ buffer_json_cloud_status(wb, now_s);
|
|
|
+
|
|
|
+ // and this is the status of the claiming command we run
|
|
|
+ buffer_json_member_add_boolean(wb, "success", success);
|
|
|
+ buffer_json_member_add_string(wb, "message", msg);
|
|
|
+ }
|
|
|
+
|
|
|
+ if(can_be_claimed)
|
|
|
+ buffer_json_member_add_string(wb, "key_filename", netdata_random_session_id_get_filename());
|
|
|
+
|
|
|
+ buffer_json_finalize(wb);
|
|
|
+
|
|
|
+ return HTTP_RESP_OK;
|
|
|
}
|