diff options
author | thiagoftsm <49162938+thiagoftsm@users.noreply.github.com> | 2019-05-31 14:27:35 +0000 |
---|---|---|
committer | Chris Akritidis <43294513+cakrit@users.noreply.github.com> | 2019-05-31 16:27:35 +0200 |
commit | b6088e08a7dcf40c89dc859f85be11b2f9883a23 (patch) | |
tree | b3a97848aa65905243839f8a09855e667ae3ec0e /libnetdata | |
parent | 0c39c626ff1cba2d3ebde70c175fee12bade45f8 (diff) |
SSL implementation for Netdata (#5956)
* SSL implementation for Netdata
* Upload of fixes asked by @paulkatsoulakis and @cakrit
* Fix local computer
* Adding openssl to webserver
* fixing..
* HTTPS almost there
* Codacity
* HTTPS day 3
* HTTPS without Bio step 1
* HTTPS without Bio step 2
* HTTPS without Bio step 3
* HTTPS without Bio step 4
* HTTPS without Bio step 5
* HTTPS without Bio step 6
* HTTPS without Bio step 7
* HTTPS without Bio step 8
* HTTPS without Bio step 9
* HTTPS without Bio step 10
* SSL on streaming 1
* Daily pull
* HTTPS without Bio step 11
* HTTPS without Bio step 12
* HTTPS without Bio step 13
* HTTPS without Bio step 14
* SSL_Interception change documentation
* HTTPS without Bio step 15
* HTTPS without Bio step 16
* SSL_Interception fix codacity
* SSL_Interception fix doc
* SSL_Interception comments
* SSL_Interception fixing problems!
* SSL_Interception killing bugs
* SSL_Interception changing parameter
* SSL_Implementation documentation and script
* SSL_Implementation multiple fixes
* SSL_Implementation installer and cipher
* SSL_Implementation Redirect 301
* SSL_Implementation webserver doc and install-or-update.sh
* SSL_Implementation error 00000001:lib(0):func(0):reason(1)
* SSL_Implementation web server doc
* SSL_Implementation SEGFAULT on Fedora
* SSL_Implementation fix ^SSL=force|optional
* SSL_Implementation Redirect and Ciphers
* SSL_Implementation race condition 1
* SSL_Implementation Fix Location
* SSL_Implementation Fix Location 2
* SSL_Implementation Fix stream
* SSL_Implementation Fix stream 2
* SSL_Implementation Fix stream 3
* SSL_Implementation last problems!
* SSL_Implementation adjusts to commit!
* SSL_Implementation documentation permission!
* SSL_Implementation documentation permission 2!
* SSL_Implementation documentation permission 3!
Diffstat (limited to 'libnetdata')
-rw-r--r-- | libnetdata/libnetdata.h | 1 | ||||
-rw-r--r-- | libnetdata/socket/security.c | 215 | ||||
-rw-r--r-- | libnetdata/socket/security.h | 38 | ||||
-rw-r--r-- | libnetdata/socket/socket.c | 51 | ||||
-rw-r--r-- | libnetdata/socket/socket.h | 6 |
5 files changed, 310 insertions, 1 deletions
diff --git a/libnetdata/libnetdata.h b/libnetdata/libnetdata.h index 230dc24422..eaedfe7036 100644 --- a/libnetdata/libnetdata.h +++ b/libnetdata/libnetdata.h @@ -298,6 +298,7 @@ extern char *netdata_configured_host_prefix; #include "clocks/clocks.h" #include "popen/popen.h" #include "simple_pattern/simple_pattern.h" +#include "socket/security.h" #include "socket/socket.h" #include "config/appconfig.h" #include "log/log.h" diff --git a/libnetdata/socket/security.c b/libnetdata/socket/security.c new file mode 100644 index 0000000000..74a07a573b --- /dev/null +++ b/libnetdata/socket/security.c @@ -0,0 +1,215 @@ +#include "../libnetdata.h" + +SSL_CTX *netdata_cli_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; + +static void security_info_callback(const SSL *ssl, int where, int ret) { + (void)ssl; + if ( where & SSL_CB_ALERT ) { + debug(D_WEB_CLIENT,"SSL INFO CALLBACK %s %s",SSL_alert_type_string( ret ),SSL_alert_desc_string_long(ret)); + } +} + +void security_openssl_library() +{ +#if OPENSSL_VERSION_NUMBER < 0x10100000L +# if (SSLEAY_VERSION_NUMBER >= 0x0907000L) + OPENSSL_config(NULL); +# endif + +# if OPENSSL_API_COMPAT < 0x10100000L + SSL_load_error_strings(); +# endif + + SSL_library_init(); +#else + if ( OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG,NULL) != 1 ){ + error("SSL library cannot be initialized."); + } +#endif +} + +void security_openssl_common_options(SSL_CTX *ctx){ +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + static char *ciphers = {"ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA"}; +#endif +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SSL_CTX_set_options (ctx,SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_COMPRESSION); +#else + SSL_CTX_set_min_proto_version(ctx,TLS1_2_VERSION); + //We are avoiding the TLS v1.3 for while, because Google Chrome + //is giving the message net::ERR_SSL_VERSION_INTERFERENCE with it. + SSL_CTX_set_max_proto_version(ctx,TLS1_2_VERSION); +#endif + SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + if (!SSL_CTX_set_cipher_list(ctx,ciphers) ){ + error("SSL error. cannot set the cipher list"); + } +#endif + + +} + +static SSL_CTX * security_initialize_openssl_client() { + SSL_CTX *ctx; +#if OPENSSL_VERSION_NUMBER < 0x10100000L + ctx = SSL_CTX_new(SSLv23_client_method()); +#else + ctx = SSL_CTX_new(TLS_client_method()); +#endif + security_openssl_common_options(ctx); + + return ctx; +} + +static SSL_CTX * security_initialize_openssl_server(){ + SSL_CTX *ctx; + char lerror[512]; + static int netdata_id_context = 1; + + //TO DO: Confirm the necessity to check return for other OPENSSL function +#if OPENSSL_VERSION_NUMBER < 0x10100000L + ctx = SSL_CTX_new(SSLv23_server_method()); + if ( !ctx ) { + error("Cannot create a new SSL context, netdata won't encrypt communication"); + return NULL; + } + + SSL_CTX_use_certificate_file(ctx, security_cert, SSL_FILETYPE_PEM); +#else + ctx = SSL_CTX_new(TLS_server_method()); + if ( !ctx ){ + error("Cannot create a new SSL context, netdata won't encrypt communication"); + return NULL; + } + + SSL_CTX_use_certificate_chain_file(ctx, security_cert ); +#endif + security_openssl_common_options(ctx); + + SSL_CTX_use_PrivateKey_file(ctx,security_key,SSL_FILETYPE_PEM); + + if ( !SSL_CTX_check_private_key(ctx) ){ + ERR_error_string_n(ERR_get_error(),lerror,sizeof(lerror)); + error("SSL cannot check the private key: %s",lerror); + SSL_CTX_free(ctx); + return NULL; + } + + SSL_CTX_set_session_id_context(ctx,(void*)&netdata_id_context,(unsigned int)sizeof(netdata_id_context)); + SSL_CTX_set_info_callback(ctx,security_info_callback); + +#if (OPENSSL_VERSION_NUMBER < 0x00905100L) + SSL_CTX_set_verify_depth(ctx,1); +#endif + debug(D_WEB_CLIENT,"SSL GLOBAL CONTEXT STARTED\n"); + + return ctx; +} + +void security_start_ssl(int type){ + if ( !type){ + struct stat statbuf; + if ( (stat(security_key,&statbuf)) || (stat(security_cert,&statbuf)) ){ + info("To use encryption it is necessary to set \"ssl certificate\" and \"ssl key\" in [web] !\n"); + return; + } + + netdata_srv_ctx = security_initialize_openssl_server(); + } + else { + netdata_cli_ctx = security_initialize_openssl_client(); + } +} + +void security_clean_openssl(){ + if ( netdata_srv_ctx ) + { + SSL_CTX_free(netdata_srv_ctx); + } + + if ( netdata_cli_ctx ) + { + SSL_CTX_free(netdata_cli_ctx); + } + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + ERR_free_strings(); +#endif +} + +int security_process_accept(SSL *ssl,int msg) { + int sock = SSL_get_fd(ssl); + int test; + if (msg > 0x17) + { + return NETDATA_SSL_NO_HANDSHAKE; + } + + ERR_clear_error(); + if ((test = SSL_accept(ssl)) <= 0) { + int sslerrno = SSL_get_error(ssl, test); + switch(sslerrno) { + case SSL_ERROR_WANT_READ: + { + error("SSL handshake did not finish and it wanna read on socket %d!",sock); + return NETDATA_SSL_WANT_READ; + } + case SSL_ERROR_WANT_WRITE: + { + error("SSL handshake did not finish and it wanna read on socket %d!",sock); + return NETDATA_SSL_WANT_WRITE; + } + case SSL_ERROR_NONE: + case SSL_ERROR_SSL: + case SSL_ERROR_SYSCALL: + 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,test),NULL),sock); + } + return NETDATA_SSL_NO_HANDSHAKE; + } + } + } + + if ( SSL_is_init_finished(ssl) ) + { + 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; +} + +int security_test_certificate(SSL *ssl){ + X509* cert = SSL_get_peer_certificate(ssl); + int ret; + long status; + if (!cert){ + return -1; + } + + status = SSL_get_verify_result(ssl); + if((X509_V_OK != status)) + { + char error[512]; + ERR_error_string_n(ERR_get_error(),error,sizeof(error)); + error("SSL RFC4158 check: We have a invalid certificate, the tests result with %ld and message %s",status,error); + ret = -1; + } + else { + ret = 0; + } + return ret; +}
\ No newline at end of file diff --git a/libnetdata/socket/security.h b/libnetdata/socket/security.h new file mode 100644 index 0000000000..f4771414f7 --- /dev/null +++ b/libnetdata/socket/security.h @@ -0,0 +1,38 @@ +#ifndef NETDATA_SECURITY_H +# define NETDATA_SECURITY_H + +# include <openssl/ssl.h> +# include <openssl/err.h> +# if (SSLEAY_VERSION_NUMBER >= 0x0907000L) && (OPENSSL_VERSION_NUMBER < 0x10100000L) +# include <openssl/conf.h> +# endif + +#define NETDATA_SSL_HANDSHAKE_COMPLETE 0 //All the steps were successful +#define NETDATA_SSL_START 1 //Starting handshake, conn variable is NULL +#define NETDATA_SSL_WANT_READ 2 //The connection wanna read from socket +#define NETDATA_SSL_WANT_WRITE 4 //The connection wanna write on socket +#define NETDATA_SSL_NO_HANDSHAKE 8 //Continue without encrypt connection. +#define NETDATA_SSL_OPTIONAL 16 //Flag to define the HTTP request +#define NETDATA_SSL_FORCE 32 //We only accepts HTTPS request +#define NETDATA_SSL_INVALID_CERTIFICATE 64 //Accepts invalid certificate +#define NETDATA_SSL_VALID_CERTIFICATE 128 //Accepts invalid certificate +struct netdata_ssl{ + SSL *conn; //SSL connection + int flags; +}; + +extern SSL_CTX *netdata_cli_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(); +void security_clean_openssl(); +void security_start_ssl(int type); +int security_process_accept(SSL *ssl,int msg); +int security_test_certificate(SSL *ssl); + +#endif //NETDATA_SECURITY_H diff --git a/libnetdata/socket/socket.c b/libnetdata/socket/socket.c index bf9c60ea14..3ac5a26e5a 100644 --- a/libnetdata/socket/socket.c +++ b/libnetdata/socket/socket.c @@ -301,14 +301,37 @@ void listen_sockets_close(LISTEN_SOCKETS *sockets) { sockets->failed = 0; } +WEB_CLIENT_ACL socket_ssl_acl(char *ssl){ + 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; + } + + 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); + } + 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; - return WEB_CLIENT_ACL_NONE; + + return socket_ssl_acl(st); } static inline int bind_to_this(LISTEN_SOCKETS *sockets, const char *definition, uint16_t default_port, int listen_backlog) { @@ -824,7 +847,12 @@ int connect_to_one_of(const char *destination, int default_port, struct timeval // -------------------------------------------------------------------------------------------------------------------- // helpers to send/receive data in one call, in blocking mode, with a timeout +#ifdef ENABLE_HTTPS +ssize_t recv_timeout(struct netdata_ssl *ssl,int sockfd, void *buf, size_t len, int flags, int timeout) { +#else ssize_t recv_timeout(int sockfd, void *buf, size_t len, int flags, int timeout) { +#endif + for(;;) { struct pollfd fd = { .fd = sockfd, @@ -852,10 +880,22 @@ ssize_t recv_timeout(int sockfd, void *buf, size_t len, int flags, int timeout) if(fd.events & POLLIN) break; } +#ifdef ENABLE_HTTPS + if (ssl->conn){ + if (!ssl->flags){ + return SSL_read(ssl->conn,buf,len); + } + } +#endif return recv(sockfd, buf, len, flags); } +#ifdef ENABLE_HTTPS +ssize_t send_timeout(struct netdata_ssl *ssl,int sockfd, void *buf, size_t len, int flags, int timeout) { +#else ssize_t send_timeout(int sockfd, void *buf, size_t len, int flags, int timeout) { +#endif + for(;;) { struct pollfd fd = { .fd = sockfd, @@ -883,6 +923,13 @@ ssize_t send_timeout(int sockfd, void *buf, size_t len, int flags, int timeout) if(fd.events & POLLOUT) break; } +#ifdef ENABLE_HTTPS + if(ssl->conn){ + if (!ssl->flags){ + return SSL_write(ssl->conn, buf, len); + } + } +#endif return send(sockfd, buf, len, flags); } @@ -1291,6 +1338,8 @@ static void poll_events_process(POLLJOB *p, POLLINFO *pi, struct pollfd *pf, sho do { char client_ip[NI_MAXHOST + 1]; char client_port[NI_MAXSERV + 1]; + client_ip[0] = 0x00; + client_port[0] = 0x00; debug(D_POLLFD, "POLLFD: LISTENER: calling accept4() slot %zu (fd %d)", i, fd); nfd = accept_socket(fd, SOCK_NONBLOCK, client_ip, NI_MAXHOST + 1, client_port, NI_MAXSERV + 1, p->access_list); diff --git a/libnetdata/socket/socket.h b/libnetdata/socket/socket.h index c69d4897f3..1356765168 100644 --- a/libnetdata/socket/socket.h +++ b/libnetdata/socket/socket.h @@ -3,6 +3,7 @@ #ifndef NETDATA_SOCKET_H #define NETDATA_SOCKET_H +#include <openssl/ossl_typ.h> #include "../libnetdata.h" #ifndef MAX_LISTEN_FDS @@ -51,8 +52,13 @@ extern void listen_sockets_close(LISTEN_SOCKETS *sockets); extern int connect_to_this(const char *definition, int default_port, struct timeval *timeout); extern int connect_to_one_of(const char *destination, int default_port, struct timeval *timeout, size_t *reconnects_counter, char *connected_to, size_t connected_to_size); +#ifdef ENABLE_HTTPS +extern ssize_t recv_timeout(struct netdata_ssl *ssl,int sockfd, void *buf, size_t len, int flags, int timeout); +extern ssize_t send_timeout(struct netdata_ssl *ssl,int sockfd, void *buf, size_t len, int flags, int timeout); +#else extern ssize_t recv_timeout(int sockfd, void *buf, size_t len, int flags, int timeout); extern ssize_t send_timeout(int sockfd, void *buf, size_t len, int flags, int timeout); +#endif extern int sock_setnonblock(int fd); extern int sock_delnonblock(int fd); |