diff options
author | Costa Tsaousis <costa@netdata.cloud> | 2022-10-05 14:13:46 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-05 14:13:46 +0300 |
commit | 8fc3b351a2e7fc96eced8f924de2e9cec9842128 (patch) | |
tree | bde41c66573ccaf8876c280e00742cc6096b587c /libnetdata/socket | |
parent | 6850878e697d66dc90b9af1e750b22238c63c292 (diff) |
Allow netdata plugins to expose functions for querying more information about specific charts (#13720)
* function renames and code cleanup in popen.c; no actual code changes
* netdata popen() now opens both child process stdin and stdout and returns FILE * for both
* pass both input and output to parser structures
* updated rrdset to call custom functions
* RRDSET FUNCTION leading calls for both sync and async operation
* put RRDSET functions to a separate file
* added format and timeout at function definition
* support for synchronous (internal plugins) and asynchronous (external plugins and children) functions
* /api/v1/function endpoint
* functions are now attached to the host and there is a dictionary view per chart
* functions implemented at plugins.d
* remove the defer until keyword hook from plugins.d when it is done
* stream sender implementation of functions
* sanitization of all functions so that certain characters are only allowed
* strictier sanitization
* common max size
* 1st working plugins.d example
* always init inflight dictionary
* properly destroy dictionaries to avoid parallel insertion of items
* add more debugging on disconnection reasons
* add more debugging on disconnection reasons again
* streaming receiver respects newlines
* dont use the same fp for both streaming receive and send
* dont free dbengine memory with internal checks
* make sender proceed in the buffer
* added timing info and garbage collection at plugins.d
* added info about routing nodes
* added info about routing nodes with delay
* added more info about delays
* added more info about delays again
* signal sending thread to wake up
* streaming version labeling and commented code to support capabilities
* added functions to /api/v1/data, /api/v1/charts, /api/v1/chart, /api/v1/info
* redirect top output to stdout
* address coverity findings
* fix resource leaks of popen
* log attempts to connect to individual destinations
* better messages
* properly parse destinations
* try to find a function from the most matching to the least matching
* log added streaming destinations
* rotate destinations bypassing a node in the middle that does not accept our connection
* break the loops properly
* use typedef to define callbacks
* capabilities negotiation during streaming
* functions exposed upstream based on capabilities; compression disabled per node persisting reconnects; always try to connect with all capabilities
* restore functionality to lookup functions
* better logging of capabilities
* remove old versions from capabilities when a newer version is there
* fix formatting
* optimization for plugins.d rrdlabels to avoid creating and destructing dictionaries all the time
* delayed health initialization for rrddim and rrdset
* cleanup health initialization
* fix for popen() not returning the right value
* add health worker jobs for initializing rrdset and rrddim
* added content type support for functions; apps.plugin permanent function to display all the processes
* fixes for functions parameters parsing in apps.plugin
* fix for process matching in apps.plugiin
* first working function for apps.plugin
* Dashboard ACL is disabled for functions; Function errors are all in JSON format
* apps.plugin function processes returns json table
* use json_escape_string() to escape message
* fix formatting
* apps.plugin exposes all its metrics to function processes
* fix json formatting when filtering out some rows
* reopen the internal pipe of rrdpush in case of errors
* misplaced statement
* do not use buffer->len
* support for GLOBAL functions (functions that are not linked to a chart
* added /api/v1/functions endpoint; removed format from the FUNCTIONS api;
* swagger documentation about the new api end points
* added plugins.d documentation about functions
* never re-close a file
* remove uncessesary ifdef
* fixed issues identified by codacy
* fix for null label value
* make edit-config copy-and-paste friendly
* Revert "make edit-config copy-and-paste friendly"
This reverts commit 54500c0e0a97f65a0c66c4d34e966f6a9056698e.
* reworked sender handshake to fix coverity findings
* timeout is zero, for both send_timeout() and recv_timeout()
* properly detect that parent closed the socket
* support caching of function responses; limit function response to 10MB; added protection from malformed function responses
* disabled excessive logging
* added units to apps.plugin function processes and normalized all values to be human readable
* shorter field names
* fixed issues reported
* fixed apps.plugin error response; tested that pluginsd can properly handle faulty responses
* use double linked list macros for double linked list management
* faster apps.plugin function printing by minimizing file operations
* added memory percentage
* fix compatibility issues with older compilers and FreeBSD
* rrdpush sender code cleanup; rrhost structure cleanup from sender flags and variables;
* fix letftover variable in ifdef
* apps.plugin: do not call detach from the thread; exit immediately when input is broken
* exclude AR charts from health
* flush cleaner; prefer sender output
* clarity
* do not fill the cbuffer if not connected
* fix
* dont enabled host->sender if streaming is not enabled; send host label updates to parent;
* functions are only available through ACLK
* Prepared statement reports only in dev mode
* fix AR chart detection
* fix for streaming not being enabling itself
* more cleanup of sender and receiver structures
* moved read-only flags and configuration options to rrdhost->options
* fixed merge with master
* fix for incomplete rename
* prevent service thread from working on charts that are being collected
Co-authored-by: Stelios Fragkakis <52996999+stelfrag@users.noreply.github.com>
Diffstat (limited to 'libnetdata/socket')
-rw-r--r-- | libnetdata/socket/security.c | 45 | ||||
-rw-r--r-- | libnetdata/socket/security.h | 14 | ||||
-rw-r--r-- | libnetdata/socket/socket.c | 101 | ||||
-rw-r--r-- | libnetdata/socket/socket.h | 28 |
4 files changed, 124 insertions, 64 deletions
diff --git a/libnetdata/socket/security.c b/libnetdata/socket/security.c index 6ac512de5e..f229b7aa36 100644 --- a/libnetdata/socket/security.c +++ b/libnetdata/socket/security.c @@ -2,14 +2,14 @@ #ifdef ENABLE_HTTPS -SSL_CTX *netdata_exporting_ctx=NULL; -SSL_CTX *netdata_client_ctx=NULL; -SSL_CTX *netdata_srv_ctx=NULL; -const char *security_key=NULL; -const char *security_cert=NULL; +SSL_CTX *netdata_ssl_exporting_ctx =NULL; +SSL_CTX *netdata_ssl_client_ctx =NULL; +SSL_CTX *netdata_ssl_srv_ctx =NULL; +const char *ssl_security_key =NULL; +const char *ssl_security_cert =NULL; const char *tls_version=NULL; const char *tls_ciphers=NULL; -int netdata_validate_server = NETDATA_SSL_VALID_CERTIFICATE; +int netdata_ssl_validate_server = NETDATA_SSL_VALID_CERTIFICATE; /** * Info Callback @@ -161,7 +161,7 @@ static SSL_CTX * security_initialize_openssl_server() { return NULL; } - SSL_CTX_use_certificate_file(ctx, security_cert, SSL_FILETYPE_PEM); + SSL_CTX_use_certificate_file(ctx, ssl_security_cert, SSL_FILETYPE_PEM); #else ctx = SSL_CTX_new(TLS_server_method()); if (!ctx) { @@ -169,11 +169,11 @@ static SSL_CTX * security_initialize_openssl_server() { return NULL; } - SSL_CTX_use_certificate_chain_file(ctx, security_cert); + SSL_CTX_use_certificate_chain_file(ctx, ssl_security_cert); #endif security_openssl_common_options(ctx, 0); - SSL_CTX_use_PrivateKey_file(ctx,security_key,SSL_FILETYPE_PEM); + SSL_CTX_use_PrivateKey_file(ctx, ssl_security_key,SSL_FILETYPE_PEM); if (!SSL_CTX_check_private_key(ctx)) { ERR_error_string_n(ERR_get_error(),lerror,sizeof(lerror)); @@ -207,24 +207,25 @@ void security_start_ssl(int selector) { switch (selector) { case NETDATA_SSL_CONTEXT_SERVER: { struct stat statbuf; - if (stat(security_key, &statbuf) || stat(security_cert, &statbuf)) { + if (stat(ssl_security_key, &statbuf) || stat(ssl_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(); - SSL_CTX_set_mode(netdata_srv_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); + netdata_ssl_srv_ctx = security_initialize_openssl_server(); + SSL_CTX_set_mode(netdata_ssl_srv_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); break; } case NETDATA_SSL_CONTEXT_STREAMING: { - netdata_client_ctx = security_initialize_openssl_client(); + netdata_ssl_client_ctx = security_initialize_openssl_client(); //This is necessary for the stream, because it is working sometimes with nonblock socket. //It returns the bitmask after 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); + SSL_CTX_set_mode( + netdata_ssl_client_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE |SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER |SSL_MODE_AUTO_RETRY); break; } case NETDATA_SSL_CONTEXT_EXPORTING: { - netdata_exporting_ctx = security_initialize_openssl_client(); + netdata_ssl_exporting_ctx = security_initialize_openssl_client(); break; } } @@ -237,16 +238,16 @@ void security_start_ssl(int selector) { */ void security_clean_openssl() { - if (netdata_srv_ctx) { - SSL_CTX_free(netdata_srv_ctx); + if (netdata_ssl_srv_ctx) { + SSL_CTX_free(netdata_ssl_srv_ctx); } - if (netdata_client_ctx) { - SSL_CTX_free(netdata_client_ctx); + if (netdata_ssl_client_ctx) { + SSL_CTX_free(netdata_ssl_client_ctx); } - if (netdata_exporting_ctx) { - SSL_CTX_free(netdata_exporting_ctx); + if (netdata_ssl_exporting_ctx) { + SSL_CTX_free(netdata_ssl_exporting_ctx); } #if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_110 @@ -355,7 +356,7 @@ int security_test_certificate(SSL *ssl) { * * @return It returns 0 on success and -1 otherwise. */ -int security_location_for_context(SSL_CTX *ctx, char *file, char *path) { +int ssl_security_location_for_context(SSL_CTX *ctx, char *file, char *path) { struct stat statbuf; if (stat(file, &statbuf)) { info("Netdata does not have the parent's SSL certificate, so it will use the default OpenSSL configuration to validate certificates!"); diff --git a/libnetdata/socket/security.h b/libnetdata/socket/security.h index dbf71a6fea..a9b41fac46 100644 --- a/libnetdata/socket/security.h +++ b/libnetdata/socket/security.h @@ -42,15 +42,15 @@ struct netdata_ssl{ uint32_t flags; //The flags for SSL connection }; -extern SSL_CTX *netdata_exporting_ctx; -extern SSL_CTX *netdata_client_ctx; -extern SSL_CTX *netdata_srv_ctx; -extern const char *security_key; -extern const char *security_cert; +extern SSL_CTX *netdata_ssl_exporting_ctx; +extern SSL_CTX *netdata_ssl_client_ctx; +extern SSL_CTX *netdata_ssl_srv_ctx; +extern const char *ssl_security_key; +extern const char *ssl_security_cert; extern const char *tls_version; extern const char *tls_ciphers; -extern int netdata_validate_server; -extern int security_location_for_context(SSL_CTX *ctx,char *file,char *path); +extern int netdata_ssl_validate_server; +extern int ssl_security_location_for_context(SSL_CTX *ctx,char *file,char *path); void security_openssl_library(); void security_clean_openssl(); diff --git a/libnetdata/socket/socket.c b/libnetdata/socket/socket.c index df6d3148ba..fd8613030e 100644 --- a/libnetdata/socket/socket.c +++ b/libnetdata/socket/socket.c @@ -779,6 +779,10 @@ int connect_to_this(const char *definition, int default_port, struct timeval *ti char *path = host + 5; return connect_to_unix(path, timeout); } + else if(*host == '/') { + char *path = host; + return connect_to_unix(path, timeout); + } char *e = host; if(*e == '[') { @@ -826,41 +830,92 @@ int connect_to_this(const char *definition, int default_port, struct timeval *ti return connect_to_this_ip46(protocol, socktype, host, scope_id, service, timeout); } -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) { - int sock = -1; - +void foreach_entry_in_connection_string(const char *destination, bool (*callback)(char *entry, void *data), void *data) { const char *s = destination; while(*s) { const char *e = s; - // skip path, moving both s(tart) and e(nd) - if(*e == '/') - while(!isspace(*e) && *e != ',') s = ++e; - // skip separators, moving both s(tart) and e(nd) while(isspace(*e) || *e == ',') s = ++e; // move e(nd) to the first separator - while(*e && !isspace(*e) && *e != ',' && *e != '/') e++; + while(*e && !isspace(*e) && *e != ',') e++; // is there anything? if(!*s || s == e) break; char buf[e - s + 1]; strncpyz(buf, s, e - s); - if(reconnects_counter) *reconnects_counter += 1; - sock = connect_to_this(buf, default_port, timeout); - if(sock != -1) { - if(connected_to && connected_to_size) { - strncpy(connected_to, buf, connected_to_size); - connected_to[connected_to_size - 1] = '\0'; - } - break; - } + + if(callback(buf, data)) break; + s = e; } +} - return sock; +struct connect_to_one_of_data { + int default_port; + struct timeval *timeout; + size_t *reconnects_counter; + char *connected_to; + size_t connected_to_size; + int sock; +}; + +static bool connect_to_one_of_callback(char *entry, void *data) { + struct connect_to_one_of_data *t = data; + + if(t->reconnects_counter) + t->reconnects_counter++; + + t->sock = connect_to_this(entry, t->default_port, t->timeout); + if(t->sock != -1) { + if(t->connected_to && t->connected_to_size) { + strncpyz(t->connected_to, entry, t->connected_to_size); + t->connected_to[t->connected_to_size - 1] = '\0'; + } + + return true; + } + + return false; +} + +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) { + struct connect_to_one_of_data t = { + .default_port = default_port, + .timeout = timeout, + .reconnects_counter = reconnects_counter, + .connected_to = connected_to, + .connected_to_size = connected_to_size, + .sock = -1, + }; + + foreach_entry_in_connection_string(destination, connect_to_one_of_callback, &t); + + return t.sock; +} + +static bool connect_to_one_of_urls_callback(char *entry, void *data) { + char *s = strchr(entry, '/'); + if(s) *s = '\0'; + + return connect_to_one_of_callback(entry, data); +} + +int connect_to_one_of_urls(const char *destination, int default_port, struct timeval *timeout, size_t *reconnects_counter, char *connected_to, size_t connected_to_size) { + struct connect_to_one_of_data t = { + .default_port = default_port, + .timeout = timeout, + .reconnects_counter = reconnects_counter, + .connected_to = connected_to, + .connected_to_size = connected_to_size, + .sock = -1, + }; + + foreach_entry_in_connection_string(destination, connect_to_one_of_urls_callback, &t); + + return t.sock; } @@ -1087,12 +1142,11 @@ int accept_socket(int fd, int flags, char *client_ip, size_t ipsize, char *clien if (getnameinfo((struct sockaddr *)&sadr, addrlen, client_ip, (socklen_t)ipsize, client_port, (socklen_t)portsize, NI_NUMERICHOST | NI_NUMERICSERV) != 0) { error("LISTENER: cannot getnameinfo() on received client connection."); - strncpyz(client_ip, "UNKNOWN", ipsize - 1); - strncpyz(client_port, "UNKNOWN", portsize - 1); + strncpyz(client_ip, "UNKNOWN", ipsize); + strncpyz(client_port, "UNKNOWN", portsize); } if (!strcmp(client_ip, "127.0.0.1") || !strcmp(client_ip, "::1")) { - strncpy(client_ip, "localhost", ipsize); - client_ip[ipsize - 1] = '\0'; + strncpyz(client_ip, "localhost", ipsize); } #ifdef __FreeBSD__ @@ -1107,8 +1161,7 @@ int accept_socket(int fd, int flags, char *client_ip, size_t ipsize, char *clien case AF_UNIX: debug(D_LISTENER, "New UNIX domain web client from %s on socket %d.", client_ip, fd); // set the port - certain versions of libc return garbage on unix sockets - strncpy(client_port, "UNIX", portsize); - client_port[portsize - 1] = '\0'; + strncpyz(client_port, "UNIX", portsize); break; case AF_INET: diff --git a/libnetdata/socket/socket.h b/libnetdata/socket/socket.h index a40d801ddd..2c78bb9744 100644 --- a/libnetdata/socket/socket.h +++ b/libnetdata/socket/socket.h @@ -10,19 +10,22 @@ #endif typedef enum web_client_acl { - WEB_CLIENT_ACL_NONE = 0, - WEB_CLIENT_ACL_NOCHECK = 0, - WEB_CLIENT_ACL_DASHBOARD = 1 << 0, - WEB_CLIENT_ACL_REGISTRY = 1 << 1, - 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_NONE = 0, + WEB_CLIENT_ACL_NOCHECK = 0, + WEB_CLIENT_ACL_DASHBOARD = 1 << 0, + WEB_CLIENT_ACL_REGISTRY = 1 << 1, + 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_SSL_OPTIONAL = 1 << 6, - WEB_CLIENT_ACL_SSL_FORCE = 1 << 7, - WEB_CLIENT_ACL_SSL_DEFAULT = 1 << 8 + WEB_CLIENT_ACL_SSL_FORCE = 1 << 7, + WEB_CLIENT_ACL_SSL_DEFAULT = 1 << 8, + WEB_CLIENT_ACL_ACLK = 1 << 9, } WEB_CLIENT_ACL; +#define WEB_CLIENT_ACL_ALL 0xFFFF + #define web_client_can_access_dashboard(w) ((w)->acl & WEB_CLIENT_ACL_DASHBOARD) #define web_client_can_access_registry(w) ((w)->acl & WEB_CLIENT_ACL_REGISTRY) #define web_client_can_access_badges(w) ((w)->acl & WEB_CLIENT_ACL_BADGE) @@ -54,9 +57,12 @@ extern char *strdup_client_description(int family, const char *protocol, const c extern int listen_sockets_setup(LISTEN_SOCKETS *sockets); extern void listen_sockets_close(LISTEN_SOCKETS *sockets); +extern void foreach_entry_in_connection_string(const char *destination, bool (*callback)(char *entry, void *data), void *data); +extern int connect_to_this_ip46(int protocol, int socktype, const char *host, uint32_t scope_id, const char *service, struct timeval *timeout); 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); -int connect_to_this_ip46(int protocol, int socktype, const char *host, uint32_t scope_id, const char *service, struct timeval *timeout); +extern int connect_to_one_of_urls(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); |