add ACL allow dashboard from; fixes #2828
Costa Tsaousis (ktsaou) committed
Oct 4, 2017 at 00:57 UTC
e43a44134eb7097f2cd3aeba7f2868e40b27ef58
4 files changed
+125
-61
src/main.c
+4
-3
@@ -84,9 +84,10 @@ void web_server_config_options(void) {
84
if(!*web_x_frame_options) web_x_frame_options = NULL;
85
86
web_allow_connections_from = simple_pattern_create(config_get(CONFIG_SECTION_WEB, "allow connections from", "localhost *"), SIMPLE_PATTERN_EXACT);
87
- web_allow_badges_from = simple_pattern_create(config_get(CONFIG_SECTION_WEB, "allow badges from", "*"), SIMPLE_PATTERN_EXACT);
88
- web_allow_registry_from = simple_pattern_create(config_get(CONFIG_SECTION_REGISTRY, "allow from", "*"), SIMPLE_PATTERN_EXACT);
89
- web_allow_streaming_from = simple_pattern_create(config_get(CONFIG_SECTION_WEB, "allow streaming from", "*"), SIMPLE_PATTERN_EXACT);
87
+ web_allow_dashboard_from = simple_pattern_create(config_get(CONFIG_SECTION_WEB, "allow dashboard from", "localhost *"), SIMPLE_PATTERN_EXACT);
88
+ web_allow_badges_from = simple_pattern_create(config_get(CONFIG_SECTION_WEB, "allow badges from", "*"), SIMPLE_PATTERN_EXACT);
89
+ web_allow_registry_from = simple_pattern_create(config_get(CONFIG_SECTION_REGISTRY, "allow from", "*"), SIMPLE_PATTERN_EXACT);
90
+ web_allow_streaming_from = simple_pattern_create(config_get(CONFIG_SECTION_WEB, "allow streaming from", "*"), SIMPLE_PATTERN_EXACT);
91
web_allow_netdataconf_from = simple_pattern_create(config_get(CONFIG_SECTION_WEB, "allow netdata.conf from", "localhost fd* 10.* 192.168.* 172.16.* 172.17.* 172.18.* 172.19.* 172.20.* 172.21.* 172.22.* 172.23.* 172.24.* 172.25.* 172.26.* 172.27.* 172.28.* 172.29.* 172.30.* 172.31.*"), SIMPLE_PATTERN_EXACT);
92
93
#ifdef NETDATA_WITH_ZLIB
src/web_api_v1.c
+48
-48
@@ -298,9 +298,6 @@ inline int web_client_api_request_v1_chart(RRDHOST *host, struct web_client *w,
298
}
299
300
int web_client_api_request_v1_badge(RRDHOST *host, struct web_client *w, char *url) {
301
- if(unlikely(web_allow_badges_from && !simple_pattern_matches(web_allow_badges_from, w->client_ip)))
302
- return web_client_permission_denied(w);
303
-
301
int ret = 400;
302
buffer_flush(w->response.data);
303
@@ -825,9 +822,16 @@ inline int web_client_api_request_v1_registry(RRDHOST *host, struct web_client *
822
return 400;
823
}
824
828
- // for all calls except HELLO, we have to check the ACL
829
- if(unlikely(action != 'H' && web_allow_registry_from && !simple_pattern_matches(web_allow_registry_from, w->client_ip)))
830
- return web_client_permission_denied(w);
825
+ if(unlikely(action == 'H')) {
826
+ // HELLO request, dashboard ACL
827
+ if(unlikely(!web_client_can_access_dashboard(w)))
828
+ return web_client_permission_denied(w);
829
+ }
830
+ else {
831
+ // everything else, registry ACL
832
+ if(unlikely(!web_client_can_access_registry(w)))
833
+ return web_client_permission_denied(w);
834
+ }
835
836
switch(action) {
837
case 'A':
@@ -884,19 +888,35 @@ inline int web_client_api_request_v1_registry(RRDHOST *host, struct web_client *
888
}
889
}
890
891
+static struct api_command {
892
+ const char *command;
893
+ uint32_t hash;
894
+ WEB_CLIENT_ACL acl;
895
+ int (*callback)(RRDHOST *host, struct web_client *w, char *url);
896
+} api_commands[] = {
897
+ { "data", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_data },
898
+ { "chart", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_chart },
899
+ { "charts", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_charts },
900
+ { "registry", 0, WEB_CLIENT_ACL_NONE, web_client_api_request_v1_registry },
901
+ { "badge.svg", 0, WEB_CLIENT_ACL_BADGE, web_client_api_request_v1_badge },
902
+ { "alarms", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_alarms },
903
+ { "alarm_log", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_alarm_log },
904
+ { "alarm_variables", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_alarm_variables },
905
+ { "allmetrics", 0, WEB_CLIENT_ACL_DASHBOARD, web_client_api_request_v1_allmetrics },
906
+
907
+ // terminator
908
+ { NULL, 0, WEB_CLIENT_ACL_NONE, NULL },
909
+};
910
+
911
inline int web_client_api_request_v1(RRDHOST *host, struct web_client *w, char *url) {
888
- static uint32_t hash_data = 0, hash_chart = 0, hash_charts = 0, hash_registry = 0, hash_badge = 0, hash_alarms = 0, hash_alarm_log = 0, hash_alarm_variables = 0, hash_raw = 0;
912
+ static int initialized = 0;
913
+ int i;
914
890
- if(unlikely(hash_data == 0)) {
891
- hash_data = simple_hash("data");
892
- hash_chart = simple_hash("chart");
893
- hash_charts = simple_hash("charts");
894
- hash_registry = simple_hash("registry");
895
- hash_badge = simple_hash("badge.svg");
896
- hash_alarms = simple_hash("alarms");
897
- hash_alarm_log = simple_hash("alarm_log");
898
- hash_alarm_variables = simple_hash("alarm_variables");
899
- hash_raw = simple_hash("allmetrics");
915
+ if(unlikely(initialized == 0)) {
916
+ initialized = 1;
917
+
918
+ for(i = 0; api_commands[i].command ; i++)
919
+ api_commands[i].hash = simple_hash(api_commands[i].command);
920
}
921
922
// get the command
@@ -905,39 +925,19 @@ inline int web_client_api_request_v1(RRDHOST *host, struct web_client *w, char *
925
debug(D_WEB_CLIENT, "%llu: Searching for API v1 command '%s'.", w->id, tok);
926
uint32_t hash = simple_hash(tok);
927
908
- if(hash == hash_data && !strcmp(tok, "data"))
909
- return web_client_api_request_v1_data(host, w, url);
910
-
911
- else if(hash == hash_chart && !strcmp(tok, "chart"))
912
- return web_client_api_request_v1_chart(host, w, url);
913
-
914
- else if(hash == hash_charts && !strcmp(tok, "charts"))
915
- return web_client_api_request_v1_charts(host, w, url);
916
-
917
- else if(hash == hash_registry && !strcmp(tok, "registry"))
918
- return web_client_api_request_v1_registry(host, w, url);
919
-
920
- else if(hash == hash_badge && !strcmp(tok, "badge.svg"))
921
- return web_client_api_request_v1_badge(host, w, url);
928
+ for(i = 0; api_commands[i].command ;i++) {
929
+ if(unlikely(hash == api_commands[i].hash && !strcmp(tok, api_commands[i].command))) {
930
+ if(unlikely(api_commands[i].acl != WEB_CLIENT_ACL_NONE) && !(w->acl & api_commands[i].acl))
931
+ return web_client_permission_denied(w);
932
923
- else if(hash == hash_alarms && !strcmp(tok, "alarms"))
924
- return web_client_api_request_v1_alarms(host, w, url);
925
-
926
- else if(hash == hash_alarm_log && !strcmp(tok, "alarm_log"))
927
- return web_client_api_request_v1_alarm_log(host, w, url);
928
-
929
- else if(hash == hash_alarm_variables && !strcmp(tok, "alarm_variables"))
930
- return web_client_api_request_v1_alarm_variables(host, w, url);
931
-
932
- else if(hash == hash_raw && !strcmp(tok, "allmetrics"))
933
- return web_client_api_request_v1_allmetrics(host, w, url);
934
-
935
- else {
936
- buffer_flush(w->response.data);
937
- buffer_strcat(w->response.data, "Unsupported v1 API command: ");
938
- buffer_strcat_htmlescape(w->response.data, tok);
939
- return 404;
933
+ return api_commands[i].callback(host, w, url);
934
+ }
935
}
936
+
937
+ buffer_flush(w->response.data);
938
+ buffer_strcat(w->response.data, "Unsupported v1 API command: ");
939
+ buffer_strcat_htmlescape(w->response.data, tok);
940
+ return 404;
941
}
942
else {
943
buffer_flush(w->response.data);
src/web_client.c
+53
-9
@@ -9,11 +9,14 @@ int respect_web_browser_do_not_track_policy = 0;
9
char *web_x_frame_options = NULL;
10
11
SIMPLE_PATTERN *web_allow_connections_from = NULL;
12
-SIMPLE_PATTERN *web_allow_registry_from = NULL;
13
-SIMPLE_PATTERN *web_allow_badges_from = NULL;
12
SIMPLE_PATTERN *web_allow_streaming_from = NULL;
13
SIMPLE_PATTERN *web_allow_netdataconf_from = NULL;
14
15
+// WEB_CLIENT_ACL
16
+SIMPLE_PATTERN *web_allow_dashboard_from = NULL;
17
+SIMPLE_PATTERN *web_allow_registry_from = NULL;
18
+SIMPLE_PATTERN *web_allow_badges_from = NULL;
19
+
20
#ifdef NETDATA_WITH_ZLIB
21
int web_enable_gzip = 1, web_gzip_level = 3, web_gzip_strategy = Z_DEFAULT_STRATEGY;
22
#endif /* NETDATA_WITH_ZLIB */
@@ -68,6 +71,19 @@ static void log_connection(struct web_client *w, const char *msg) {
71
log_access("%llu: %d '[%s]:%s' '%s'", w->id, gettid(), w->client_ip, w->client_port, msg);
72
}
73
74
+static void web_client_update_acl_matches(struct web_client *w) {
75
+ w->acl = WEB_CLIENT_ACL_NONE;
76
+
77
+ if(!web_allow_dashboard_from || simple_pattern_matches(web_allow_dashboard_from, w->client_ip))
78
+ w->acl |= WEB_CLIENT_ACL_DASHBOARD;
79
+
80
+ if(!web_allow_registry_from || simple_pattern_matches(web_allow_registry_from, w->client_ip))
81
+ w->acl |= WEB_CLIENT_ACL_REGISTRY;
82
+
83
+ if(!web_allow_badges_from || simple_pattern_matches(web_allow_badges_from, w->client_ip))
84
+ w->acl |= WEB_CLIENT_ACL_BADGE;
85
+}
86
+
87
struct web_client *web_client_create(int listener) {
88
struct web_client *w;
89
@@ -106,6 +122,8 @@ struct web_client *web_client_create(int listener) {
122
error("%llu: Cannot set SO_KEEPALIVE on socket.", w->id);
123
}
124
125
+ web_client_update_acl_matches(w);
126
+
127
w->response.data = buffer_create(INITIAL_WEB_DATA_LENGTH);
128
w->response.header = buffer_create(HTTP_RESPONSE_HEADER_SIZE);
129
w->response.header_output = buffer_create(HTTP_RESPONSE_HEADER_SIZE);
@@ -611,6 +629,13 @@ static inline int check_host_and_call(RRDHOST *host, struct web_client *w, char
629
return func(host, w, url);
630
}
631
632
+static inline int check_host_and_dashboard_acl_and_call(RRDHOST *host, struct web_client *w, char *url, int (*func)(RRDHOST *, struct web_client *, char *)) {
633
+ if(!web_client_can_access_dashboard(w))
634
+ return web_client_permission_denied(w);
635
+
636
+ return check_host_and_call(host, w, url, func);
637
+}
638
+
639
int web_client_api_request(RRDHOST *host, struct web_client *w, char *url)
640
{
641
// get the api version
@@ -1140,26 +1165,26 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
1165
}
1166
else if(unlikely(hash == hash_data && strcmp(tok, WEB_PATH_DATA) == 0)) { // old API "data"
1167
debug(D_WEB_CLIENT_ACCESS, "%llu: old API data request...", w->id);
1143
- return check_host_and_call(host, w, url, web_client_api_old_data_request_json);
1168
+ return check_host_and_dashboard_acl_and_call(host, w, url, web_client_api_old_data_request_json);
1169
}
1170
else if(unlikely(hash == hash_datasource && strcmp(tok, WEB_PATH_DATASOURCE) == 0)) { // old API "datasource"
1171
debug(D_WEB_CLIENT_ACCESS, "%llu: old API datasource request...", w->id);
1147
- return check_host_and_call(host, w, url, web_client_api_old_data_request_jsonp);
1172
+ return check_host_and_dashboard_acl_and_call(host, w, url, web_client_api_old_data_request_jsonp);
1173
}
1174
else if(unlikely(hash == hash_graph && strcmp(tok, WEB_PATH_GRAPH) == 0)) { // old API "graph"
1175
debug(D_WEB_CLIENT_ACCESS, "%llu: old API graph request...", w->id);
1151
- return check_host_and_call(host, w, url, web_client_api_old_graph_request);
1176
+ return check_host_and_dashboard_acl_and_call(host, w, url, web_client_api_old_graph_request);
1177
}
1178
else if(unlikely(hash == hash_list && strcmp(tok, "list") == 0)) { // old API "list"
1179
debug(D_WEB_CLIENT_ACCESS, "%llu: old API list request...", w->id);
1155
- return check_host_and_call(host, w, url, web_client_api_old_list_request);
1180
+ return check_host_and_dashboard_acl_and_call(host, w, url, web_client_api_old_list_request);
1181
}
1182
else if(unlikely(hash == hash_all_json && strcmp(tok, "all.json") == 0)) { // old API "all.json"
1183
debug(D_WEB_CLIENT_ACCESS, "%llu: old API all.json request...", w->id);
1159
- return check_host_and_call(host, w, url, web_client_api_old_all_json);
1184
+ return check_host_and_dashboard_acl_and_call(host, w, url, web_client_api_old_all_json);
1185
}
1186
else if(unlikely(hash == hash_netdata_conf && strcmp(tok, "netdata.conf") == 0)) { // netdata.conf
1162
- if(unlikely(web_allow_netdataconf_from && !simple_pattern_matches(web_allow_netdataconf_from, w->client_ip)))
1187
+ if(unlikely(!web_client_can_access_netdataconf(w)))
1188
return web_client_permission_denied(w);
1189
1190
debug(D_WEB_CLIENT_ACCESS, "%llu: generating netdata.conf ...", w->id);
@@ -1170,6 +1195,9 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
1195
}
1196
#ifdef NETDATA_INTERNAL_CHECKS
1197
else if(unlikely(hash == hash_exit && strcmp(tok, "exit") == 0)) {
1198
+ if(unlikely(!web_client_can_access_netdataconf(w)))
1199
+ return web_client_permission_denied(w);
1200
+
1201
w->response.data->contenttype = CT_TEXT_PLAIN;
1202
buffer_flush(w->response.data);
1203
@@ -1183,6 +1211,9 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
1211
return 200;
1212
}
1213
else if(unlikely(hash == hash_debug && strcmp(tok, "debug") == 0)) {
1214
+ if(unlikely(!web_client_can_access_netdataconf(w)))
1215
+ return web_client_permission_denied(w);
1216
+
1217
buffer_flush(w->response.data);
1218
1219
// get the name of the data to show
@@ -1220,6 +1251,9 @@ static inline int web_client_process_url(RRDHOST *host, struct web_client *w, ch
1251
return 400;
1252
}
1253
else if(unlikely(hash == hash_mirror && strcmp(tok, "mirror") == 0)) {
1254
+ if(unlikely(!web_client_can_access_netdataconf(w)))
1255
+ return web_client_permission_denied(w);
1256
+
1257
debug(D_WEB_CLIENT_ACCESS, "%llu: Mirroring...", w->id);
1258
1259
// replace the zero bytes with spaces
@@ -1250,7 +1284,7 @@ void web_client_process_request(struct web_client *w) {
1284
case HTTP_VALIDATION_OK:
1285
switch(w->mode) {
1286
case WEB_CLIENT_MODE_STREAM:
1253
- if(unlikely(web_allow_streaming_from && !simple_pattern_matches(web_allow_streaming_from, w->client_ip))) {
1287
+ if(unlikely(!web_client_can_access_stream(w))) {
1288
web_client_permission_denied(w);
1289
return;
1290
}
@@ -1259,6 +1293,11 @@ void web_client_process_request(struct web_client *w) {
1293
return;
1294
1295
case WEB_CLIENT_MODE_OPTIONS:
1296
+ if(unlikely(!web_client_can_access_dashboard(w) && !web_client_can_access_registry(w) && !web_client_can_access_badges(w))) {
1297
+ web_client_permission_denied(w);
1298
+ return;
1299
+ }
1300
+
1301
w->response.data->contenttype = CT_TEXT_PLAIN;
1302
buffer_flush(w->response.data);
1303
buffer_strcat(w->response.data, "OK");
@@ -1267,6 +1306,11 @@ void web_client_process_request(struct web_client *w) {
1306
1307
case WEB_CLIENT_MODE_FILECOPY:
1308
case WEB_CLIENT_MODE_NORMAL:
1309
+ if(unlikely(!web_client_can_access_dashboard(w) && !web_client_can_access_registry(w) && !web_client_can_access_badges(w))) {
1310
+ web_client_permission_denied(w);
1311
+ return;
1312
+ }
1313
+
1314
w->response.code = web_client_process_url(localhost, w, w->decoded_url);
1315
break;
1316
}
src/web_client.h
+20
-1
@@ -44,7 +44,7 @@ typedef enum web_client_flags {
44
//#define web_client_flag_set(w, flag) __atomic_or_fetch(&((w)->flags), flag, __ATOMIC_SEQ_CST)
45
//#define web_client_flag_clear(w, flag) __atomic_and_fetch(&((w)->flags), ~flag, __ATOMIC_SEQ_CST)
46
//#else
47
-#define web_client_flag_check(w, flag) ((w)->flags & flag)
47
+#define web_client_flag_check(w, flag) ((w)->flags & (flag))
48
#define web_client_flag_set(w, flag) (w)->flags |= flag
49
#define web_client_flag_clear(w, flag) (w)->flags &= ~flag
50
//#endif
@@ -108,11 +108,29 @@ struct response {
108
109
};
110
111
+typedef enum web_client_acl {
112
+ WEB_CLIENT_ACL_NONE = 0,
113
+ WEB_CLIENT_ACL_DASHBOARD = 1 << 0,
114
+ WEB_CLIENT_ACL_REGISTRY = 1 << 1,
115
+ WEB_CLIENT_ACL_BADGE = 1 << 2
116
+} WEB_CLIENT_ACL;
117
+
118
+#define web_client_can_access_dashboard(w) ((w)->acl & WEB_CLIENT_ACL_DASHBOARD)
119
+#define web_client_can_access_registry(w) ((w)->acl & WEB_CLIENT_ACL_REGISTRY)
120
+#define web_client_can_access_badges(w) ((w)->acl & WEB_CLIENT_ACL_BADGE)
121
+
122
+#define web_client_can_access_stream(w) \
123
+ (!web_allow_streaming_from || simple_pattern_matches(web_allow_streaming_from, (w)->client_ip))
124
+
125
+#define web_client_can_access_netdataconf(w) \
126
+ (!web_allow_netdataconf_from || simple_pattern_matches(web_allow_netdataconf_from, (w)->client_ip))
127
+
128
struct web_client {
129
unsigned long long id;
130
131
WEB_CLIENT_FLAGS flags; // status flags for the client
132
WEB_CLIENT_MODE mode; // the operational mode of the client
133
+ WEB_CLIENT_ACL acl; // the access list of the client
134
135
int tcp_cork; // 1 = we have a cork on the socket
136
@@ -144,6 +162,7 @@ struct web_client {
162
163
extern struct web_client *web_clients;
164
extern SIMPLE_PATTERN *web_allow_connections_from;
165
+extern SIMPLE_PATTERN *web_allow_dashboard_from;
166
extern SIMPLE_PATTERN *web_allow_registry_from;
167
extern SIMPLE_PATTERN *web_allow_badges_from;
168
extern SIMPLE_PATTERN *web_allow_streaming_from;