Dyncfg users actions log (#19674)
* log all DYNCFG user actions that alter data * do not show sensitive user info when the user is not logged in * log template actions * add DYNFG message id to annotations --------- Co-authored-by: Ilya Mashchenko <ilya@netdata.cloud>
Costa Tsaousis committed
Feb 19, 2025 at 13:29 UTC
fb561f84ab7a39b31a560a1f48d6a4492cedc2dc
11 files changed
+264
-56
CMakeLists.txt
+2
@@ -1674,6 +1674,8 @@ set(WEB_PLUGIN_FILES
1674
src/web/api/queries/backfill.h
1675
src/web/api/functions/function-metrics-cardinality.c
1676
src/web/api/functions/function-metrics-cardinality.h
1677
+ src/web/api/request_source.c
1678
+ src/web/api/request_source.h
1679
)
1680
1681
set(CLAIM_PLUGIN_FILES
src/collectors/systemd-journal.plugin/systemd-journal-annotations.c
+1
@@ -619,6 +619,7 @@ static void netdata_systemd_journal_message_ids_init(void) {
619
msgid_into_dict("ec87a56120d5431bace51e2fb8bba243", "Netdata log flood protection");
620
msgid_into_dict("acb33cb95778476baac702eb7e4e151d", "Netdata Cloud connection");
621
msgid_into_dict("d1f59606dd4d41e3b217a0cfcae8e632", "Netdata extreme cardinality");
622
+ msgid_into_dict("4fdf40816c124623a032b7fe73beacb8", "Netdata dynamic configuration");
623
}
624
625
void netdata_systemd_journal_transform_message_id(FACETS *facets __maybe_unused, BUFFER *wb, FACETS_TRANSFORMATION_SCOPE scope __maybe_unused, void *data __maybe_unused) {
src/daemon/dyncfg/dyncfg-intercept.c
+123
-11
@@ -3,21 +3,112 @@
3
#include "dyncfg-internals.h"
4
#include "dyncfg.h"
5
6
-// ----------------------------------------------------------------------------
7
-// we intercept the config function calls of the plugin
8
-
6
struct dyncfg_call {
7
+ ND_UUID transaction;
8
BUFFER *payload;
11
- char *function;
12
- char *id;
13
- char *add_name;
14
- char *source;
9
+ const char *function;
10
+ const char *id;
11
+ const char *add_name;
12
+ const char *source;
13
DYNCFG_CMDS cmd;
14
rrd_function_result_callback_t result_cb;
15
void *result_cb_data;
16
bool from_dyncfg_echo;
17
};
18
19
+// ----------------------------------------------------------------------------
20
+
21
+ENUM_STR_MAP_DEFINE(DYNCFG_CMDS) = {
22
+ { DYNCFG_CMD_GET, "get" },
23
+ { DYNCFG_CMD_SCHEMA, "schema" },
24
+ { DYNCFG_CMD_UPDATE, "update" },
25
+ { DYNCFG_CMD_ADD, "add" },
26
+ { DYNCFG_CMD_TEST, "test" },
27
+ { DYNCFG_CMD_REMOVE, "remove" },
28
+ { DYNCFG_CMD_ENABLE, "enable" },
29
+ { DYNCFG_CMD_DISABLE, "disable" },
30
+ { DYNCFG_CMD_RESTART, "restart" },
31
+ { DYNCFG_CMD_USERCONFIG, "userconfig" },
32
+
33
+ // terminator
34
+ { 0, NULL }
35
+};
36
+
37
+ENUM_STR_DEFINE_FUNCTIONS(DYNCFG_CMDS, DYNCFG_CMD_NONE, "none");
38
+
39
+static void dyncfg_log_user_action(DYNCFG *df, struct dyncfg_call *dc) {
40
+ if(dc->cmd == DYNCFG_CMD_USERCONFIG || dc->cmd == DYNCFG_CMD_GET || dc->cmd == DYNCFG_CMD_SCHEMA)
41
+ return;
42
+
43
+ const char *type;
44
+ switch(df->type) {
45
+ default:
46
+ case DYNCFG_TYPE_SINGLE:
47
+ type = "on";
48
+ break;
49
+
50
+ case DYNCFG_TYPE_TEMPLATE:
51
+ type = "on template";
52
+ break;
53
+ case DYNCFG_TYPE_JOB:
54
+ type = "on job";
55
+ break;
56
+ }
57
+
58
+ PARSED_REQUEST_SOURCE req;
59
+ if(!parse_request_source(dc->source, &req)) {
60
+ ND_LOG_STACK lgs[] = {
61
+ ND_LOG_FIELD_TXT(NDF_MODULE, "DYNCFG"),
62
+ ND_LOG_FIELD_STR(NDF_NIDL_NODE, localhost->hostname),
63
+ ND_LOG_FIELD_TXT(NDF_REQUEST, dc->function),
64
+ ND_LOG_FIELD_UUID(NDF_TRANSACTION_ID, &dc->transaction.uuid),
65
+ ND_LOG_FIELD_UUID(NDF_MESSAGE_ID, &dyncfg_user_action_msgid),
66
+ ND_LOG_FIELD_END(),
67
+ };
68
+ ND_LOG_STACK_PUSH(lgs);
69
+
70
+ nd_log(NDLS_DAEMON, NDLP_NOTICE,
71
+ "DYNCFG USER ACTION '%s' %s%s%s '%s' from source: %s",
72
+ DYNCFG_CMDS_2str(dc->cmd),
73
+ dc->add_name ? dc->add_name : "",
74
+ dc->add_name ? " " : "",
75
+ type, dc->id, dc->source);
76
+
77
+ return;
78
+ }
79
+
80
+ char access_str[1024];
81
+ http_access2txt(access_str, sizeof(access_str), " ", req.access);
82
+
83
+ ND_LOG_STACK lgs[] = {
84
+ ND_LOG_FIELD_TXT(NDF_MODULE, "DYNCFG"),
85
+ ND_LOG_FIELD_STR(NDF_NIDL_NODE, localhost->hostname),
86
+ ND_LOG_FIELD_TXT(NDF_REQUEST, dc->function),
87
+ ND_LOG_FIELD_UUID(NDF_TRANSACTION_ID, &dc->transaction.uuid),
88
+ ND_LOG_FIELD_UUID(NDF_MESSAGE_ID, &dyncfg_user_action_msgid),
89
+
90
+ ND_LOG_FIELD_UUID(NDF_ACCOUNT_ID, &req.cloud_account_id.uuid),
91
+ ND_LOG_FIELD_TXT(NDF_SRC_IP, req.client_ip),
92
+ ND_LOG_FIELD_TXT(NDF_SRC_FORWARDED_FOR, req.forwarded_for),
93
+ ND_LOG_FIELD_TXT(NDF_USER_NAME, req.client_name),
94
+ ND_LOG_FIELD_TXT(NDF_USER_ROLE, http_id2user_role(req.user_role)),
95
+ ND_LOG_FIELD_CB(NDF_USER_ACCESS, log_cb_http_access_to_hex, &req.access),
96
+ ND_LOG_FIELD_END(),
97
+ };
98
+ ND_LOG_STACK_PUSH(lgs);
99
+
100
+ nd_log(NDLS_DAEMON, NDLP_NOTICE,
101
+ "DYNCFG USER ACTION '%s' %s%s%s '%s' by user '%s', IP '%s'",
102
+ DYNCFG_CMDS_2str(dc->cmd),
103
+ dc->add_name ? dc->add_name : "",
104
+ dc->add_name ? " " : "",
105
+ type, dc->id, req.client_name,
106
+ req.forwarded_for[0] ? req.forwarded_for : req.client_ip);
107
+}
108
+
109
+// ----------------------------------------------------------------------------
110
+// we intercept the config function calls of the plugin
111
+
112
static void dyncfg_function_intercept_job_successfully_added(DYNCFG *df_template, int code, struct dyncfg_call *dc) {
113
char id[strlen(dc->id) + 1 + strlen(dc->add_name) + 1];
114
snprintfz(id, sizeof(id), "%s:%s", dc->id, dc->add_name);
@@ -107,6 +198,8 @@ void dyncfg_function_intercept_result_cb(BUFFER *wb, int code, void *result_cb_d
198
199
if (save_required || old_user_disabled != df->dyncfg.user_disabled)
200
dyncfg_file_save(dc->id, df);
201
+
202
+ dyncfg_log_user_action(df, dc);
203
}
204
else
205
nd_log(NDLS_DAEMON, NDLP_ERR,
@@ -125,10 +218,10 @@ void dyncfg_function_intercept_result_cb(BUFFER *wb, int code, void *result_cb_d
218
dc->result_cb(wb, code, dc->result_cb_data);
219
220
buffer_free(dc->payload);
128
- freez(dc->function);
129
- freez(dc->id);
130
- freez(dc->source);
131
- freez(dc->add_name);
221
+ freez((void *)dc->function);
222
+ freez((void *)dc->id);
223
+ freez((void *)dc->source);
224
+ freez((void *)dc->add_name);
225
freez(dc);
226
}
227
@@ -377,6 +470,24 @@ int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data __
470
471
if (df->dyncfg.user_disabled != old_user_disabled)
472
dyncfg_file_save(id, df);
473
+
474
+ // log it
475
+ {
476
+ struct dyncfg_call dc = {
477
+ .function = rfe->function,
478
+ .id = id,
479
+ .source = rfe->source,
480
+ .add_name = add_name,
481
+ .cmd = cmd,
482
+ .result_cb = NULL,
483
+ .result_cb_data = NULL,
484
+ .payload = rfe->payload,
485
+ .from_dyncfg_echo = called_from_dyncfg_echo,
486
+ };
487
+ uuid_copy(dc.transaction.uuid, *rfe->transaction);
488
+
489
+ dyncfg_log_user_action(df, &dc);
490
+ }
491
}
492
493
dyncfg_apply_action_on_all_template_jobs(rfe, id, cmd);
@@ -405,6 +516,7 @@ int dyncfg_function_intercept_cb(struct rrd_function_execute *rfe, void *data __
516
517
if(make_the_call_to_plugin) {
518
struct dyncfg_call *dc = callocz(1, sizeof(*dc));
519
+ uuid_copy(dc->transaction.uuid, *rfe->transaction);
520
dc->function = strdupz(rfe->function);
521
dc->id = strdupz(id);
522
dc->source = rfe->source ? strdupz(rfe->source) : NULL;
src/daemon/dyncfg/dyncfg-tree.c
+6
-6
@@ -17,7 +17,7 @@ static int dyncfg_tree_compar(const void *a, const void *b) {
17
return rc;
18
}
19
20
-static void dyncfg_to_json(DYNCFG *df, const char *id, BUFFER *wb) {
20
+static void dyncfg_to_json(DYNCFG *df, const char *id, BUFFER *wb, bool anonymous) {
21
buffer_json_member_add_object(wb, id);
22
{
23
buffer_json_member_add_string(wb, "type", dyncfg_id2type(df->type));
@@ -34,7 +34,7 @@ static void dyncfg_to_json(DYNCFG *df, const char *id, BUFFER *wb) {
34
}
35
buffer_json_object_close(wb);
36
buffer_json_member_add_string(wb, "source_type", dyncfg_id2source_type(df->current.source_type));
37
- buffer_json_member_add_string(wb, "source", string2str(df->current.source));
37
+ buffer_json_member_add_string(wb, "source", df->current.source_type == DYNCFG_SOURCE_TYPE_DYNCFG && anonymous ? "anonymous access does not present sensitive user info" : string2str(df->current.source));
38
buffer_json_member_add_boolean(wb, "sync", df->sync);
39
buffer_json_member_add_boolean(wb, "user_disabled", df->dyncfg.user_disabled);
40
buffer_json_member_add_boolean(wb, "restart_required", df->dyncfg.restart_required);
@@ -45,7 +45,7 @@ static void dyncfg_to_json(DYNCFG *df, const char *id, BUFFER *wb) {
45
buffer_json_member_add_boolean(wb, "available", true);
46
buffer_json_member_add_string(wb, "status", dyncfg_id2status(df->dyncfg.status));
47
buffer_json_member_add_string(wb, "source_type", dyncfg_id2source_type(df->dyncfg.source_type));
48
- buffer_json_member_add_string(wb, "source", string2str(df->dyncfg.source));
48
+ buffer_json_member_add_string(wb, "source", df->dyncfg.source_type == DYNCFG_SOURCE_TYPE_DYNCFG && anonymous ? "anonymous access does not present sensitive user info" :string2str(df->dyncfg.source));
49
buffer_json_member_add_uint64(wb, "created_ut", df->dyncfg.created_ut);
50
buffer_json_member_add_uint64(wb, "modified_ut", df->dyncfg.modified_ut);
51
buffer_json_member_add_string(wb, "content_type", content_type_id2string(df->dyncfg.payload->content_type));
@@ -61,7 +61,7 @@ static void dyncfg_to_json(DYNCFG *df, const char *id, BUFFER *wb) {
61
buffer_json_object_close(wb);
62
}
63
64
-static void dyncfg_tree_for_host(RRDHOST *host, BUFFER *wb, const char *path, const char *id) {
64
+static void dyncfg_tree_for_host(RRDHOST *host, BUFFER *wb, const char *path, const char *id, bool anonymous) {
65
size_t entries = dictionary_entries(dyncfg_globals.nodes);
66
size_t used = 0;
67
const DICTIONARY_ITEM *items[entries];
@@ -112,7 +112,7 @@ static void dyncfg_tree_for_host(RRDHOST *host, BUFFER *wb, const char *path, co
112
buffer_json_member_add_object(wb, string2str(last_path));
113
}
114
115
- dyncfg_to_json(df, dictionary_acquired_item_name(items[i]), wb);
115
+ dyncfg_to_json(df, dictionary_acquired_item_name(items[i]), wb, anonymous);
116
117
if (df->dyncfg.plugin_rejected)
118
plugin_rejected++;
@@ -195,7 +195,7 @@ static int dyncfg_config_execute_cb(struct rrd_function_execute *rfe, void *data
195
}
196
197
code = HTTP_RESP_OK;
198
- dyncfg_tree_for_host(host, rfe->result.wb, path, id);
198
+ dyncfg_tree_for_host(host, rfe->result.wb, path, id, !(rfe->user_access & HTTP_ACCESS_SENSITIVE_DATA));
199
}
200
else {
201
const char *name = id;
src/libnetdata/http/http_access.c
+8
-1
@@ -166,6 +166,13 @@ HTTP_ACCESS http_access_from_hex(const char *str) {
166
return (HTTP_ACCESS)strtoull(str, NULL, 16) & HTTP_ACCESS_ALL;
167
}
168
169
+HTTP_ACCESS http_access_from_hex_str(const char *str) {
170
+ if (!str || !*str)
171
+ return HTTP_ACCESS_NONE;
172
+
173
+ return (HTTP_ACCESS)strtoull(str, NULL, 16) & HTTP_ACCESS_ALL;
174
+}
175
+
176
HTTP_ACCESS http_access_from_source(const char *str) {
177
if(!str || !*str)
178
return HTTP_ACCESS_NONE;
@@ -174,7 +181,7 @@ HTTP_ACCESS http_access_from_source(const char *str) {
181
182
const char *permissions = strstr(str, "permissions=");
183
if(permissions)
177
- access = (HTTP_ACCESS)strtoull(permissions + 12, NULL, 16) & HTTP_ACCESS_ALL;
184
+ access = http_access_from_hex_str(permissions + 12);
185
186
return access;
187
}
src/libnetdata/http/http_access.h
+1
@@ -74,6 +74,7 @@ void http_access2buffer_json_array(struct web_buffer *wb, const char *key, HTTP_
74
void http_access2txt(char *buf, size_t size, const char *separator, HTTP_ACCESS access);
75
HTTP_ACCESS http_access_from_hex(const char *str);
76
HTTP_ACCESS http_access_from_hex_mapping_old_roles(const char *str);
77
+HTTP_ACCESS http_access_from_hex_str(const char *str);
78
HTTP_ACCESS http_access_from_source(const char *str);
79
bool log_cb_http_access_to_hex(struct web_buffer *wb, void *data);
80
src/libnetdata/uuid/uuid.h
+1
@@ -37,6 +37,7 @@ ND_UUID_DEFINE(log_flood_protection_msgid, 0xec, 0x87, 0xa5, 0x61, 0x20, 0xd5, 0
37
ND_UUID_DEFINE(netdata_startup_msgid, 0x1e, 0x60, 0x61, 0xa9, 0xfb, 0xd4, 0x45, 0x01, 0xb3, 0xcc, 0xc3, 0x68, 0x11, 0x9f, 0x2b, 0x69);
38
ND_UUID_DEFINE(aclk_connection_msgid, 0xac, 0xb3, 0x3c, 0xb9, 0x57, 0x78, 0x47, 0x6b, 0xaa, 0xc7, 0x02, 0xeb, 0x7e, 0x4e, 0x15, 0x1d);
39
ND_UUID_DEFINE(extreme_cardinality_msgid, 0xd1, 0xf5, 0x96, 0x06, 0xdd, 0x4d, 0x41, 0xe3, 0xb2, 0x17, 0xa0, 0xcf, 0xca, 0xe8, 0xe6, 0x32);
40
+ND_UUID_DEFINE(dyncfg_user_action_msgid, 0x4f, 0xdf, 0x40, 0x81, 0x6c, 0x12, 0x46, 0x23, 0xa0, 0x32, 0xb7, 0xfe, 0x73, 0xbe, 0xac, 0xb8);
41
42
ND_UUID UUID_generate_from_hash(const void *payload, size_t payload_len);
43
src/web/api/request_source.c
new
+97
@@ -0,0 +1,97 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+#include "request_source.h"
4
+
5
+bool request_source_is_cloud(const char *source) {
6
+ return source && *source && strstartswith(source, "method=NC,");
7
+}
8
+
9
+void web_client_api_request_vX_source_to_buffer(struct web_client *w, BUFFER *source) {
10
+ if(web_client_flag_check(w, WEB_CLIENT_FLAG_AUTH_CLOUD))
11
+ buffer_sprintf(source, "method=NC");
12
+ else if(web_client_flag_check(w, WEB_CLIENT_FLAG_AUTH_BEARER))
13
+ buffer_sprintf(source, "method=api-bearer");
14
+ else
15
+ buffer_sprintf(source, "method=api");
16
+
17
+ if(web_client_flag_check(w, WEB_CLIENT_FLAG_AUTH_GOD))
18
+ buffer_strcat(source, ",role=god");
19
+ else
20
+ buffer_sprintf(source, ",role=%s", http_id2user_role(w->user_role));
21
+
22
+ buffer_sprintf(source, ",permissions="HTTP_ACCESS_FORMAT, (HTTP_ACCESS_FORMAT_CAST)w->access);
23
+
24
+ if(w->auth.client_name[0])
25
+ buffer_sprintf(source, ",user=%s", w->auth.client_name);
26
+
27
+ if(!uuid_is_null(w->auth.cloud_account_id)) {
28
+ char uuid_str[UUID_COMPACT_STR_LEN];
29
+ uuid_unparse_lower_compact(w->auth.cloud_account_id, uuid_str);
30
+ buffer_sprintf(source, ",account=%s", uuid_str);
31
+ }
32
+
33
+ if(w->client_ip[0])
34
+ buffer_sprintf(source, ",ip=%s", w->client_ip);
35
+
36
+ if(w->forwarded_for)
37
+ buffer_sprintf(source, ",forwarded_for=%s", w->forwarded_for);
38
+}
39
+
40
+bool parse_request_source(const char *src, PARSED_REQUEST_SOURCE *parsed) {
41
+ char *buf, *token, *saveptr;
42
+
43
+ if (!src || !parsed)
44
+ return false;
45
+
46
+ // Initialize the structure.
47
+ memset(parsed, 0, sizeof(*parsed));
48
+
49
+ // Duplicate the source string because strtok_r modifies it.
50
+ buf = strdupz(src);
51
+ token = strtok_r(buf, ",", &saveptr);
52
+ while (token) {
53
+ char *equal_sign = strchr(token, '=');
54
+ if (equal_sign) {
55
+ *equal_sign = '\0';
56
+ const char *key = token;
57
+ const char *value = equal_sign + 1;
58
+
59
+ if (strcmp(key, "method") == 0) {
60
+ if (strcmp(value, "NC") == 0)
61
+ parsed->auth |= WEB_CLIENT_FLAG_AUTH_CLOUD;
62
+ else if (strcmp(value, "api-bearer") == 0)
63
+ parsed->auth |= WEB_CLIENT_FLAG_AUTH_BEARER;
64
+ // "api" does not add an extra flag.
65
+ }
66
+ else if (strcmp(key, "role") == 0) {
67
+ if (strcmp(value, "god") == 0)
68
+ parsed->auth |= WEB_CLIENT_FLAG_AUTH_GOD;
69
+ else
70
+ parsed->user_role = http_user_role2id(value);
71
+ }
72
+ else if (strcmp(key, "permissions") == 0)
73
+ parsed->access = http_access_from_hex_str(value);
74
+
75
+ else if (strcmp(key, "user") == 0) {
76
+ strncpy(parsed->client_name, value, CLOUD_CLIENT_NAME_LENGTH - 1);
77
+ parsed->client_name[CLOUD_CLIENT_NAME_LENGTH - 1] = '\0';
78
+ }
79
+ else if (strcmp(key, "account") == 0) {
80
+ if (uuid_parse(value, parsed->cloud_account_id.uuid) != 0)
81
+ parsed->cloud_account_id = UUID_ZERO;
82
+ }
83
+ else if (strcmp(key, "ip") == 0) {
84
+ strncpy(parsed->client_ip, value, INET6_ADDRSTRLEN - 1);
85
+ parsed->client_ip[INET6_ADDRSTRLEN - 1] = '\0';
86
+ }
87
+ else if (strcmp(key, "forwarded_for") == 0) {
88
+ strncpy(parsed->forwarded_for, value, INET6_ADDRSTRLEN - 1);
89
+ parsed->forwarded_for[INET6_ADDRSTRLEN - 1] = '\0';
90
+ }
91
+ }
92
+ token = strtok_r(NULL, ",", &saveptr);
93
+ }
94
+
95
+ freez(buf);
96
+ return true;
97
+}
src/web/api/request_source.h
new
+24
@@ -0,0 +1,24 @@
1
+// SPDX-License-Identifier: GPL-3.0-or-later
2
+
3
+#ifndef NETDATA_REQUEST_SOURCE_H
4
+#define NETDATA_REQUEST_SOURCE_H
5
+
6
+#include "web_api.h"
7
+
8
+typedef struct {
9
+ char client_ip[INET6_ADDRSTRLEN];
10
+ char forwarded_for[INET6_ADDRSTRLEN];
11
+ char client_name[CLOUD_CLIENT_NAME_LENGTH];
12
+ ND_UUID cloud_account_id;
13
+ WEB_CLIENT_FLAGS auth;
14
+ HTTP_USER_ROLE user_role;
15
+ HTTP_ACCESS access;
16
+} PARSED_REQUEST_SOURCE;
17
+
18
+bool parse_request_source(const char *src, PARSED_REQUEST_SOURCE *parsed);
19
+
20
+bool request_source_is_cloud(const char *source);
21
+
22
+void web_client_api_request_vX_source_to_buffer(struct web_client *w, BUFFER *source);
23
+
24
+#endif //NETDATA_REQUEST_SOURCE_H
src/web/api/web_api.c
-36
@@ -138,42 +138,6 @@ void nd_web_api_init(void) {
138
time_grouping_init();
139
}
140
141
-
142
-bool request_source_is_cloud(const char *source) {
143
- return source && *source && strstartswith(source, "method=NC,");
144
-}
145
-
146
-void web_client_api_request_vX_source_to_buffer(struct web_client *w, BUFFER *source) {
147
- if(web_client_flag_check(w, WEB_CLIENT_FLAG_AUTH_CLOUD))
148
- buffer_sprintf(source, "method=NC");
149
- else if(web_client_flag_check(w, WEB_CLIENT_FLAG_AUTH_BEARER))
150
- buffer_sprintf(source, "method=api-bearer");
151
- else
152
- buffer_sprintf(source, "method=api");
153
-
154
- if(web_client_flag_check(w, WEB_CLIENT_FLAG_AUTH_GOD))
155
- buffer_strcat(source, ",role=god");
156
- else
157
- buffer_sprintf(source, ",role=%s", http_id2user_role(w->user_role));
158
-
159
- buffer_sprintf(source, ",permissions="HTTP_ACCESS_FORMAT, (HTTP_ACCESS_FORMAT_CAST)w->access);
160
-
161
- if(w->auth.client_name[0])
162
- buffer_sprintf(source, ",user=%s", w->auth.client_name);
163
-
164
- if(!uuid_is_null(w->auth.cloud_account_id)) {
165
- char uuid_str[UUID_COMPACT_STR_LEN];
166
- uuid_unparse_lower_compact(w->auth.cloud_account_id, uuid_str);
167
- buffer_sprintf(source, ",account=%s", uuid_str);
168
- }
169
-
170
- if(w->client_ip[0])
171
- buffer_sprintf(source, ",ip=%s", w->client_ip);
172
-
173
- if(w->forwarded_for)
174
- buffer_sprintf(source, ",forwarded_for=%s", w->forwarded_for);
175
-}
176
-
141
void web_client_progress_functions_update(void *data, size_t done, size_t all) {
142
// handle progress updates from the plugin
143
struct web_client *w = data;
src/web/api/web_api.h
+1
-2
@@ -16,11 +16,10 @@ struct web_client;
16
#include "web/api/http_auth.h"
17
#include "web/api/formatters/rrd2json.h"
18
#include "web/api/queries/weights.h"
19
+#include "web/api/request_source.h"
20
21
void nd_web_api_init(void);
22
22
-bool request_source_is_cloud(const char *source);
23
-void web_client_api_request_vX_source_to_buffer(struct web_client *w, BUFFER *source);
23
void web_client_progress_functions_update(void *data, size_t done, size_t all);
24
25
void host_labels2json(RRDHOST *host, BUFFER *wb, const char *key);