implemented API authentication
Costa Tsaousis (ktsaou) committed
Feb 22, 2017 at 03:40 UTC
842ada7cd5bcf8d018df718efc629571ba2103fa
8 files changed
+182
-29
conf.d/Makefile.am
+1
@@ -4,6 +4,7 @@
4
MAINTAINERCLEANFILES= $(srcdir)/Makefile.in
5
6
dist_config_DATA = \
7
+ aggregated_hosts.conf \
8
apps_groups.conf \
9
charts.d.conf \
10
fping.conf \
conf.d/aggregated_hosts.conf
new
+75
@@ -0,0 +1,75 @@
1
+# netdata configuration for aggregating data from remote hosts
2
+#
3
+# 1. You need an API key: API_KEY_GENERATED_BY_UUIDGEN
4
+#
5
+# You can generate one with the command: uuidgen
6
+# You can add many API key sections, for different API keys
7
+#
8
+# 2. All options below are used in this order:
9
+#
10
+# a) MACHINE_GUID (settings for each machine)
11
+# b) API_KEY (settings for the API key)
12
+# c) this netdata defaults (as in netdata.conf)
13
+#
14
+# You can combine the above (the more specific will be used).
15
+#
16
+# 3. At the remote host that will be sending metrics, you need
17
+# to add in netdata.conf, the following:
18
+#
19
+# [global]
20
+# central netdata to send all data = 10.11.12.1:19999
21
+# central netdata api key = API_KEY_GENERATED_BY_UUIDGEN
22
+#
23
+# Of course, the same API_KEY_GENERATED_BY_UUIDGEN key must
24
+# given there (the remote host) and here (the central netdata).
25
+#
26
+# -----------------------------------------------------------------------------
27
+
28
+[API_KEY_GENERATED_BY_UUIDGEN]
29
+ # You can disable the API key, by setting this to: no
30
+ # The default (for unknown API keys) is also: no
31
+# enabled = yes
32
+
33
+ # The default history in entries, for all hosts using this API key.
34
+ # You can also set it per host below.
35
+ # If you don't set it here, the history size of the central netdata
36
+ # will be used
37
+# default history = 3600
38
+
39
+ # The default memory mode to be used for all hosts using this API key.
40
+ # You can also set it per host below.
41
+ # If you don't set it here, the memory mode of the central netdata
42
+ # will be used
43
+# default memory mode = save
44
+
45
+ # Shall we enable health monitoring for the hosts using this API key?
46
+ # 3 values:
47
+ # yes enable alarms
48
+ # no do not enable alarms
49
+ # auto enable alarms, only when the host is streaming metrics
50
+ # You can also set it per host, below.
51
+ # The default is the same as the central netdata
52
+# health enabled by default = auto
53
+
54
+
55
+# -----------------------------------------------------------------------------
56
+# Each netdata has a unique GUID - generated the first time netdata starts.
57
+# You can find it at /var/lib/netdata/registry/netdata.public.unique.id
58
+# The host sending data will have one. If it is static and you can find it,
59
+# you can give settings for each specific host here.
60
+
61
+[MACHINE_GUID]
62
+ # This can be used to stop receiving data
63
+ # THIS IS NOT A SECURITY MECHANISM - AN ATTACKER CAN SET ANY OTHER GUID.
64
+ # Use only the API key for security.
65
+# enabled = yes
66
+
67
+ # The number of entries in the database
68
+# history = 3600
69
+
70
+ # The memory mode of the database
71
+# memory mode = save
72
+
73
+ # Health / alarms control
74
+# health enabled = yes
75
+
src/main.c
+5
-1
@@ -844,8 +844,12 @@ int main(int argc, char **argv) {
844
// --------------------------------------------------------------------
845
// create the listening sockets
846
847
- if(!check_config && !central_netdata_to_push_data)
847
+ if(!check_config && !central_netdata_to_push_data) {
848
+ char filename[FILENAME_MAX + 1];
849
+ snprintfz(filename, FILENAME_MAX, "%s/aggregated_hosts.conf", netdata_configured_config_dir);
850
+ appconfig_load(&stream_config, filename, 0);
851
create_listen_sockets();
852
+ }
853
}
854
855
// initialize the log files
src/plugins_d.c
+3
-3
@@ -96,7 +96,7 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int
96
char line[PLUGINSD_LINE_MAX + 1];
97
98
char *words[MAX_WORDS] = { NULL };
99
- uint32_t HOST_HASH = simple_hash("HOST");
99
+ /* uint32_t HOST_HASH = simple_hash("HOST"); */
100
uint32_t BEGIN_HASH = simple_hash("BEGIN");
101
uint32_t END_HASH = simple_hash("END");
102
uint32_t FLUSH_HASH = simple_hash("FLUSH");
@@ -205,7 +205,7 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int
205
206
count++;
207
}
208
- else if(likely(hash == HOST_HASH && !strcmp(s, "HOST"))) {
208
+/* else if(likely(hash == HOST_HASH && !strcmp(s, "HOST"))) {
209
char *guid = words[1];
210
char *hostname = words[2];
211
@@ -221,7 +221,7 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int
221
}
222
223
host = rrdhost_find_or_create(hostname, guid);
224
- }
224
+ } */
225
else if(likely(hash == FLUSH_HASH && !strcmp(s, "FLUSH"))) {
226
debug(D_PLUGINSD, "PLUGINSD: '%s' is requesting a FLUSH", cd->fullfilename);
227
st = NULL;
src/rrd.h
+1
-1
@@ -406,7 +406,7 @@ extern pthread_rwlock_t rrd_rwlock;
406
extern void rrd_init(char *hostname);
407
408
extern RRDHOST *rrdhost_find(const char *guid, uint32_t hash);
409
-extern RRDHOST *rrdhost_find_or_create(const char *hostname, const char *guid);
409
+extern RRDHOST *rrdhost_find_or_create(const char *hostname, const char *guid, int update_every, int history, RRD_MEMORY_MODE mode, int health_enabled);
410
411
#ifdef NETDATA_INTERNAL_CHECKS
412
extern void rrdhost_check_wrlock_int(RRDHOST *host, const char *file, const char *function, const unsigned long line);
src/rrdhost.c
+23
-9
@@ -176,18 +176,32 @@ RRDHOST *rrdhost_create(const char *hostname,
176
return host;
177
}
178
179
-RRDHOST *rrdhost_find_or_create(const char *hostname, const char *guid) {
179
+RRDHOST *rrdhost_find_or_create(const char *hostname, const char *guid, int update_every, int history, RRD_MEMORY_MODE mode, int health_enabled) {
180
debug(D_RRDHOST, "Searching for host '%s' with guid '%s'", hostname, guid);
181
182
RRDHOST *host = rrdhost_find(guid, 0);
183
- if(!host)
184
- host = rrdhost_create(hostname,
185
- guid,
186
- default_rrd_update_every,
187
- default_rrd_history_entries,
188
- default_rrd_memory_mode,
189
- default_health_enabled
190
- );
183
+ if(!host) {
184
+ host = rrdhost_create(hostname, guid, update_every, history, mode, health_enabled);
185
+ }
186
+ else {
187
+ host->health_enabled = health_enabled;
188
+
189
+ if(strcmp(host->hostname, hostname)) {
190
+ char *t = host->hostname;
191
+ char *n = strdupz(hostname);
192
+ host->hostname = n;
193
+ freez(t);
194
+ }
195
+
196
+ if(host->rrd_update_every != update_every)
197
+ error("Host '%s' has an update frequency of %d seconds, but the wanted one is %d seconds.", host->hostname, host->rrd_update_every, update_every);
198
+
199
+ if(host->rrd_history_entries != history)
200
+ error("Host '%s' has history of %d entries, but the wanted one is %d entries.", host->hostname, host->rrd_history_entries, history);
201
+
202
+ if(host->rrd_memory_mode != mode)
203
+ error("Host '%s' has memory mode '%s', but the wanted one is '%s'.", host->hostname, rrd_memory_mode_name(host->rrd_memory_mode), rrd_memory_mode_name(mode));
204
+ }
205
206
return host;
207
}
src/rrdpush.c
+10
-10
@@ -7,7 +7,6 @@ int rrdpush_pipe[2];
7
8
static BUFFER *rrdpush_buffer = NULL;
9
static pthread_mutex_t rrdpush_mutex = PTHREAD_MUTEX_INITIALIZER;
10
-static volatile RRDHOST *last_host = NULL;
10
static volatile int rrdpush_connected = 0;
11
12
static inline void rrdpush_lock() {
@@ -99,8 +98,6 @@ static void reset_all_charts(void) {
98
rrdhost_unlock(host);
99
}
100
rrd_unlock();
102
-
103
- last_host = NULL;
101
}
102
103
void rrdset_done_push(RRDSET *st) {
@@ -123,11 +120,6 @@ void rrdset_done_push(RRDSET *st) {
120
}
121
error_shown = 0;
122
126
- if(st->rrdhost != last_host) {
127
- buffer_sprintf(rrdpush_buffer, "HOST '%s' '%s'\n", st->rrdhost->machine_guid, st->rrdhost->hostname);
128
- last_host = st->rrdhost;
129
- }
130
-
123
rrdset_rdlock(st);
124
if(need_to_send_chart_definition(st))
125
send_chart_definition(st);
@@ -149,7 +141,6 @@ static inline void rrdpush_flush(void) {
141
142
buffer_flush(rrdpush_buffer);
143
reset_all_charts();
152
- last_host = NULL;
144
rrdpush_unlock();
145
}
146
@@ -209,7 +200,16 @@ void *central_netdata_push_thread(void *ptr) {
200
info("STREAM: initializing communication to central netdata at: %s", central_netdata_to_push_data);
201
202
char http[1000 + 1];
212
- snprintfz(http, 1000, "GET /stream?key=%s HTTP/1.1\r\nUser-Agent: netdata-push-service/%s\r\nAccept: */*\r\n\r\n", config_get("global", "central netdata api key", ""), program_version);
203
+ snprintfz(http, 1000, "GET /stream?key=%s&hostname=%s&machine_guid=%s&update_every=%d HTTP/1.1\r\n"
204
+ "User-Agent: netdata-push-service/%s\r\n"
205
+ "Accept: */*\r\n\r\n"
206
+ , config_get("global", "central netdata api key", "")
207
+ , localhost->hostname
208
+ , localhost->machine_guid
209
+ , default_rrd_update_every
210
+ , program_version
211
+ );
212
+
213
if(send_timeout(sock, http, strlen(http), 0, 60) == -1) {
214
close(sock);
215
sock = -1;
src/web_client.c
+64
-5
@@ -1668,13 +1668,18 @@ int web_client_api_old_data_request(RRDHOST *host, struct web_client *w, char *u
1668
}
1669
1670
int validate_stream_api_key(const char *key) {
1671
+ if(appconfig_get(&stream_config, key, "enabled", 0))
1672
+ return 1;
1673
+
1674
return 0;
1675
}
1676
1677
int web_client_stream_request(RRDHOST *host, struct web_client *w, char *url) {
1675
- info("STREAM request from client '%s:%s', starting as host '%s'", w->client_ip, w->client_port, host->hostname);
1676
-
1677
- char *key = NULL;
1678
+ char *key = NULL, *hostname = NULL, *machine_guid = NULL;
1679
+ int update_every = default_rrd_update_every;
1680
+ int history = default_rrd_history_entries;
1681
+ RRD_MEMORY_MODE mode = default_rrd_memory_mode;
1682
+ int health_enabled = default_health_enabled;
1683
1684
while(url) {
1685
char *value = mystrsep(&url, "?&");
@@ -1686,6 +1691,12 @@ int web_client_stream_request(RRDHOST *host, struct web_client *w, char *url) {
1691
1692
if(!strcmp(name, "key"))
1693
key = value;
1694
+ else if(!strcmp(name, "hostname"))
1695
+ hostname = value;
1696
+ else if(!strcmp(name, "machine_guid"))
1697
+ machine_guid = value;
1698
+ else if(!strcmp(name, "update_every"))
1699
+ update_every = (int)strtoul(value, NULL, 0);
1700
}
1701
1702
if(!key || !*key) {
@@ -1695,6 +1706,20 @@ int web_client_stream_request(RRDHOST *host, struct web_client *w, char *url) {
1706
return 401;
1707
}
1708
1709
+ if(!hostname || !*hostname) {
1710
+ error("STREAM [%s]:%s: request without a hostname. Forbidding access.", w->client_ip, w->client_port);
1711
+ buffer_flush(w->response.data);
1712
+ buffer_sprintf(w->response.data, "You need to send a hostname too.");
1713
+ return 400;
1714
+ }
1715
+
1716
+ if(!machine_guid || !*machine_guid) {
1717
+ error("STREAM [%s]:%s: request without a machine GUID. Forbidding access.", w->client_ip, w->client_port);
1718
+ buffer_flush(w->response.data);
1719
+ buffer_sprintf(w->response.data, "You need to send a machine GUID too.");
1720
+ return 400;
1721
+ }
1722
+
1723
if(!validate_stream_api_key(key)) {
1724
error("STREAM [%s]:%s: API key '%s' is not allowed. Forbidding access.", w->client_ip, w->client_port, key);
1725
buffer_flush(w->response.data);
@@ -1702,6 +1727,38 @@ int web_client_stream_request(RRDHOST *host, struct web_client *w, char *url) {
1727
return 401;
1728
}
1729
1730
+ if(!appconfig_get_boolean(&stream_config, machine_guid, "enabled", 1)) {
1731
+ error("STREAM [%s]:%s: machine GUID '%s' is not allowed. Forbidding access.", w->client_ip, w->client_port, machine_guid);
1732
+ buffer_flush(w->response.data);
1733
+ buffer_sprintf(w->response.data, "Your machine guide is not permitted access.");
1734
+ return 404;
1735
+ }
1736
+
1737
+ // update_every = (int)appconfig_get_number(&stream_config, key, "default update every", update_every);
1738
+ update_every = (int)appconfig_get_number(&stream_config, machine_guid, "update every", update_every);
1739
+ if(update_every < 0) update_every = 1;
1740
+
1741
+ history = (int)appconfig_get_number(&stream_config, key, "default history", history);
1742
+ history = (int)appconfig_get_number(&stream_config, machine_guid, "history", history);
1743
+ if(history < 5) history = 5;
1744
+
1745
+ mode = rrd_memory_mode_id(appconfig_get(&stream_config, key, "default memory mode", rrd_memory_mode_name(mode)));
1746
+ mode = rrd_memory_mode_id(appconfig_get(&stream_config, machine_guid, "memory mode", rrd_memory_mode_name(mode)));
1747
+
1748
+ health_enabled = appconfig_get_boolean_ondemand(&stream_config, key, "health enabled by default", health_enabled);
1749
+ health_enabled = appconfig_get_boolean_ondemand(&stream_config, machine_guid, "health enabled", health_enabled);
1750
+
1751
+ host = rrdhost_find_or_create(hostname, machine_guid, update_every, history, mode, health_enabled?1:0);
1752
+
1753
+ info("STREAM request from client '%s:%s' for host '%s' with machine_guid '%s': update every = %d, history = %d, memory mode = %s, health %s",
1754
+ w->client_ip, w->client_port,
1755
+ hostname, machine_guid,
1756
+ update_every,
1757
+ history,
1758
+ rrd_memory_mode_name(mode),
1759
+ (health_enabled == CONFIG_BOOLEAN_NO)?"disabled":((health_enabled == CONFIG_BOOLEAN_YES)?"enabled":"auto")
1760
+ );
1761
+
1762
struct plugind cd = {
1763
.enabled = 1,
1764
.update_every = default_rrd_update_every,
@@ -1750,9 +1807,11 @@ int web_client_stream_request(RRDHOST *host, struct web_client *w, char *url) {
1807
}
1808
1809
// call the plugins.d processor to receive the metrics
1753
- info("STREAM [%s]:%s: connecting client to plugins.d.", w->client_ip, w->client_port);
1810
+ info("STREAM [%s]:%s: connecting client to plugins.d on host '%s' with machine GUID '%s'.", w->client_ip, w->client_port, host->hostname, host->machine_guid);
1811
size_t count = pluginsd_process(host, &cd, fp, 1);
1755
- error("STREAM [%s]:%s: client disconnected.", w->client_ip, w->client_port);
1812
+ error("STREAM [%s]:%s: client disconnected (host '%s', machine GUID '%s').", w->client_ip, w->client_port, host->hostname, host->machine_guid);
1813
+ if(health_enabled == CONFIG_BOOLEAN_AUTO)
1814
+ host->health_enabled = 0;
1815
1816
// close all sockets, to let the socket worker we are done
1817
fclose(fp);