From 7a0838da3ae1fbd4b85a02a6bb36c4f3d42e57a5 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Tue, 3 Nov 2020 21:25:42 +0100 Subject: [PATCH] * Restconf configuration has a new configure model: `clixon-restconf.yang` enabling restconf daemon configuration from datastore instead of from config file. --- CHANGELOG.md | 17 + apps/cli/cli_main.c | 2 +- apps/restconf/restconf_main_evhtp.c | 770 +++++++++++++++-- lib/src/clixon_netconf_lib.c | 4 + test/test_feature.sh | 2 +- test/test_restconf_backend_config.sh | 120 +++ test/test_restconf_jukebox.sh | 2 +- test/test_upgrade_quit.sh | 14 +- yang/clixon/Makefile.in | 3 +- yang/clixon/clixon-config@2020-06-17.yang | 803 ------------------ ...-17.yang => clixon-config@2020-11-03.yang} | 100 ++- yang/clixon/clixon-restconf@2020-10-30.yang | 190 +++++ yang/mandatory/Makefile.in | 2 +- .../mandatory/ietf-inet-types@2020-07-06.yang | 589 +++++++++++++ 14 files changed, 1718 insertions(+), 900 deletions(-) create mode 100755 test/test_restconf_backend_config.sh delete mode 100644 yang/clixon/clixon-config@2020-06-17.yang rename yang/clixon/{clixon-config@2020-08-17.yang => clixon-config@2020-11-03.yang} (89%) create mode 100644 yang/clixon/clixon-restconf@2020-10-30.yang create mode 100644 yang/mandatory/ietf-inet-types@2020-07-06.yang diff --git a/CHANGELOG.md b/CHANGELOG.md index 476112185..5672d37a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,23 @@ ## 4.9.0 Expected: 15 Dec 2020 +### New features + +* Restconf configuration has a new configure model: `clixon-restconf.yang` enabling restconf daemon configuration from datastore instead of from config file. + * Restconf config data, such as addresses, authentication type, etc, is read from the backend datastore instead of the clixon-config file on startup. + * This is enabled by setting `CLIXON_RESTCONF_CONFIG` to true (or start clixon-restconf with `-b`), in which case restconf data can be set in the datastore. + * This only applies to the evhtp restconf daemon, not fcgi/nginx. + * If enabled, most RESTCONF clixon-config options are obsolete + * Thanks to Dave Cornejo for the idea + * Example: instead of setting `file` in clixon.xml, set: `arestconf>file` in the running datastore before starting restconf. + +### API changes on existing protocol/config features + +Users may have to change how they access the system + +* New clixon-config@2020-11-03.yang revision + * Added option: `CLICON_RESTCONF_CONFIG` for reading restconf daemon config frm datastore + ### Minor changes * Added new backend plugin: ca_pre-demon called if backend is daemonized just prior to forking. diff --git a/apps/cli/cli_main.c b/apps/cli/cli_main.c index 0c56c84bf..558e35db3 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -262,7 +262,7 @@ autocli_tree(clicon_handle h, enum genmodel_type gt, int state, int printgen, - int show_tree) + int show_tree) { int retval = -1; parse_tree *pt = NULL; /* cli parse tree */ diff --git a/apps/restconf/restconf_main_evhtp.c b/apps/restconf/restconf_main_evhtp.c index 73494c85c..fc8316f89 100644 --- a/apps/restconf/restconf_main_evhtp.c +++ b/apps/restconf/restconf_main_evhtp.c @@ -79,26 +79,35 @@ #include "restconf_err.h" #include "restconf_root.h" + /* Command line options to be passed to getopt(3) */ -#define RESTCONF_OPTS "hD:f:E:l:p:d:y:a:u:ro:scP:" +#define RESTCONF_OPTS "hD:f:E:l:p:d:y:a:u:ro:bscP:" /* See see listen(5) */ #define SOCKET_LISTEN_BACKLOG 16 -/* Need global variable to for signal handler XXX */ -static clicon_handle _CLICON_HANDLE = NULL; - -static struct evhtp_handle{ +/* clixon evhtp handle */ +typedef struct { evhtp_t *eh_htp; struct event_base *eh_evbase; evhtp_ssl_cfg_t *eh_ssl_config; -} _EVHTP_HANDLE = {0,}; +} cx_evhtp_handle; + +/* Need this global to pass to signal handler + * XXX Try to get rid of code in signal handler + */ +static cx_evhtp_handle *_EVHTP_HANDLE = NULL; + +/* Need global variable to for signal handler XXX */ +static clicon_handle _CLICON_HANDLE = NULL; static void -evhtp_terminate(struct evhtp_handle *eh) +evhtp_terminate(cx_evhtp_handle *eh) { evhtp_ssl_cfg_t *sc; + if (eh == NULL) + return; if (eh->eh_htp){ evhtp_unbind_socket(eh->eh_htp); evhtp_free(eh->eh_htp); @@ -114,9 +123,11 @@ evhtp_terminate(struct evhtp_handle *eh) free(sc->privfile); free(sc); } + free(eh); } /*! Signall terminates process + * XXX Try to get rid of code in signal handler -> so we can get rid of global variables */ static void restconf_sig_term(int arg) @@ -128,7 +139,8 @@ restconf_sig_term(int arg) __PROGRAM__, __FUNCTION__, getpid(), arg); else exit(-1); - evhtp_terminate(&_EVHTP_HANDLE); + if (_EVHTP_HANDLE) /* global */ + evhtp_terminate(_EVHTP_HANDLE); if (_CLICON_HANDLE){ // stream_child_freeall(_CLICON_HANDLE); restconf_terminate(_CLICON_HANDLE); @@ -492,9 +504,84 @@ cx_path_restconf(evhtp_request_t *req, return; /* void */ } +/*! Get Server cert ssl info + * @param[in] h Clicon handle + * @param[in] server_cert_path Path to server ssl cert file + * @param[in] server_key_path Path to server ssl key file + * @param[in,out] ssl_config evhtp ssl config struct + * @retval 0 OK + * @retval -1 Error + */ +static int +cx_get_ssl_server_certs(clicon_handle h, + const char *server_cert_path, + const char *server_key_path, + evhtp_ssl_cfg_t *ssl_config) +{ + int retval = -1; + struct stat f_stat; + + if (ssl_config == NULL || server_cert_path == NULL){ + clicon_err(OE_CFG, EINVAL, "Input parameter is NULL"); + goto done; + } + if ((ssl_config->pemfile = strdup(server_cert_path)) == NULL){ + clicon_err(OE_CFG, errno, "strdup"); + goto done; + } + if (stat(ssl_config->pemfile, &f_stat) != 0) { + clicon_err(OE_FATAL, errno, "Cannot load SSL cert '%s'", ssl_config->pemfile); + goto done; + } + if ((ssl_config->privfile = strdup(server_key_path)) == NULL){ + clicon_err(OE_CFG, errno, "strdup"); + goto done; + } + if (stat(ssl_config->privfile, &f_stat) != 0) { + clicon_err(OE_FATAL, errno, "Cannot load SSL key '%s'", ssl_config->privfile); + goto done; + } + retval = 0; + done: + return retval; +} + +/*! Get client ssl cert info + * @param[in] h Clicon handle + * @param[in] server_ca_cert_path Path to server ssl CA file for client certs + * @param[in,out] ssl_config evhtp ssl config struct + * @retval 0 OK + * @retval -1 Error + */ +static int +cx_get_ssl_client_certs(clicon_handle h, + const char *server_ca_cert_path, + evhtp_ssl_cfg_t *ssl_config) +{ + int retval = -1; + struct stat f_stat; + + if (ssl_config == NULL || server_ca_cert_path == NULL){ + clicon_err(OE_CFG, EINVAL, "Input parameter is NULL"); + goto done; + } + if ((ssl_config->cafile = strdup(server_ca_cert_path)) == NULL){ + clicon_err(OE_CFG, errno, "strdup"); + goto done; + } + if (stat(ssl_config->cafile, &f_stat) != 0) { + clicon_err(OE_FATAL, errno, "Cannot load SSL key '%s'", ssl_config->privfile); + goto done; + } + retval = 0; + done: + return retval; +} + /*! Get Server cert info - * @param[in] h Clicon handle - * @param[out] ssl_config evhtp ssl config struct + * @param[in] h Clicon handle + * @param[in] ssl_verify_clients If true, verify client certs + * @param[out] ssl_config evhtp ssl config struct */ static int cx_get_certs(clicon_handle h, @@ -602,10 +689,11 @@ usage(clicon_handle h, "\t-a UNIX|IPv4|IPv6 Internal backend socket family\n" "\t-u \t Internal socket domain path or IP addr (see -a)\n" "\t-r \t\t Do not drop privileges if run as root\n" + "\t-b \t\t Read config from backend - not local (same as CLICON_RESTCONF_CONF=true) \n" "\t-o