Add HTTP basic authentication to some exporting connectors (#11394)
Vladimir Kobal committed
Aug 4, 2021 at 16:26 UTC
416e9f4a4a4d5f395592f3aaa8212755bbb5851e
17 files changed
+128
-6
exporting/README.md
+2
@@ -164,6 +164,8 @@ You can configure each connector individually using the available [options](#opt
164
[opentsdb:http:my_opentsdb_http_instance]
165
enabled = yes
166
destination = localhost:4242
167
+ username = my_username
168
+ password = my_password
169
170
[opentsdb:https:my_opentsdb_https_instance]
171
enabled = yes
exporting/clean_connectors.c
+4
@@ -15,6 +15,8 @@ static void clean_instance_config(struct instance_config *config)
15
freez((void *)config->type_name);
16
freez((void *)config->name);
17
freez((void *)config->destination);
18
+ freez((void *)config->username);
19
+ freez((void *)config->password);
20
freez((void *)config->prefix);
21
freez((void *)config->hostname);
22
@@ -49,6 +51,8 @@ void simple_connector_cleanup(struct instance *instance)
51
struct simple_connector_data *simple_connector_data =
52
(struct simple_connector_data *)instance->connector_specific_data;
53
54
+ freez(simple_connector_data->auth_string);
55
+
56
buffer_free(instance->buffer);
57
buffer_free(simple_connector_data->buffer);
58
buffer_free(simple_connector_data->header);
exporting/exporting.conf
+5
@@ -17,6 +17,9 @@
17
# [graphite:my_graphite_instance]
18
# enabled = no
19
# destination = localhost
20
+ # Credentials for basic HTTP authentication
21
+ # username = my_username
22
+ # password = my_password
23
# data source = average
24
# prefix = netdata
25
# hostname = my_hostname
@@ -31,6 +34,8 @@
34
# enabled = no
35
# destination = localhost
36
# remote write URL path = /receive
37
+ # username = my_username
38
+ # password = my_password
39
# data source = average
40
# prefix = netdata
41
# hostname = my_hostname
exporting/exporting_engine.h
+4
@@ -66,6 +66,8 @@ struct instance_config {
66
67
const char *name;
68
const char *destination;
69
+ const char *username;
70
+ const char *password;
71
const char *prefix;
72
const char *hostname;
73
@@ -104,6 +106,8 @@ struct simple_connector_data {
106
void *connector_specific_data;
107
108
char connected_to[CONNECTED_TO_MAX];
109
+
110
+ char *auth_string;
111
112
size_t total_buffered_metrics;
113
exporting/graphite/README.md
+6
-1
@@ -22,7 +22,12 @@ directory and set the following options:
22
```
23
24
Add `:http` or `:https` modifiers to the connector type if you need to use other than a plaintext protocol. For example: `graphite:http:my_graphite_instance`,
25
-`graphite:https:my_graphite_instance`.
25
+`graphite:https:my_graphite_instance`. You can set basic HTTP authentication credentials using
26
+
27
+```conf
28
+ username = my_username
29
+ password = my_password
30
+```
31
32
The Graphite connector is further configurable using additional settings. See the [exporting reference
33
doc](/exporting/README.md#options) for details.
exporting/graphite/graphite.c
+2
@@ -218,10 +218,12 @@ void graphite_http_prepare_header(struct instance *instance)
218
simple_connector_data->last_buffer->header,
219
"POST /api/put HTTP/1.1\r\n"
220
"Host: %s\r\n"
221
+ "%s"
222
"Content-Type: application/graphite\r\n"
223
"Content-Length: %lu\r\n"
224
"\r\n",
225
instance->config.destination,
226
+ simple_connector_data->auth_string ? simple_connector_data->auth_string : "",
227
buffer_strlen(simple_connector_data->last_buffer->buffer));
228
229
return;
exporting/init_connectors.c
+70
-1
@@ -105,8 +105,57 @@ int init_connectors(struct engine *engine)
105
return 0;
106
}
107
108
+// TODO: use a base64 encoder from a library
109
+static size_t base64_encode(unsigned char *input, size_t input_size, char *output, size_t output_size)
110
+{
111
+ uint32_t value;
112
+ static char lookup[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
113
+ "abcdefghijklmnopqrstuvwxyz"
114
+ "0123456789+/";
115
+ if ((input_size / 3 + 1) * 4 >= output_size) {
116
+ error("Output buffer for encoding size=%zu is not large enough for %zu-bytes input", output_size, input_size);
117
+ return 0;
118
+ }
119
+ size_t count = 0;
120
+ while (input_size > 3) {
121
+ value = ((input[0] << 16) + (input[1] << 8) + input[2]) & 0xffffff;
122
+ output[0] = lookup[value >> 18];
123
+ output[1] = lookup[(value >> 12) & 0x3f];
124
+ output[2] = lookup[(value >> 6) & 0x3f];
125
+ output[3] = lookup[value & 0x3f];
126
+ //error("Base-64 encode (%04x) -> %c %c %c %c\n", value, output[0], output[1], output[2], output[3]);
127
+ output += 4;
128
+ input += 3;
129
+ input_size -= 3;
130
+ count += 4;
131
+ }
132
+ switch (input_size) {
133
+ case 2:
134
+ value = (input[0] << 10) + (input[1] << 2);
135
+ output[0] = lookup[(value >> 12) & 0x3f];
136
+ output[1] = lookup[(value >> 6) & 0x3f];
137
+ output[2] = lookup[value & 0x3f];
138
+ output[3] = '=';
139
+ //error("Base-64 encode (%06x) -> %c %c %c %c\n", (value>>2)&0xffff, output[0], output[1], output[2], output[3]);
140
+ count += 4;
141
+ break;
142
+ case 1:
143
+ value = input[0] << 4;
144
+ output[0] = lookup[(value >> 6) & 0x3f];
145
+ output[1] = lookup[value & 0x3f];
146
+ output[2] = '=';
147
+ output[3] = '=';
148
+ //error("Base-64 encode (%06x) -> %c %c %c %c\n", value, output[0], output[1], output[2], output[3]);
149
+ count += 4;
150
+ break;
151
+ case 0:
152
+ break;
153
+ }
154
+ return count;
155
+}
156
+
157
/**
109
- * Initialize a ring buffer for a simple connector
158
+ * Initialize a ring buffer and credentials for a simple connector
159
*
160
* @param instance an instance data structure.
161
*/
@@ -141,5 +190,25 @@ void simple_connector_init(struct instance *instance)
190
first_buffer->next = connector_specific_data->first_buffer;
191
connector_specific_data->last_buffer = connector_specific_data->first_buffer;
192
193
+ if (*instance->config.username || *instance->config.password) {
194
+ BUFFER *auth_string = buffer_create(0);
195
+
196
+ buffer_sprintf(auth_string, "%s:%s", instance->config.username, instance->config.password);
197
+
198
+ size_t encoded_size = (buffer_strlen(auth_string) / 3 + 1) * 4 + 1;
199
+ char *encoded_credentials = callocz(1, encoded_size);
200
+
201
+ base64_encode((unsigned char*)buffer_tostring(auth_string), buffer_strlen(auth_string), encoded_credentials, encoded_size);
202
+
203
+ buffer_flush(auth_string);
204
+ buffer_sprintf(auth_string, "Authorization: Basic %s\n", encoded_credentials);
205
+
206
+ freez(encoded_credentials);
207
+
208
+ connector_specific_data->auth_string = strdupz(buffer_tostring(auth_string));
209
+
210
+ buffer_free(auth_string);
211
+ }
212
+
213
return;
214
}
exporting/json/README.md
+6
-1
@@ -22,7 +22,12 @@ directory and set the following options:
22
```
23
24
Add `:http` or `:https` modifiers to the connector type if you need to use other than a plaintext protocol. For example: `json:http:my_json_instance`,
25
-`json:https:my_json_instance`.
25
+`json:https:my_json_instance`. You can set basic HTTP authentication credentials using
26
+
27
+```conf
28
+ username = my_username
29
+ password = my_password
30
+```
31
32
The JSON connector is further configurable using additional settings. See the [exporting reference
33
doc](/exporting/README.md#options) for details.
exporting/json/json.c
+2
@@ -352,10 +352,12 @@ void json_http_prepare_header(struct instance *instance)
352
simple_connector_data->last_buffer->header,
353
"POST /api/put HTTP/1.1\r\n"
354
"Host: %s\r\n"
355
+ "%s"
356
"Content-Type: application/json\r\n"
357
"Content-Length: %lu\r\n"
358
"\r\n",
359
instance->config.destination,
360
+ simple_connector_data->auth_string ? simple_connector_data->auth_string : "",
361
buffer_strlen(simple_connector_data->last_buffer->buffer));
362
363
return;
exporting/opentsdb/README.md
+6
-1
@@ -22,7 +22,12 @@ directory and set the following options:
22
```
23
24
Add `:http` or `:https` modifiers to the connector type if you need to use other than a plaintext protocol. For example: `opentsdb:http:my_opentsdb_instance`,
25
-`opentsdb:https:my_opentsdb_instance`.
25
+`opentsdb:https:my_opentsdb_instance`. You can set basic HTTP authentication credentials using
26
+
27
+```conf
28
+ username = my_username
29
+ password = my_password
30
+```
31
32
The OpenTSDB connector is further configurable using additional settings. See the [exporting reference
33
doc](/exporting/README.md#options) for details.
exporting/opentsdb/opentsdb.c
+2
@@ -269,10 +269,12 @@ void opentsdb_http_prepare_header(struct instance *instance)
269
simple_connector_data->last_buffer->header,
270
"POST /api/put HTTP/1.1\r\n"
271
"Host: %s\r\n"
272
+ "%s"
273
"Content-Type: application/json\r\n"
274
"Content-Length: %lu\r\n"
275
"\r\n",
276
instance->config.destination,
277
+ simple_connector_data->auth_string ? simple_connector_data->auth_string : "",
278
buffer_strlen(simple_connector_data->last_buffer->buffer));
279
280
return;
exporting/prometheus/remote_write/README.md
+7
@@ -41,6 +41,13 @@ For example, if your endpoint is `http://example.domain:example_port/storage/rea
41
remote write URL path = /storage/read
42
```
43
44
+You can set basic HTTP authentication credentials using
45
+
46
+```conf
47
+ username = my_username
48
+ password = my_password
49
+```
50
+
51
`buffered` and `lost` dimensions in the Netdata Exporting Connector Data Size operation monitoring chart estimate uncompressed
52
buffer size on failures.
53
exporting/prometheus/remote_write/remote_write.c
+2
@@ -25,6 +25,7 @@ void prometheus_remote_write_prepare_header(struct instance *instance)
25
"POST %s HTTP/1.1\r\n"
26
"Host: %s\r\n"
27
"Accept: */*\r\n"
28
+ "%s"
29
"Content-Encoding: snappy\r\n"
30
"Content-Type: application/x-protobuf\r\n"
31
"X-Prometheus-Remote-Write-Version: 0.1.0\r\n"
@@ -32,6 +33,7 @@ void prometheus_remote_write_prepare_header(struct instance *instance)
33
"\r\n",
34
connector_specific_config->remote_write_path,
35
simple_connector_data->connected_to,
36
+ simple_connector_data->auth_string ? simple_connector_data->auth_string : "",
37
buffer_strlen(simple_connector_data->last_buffer->buffer));
38
}
39
exporting/read_config.c
+4
@@ -456,6 +456,10 @@ struct engine *read_exporting_config()
456
457
tmp_instance->config.destination = strdupz(exporter_get(instance_name, "destination", default_destination));
458
459
+ tmp_instance->config.username = strdupz(exporter_get(instance_name, "username", ""));
460
+
461
+ tmp_instance->config.password = strdupz(exporter_get(instance_name, "password", ""));
462
+
463
tmp_instance->config.prefix = strdupz(exporter_get(instance_name, "prefix", "netdata"));
464
465
tmp_instance->config.hostname = strdupz(exporter_get(instance_name, "hostname", engine->config.hostname));
exporting/tests/exporting_doubles.c
+2
@@ -22,6 +22,8 @@ struct engine *__mock_read_exporting_config()
22
instance->config.type = EXPORTING_CONNECTOR_TYPE_GRAPHITE;
23
instance->config.name = strdupz("instance_name");
24
instance->config.destination = strdupz("localhost");
25
+ instance->config.username = strdupz("");
26
+ instance->config.password = strdupz("");
27
instance->config.prefix = strdupz("netdata");
28
instance->config.hostname = strdupz("test-host");
29
instance->config.update_every = 1;
exporting/tests/exporting_fixtures.c
+2
@@ -18,6 +18,8 @@ int teardown_configured_engine(void **state)
18
19
struct instance *instance = engine->instance_root;
20
free((void *)instance->config.destination);
21
+ free((void *)instance->config.username);
22
+ free((void *)instance->config.password);
23
free((void *)instance->config.name);
24
free((void *)instance->config.prefix);
25
free((void *)instance->config.hostname);
exporting/tests/test_exporting_engine.c
+2
-2
@@ -1053,7 +1053,7 @@ static void test_format_host_labels_prometheus(void **state)
1053
instance->config.options |= EXPORTING_OPTION_SEND_AUTOMATIC_LABELS;
1054
1055
format_host_labels_prometheus(instance, localhost);
1056
- assert_string_equal(buffer_tostring(instance->labels), "key1=\"netdata\",key2=\"value2\"");
1056
+ assert_string_equal(buffer_tostring(instance->labels), "key1=\"value1\",key2=\"value2\"");
1057
}
1058
1059
static void rrd_stats_api_v1_charts_allmetrics_prometheus(void **state)
@@ -1877,7 +1877,7 @@ int main(void)
1877
cmocka_unit_test_setup_teardown(test_prometheus_label_copy, setup_prometheus, teardown_prometheus),
1878
cmocka_unit_test_setup_teardown(test_prometheus_units_copy, setup_prometheus, teardown_prometheus),
1879
cmocka_unit_test_setup_teardown(
1880
- test_format_host_labels_prometheus, setup_configured_engine, teardown_configured_engine),
1880
+ test_format_host_labels_prometheus, setup_initialized_engine, teardown_initialized_engine),
1881
cmocka_unit_test_setup_teardown(
1882
rrd_stats_api_v1_charts_allmetrics_prometheus, setup_prometheus, teardown_prometheus),
1883
};