added X-Frame-Options HTTP header support; fixes #1735
Costa Tsaousis (ktsaou) committed
Feb 15, 2017 at 21:56 UTC
2b797b0600c65a8fe5a8b839863ec7cb0e98fe3d
3 files changed
+156
-142
src/main.c
+3
-1
@@ -71,7 +71,9 @@ void web_server_threading_selection(void) {
71
72
web_client_timeout = (int) config_get_number("global", "disconnect idle web clients after seconds", DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS);
73
74
- web_donotrack_comply = config_get_boolean("global", "respect web browser do not track policy", web_donotrack_comply);
74
+ respect_web_browser_do_not_track_policy = config_get_boolean("global", "respect web browser do not track policy", respect_web_browser_do_not_track_policy);
75
+ web_x_frame_options = config_get("global", "web x-frame-options header", "");
76
+ if(!*web_x_frame_options) web_x_frame_options = NULL;
77
78
#ifdef NETDATA_WITH_ZLIB
79
web_enable_gzip = config_get_boolean("global", "enable web responses gzip compression", web_enable_gzip);
src/web_client.c
+147
-140
@@ -5,7 +5,8 @@
5
#define TOO_BIG_REQUEST 16384
6
7
int web_client_timeout = DEFAULT_DISCONNECT_IDLE_WEB_CLIENTS_AFTER_SECONDS;
8
-int web_donotrack_comply = 0;
8
+int respect_web_browser_do_not_track_policy = 0;
9
+char *web_x_frame_options = NULL;
10
11
#ifdef NETDATA_WITH_ZLIB
12
int web_enable_gzip = 1, web_gzip_level = 3, web_gzip_strategy = Z_DEFAULT_STRATEGY;
@@ -1347,7 +1348,7 @@ int web_client_api_request_v1_registry(struct web_client *w, char *url)
1348
#endif /* NETDATA_INTERNAL_CHECKS */
1349
}
1350
1350
- if(web_donotrack_comply && w->donottrack) {
1351
+ if(respect_web_browser_do_not_track_policy && w->donottrack) {
1352
buffer_flush(w->response.data);
1353
buffer_sprintf(w->response.data, "Your web browser is sending 'DNT: 1' (Do Not Track). The registry requires persistent cookies on your browser to work.");
1354
return 400;
@@ -1824,7 +1825,7 @@ static inline char *http_header_parse(struct web_client *w, char *s) {
1825
if(strcasestr(v, "keep-alive"))
1826
w->keepalive = 1;
1827
}
1827
- else if(web_donotrack_comply && hash == hash_donottrack && !strcasecmp(s, "DNT")) {
1828
+ else if(respect_web_browser_do_not_track_policy && hash == hash_donottrack && !strcasecmp(s, "DNT")) {
1829
if(*v == '0') w->donottrack = 0;
1830
else if(*v == '1') w->donottrack = 1;
1831
}
@@ -1926,6 +1927,148 @@ static inline int http_request_validate(struct web_client *w) {
1927
return -3;
1928
}
1929
1930
+static inline void web_client_send_http_header(struct web_client *w) {
1931
+ if(unlikely(w->response.code != 200))
1932
+ buffer_no_cacheable(w->response.data);
1933
+
1934
+ // set a proper expiration date, if not already set
1935
+ if(unlikely(!w->response.data->expires)) {
1936
+ if(w->response.data->options & WB_CONTENT_NO_CACHEABLE)
1937
+ w->response.data->expires = w->tv_ready.tv_sec + rrd_update_every;
1938
+ else
1939
+ w->response.data->expires = w->tv_ready.tv_sec + 86400;
1940
+ }
1941
+
1942
+ // prepare the HTTP response header
1943
+ debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, w->response.code);
1944
+
1945
+ const char *content_type_string = web_content_type_to_string(w->response.data->contenttype);
1946
+ const char *code_msg = web_response_code_to_string(w->response.code);
1947
+
1948
+ // prepare the last modified and expiration dates
1949
+ char date[32], edate[32];
1950
+ {
1951
+ struct tm tmbuf, *tm;
1952
+
1953
+ tm = gmtime_r(&w->response.data->date, &tmbuf);
1954
+ strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %Z", tm);
1955
+
1956
+ tm = gmtime_r(&w->response.data->expires, &tmbuf);
1957
+ strftime(edate, sizeof(edate), "%a, %d %b %Y %H:%M:%S %Z", tm);
1958
+ }
1959
+
1960
+ buffer_sprintf(w->response.header_output,
1961
+ "HTTP/1.1 %d %s\r\n"
1962
+ "Connection: %s\r\n"
1963
+ "Server: NetData Embedded HTTP Server\r\n"
1964
+ "Access-Control-Allow-Origin: %s\r\n"
1965
+ "Access-Control-Allow-Credentials: true\r\n"
1966
+ "Content-Type: %s\r\n"
1967
+ "Date: %s\r\n"
1968
+ , w->response.code, code_msg
1969
+ , w->keepalive?"keep-alive":"close"
1970
+ , w->origin
1971
+ , content_type_string
1972
+ , date
1973
+ );
1974
+
1975
+ if(unlikely(web_x_frame_options))
1976
+ buffer_sprintf(w->response.header_output, "X-Frame-Options: %s\r\n", web_x_frame_options);
1977
+
1978
+ if(w->cookie1[0] || w->cookie2[0]) {
1979
+ if(w->cookie1[0]) {
1980
+ buffer_sprintf(w->response.header_output,
1981
+ "Set-Cookie: %s\r\n",
1982
+ w->cookie1);
1983
+ }
1984
+
1985
+ if(w->cookie2[0]) {
1986
+ buffer_sprintf(w->response.header_output,
1987
+ "Set-Cookie: %s\r\n",
1988
+ w->cookie2);
1989
+ }
1990
+
1991
+ if(respect_web_browser_do_not_track_policy)
1992
+ buffer_sprintf(w->response.header_output,
1993
+ "Tk: T;cookies\r\n");
1994
+ }
1995
+ else {
1996
+ if(respect_web_browser_do_not_track_policy) {
1997
+ if(w->tracking_required)
1998
+ buffer_sprintf(w->response.header_output,
1999
+ "Tk: T;cookies\r\n");
2000
+ else
2001
+ buffer_sprintf(w->response.header_output,
2002
+ "Tk: N\r\n");
2003
+ }
2004
+ }
2005
+
2006
+ if(w->mode == WEB_CLIENT_MODE_OPTIONS) {
2007
+ buffer_strcat(w->response.header_output,
2008
+ "Access-Control-Allow-Methods: GET, OPTIONS\r\n"
2009
+ "Access-Control-Allow-Headers: accept, x-requested-with, origin, content-type, cookie, pragma, cache-control\r\n"
2010
+ "Access-Control-Max-Age: 1209600\r\n" // 86400 * 14
2011
+ );
2012
+ }
2013
+ else {
2014
+ buffer_sprintf(w->response.header_output,
2015
+ "Cache-Control: %s\r\n"
2016
+ "Expires: %s\r\n",
2017
+ (w->response.data->options & WB_CONTENT_NO_CACHEABLE)?"no-cache":"public",
2018
+ edate);
2019
+ }
2020
+
2021
+ // copy a possibly available custom header
2022
+ if(unlikely(buffer_strlen(w->response.header)))
2023
+ buffer_strcat(w->response.header_output, buffer_tostring(w->response.header));
2024
+
2025
+ // headers related to the transfer method
2026
+ if(likely(w->response.zoutput)) {
2027
+ buffer_strcat(w->response.header_output,
2028
+ "Content-Encoding: gzip\r\n"
2029
+ "Transfer-Encoding: chunked\r\n"
2030
+ );
2031
+ }
2032
+ else {
2033
+ if(likely((w->response.data->len || w->response.rlen))) {
2034
+ // we know the content length, put it
2035
+ buffer_sprintf(w->response.header_output, "Content-Length: %zu\r\n", w->response.data->len? w->response.data->len: w->response.rlen);
2036
+ }
2037
+ else {
2038
+ // we don't know the content length, disable keep-alive
2039
+ w->keepalive = 0;
2040
+ }
2041
+ }
2042
+
2043
+ // end of HTTP header
2044
+ buffer_strcat(w->response.header_output, "\r\n");
2045
+
2046
+ // sent the HTTP header
2047
+ debug(D_WEB_DATA, "%llu: Sending response HTTP header of size %zu: '%s'"
2048
+ , w->id
2049
+ , buffer_strlen(w->response.header_output)
2050
+ , buffer_tostring(w->response.header_output)
2051
+ );
2052
+
2053
+ web_client_crock_socket(w);
2054
+
2055
+ ssize_t bytes = send(w->ofd, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output), 0);
2056
+ if(bytes != (ssize_t) buffer_strlen(w->response.header_output)) {
2057
+ if(bytes > 0)
2058
+ w->stats_sent_bytes += bytes;
2059
+
2060
+ debug(D_WEB_CLIENT, "%llu: HTTP Header failed to be sent (I sent %zu bytes but the system sent %zd bytes). Closing web client."
2061
+ , w->id
2062
+ , buffer_strlen(w->response.header_output)
2063
+ , bytes);
2064
+
2065
+ WEB_CLIENT_IS_DEAD(w);
2066
+ return;
2067
+ }
2068
+ else
2069
+ w->stats_sent_bytes += bytes;
2070
+}
2071
+
2072
void web_client_process(struct web_client *w) {
2073
static uint32_t
2074
hash_api = 0,
@@ -1959,7 +2102,6 @@ void web_client_process(struct web_client *w) {
2102
}
2103
2104
int code = 500;
1962
- ssize_t bytes;
2105
2106
int what_to_do = http_request_validate(w);
2107
@@ -2160,142 +2302,7 @@ void web_client_process(struct web_client *w) {
2302
if(unlikely(!w->response.data->date))
2303
w->response.data->date = w->tv_ready.tv_sec;
2304
2163
- if(unlikely(code != 200))
2164
- buffer_no_cacheable(w->response.data);
2165
-
2166
- // set a proper expiration date, if not already set
2167
- if(unlikely(!w->response.data->expires)) {
2168
- if(w->response.data->options & WB_CONTENT_NO_CACHEABLE)
2169
- w->response.data->expires = w->tv_ready.tv_sec + rrd_update_every;
2170
- else
2171
- w->response.data->expires = w->tv_ready.tv_sec + 86400;
2172
- }
2173
-
2174
- // prepare the HTTP response header
2175
- debug(D_WEB_CLIENT, "%llu: Generating HTTP header with response %d.", w->id, code);
2176
-
2177
- const char *content_type_string = web_content_type_to_string(w->response.data->contenttype);
2178
- const char *code_msg = web_response_code_to_string(code);
2179
-
2180
- // prepare the last modified and expiration dates
2181
- char date[32], edate[32];
2182
- {
2183
- struct tm tmbuf, *tm;
2184
-
2185
- tm = gmtime_r(&w->response.data->date, &tmbuf);
2186
- strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %Z", tm);
2187
-
2188
- tm = gmtime_r(&w->response.data->expires, &tmbuf);
2189
- strftime(edate, sizeof(edate), "%a, %d %b %Y %H:%M:%S %Z", tm);
2190
- }
2191
-
2192
- buffer_sprintf(w->response.header_output,
2193
- "HTTP/1.1 %d %s\r\n"
2194
- "Connection: %s\r\n"
2195
- "Server: NetData Embedded HTTP Server\r\n"
2196
- "Access-Control-Allow-Origin: %s\r\n"
2197
- "Access-Control-Allow-Credentials: true\r\n"
2198
- "Content-Type: %s\r\n"
2199
- "Date: %s\r\n"
2200
- , code, code_msg
2201
- , w->keepalive?"keep-alive":"close"
2202
- , w->origin
2203
- , content_type_string
2204
- , date
2205
- );
2206
-
2207
- if(w->cookie1[0] || w->cookie2[0]) {
2208
- if(w->cookie1[0]) {
2209
- buffer_sprintf(w->response.header_output,
2210
- "Set-Cookie: %s\r\n",
2211
- w->cookie1);
2212
- }
2213
-
2214
- if(w->cookie2[0]) {
2215
- buffer_sprintf(w->response.header_output,
2216
- "Set-Cookie: %s\r\n",
2217
- w->cookie2);
2218
- }
2219
-
2220
- if(web_donotrack_comply)
2221
- buffer_sprintf(w->response.header_output,
2222
- "Tk: T;cookies\r\n");
2223
- }
2224
- else {
2225
- if(web_donotrack_comply) {
2226
- if(w->tracking_required)
2227
- buffer_sprintf(w->response.header_output,
2228
- "Tk: T;cookies\r\n");
2229
- else
2230
- buffer_sprintf(w->response.header_output,
2231
- "Tk: N\r\n");
2232
- }
2233
- }
2234
-
2235
- if(w->mode == WEB_CLIENT_MODE_OPTIONS) {
2236
- buffer_strcat(w->response.header_output,
2237
- "Access-Control-Allow-Methods: GET, OPTIONS\r\n"
2238
- "Access-Control-Allow-Headers: accept, x-requested-with, origin, content-type, cookie, pragma, cache-control\r\n"
2239
- "Access-Control-Max-Age: 1209600\r\n" // 86400 * 14
2240
- );
2241
- }
2242
- else {
2243
- buffer_sprintf(w->response.header_output,
2244
- "Cache-Control: %s\r\n"
2245
- "Expires: %s\r\n",
2246
- (w->response.data->options & WB_CONTENT_NO_CACHEABLE)?"no-cache":"public",
2247
- edate);
2248
- }
2249
-
2250
- // copy a possibly available custom header
2251
- if(unlikely(buffer_strlen(w->response.header)))
2252
- buffer_strcat(w->response.header_output, buffer_tostring(w->response.header));
2253
-
2254
- // headers related to the transfer method
2255
- if(likely(w->response.zoutput)) {
2256
- buffer_strcat(w->response.header_output,
2257
- "Content-Encoding: gzip\r\n"
2258
- "Transfer-Encoding: chunked\r\n"
2259
- );
2260
- }
2261
- else {
2262
- if(likely((w->response.data->len || w->response.rlen))) {
2263
- // we know the content length, put it
2264
- buffer_sprintf(w->response.header_output, "Content-Length: %zu\r\n", w->response.data->len? w->response.data->len: w->response.rlen);
2265
- }
2266
- else {
2267
- // we don't know the content length, disable keep-alive
2268
- w->keepalive = 0;
2269
- }
2270
- }
2271
-
2272
- // end of HTTP header
2273
- buffer_strcat(w->response.header_output, "\r\n");
2274
-
2275
- // sent the HTTP header
2276
- debug(D_WEB_DATA, "%llu: Sending response HTTP header of size %zu: '%s'"
2277
- , w->id
2278
- , buffer_strlen(w->response.header_output)
2279
- , buffer_tostring(w->response.header_output)
2280
- );
2281
-
2282
- web_client_crock_socket(w);
2283
-
2284
- bytes = send(w->ofd, buffer_tostring(w->response.header_output), buffer_strlen(w->response.header_output), 0);
2285
- if(bytes != (ssize_t) buffer_strlen(w->response.header_output)) {
2286
- if(bytes > 0)
2287
- w->stats_sent_bytes += bytes;
2288
-
2289
- debug(D_WEB_CLIENT, "%llu: HTTP Header failed to be sent (I sent %zu bytes but the system sent %zd bytes). Closing web client."
2290
- , w->id
2291
- , buffer_strlen(w->response.header_output)
2292
- , bytes);
2293
-
2294
- WEB_CLIENT_IS_DEAD(w);
2295
- return;
2296
- }
2297
- else
2298
- w->stats_sent_bytes += bytes;
2305
+ web_client_send_http_header(w);
2306
2307
// enable sending immediately if we have data
2308
if(w->response.data->len) w->wait_send = 1;
src/web_client.h
+6
-1
@@ -5,9 +5,14 @@
5
extern int web_client_timeout;
6
7
#ifdef NETDATA_WITH_ZLIB
8
-extern int web_enable_gzip, web_gzip_level, web_gzip_strategy, web_donotrack_comply;
8
+extern int web_enable_gzip,
9
+ web_gzip_level,
10
+ web_gzip_strategy;
11
#endif /* NETDATA_WITH_ZLIB */
12
13
+extern int respect_web_browser_do_not_track_policy;
14
+extern char *web_x_frame_options;
15
+
16
#define WEB_CLIENT_MODE_NORMAL 0
17
#define WEB_CLIENT_MODE_FILECOPY 1
18
#define WEB_CLIENT_MODE_OPTIONS 2