summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorthiagoftsm <thiagoftsm@gmail.com>2019-07-25 16:43:04 +0000
committerChris Akritidis <43294513+cakrit@users.noreply.github.com>2019-07-25 18:43:04 +0200
commit2f7962c9e154dcce1684f2275387c9b6ac4d0b4c (patch)
tree10a3f5d7451d429a1bd8df91176b04cd82551abb
parentd95912af0834c45fa8a4aee2892b97fbd2e9b520 (diff)
Fix parsing SSL ACL along with others (#6468)
* sslstream: ACL parser It was noticed in the issue 6457 that the some ACLs were not parsing correctly when they were along SSL acl, this commit fixes this' * sslstream: remove comments This commit removes the comments that were present while I was testing the code * sslstream: Tests This commit adds ACL tests to check the Netdata response to them * sslstream: Tests Fix the extension to upload the files * sslstream: more tests In this commit I am bringing more tests, including the ssl tests' * sslstream: leading space Remove leading space from variable that was creating problem with shellcheck * sslstream: glob Remove special character from script * sslstream: Makefile The Makefile diretives were pointed to wrong files * sslstream: Missing stream encrypt This commit solves the problem of the stream not be encrypted, but it is not the final solution, because the parser made is incomplete. * sslstream: Finish encrypt channel This commit brings the step that I was missing, the complete encryptation in the communication between Master and Slave * sslstream: Fix argument in script After the latest tests, it was verified that two arguments given to a function inside the script were not correct, with this PR I am fixing this! * sslstream: Fix argument in info Instead to call a function to deliver an integer I was passing a size_t value. Only cmake showed this, but not in my clion! :/ * sslstream: Fix redirect When we were having different SSL configuration, the system were not applying the option for all * sslstream: Update documentation Our documentation was not clear about the rules according our code so I am updating the text to explain for the users * sslstream: Adjust script With this last commit, I am adjusting the tests to avoid false positive * sslstream: Missing elif The previous commit had a missing elif in the shell script * sslstream: Split ports Before this commit Netdata was having SSL as a global option, now it has as a real ACL. * sslstream: reduce context The stream variable will not be affected in the master side, it is only necessary on the slave side, so I am reducing the context of it * sslstream: Force SSL When the user has certificate and he does not set any SSL flag, it is necessary to append the SSL=force flag * sslstream: Default flag It is necessary to have a default flag when the SSL flags are not SET * sslstream: remove comments Remove comments from the scrip * sslstream: moving flag It is better the flag to be set inside socket instead everytime there is a new connection * sslstream: documentation Fix a sentence in the web/server/README.md
-rw-r--r--.gitignore1
-rw-r--r--collectors/plugins.d/plugins_d.c132
-rw-r--r--collectors/plugins.d/plugins_d.h1
-rw-r--r--libnetdata/socket/security.c18
-rw-r--r--libnetdata/socket/security.h4
-rw-r--r--libnetdata/socket/socket.c66
-rw-r--r--libnetdata/socket/socket.h8
-rw-r--r--streaming/rrdpush.c15
-rw-r--r--tests/Makefile.am6
-rw-r--r--tests/acls/acl.sh.in119
-rw-r--r--tests/acls/netdata.cfg20
-rw-r--r--tests/acls/netdata.ssl.cfg24
-rw-r--r--web/server/README.md14
-rw-r--r--web/server/web_client.c8
14 files changed, 399 insertions, 37 deletions
diff --git a/.gitignore b/.gitignore
index c48d87e6c6..82000619b7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -166,6 +166,7 @@ callgrind.out.*
gmon.out
gmon.txt
sitespeed-result/
+tests/acls/acl.sh
tests/urls/request.sh
# tests and temp files
diff --git a/collectors/plugins.d/plugins_d.c b/collectors/plugins.d/plugins_d.c
index d333ae2413..0c6010103c 100644
--- a/collectors/plugins.d/plugins_d.c
+++ b/collectors/plugins.d/plugins_d.c
@@ -117,6 +117,103 @@ inline int pluginsd_split_words(char *str, char **words, int max_words) {
return quoted_strings_splitter(str, words, max_words, pluginsd_space);
}
+#ifdef ENABLE_HTTPS
+/**
+ * Update Buffer
+ *
+ * Update the temporary buffer used to parse data received from slave
+ *
+ * @param output is a pointer to the vector where I will store the data
+ * @param ssl is the connection pointer with the server
+ *
+ * @return it returns the total of bytes read on success and a negative number otherwise
+ */
+int pluginsd_update_buffer(char *output, SSL *ssl) {
+ ERR_clear_error();
+ int bytesleft = SSL_read(ssl, output, PLUGINSD_LINE_MAX_SSL_READ);
+ if(bytesleft <= 0) {
+ int sslerrno = SSL_get_error(ssl, bytesleft);
+ switch(sslerrno) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ {
+ break;
+ }
+ default:
+ {
+ u_long err;
+ char buf[256];
+ int counter = 0;
+ while ((err = ERR_get_error()) != 0) {
+ ERR_error_string_n(err, buf, sizeof(buf));
+ info("%d SSL Handshake error (%s) on socket %d ", counter++, ERR_error_string((long)SSL_get_error(ssl, bytesleft), NULL), SSL_get_fd(ssl));
+ }
+ }
+
+ }
+ } else {
+ output[bytesleft] = '\0';
+ }
+
+ return bytesleft;
+}
+
+/**
+ * Get from Buffer
+ *
+ * Get data to process from buffer
+ *
+ * @param output is the output vector that will be used to parse the string.
+ * @param bytesread the amount of bytes read in the previous iteration.
+ * @param input the input vector where there are data to process
+ * @param ssl a pointer to the connection with the server
+ * @param src the first address of the input, because sometime will be necessary to restart the addr with it.
+ *
+ * @return It returns a pointer for the next iteration on success and NULL otherwise.
+ */
+char * pluginsd_get_from_buffer(char *output, int *bytesread, char *input, SSL *ssl, char *src) {
+ int copying = 1;
+ char *endbuffer;
+ size_t length;
+ while(copying) {
+ if(*bytesread > 0) {
+ endbuffer = strchr(input, '\n');
+ if(endbuffer) {
+ copying = 0;
+ endbuffer++; //Advance due the fact I wanna copy '\n'
+ length = endbuffer - input;
+ *bytesread -= length;
+
+ memcpy(output, input, length);
+ output += length;
+ *output = '\0';
+ input += length;
+ }else {
+ length = strlen(input);
+ memcpy(output, input, length);
+ output += length;
+ input = src;
+
+ *bytesread = pluginsd_update_buffer(input, ssl);
+ if(*bytesread <= 0) {
+ input = NULL;
+ copying = 0;
+ }
+ }
+ }else {
+ //reduce sample of bytes read, print the length
+ *bytesread = pluginsd_update_buffer(input, ssl);
+ if(*bytesread <= 0) {
+ input = NULL;
+ copying = 0;
+ }
+ }
+ }
+
+ return input;
+}
+#endif
+
inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int trust_durations) {
int enabled = cd->enabled;
@@ -149,10 +246,43 @@ inline size_t pluginsd_process(RRDHOST *host, struct plugind *cd, FILE *fp, int
goto cleanup;
}
+#ifdef ENABLE_HTTPS
+ int bytesleft = 0;
+ char tmpbuffer[PLUGINSD_LINE_MAX];
+ char *readfrom;
+#endif
while(!ferror(fp)) {
if(unlikely(netdata_exit)) break;
- char *r = fgets(line, PLUGINSD_LINE_MAX, fp);
+ char *r;
+#ifdef ENABLE_HTTPS
+ int normalread = 1;
+ if(netdata_srv_ctx) {
+ if(host->ssl.conn && !host->ssl.flags) {
+ if(!bytesleft) {
+ r = line;
+ readfrom = tmpbuffer;
+ bytesleft = pluginsd_update_buffer(readfrom, host->ssl.conn);
+ if(bytesleft <= 0) {
+ break;
+ }
+ }
+
+ readfrom = pluginsd_get_from_buffer(line, &bytesleft, readfrom, host->ssl.conn, tmpbuffer);
+ if(!readfrom) {
+ r = NULL;
+ }
+
+ normalread = 0;
+ }
+ }
+
+ if(normalread) {
+ r = fgets(line, PLUGINSD_LINE_MAX, fp);
+ }
+#else
+ r = fgets(line, PLUGINSD_LINE_MAX, fp);
+#endif
if(unlikely(!r)) {
if(feof(fp))
error("read failed: end of file");
diff --git a/collectors/plugins.d/plugins_d.h b/collectors/plugins.d/plugins_d.h
index 04d5de3d32..7d5c7dda47 100644
--- a/collectors/plugins.d/plugins_d.h
+++ b/collectors/plugins.d/plugins_d.h
@@ -31,6 +31,7 @@
#define PLUGINSD_KEYWORD_VARIABLE "VARIABLE"
#define PLUGINSD_LINE_MAX 1024
+#define PLUGINSD_LINE_MAX_SSL_READ 512
#define PLUGINSD_MAX_WORDS 20
#define PLUGINSD_MAX_DIRECTORIES 20
diff --git a/libnetdata/socket/security.c b/libnetdata/socket/security.c
index 7f69b17e00..9eb8e60247 100644
--- a/libnetdata/socket/security.c
+++ b/libnetdata/socket/security.c
@@ -7,8 +7,6 @@ SSL_CTX *netdata_client_ctx=NULL;
SSL_CTX *netdata_srv_ctx=NULL;
const char *security_key=NULL;
const char *security_cert=NULL;
-int netdata_use_ssl_on_stream = NETDATA_SSL_OPTIONAL;
-int netdata_use_ssl_on_http = NETDATA_SSL_FORCE; //We force SSL due safety reasons
int netdata_validate_server = NETDATA_SSL_VALID_CERTIFICATE;
/**
@@ -176,6 +174,9 @@ void security_start_ssl(int selector) {
}
case NETDATA_SSL_CONTEXT_STREAMING: {
netdata_client_ctx = security_initialize_openssl_client();
+ //This is necessary for the stream, because it is working sometimes with nonblock socket.
+ //It returns the bitmask afte to change, there is not any description of errors in the documentation
+ SSL_CTX_set_mode(netdata_client_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE |SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER |SSL_MODE_AUTO_RETRY);
break;
}
case NETDATA_SSL_CONTEXT_OPENTSDB: {
@@ -206,6 +207,17 @@ void security_clean_openssl() {
#endif
}
+/**
+ * Process accept
+ *
+ * Process the SSL handshake with the client case it is necessary.
+ *
+ * @param ssl is a pointer for the SSL structure
+ * @param msg is a copy of the first 8 bytes of the initial message received
+ *
+ * @return it returns 0 case it performs the handshake, 8 case it is clean connection
+ * and another integer power of 2 otherwise.
+ */
int security_process_accept(SSL *ssl,int msg) {
int sock = SSL_get_fd(ssl);
int test;
@@ -250,7 +262,7 @@ int security_process_accept(SSL *ssl,int msg) {
debug(D_WEB_CLIENT_ACCESS,"SSL Handshake finished %s errno %d on socket fd %d", ERR_error_string((long)SSL_get_error(ssl, test), NULL), errno, sock);
}
- return 0;
+ return NETDATA_SSL_HANDSHAKE_COMPLETE;
}
int security_test_certificate(SSL *ssl) {
diff --git a/libnetdata/socket/security.h b/libnetdata/socket/security.h
index 8beb9672f2..cc870ce17b 100644
--- a/libnetdata/socket/security.h
+++ b/libnetdata/socket/security.h
@@ -25,7 +25,7 @@
struct netdata_ssl{
SSL *conn; //SSL connection
- int flags;
+ int flags; //The flags for SSL connection
};
extern SSL_CTX *netdata_opentsdb_ctx;
@@ -33,8 +33,6 @@ extern SSL_CTX *netdata_client_ctx;
extern SSL_CTX *netdata_srv_ctx;
extern const char *security_key;
extern const char *security_cert;
-extern int netdata_use_ssl_on_stream;
-extern int netdata_use_ssl_on_http;
extern int netdata_validate_server;
void security_openssl_library();
diff --git a/libnetdata/socket/socket.c b/libnetdata/socket/socket.c
index 2827100815..22abb47f4b 100644
--- a/libnetdata/socket/socket.c
+++ b/libnetdata/socket/socket.c
@@ -301,39 +301,47 @@ void listen_sockets_close(LISTEN_SOCKETS *sockets) {
sockets->failed = 0;
}
-WEB_CLIENT_ACL socket_ssl_acl(char *ssl) {
+/*
+ * SSL ACL
+ *
+ * Search the SSL acl and apply it case it is set.
+ *
+ * @param acl is the acl given by the user.
+ */
+WEB_CLIENT_ACL socket_ssl_acl(char *acl) {
+ char *ssl = strchr(acl,'^');
+ if(ssl) {
+ //Due the format of the SSL command it is always the last command,
+ //we finish it here to avoid problems with the ACLs
+ *ssl = '\0';
#ifdef ENABLE_HTTPS
- if (!strcmp(ssl,"optional")) {
- netdata_use_ssl_on_http = NETDATA_SSL_OPTIONAL;
- return WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_REGISTRY | WEB_CLIENT_ACL_BADGE | WEB_CLIENT_ACL_MGMT | WEB_CLIENT_ACL_NETDATACONF | WEB_CLIENT_ACL_STREAMING;
- }
- else if (!strcmp(ssl,"force")) {
- netdata_use_ssl_on_stream = NETDATA_SSL_FORCE;
- return WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_REGISTRY | WEB_CLIENT_ACL_BADGE | WEB_CLIENT_ACL_MGMT | WEB_CLIENT_ACL_NETDATACONF | WEB_CLIENT_ACL_STREAMING;
- }
+ ssl++;
+ if (!strncmp("SSL=",ssl,4)) {
+ ssl += 4;
+ if (!strcmp(ssl,"optional")) {
+ return WEB_CLIENT_ACL_SSL_OPTIONAL;
+ }
+ else if (!strcmp(ssl,"force")) {
+ return WEB_CLIENT_ACL_SSL_FORCE;
+ }
+ }
#endif
+ }
return WEB_CLIENT_ACL_NONE;
}
WEB_CLIENT_ACL read_acl(char *st) {
- char *ssl = strchr(st,'^');
- if (ssl) {
- ssl++;
- if (!strncmp("SSL=",ssl,4)) {
- ssl += 4;
- }
- socket_ssl_acl(ssl);
- }
+ WEB_CLIENT_ACL ret = socket_ssl_acl(st);
- if (!strcmp(st,"dashboard")) return WEB_CLIENT_ACL_DASHBOARD;
- if (!strcmp(st,"registry")) return WEB_CLIENT_ACL_REGISTRY;
- if (!strcmp(st,"badges")) return WEB_CLIENT_ACL_BADGE;
- if (!strcmp(st,"management")) return WEB_CLIENT_ACL_MGMT;
- if (!strcmp(st,"streaming")) return WEB_CLIENT_ACL_STREAMING;
- if (!strcmp(st,"netdata.conf")) return WEB_CLIENT_ACL_NETDATACONF;
+ if (!strcmp(st,"dashboard")) ret |= WEB_CLIENT_ACL_DASHBOARD;
+ if (!strcmp(st,"registry")) ret |= WEB_CLIENT_ACL_REGISTRY;
+ if (!strcmp(st,"badges")) ret |= WEB_CLIENT_ACL_BADGE;
+ if (!strcmp(st,"management")) ret |= WEB_CLIENT_ACL_MGMT;
+ if (!strcmp(st,"streaming")) ret |= WEB_CLIENT_ACL_STREAMING;
+ if (!strcmp(st,"netdata.conf")) ret |= WEB_CLIENT_ACL_NETDATACONF;
- return socket_ssl_acl(st);
+ return ret;
}
static inline int bind_to_this(LISTEN_SOCKETS *sockets, const char *definition, uint16_t default_port, int listen_backlog) {
@@ -375,7 +383,7 @@ static inline int bind_to_this(LISTEN_SOCKETS *sockets, const char *definition,
error("LISTENER: Cannot create unix socket '%s'", path);
sockets->failed++;
} else {
- acl_flags = WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_REGISTRY | WEB_CLIENT_ACL_BADGE | WEB_CLIENT_ACL_MGMT | WEB_CLIENT_ACL_NETDATACONF | WEB_CLIENT_ACL_STREAMING;
+ acl_flags = WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_REGISTRY | WEB_CLIENT_ACL_BADGE | WEB_CLIENT_ACL_MGMT | WEB_CLIENT_ACL_NETDATACONF | WEB_CLIENT_ACL_STREAMING | WEB_CLIENT_ACL_SSL_DEFAULT;
listen_sockets_add(sockets, fd, AF_UNIX, socktype, protocol_str, path, 0, acl_flags);
added++;
}
@@ -425,7 +433,13 @@ static inline int bind_to_this(LISTEN_SOCKETS *sockets, const char *definition,
}
acl_flags |= read_acl(portconfig);
} else {
- acl_flags = WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_REGISTRY | WEB_CLIENT_ACL_BADGE | WEB_CLIENT_ACL_MGMT | WEB_CLIENT_ACL_NETDATACONF | WEB_CLIENT_ACL_STREAMING;
+ acl_flags = WEB_CLIENT_ACL_DASHBOARD | WEB_CLIENT_ACL_REGISTRY | WEB_CLIENT_ACL_BADGE | WEB_CLIENT_ACL_MGMT | WEB_CLIENT_ACL_NETDATACONF | WEB_CLIENT_ACL_STREAMING | WEB_CLIENT_ACL_SSL_DEFAULT;
+ }
+
+ //Case the user does not set the option SSL in the "bind to", but he has
+ //the certificates, I must redirect, so I am assuming here the default option
+ if(!(acl_flags & WEB_CLIENT_ACL_SSL_OPTIONAL) && !(acl_flags & WEB_CLIENT_ACL_SSL_FORCE)) {
+ acl_flags |= WEB_CLIENT_ACL_SSL_DEFAULT;
}
uint32_t scope_id = 0;
diff --git a/libnetdata/socket/socket.h b/libnetdata/socket/socket.h
index 9ea83bcc0f..76b15def52 100644
--- a/libnetdata/socket/socket.h
+++ b/libnetdata/socket/socket.h
@@ -17,7 +17,10 @@ typedef enum web_client_acl {
WEB_CLIENT_ACL_BADGE = 1 << 2,
WEB_CLIENT_ACL_MGMT = 1 << 3,
WEB_CLIENT_ACL_STREAMING = 1 << 4,
- WEB_CLIENT_ACL_NETDATACONF = 1 << 5
+ WEB_CLIENT_ACL_NETDATACONF = 1 << 5,
+ WEB_CLIENT_ACL_SSL_OPTIONAL = 1 << 6,
+ WEB_CLIENT_ACL_SSL_FORCE = 1 << 7,
+ WEB_CLIENT_ACL_SSL_DEFAULT = 1 << 8
} WEB_CLIENT_ACL;
#define web_client_can_access_dashboard(w) ((w)->acl & WEB_CLIENT_ACL_DASHBOARD)
@@ -26,6 +29,9 @@ typedef enum web_client_acl {
#define web_client_can_access_mgmt(w) ((w)->acl & WEB_CLIENT_ACL_MGMT)
#define web_client_can_access_stream(w) ((w)->acl & WEB_CLIENT_ACL_STREAMING)
#define web_client_can_access_netdataconf(w) ((w)->acl & WEB_CLIENT_ACL_NETDATACONF)
+#define web_client_is_using_ssl_optional(w) ((w)->port_acl & WEB_CLIENT_ACL_SSL_OPTIONAL)
+#define web_client_is_using_ssl_force(w) ((w)->port_acl & WEB_CLIENT_ACL_SSL_FORCE)
+#define web_client_is_using_ssl_default(w) ((w)->port_acl & WEB_CLIENT_ACL_SSL_DEFAULT)
typedef struct listen_sockets {
struct config *config; // the config file to use
diff --git a/streaming/rrdpush.c b/streaming/rrdpush.c
index 954b1d7d19..1763819354 100644
--- a/streaming/rrdpush.c
+++ b/streaming/rrdpush.c
@@ -48,6 +48,7 @@ unsigned int default_rrdpush_enabled = 0;
char *default_rrdpush_destination = NULL;
char *default_rrdpush_api_key = NULL;
char *default_rrdpush_send_charts_matching = NULL;
+int netdata_use_ssl_on_stream = NETDATA_SSL_OPTIONAL;
static void load_stream_conf() {
errno = 0;
@@ -801,7 +802,17 @@ void *rrdpush_sender_thread(void *ptr) {
rrdpush_buffer_lock(host);
debug(D_STREAM, "STREAM: Sending data, starting from %zu, size %zu...", begin, buffer_strlen(host->rrdpush_sender_buffer));
- ssize_t ret = send(host->rrdpush_sender_socket, &host->rrdpush_sender_buffer->buffer[begin], buffer_strlen(host->rrdpush_sender_buffer) - begin, MSG_DONTWAIT);
+ ssize_t ret;
+#ifdef ENABLE_HTTPS
+ SSL *conn = host->ssl.conn ;
+ if(conn && !host->ssl.flags) {
+ ret = SSL_write(conn,&host->rrdpush_sender_buffer->buffer[begin], buffer_strlen(host->rrdpush_sender_buffer) - begin);
+ } else {
+ ret = send(host->rrdpush_sender_socket, &host->rrdpush_sender_buffer->buffer[begin], buffer_strlen(host->rrdpush_sender_buffer) - begin, MSG_DONTWAIT);
+ }
+#else
+ ret = send(host->rrdpush_sender_socket, &host->rrdpush_sender_buffer->buffer[begin], buffer_strlen(host->rrdpush_sender_buffer) - begin, MSG_DONTWAIT);
+#endif
if (unlikely(ret == -1)) {
if (errno != EAGAIN && errno != EINTR && errno != EWOULDBLOCK) {
debug(D_STREAM, "STREAM: Send failed - closing socket...");
@@ -1059,6 +1070,8 @@ static int rrdpush_receive(int fd
info("STREAM %s [receive from [%s]:%s]: initializing communication...", host->hostname, client_ip, client_port);
#ifdef ENABLE_HTTPS
+ host->ssl.conn = ssl->conn;
+ host->ssl.flags = ssl->flags;
if(send_timeout(ssl,fd, START_STREAMING_PROMPT, strlen(START_STREAMING_PROMPT), 0, 60) != strlen(START_STREAMING_PROMPT)) {
#else
if(send_timeout(fd, START_STREAMING_PROMPT, strlen(START_STREAMING_PROMPT), 0, 60) != strlen(START_STREAMING_PROMPT)) {
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 5488752dc7..92e6db0f36 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -5,7 +5,11 @@ MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
CLEANFILES = \
health_mgmtapi/health-cmdapi-test.sh \
+<<<<<<< HEAD
+ acls/acl.sh \
+=======
urls/request.sh \
+>>>>>>> 63a4cadd346df71255d2350128eebcf317e81d0f
$(NULL)
include $(top_srcdir)/build/subst.inc
@@ -23,11 +27,13 @@ dist_noinst_DATA = \
node.d/fronius.process.spec.js \
node.d/fronius.validation.spec.js \
health_mgmtapi/health-cmdapi-test.sh.in \
+ acls/acl.sh.in \
urls/request.sh.in \
$(NULL)
dist_plugins_SCRIPTS = \
health_mgmtapi/health-cmdapi-test.sh \
+ acls/acl.sh \
urls/request.sh \
$(NULL)
diff --git a/tests/acls/acl.sh.in b/tests/acls/acl.sh.in
new file mode 100644
index 0000000000..772d664088
--- /dev/null
+++ b/tests/acls/acl.sh.in
@@ -0,0 +1,119 @@
+#!/bin/bash -x
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+BASICURL="http://127.0.0.1"
+BASICURLS="https://127.0.0.1"
+
+NETDATA_VARLIB_DIR="/var/lib/netdata"
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[0;43m'
+
+#change the previous acl file and with a new
+#and store it on a new file
+change_file(){
+ sed "s/$1/$2/g" netdata.cfg > "$4"
+}
+
+change_ssl_file(){
+ KEYROW="ssl key = $3/key.pem"
+ CERTROW="ssl certificate = $3/cert.pem"
+ sed "s@ssl key =@$KEYROW@g" netdata.ssl.cfg > tmp
+ sed "s@ssl certificate =@$CERTROW@g" tmp > tmp2
+ sed "s/$1/$2/g" tmp2 > "$4"
+}
+
+run_acl_tests() {
+ #Give a time for netdata start properly
+ sleep 2
+
+ curl -v -k --tls-max 1.2 --create-dirs -o index.html "$2" 2> log_index.txt
+ curl -v -k --tls-max 1.2 --create-dirs -o netdata.txt "$2/netdata.conf" 2> log_nc.txt
+ curl -v -k --tls-max 1.2 --create-dirs -o badge.csv "$2/api/v1/badge.svg?chart=cpu.cpu0_interrupts" 2> log_badge.txt
+ curl -v -k --tls-max 1.2 --create-dirs -o info.txt "$2/api/v1/info" 2> log_info.txt
+ curl -H "X-Auth-Token: $1" -v -k --tls-max 1.2 --create-dirs -o health.csv "$2/api/v1/manage/health?cmd=LIST" 2> log_health.txt
+
+ TOT=$(grep -c "HTTP/1.1 301" log_*.txt | cut -d: -f2| grep -c 1)
+ if [ "$TOT" -ne "$4" ]; then
+ echo -e "${RED}I got a wrong number of redirects($TOT) when SSL is activated, It was expected $4"
+ rm log_* netdata.conf.test* netdata.txt health.csv index.html badge.csv tmp* key.pem cert.pem info.txt
+ killall netdata
+ exit 1
+ elif [ "$TOT" -eq "$4" ] && [ "$4" -ne "0" ]; then
+ echo -e "${YELLOW}I got the correct number of redirects($4) when SSL is activated and I try to access with HTTP."
+ return
+ fi
+
+ TOT=$(grep -c "HTTP/1.1 200 OK" log_* | cut -d: -f2| grep -c 1)
+ if [ "$TOT" -ne "$3" ]; then
+ echo -e "${RED}I got a wrong number of \"200 OK\" from the queries, it was expected $3."
+ killall netdata
+ rm log_* netdata.conf.test* netdata.txt health.csv index.html badge.csv tmp* key.pem cert.pem info.txt
+ exit 1
+ fi
+
+ echo -e "${GREEN}ACLs were applied correctly"
+}
+
+CONF=$(grep "bind" netdata.cfg)
+MUSER=$(grep run netdata.cfg | cut -d= -f2|sed 's/^[ \t]*//')
+
+openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 -sha512 -subj "/C=US/ST=Denied/L=Somewhere/O=Dis/CN=www.example.com" -keyout key.pem -out cert.pem
+chown "$MUSER" key.pem cert.pem
+CWD=$(pwd)
+
+if [ -f "${NETDATA_VARLIB_DIR}/netdata.api.key" ] ;then
+ read -r TOKEN < "${NETDATA_VARLIB_DIR}/netdata.api.key"
+else
+ TOKEN="NULL"
+fi
+
+change_file "$CONF" " bind to = *" "$CWD" "netdata.conf.test0"
+netdata -c "netdata.conf.test0"
+run_acl_tests $TOKEN "$BASICURL:19999" 5 0
+killall netdata
+
+change_ssl_file "$CONF" " bind to = *=dashboard|registry|badges|management|netdata.conf *:20000=dashboard|registry|badges|management *:20001=dashboard|registry|netdata.conf^SSL=optional *:20002=dashboard|registry" "$CWD" "netdata.conf.test1"
+netdata -c "netdata.conf.test1"
+run_acl_tests $TOKEN "$BASICURL:19999" 5 5
+run_acl_tests $TOKEN "$BASICURLS:19999" 5 0
+
+run_acl_tests $TOKEN "$BASICURL:20000" 4 5
+run_acl_tests $TOKEN "$BASICURLS:20000" 4 0
+
+run_acl_tests $TOKEN "$BASICURL:20001" 4 0
+run_acl_tests $TOKEN "$BASICURLS:20001" 4 0
+
+run_acl_tests $TOKEN "$BASICURL:20002" 3 5
+run_acl_tests $TOKEN "$BASICURLS:20002" 3 0
+killall netdata
+
+change_ssl_file "$CONF" " bind to = *=dashboard|registry|badges|management|netdata.conf *:20000=dashboard|registry|badges|management *:20001=dashboard|registry|netdata.conf^SSL=force *:20002=dashboard|registry" "$CWD" "netdata.conf.test2"
+netdata -c "netdata.conf.test2"
+run_acl_tests $TOKEN "$BASICURL:19999" 5 5
+run_acl_tests $TOKEN "$BASICURLS:19999" 5 0
+
+run_acl_tests $TOKEN "$BASICURL:20000" 4 5
+run_acl_tests $TOKEN "$BASICURLS:20000" 4 0
+
+run_acl_tests $TOKEN "$BASICURL:20001" 4 5
+run_acl_tests $TOKEN "$BASICURLS:20001" 4 0
+
+run_acl_tests $TOKEN "$BASICURL:20002" 3 5
+run_acl_tests $TOKEN "$BASICURLS:20002" 3 0
+killall netdata
+
+change_ssl_file "$CONF" " bind to = *=dashboard|registry|badges|management|netdata.conf *:20000=dashboard|registry|badges|management^SSL=optional *:20001=dashboard|registry|netdata.conf^SSL=force" "$CWD" "netdata.conf.test3"
+netdata -c "netdata.conf.test3"
+run_acl_tests $TOKEN "$BASICURL:19999" 5 5
+run_acl_tests $TOKEN "$BASICURLS:19999" 5 0
+
+run_acl_tests $TOKEN "$BASICURL:20000" 4 0
+run_acl_tests $TOKEN "$BASICURLS:20000" 4 0
+
+run_acl_tests $TOKEN "$BASICURL:20001" 4 5
+run_acl_tests $TOKEN "$BASICURLS:20001" 4 0
+killall netdata
+
+rm log_* netdata.conf.test* netdata.txt health.csv index.html badge.csv tmp* key.pem cert.pem info.txt
+echo "All the tests were successful"
diff --git a/tests/acls/netdata.cfg b/tests/acls/netdata.cfg
new file mode 100644
index 0000000000..1dcb4a5c6c
--- /dev/null
+++ b/tests/acls/netdata.cfg
@@ -0,0 +1,20 @@
+# netdata configuration
+#
+# You can download the latest version of this file, using:
+#
+# wget -O /etc/netdata/netdata.conf http://localhost:19999/netdata.conf
+# or
+# curl -o /etc/netdata/netdata.conf http://localhost:19999/netdata.conf
+#
+# You can uncomment and change any of the options below.
+# The value shown in the commented settings, is the default value.
+#
+
+[global]
+ run as user = netdata
+
+ # the default database size - 1 hour
+ history = 3600
+
+ # by default do not expose the netdata port
+ bind to = localhost
diff --git a/tests/acls/netdata.ssl.cfg b/tests/acls/netdata.ssl.cfg
new file mode 100644
index 0000000000..28e0030d55
--- /dev/null
+++ b/tests/acls/netdata.ssl.cfg
@@ -0,0 +1,24 @@
+# netdata configuration
+#
+# You can download the latest version of this file, using:
+#
+# wget -O /etc/netdata/netdata.conf http://localhost:19999/netdata.conf
+# or
+# curl -o /etc/netdata/netdata.conf http://localhost:19999/netdata.conf
+#
+# You can uncomment and change any of the options below.
+# The value shown in the commented settings, is the default value.
+#
+
+[global]
+ run as user = netdata
+
+ # the default database size - 1 hour
+ history = 3600
+
+ # by default do not expose the netdata port
+ bind to = localhost
+
+[web]
+ ssl key =
+ ssl certificate =
diff --git a/web/server/README.md b/web/server/README.md
index 0bab9f105f..173e895963 100644
--- a/web/server/README.md
+++ b/web/server/README.md
@@ -112,6 +112,20 @@ Example:
For information how to configure the slaves to use TLS, check [securing the communication](../../streaming#securing-streaming-communications) in the streaming documentation. There you will find additional details on the expected behavior for client and server nodes, when their respective TLS options are enabled.
+When we define the use of SSL in a Netdata agent for different ports, Netdata will apply the behavior specified on each port. For example, using the configuration line below:
+
+```
+[web]
+ bind to = *=dashboard|registry|badges|management|streaming|netdata.conf^SSL=force *:20000=netdata.conf^SSL=optional *:20001=dashboard|registry
+```
+
+Netdata will:
+
+- Force all HTTP requests to the default port to be redirected to HTTPS (same port).
+- Refuse unencrypted streaming connections from slaves on the default port.
+- Allow both HTTP and HTTPS requests to port 20000 for netdata.conf
+- Force HTTP requests to port 20001 to be redirected to HTTPS (same port). Only allow requests for the dashboard, the read API and the registry on port 20001.
+
#### TLS/SSL errors
When you start using Netdata with TLS, you may find errors in the Netdata log, which is stored at `/var/log/netdata/error.log` by default.
diff --git a/web/server/web_client.c b/web/server/web_client.c
index e8566ce991..5d7d0708c2 100644
--- a/web/server/web_client.c
+++ b/web/server/web_client.c
@@ -835,7 +835,11 @@ static inline char *web_client_valid_method(struct web_client *w, char *s) {
s = &s[7];
#ifdef ENABLE_HTTPS
- if ( (w->ssl.flags) && (netdata_use_ssl_on_stream & NETDATA_SSL_FORCE)){
+ if (w->ssl.flags && web_client_is_using_ssl_force(w)){
+ w->header_parse_tries = 0;
+ w->header_parse_last_size = 0;
+ web_client_disable_wait_receive(w);
+
char hostname[256];
char *copyme = strstr(s,"hostname=");
if ( copyme ){
@@ -1065,7 +1069,7 @@ static inline HTTP_VALIDATION http_request_validate(struct web_client *w) {
}
#ifdef ENABLE_HTTPS
if ( (!web_client_check_unix(w)) && (netdata_srv_ctx) ) {
- if ((w->ssl.conn) && ((w->ssl.flags & NETDATA_SSL_NO_HANDSHAKE) && (netdata_use_ssl_on_http & NETDATA_SSL_FORCE) && (w->mode != WEB_CLIENT_MODE_STREAM)) ) {
+ if ((w->ssl.conn) && ((w->ssl.flags & NETDATA_SSL_NO_HANDSHAKE) && (web_client_is_using_ssl_force(w) || web_client_is_using_ssl_default(w)) && (w->mode != WEB_CLIENT_MODE_STREAM)) ) {
w->header_parse_tries = 0;
w->header_parse_last_size = 0;
web_client_disable_wait_receive(w);