Fixed ssl client certs for evhtp.
* Added SSL cert info as options: CLICON_SSL_SERVER_CERT, CLICON_SSL_SERVER_KEY, CLICON_SSL_CA_CERT Added config.sh for testing for autotools
This commit is contained in:
parent
9c82e97072
commit
c049a397b0
22 changed files with 515 additions and 80 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -58,6 +58,7 @@ util/clixon_util_xml
|
||||||
util/clixon_util_xpath
|
util/clixon_util_xpath
|
||||||
util/clixon_util_yang
|
util/clixon_util_yang
|
||||||
|
|
||||||
|
test/config.sh
|
||||||
test/site.sh
|
test/site.sh
|
||||||
test/vagrant/site.mk
|
test/vagrant/site.mk
|
||||||
test/cicd/site.mk
|
test/cicd/site.mk
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ Expected: July 2020
|
||||||
* Added a prefix for cli_show_config/cli_show_auto so that it can produce parseable output
|
* Added a prefix for cli_show_config/cli_show_auto so that it can produce parseable output
|
||||||
* Thanks dcornejo@netgate.com for trying it out and suggestions
|
* Thanks dcornejo@netgate.com for trying it out and suggestions
|
||||||
* Embedding restconf into the existing [libevhtp](https://github.com/criticalstack/libevhtp) embedded web server. (Experimental).
|
* Embedding restconf into the existing [libevhtp](https://github.com/criticalstack/libevhtp) embedded web server. (Experimental).
|
||||||
* The existing FCGI restconf solution will continue to be supported for NGINX and other reverese proxies with an fast CGI API.
|
* The existing FCGI restconf solution will continue to be supported for NGINX and other reverse proxies with a FCGI API.
|
||||||
* The restconf code has been refactored to support both modes. Hopefully, it should be straightforward to add another embedded server, such as GNU microhttpd.
|
* The restconf code has been refactored to support both modes. Hopefully, it should be straightforward to add another embedded server, such as GNU microhttpd.
|
||||||
* The new restconf module is selected using a compile-time autotools configure as follows:
|
* The new restconf module is selected using a compile-time autotools configure as follows:
|
||||||
* `--with-restconf=fcgi FCGI interface for stand-alone web rev-proxy eg nginx (default)`
|
* `--with-restconf=fcgi FCGI interface for stand-alone web rev-proxy eg nginx (default)`
|
||||||
|
|
@ -45,6 +45,11 @@ Expected: July 2020
|
||||||
* New clixon-config@2020-06-17.yang revision
|
* New clixon-config@2020-06-17.yang revision
|
||||||
* Added CLICON_CLI_LINES_DEFAULT for setting window row size of raw terminals
|
* Added CLICON_CLI_LINES_DEFAULT for setting window row size of raw terminals
|
||||||
* Added enum HIDE to CLICON_CLI_GENMODEL for auto-cli
|
* Added enum HIDE to CLICON_CLI_GENMODEL for auto-cli
|
||||||
|
* Added SSL cert info for evhtp restconf https:
|
||||||
|
* CLICON_SSL_SERVER_CERT
|
||||||
|
* CLICON_SSL_SERVER_KEY
|
||||||
|
* CLICON_SSL_CA_CERT
|
||||||
|
|
||||||
* Restconf FCGI (eg via nginx) have changed reply message syntax slightly as follows (due to refactoring and common code with evhtp):
|
* Restconf FCGI (eg via nginx) have changed reply message syntax slightly as follows (due to refactoring and common code with evhtp):
|
||||||
* Bodies in error reyruns including html code have been removed
|
* Bodies in error reyruns including html code have been removed
|
||||||
* Some (extra) CRLF:s have been removed
|
* Some (extra) CRLF:s have been removed
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,6 @@
|
||||||
* libevhtp code
|
* libevhtp code
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* XXX temp constant should go away, */
|
|
||||||
#undef _EVHTP_NYI
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
#include "clixon_config.h" /* generated by config & autoconf */
|
#include "clixon_config.h" /* generated by config & autoconf */
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -82,7 +79,7 @@
|
||||||
#include "restconf_root.h"
|
#include "restconf_root.h"
|
||||||
|
|
||||||
/* Command line options to be passed to getopt(3) */
|
/* Command line options to be passed to getopt(3) */
|
||||||
#define RESTCONF_OPTS "hD:f:l:p:d:y:a:u:o:P:s"
|
#define RESTCONF_OPTS "hD:f:l:p:d:y:a:u:o:P:sc"
|
||||||
|
|
||||||
/* Need global variable to for signal handler XXX */
|
/* Need global variable to for signal handler XXX */
|
||||||
static clicon_handle _CLICON_HANDLE = NULL;
|
static clicon_handle _CLICON_HANDLE = NULL;
|
||||||
|
|
@ -100,9 +97,7 @@ restconf_sig_term(int arg)
|
||||||
else
|
else
|
||||||
exit(-1);
|
exit(-1);
|
||||||
if (_CLICON_HANDLE){
|
if (_CLICON_HANDLE){
|
||||||
#ifdef _EVHTP_NYI
|
// stream_child_freeall(_CLICON_HANDLE);
|
||||||
stream_child_freeall(_CLICON_HANDLE);
|
|
||||||
#endif
|
|
||||||
restconf_terminate(_CLICON_HANDLE);
|
restconf_terminate(_CLICON_HANDLE);
|
||||||
}
|
}
|
||||||
clicon_exit_set(); /* checked in clixon_event_loop() */
|
clicon_exit_set(); /* checked in clixon_event_loop() */
|
||||||
|
|
@ -116,9 +111,6 @@ restconf_sig_child(int arg)
|
||||||
int pid;
|
int pid;
|
||||||
|
|
||||||
if ((pid = waitpid(-1, &status, 0)) != -1 && WIFEXITED(status)){
|
if ((pid = waitpid(-1, &status, 0)) != -1 && WIFEXITED(status)){
|
||||||
#ifdef _EVHTP_NYI
|
|
||||||
;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -267,10 +259,15 @@ evhtp_params_set(clicon_handle h,
|
||||||
evhtp_request_t *req,
|
evhtp_request_t *req,
|
||||||
cvec *qvec)
|
cvec *qvec)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
htp_method meth;
|
htp_method meth;
|
||||||
evhtp_uri_t *uri;
|
evhtp_uri_t *uri;
|
||||||
evhtp_path_t *path;
|
evhtp_path_t *path;
|
||||||
|
evhtp_ssl_t *ssl = NULL;
|
||||||
|
char *subject;
|
||||||
|
cvec *cvv = NULL;
|
||||||
|
char *cn;
|
||||||
|
|
||||||
|
|
||||||
if ((uri = req->uri) == NULL){
|
if ((uri = req->uri) == NULL){
|
||||||
clicon_err(OE_DAEMON, EFAULT, "No uri");
|
clicon_err(OE_DAEMON, EFAULT, "No uri");
|
||||||
|
|
@ -303,9 +300,18 @@ evhtp_params_set(clicon_handle h,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
clicon_debug(1, "%s conn->ssl:%d", __FUNCTION__, req->conn->ssl?1:0);
|
clicon_debug(1, "%s conn->ssl:%d", __FUNCTION__, req->conn->ssl?1:0);
|
||||||
if (req->conn->ssl != NULL){
|
if ((ssl = req->conn->ssl) != NULL){
|
||||||
if (restconf_param_set(h, "HTTPS", "https") < 0) /* some string or NULL */
|
if (restconf_param_set(h, "HTTPS", "https") < 0) /* some string or NULL */
|
||||||
goto done;
|
goto done;
|
||||||
|
/* SSL subject fields, eg CN (Common Name) , can add more here? */
|
||||||
|
if ((subject = (char*)htp_sslutil_subject_tostr(req->conn->ssl)) != NULL){
|
||||||
|
if (str2cvec(subject, '/', '=', &cvv) < 0)
|
||||||
|
goto done;
|
||||||
|
if ((cn = cvec_find_str(cvv, "CN")) != NULL){
|
||||||
|
if (restconf_param_set(h, "SSL_CN", cn) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Translate all http headers by capitalizing, prepend w HTTP_ and - -> _
|
/* Translate all http headers by capitalizing, prepend w HTTP_ and - -> _
|
||||||
|
|
@ -315,6 +321,8 @@ evhtp_params_set(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
retval = 1;
|
retval = 1;
|
||||||
done:
|
done:
|
||||||
|
if (cvv)
|
||||||
|
cvec_free(cvv);
|
||||||
return retval;
|
return retval;
|
||||||
fail:
|
fail:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
@ -423,6 +431,8 @@ cx_path_restconf(evhtp_request_t *req,
|
||||||
/* input debug */
|
/* input debug */
|
||||||
if (clicon_debug_get())
|
if (clicon_debug_get())
|
||||||
evhtp_headers_for_each(req->headers_in, print_header, h);
|
evhtp_headers_for_each(req->headers_in, print_header, h);
|
||||||
|
|
||||||
|
|
||||||
/* get accepted connection */
|
/* get accepted connection */
|
||||||
/* Query vector, ie the ?a=x&b=y stuff */
|
/* Query vector, ie the ?a=x&b=y stuff */
|
||||||
if ((qvec = cvec_new(0)) ==NULL){
|
if ((qvec = cvec_new(0)) ==NULL){
|
||||||
|
|
@ -446,51 +456,93 @@ cx_path_restconf(evhtp_request_t *req,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Get Server cert info
|
/*! Get Server cert info
|
||||||
* @param[out] ssl_config
|
* @param[in] h Clicon handle
|
||||||
|
* @param[out] ssl_config evhtp ssl config struct
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
get_servercerts(evhtp_ssl_cfg_t *ssl_config,
|
cx_get_certs(clicon_handle h,
|
||||||
const char *pki_dir,
|
int ssl_verify_clients,
|
||||||
const char *ssl_server_cert)
|
evhtp_ssl_cfg_t *ssl_config)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cbuf *cb = NULL;
|
|
||||||
struct stat f_stat;
|
struct stat f_stat;
|
||||||
|
char *filename;
|
||||||
|
|
||||||
if (ssl_config == NULL){
|
if (ssl_config == NULL){
|
||||||
clicon_err(OE_CFG, EINVAL, "ssl_config is NULL");
|
clicon_err(OE_CFG, EINVAL, "Input parameter is NULL");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if ((cb = cbuf_new()) == NULL){
|
if ((filename = clicon_option_str(h, "CLICON_SSL_SERVER_CERT")) == NULL){
|
||||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
clicon_err(OE_CFG, EFAULT, "CLICON_SSL_SERVER_CERT option missing");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
cprintf(cb, "%s/%s-crt.pem", pki_dir, ssl_server_cert);
|
if ((ssl_config->pemfile = strdup(filename)) == NULL){
|
||||||
if ((ssl_config->pemfile = strdup(cbuf_get(cb))) == NULL){
|
clicon_err(OE_CFG, errno, "strdup");
|
||||||
clicon_err(OE_UNIX, errno, "strdup");
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (stat(ssl_config->pemfile, &f_stat) != 0) {
|
if (stat(ssl_config->pemfile, &f_stat) != 0) {
|
||||||
clicon_err(OE_FATAL, errno, "Cannot load SSL cert '%s'", ssl_config->pemfile);
|
clicon_err(OE_FATAL, errno, "Cannot load SSL cert '%s'", ssl_config->pemfile);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
cbuf_reset(cb);
|
if ((filename = clicon_option_str(h, "CLICON_SSL_SERVER_KEY")) == NULL){
|
||||||
cprintf(cb, "%s/%s-key.pem", pki_dir, ssl_server_cert);
|
clicon_err(OE_CFG, EFAULT, "CLICON_SSL_SERVER_KEY option missing");
|
||||||
if ((ssl_config->privfile = strdup(cbuf_get(cb))) == NULL){
|
goto done;
|
||||||
clicon_err(OE_UNIX, errno, "strdup");
|
}
|
||||||
|
if ((ssl_config->privfile = strdup(filename)) == NULL){
|
||||||
|
clicon_err(OE_CFG, errno, "strdup");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (stat(ssl_config->privfile, &f_stat) != 0) {
|
if (stat(ssl_config->privfile, &f_stat) != 0) {
|
||||||
clicon_err(OE_FATAL, errno, "Cannot load SSL key '%s'", ssl_config->privfile);
|
clicon_err(OE_FATAL, errno, "Cannot load SSL key '%s'", ssl_config->privfile);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
if (ssl_verify_clients){
|
||||||
|
if ((filename = clicon_option_str(h, "CLICON_SSL_CA_CERT")) == NULL){
|
||||||
|
clicon_err(OE_CFG, EFAULT, "CLICON_SSL_CA_CERT option missing");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if ((ssl_config->cafile = strdup(filename)) == 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;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (cb)
|
|
||||||
cbuf_free(cb);
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cx_verify_certs(int pre_verify,
|
||||||
|
evhtp_x509_store_ctx_t *store)
|
||||||
|
{
|
||||||
|
#ifdef NOTYET
|
||||||
|
char buf[256];
|
||||||
|
X509 * err_cert;
|
||||||
|
int err;
|
||||||
|
int depth;
|
||||||
|
SSL * ssl;
|
||||||
|
evhtp_connection_t * connection;
|
||||||
|
evhtp_ssl_cfg_t * ssl_cfg;
|
||||||
|
|
||||||
|
fprintf(stderr, "%s %d\n", __FUNCTION__, pre_verify);
|
||||||
|
|
||||||
|
err_cert = X509_STORE_CTX_get_current_cert(store);
|
||||||
|
err = X509_STORE_CTX_get_error(store);
|
||||||
|
depth = X509_STORE_CTX_get_error_depth(store);
|
||||||
|
ssl = X509_STORE_CTX_get_ex_data(store, SSL_get_ex_data_X509_STORE_CTX_idx());
|
||||||
|
|
||||||
|
X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);
|
||||||
|
|
||||||
|
connection = SSL_get_app_data(ssl);
|
||||||
|
ssl_cfg = connection->htp->ssl_cfg;
|
||||||
|
#endif
|
||||||
|
return pre_verify;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Usage help routine
|
/*! Usage help routine
|
||||||
* @param[in] argv0 command line
|
* @param[in] argv0 command line
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
|
|
@ -513,6 +565,7 @@ usage(clicon_handle h,
|
||||||
"\t-u <path|addr>\t Internal socket domain path or IP addr (see -a)\n"
|
"\t-u <path|addr>\t Internal socket domain path or IP addr (see -a)\n"
|
||||||
"\t-o \"<option>=<value>\" Give configuration option overriding config file (see clixon-config.yang)\n"
|
"\t-o \"<option>=<value>\" Give configuration option overriding config file (see clixon-config.yang)\n"
|
||||||
"\t-s\t SSL server, https\n"
|
"\t-s\t SSL server, https\n"
|
||||||
|
"\t-c\t SSL verify client certs\n"
|
||||||
"\t-P <port>\t HTTP port (default 80, or 443 if -s is given)\n"
|
"\t-P <port>\t HTTP port (default 80, or 443 if -s is given)\n"
|
||||||
,
|
,
|
||||||
argv0,
|
argv0,
|
||||||
|
|
@ -521,6 +574,8 @@ usage(clicon_handle h,
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*! Main routine for libevhtp restconf
|
/*! Main routine for libevhtp restconf
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
|
|
@ -541,15 +596,12 @@ main(int argc,
|
||||||
size_t cligen_bufthreshold;
|
size_t cligen_bufthreshold;
|
||||||
uint16_t defaultport = 80;
|
uint16_t defaultport = 80;
|
||||||
uint16_t port = 0;
|
uint16_t port = 0;
|
||||||
#ifdef _EVHTP_NYI
|
|
||||||
char *stream_path;
|
|
||||||
#endif
|
|
||||||
evhtp_t *htp = NULL;
|
evhtp_t *htp = NULL;
|
||||||
struct event_base *evbase = NULL;
|
struct event_base *evbase = NULL;
|
||||||
evhtp_ssl_cfg_t *ssl_config = NULL;
|
evhtp_ssl_cfg_t *ssl_config = NULL;
|
||||||
int dbg = 0;
|
int dbg = 0;
|
||||||
char *ssl_server = NULL; /* SSL server name, default "server", base for srv certs */
|
int use_ssl = 0;
|
||||||
char *pki_dir = CLIXON_PKI_DIR;
|
int ssl_verify_clients = 0;
|
||||||
|
|
||||||
/* In the startup, logs to stderr & debug flag set later */
|
/* In the startup, logs to stderr & debug flag set later */
|
||||||
clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
|
clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
|
||||||
|
|
@ -607,9 +659,7 @@ main(int argc,
|
||||||
/* Find and read configfile */
|
/* Find and read configfile */
|
||||||
if (clicon_options_main(h) < 0)
|
if (clicon_options_main(h) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
#ifdef _EVHTP_NYI
|
// stream_path = clicon_option_str(h, "CLICON_STREAM_PATH");
|
||||||
stream_path = clicon_option_str(h, "CLICON_STREAM_PATH");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Now rest of options, some overwrite option file */
|
/* Now rest of options, some overwrite option file */
|
||||||
optind = 1;
|
optind = 1;
|
||||||
|
|
@ -651,9 +701,12 @@ main(int argc,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 's': /* ssl: use https */
|
case 's': /* ssl: use https */
|
||||||
ssl_server = CLIXON_SERVER_CERT;
|
use_ssl = 1;
|
||||||
defaultport = 443; /* unless explicit -P ? */
|
defaultport = 443; /* unless explicit -P ? */
|
||||||
break;
|
break;
|
||||||
|
case 'c': /* ssl: verify clients */
|
||||||
|
ssl_verify_clients = 1;
|
||||||
|
break;
|
||||||
case 'P': /* http port */
|
case 'P': /* http port */
|
||||||
if (!strlen(optarg))
|
if (!strlen(optarg))
|
||||||
usage(h, argv0);
|
usage(h, argv0);
|
||||||
|
|
@ -669,7 +722,7 @@ main(int argc,
|
||||||
if (port == 0)
|
if (port == 0)
|
||||||
port = defaultport;
|
port = defaultport;
|
||||||
/* Check server ssl certs */
|
/* Check server ssl certs */
|
||||||
if (ssl_server){
|
if (use_ssl){
|
||||||
/* Init evhtp ssl config struct */
|
/* Init evhtp ssl config struct */
|
||||||
if ((ssl_config = malloc(sizeof(evhtp_ssl_cfg_t))) == NULL){
|
if ((ssl_config = malloc(sizeof(evhtp_ssl_cfg_t))) == NULL){
|
||||||
clicon_err(OE_UNIX, errno, "malloc");
|
clicon_err(OE_UNIX, errno, "malloc");
|
||||||
|
|
@ -678,8 +731,14 @@ main(int argc,
|
||||||
memset(ssl_config, 0, sizeof(evhtp_ssl_cfg_t));
|
memset(ssl_config, 0, sizeof(evhtp_ssl_cfg_t));
|
||||||
ssl_config->ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1;
|
ssl_config->ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1;
|
||||||
|
|
||||||
if (get_servercerts(ssl_config, pki_dir, ssl_server) < 0)
|
if (cx_get_certs(h, ssl_verify_clients, ssl_config) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
ssl_config->x509_verify_cb = cx_verify_certs; /* Is extra verification necessary? */
|
||||||
|
if (ssl_verify_clients){
|
||||||
|
ssl_config->verify_peer = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
|
||||||
|
ssl_config->x509_verify_cb = cx_verify_certs;
|
||||||
|
ssl_config->verify_depth = 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ssl_verify_mode = htp_sslutil_verify2opts(optarg);
|
// ssl_verify_mode = htp_sslutil_verify2opts(optarg);
|
||||||
|
|
@ -697,7 +756,7 @@ main(int argc,
|
||||||
clicon_err(OE_UNIX, errno, "evhtp_new");
|
clicon_err(OE_UNIX, errno, "evhtp_new");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (ssl_server){
|
if (use_ssl){
|
||||||
if (evhtp_ssl_init(htp, ssl_config) < 0){
|
if (evhtp_ssl_init(htp, ssl_config) < 0){
|
||||||
clicon_err(OE_UNIX, errno, "evhtp_new");
|
clicon_err(OE_UNIX, errno, "evhtp_new");
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -835,9 +894,7 @@ main(int argc,
|
||||||
|
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
#ifdef _EVHTP_NYI
|
// stream_child_freeall(h);
|
||||||
stream_child_freeall(h);
|
|
||||||
#endif
|
|
||||||
restconf_terminate(h);
|
restconf_terminate(h);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
3
configure
vendored
3
configure
vendored
|
|
@ -5326,7 +5326,7 @@ _ACEOF
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile example/main/Makefile extras/rpm/Makefile docker/Makefile docker/main/Makefile docker/base/Makefile util/Makefile yang/Makefile yang/clixon/Makefile yang/mandatory/Makefile yang/optional/Makefile doc/Makefile test/Makefile test/cicd/Makefile test/vagrant/Makefile"
|
ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile example/main/Makefile extras/rpm/Makefile docker/Makefile docker/main/Makefile docker/base/Makefile util/Makefile yang/Makefile yang/clixon/Makefile yang/mandatory/Makefile yang/optional/Makefile doc/Makefile test/Makefile test/config.sh test/cicd/Makefile test/vagrant/Makefile"
|
||||||
|
|
||||||
cat >confcache <<\_ACEOF
|
cat >confcache <<\_ACEOF
|
||||||
# This file is a shell script that caches the results of configure
|
# This file is a shell script that caches the results of configure
|
||||||
|
|
@ -6045,6 +6045,7 @@ do
|
||||||
"yang/optional/Makefile") CONFIG_FILES="$CONFIG_FILES yang/optional/Makefile" ;;
|
"yang/optional/Makefile") CONFIG_FILES="$CONFIG_FILES yang/optional/Makefile" ;;
|
||||||
"doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;;
|
"doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;;
|
||||||
"test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;;
|
"test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;;
|
||||||
|
"test/config.sh") CONFIG_FILES="$CONFIG_FILES test/config.sh" ;;
|
||||||
"test/cicd/Makefile") CONFIG_FILES="$CONFIG_FILES test/cicd/Makefile" ;;
|
"test/cicd/Makefile") CONFIG_FILES="$CONFIG_FILES test/cicd/Makefile" ;;
|
||||||
"test/vagrant/Makefile") CONFIG_FILES="$CONFIG_FILES test/vagrant/Makefile" ;;
|
"test/vagrant/Makefile") CONFIG_FILES="$CONFIG_FILES test/vagrant/Makefile" ;;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -326,6 +326,7 @@ AC_OUTPUT(Makefile
|
||||||
yang/optional/Makefile
|
yang/optional/Makefile
|
||||||
doc/Makefile
|
doc/Makefile
|
||||||
test/Makefile
|
test/Makefile
|
||||||
|
test/config.sh
|
||||||
test/cicd/Makefile
|
test/cicd/Makefile
|
||||||
test/vagrant/Makefile
|
test/vagrant/Makefile
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@
|
||||||
* [Thanks](#thanks)
|
* [Thanks](#thanks)
|
||||||
* [References](#references)
|
* [References](#references)
|
||||||
|
|
||||||
|
NOTE: Outdated docs, see: https://clixon-docs.readthedocs.io for updated docs
|
||||||
|
|
||||||
## Background
|
## Background
|
||||||
|
|
||||||
This document describes the configuration startup mechanism of the Clixon backend. It describes the mechanism of Clixon version 3.10 which supports the following features:
|
This document describes the configuration startup mechanism of the Clixon backend. It describes the mechanism of Clixon version 3.10 which supports the following features:
|
||||||
|
|
@ -334,7 +336,7 @@ where changes to the Yang model are documented and loaded into
|
||||||
Clixon. The implementation is not complete.
|
Clixon. The implementation is not complete.
|
||||||
|
|
||||||
When upgrading, the system parses the changelog and tries to upgrade
|
When upgrading, the system parses the changelog and tries to upgrade
|
||||||
the datastore automatically. This featire is experimental and has
|
the datastore automatically. This feature is experimental and has
|
||||||
several limitations.
|
several limitations.
|
||||||
|
|
||||||
You enable the automatic upgrading by registering the changelog upgrade method in `clixon_plugin_ini()` using wildcards:
|
You enable the automatic upgrading by registering the changelog upgrade method in `clixon_plugin_ini()` using wildcards:
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,9 @@
|
||||||
/* These include signatures for plugin and transaction callbacks. */
|
/* These include signatures for plugin and transaction callbacks. */
|
||||||
#include <clixon/clixon_backend.h>
|
#include <clixon/clixon_backend.h>
|
||||||
|
|
||||||
|
/* Command line options to be passed to getopt(3) */
|
||||||
|
#define BACKEND_EXAMPLE_OPTS "rsS:iuUt"
|
||||||
|
|
||||||
/*! Variable to control if reset code is run.
|
/*! Variable to control if reset code is run.
|
||||||
* The reset code inserts "extra XML" which assumes ietf-interfaces is
|
* The reset code inserts "extra XML" which assumes ietf-interfaces is
|
||||||
* loaded, and this is not always the case.
|
* loaded, and this is not always the case.
|
||||||
|
|
@ -1015,7 +1018,7 @@ clixon_plugin_init(clicon_handle h)
|
||||||
goto done;
|
goto done;
|
||||||
opterr = 0;
|
opterr = 0;
|
||||||
optind = 1;
|
optind = 1;
|
||||||
while ((c = getopt(argc, argv, "rsS:iuUt")) != -1)
|
while ((c = getopt(argc, argv, BACKEND_EXAMPLE_OPTS)) != -1)
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'r':
|
case 'r':
|
||||||
_reset = 1;
|
_reset = 1;
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,12 @@
|
||||||
#include <clixon/clixon.h>
|
#include <clixon/clixon.h>
|
||||||
#include <clixon/clixon_restconf.h> /* minor use */
|
#include <clixon/clixon_restconf.h> /* minor use */
|
||||||
|
|
||||||
|
/* Command line options to be passed to getopt(3)
|
||||||
|
* -a basic authentication
|
||||||
|
* -s ssl client certificates
|
||||||
|
*/
|
||||||
|
#define RESTCONF_EXAMPLE_OPTS "as"
|
||||||
|
|
||||||
static const char Base64[] =
|
static const char Base64[] =
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
static const char Pad64 = '=';
|
static const char Pad64 = '=';
|
||||||
|
|
@ -60,6 +66,11 @@ static const char Pad64 = '=';
|
||||||
*/
|
*/
|
||||||
static int basic_auth = 0;
|
static int basic_auth = 0;
|
||||||
|
|
||||||
|
/* Use https ssl client certs, map subject CN to user. Set by starting restonf with:
|
||||||
|
* clixon_restconf ... -- -s
|
||||||
|
*/
|
||||||
|
static int ssl_client_certs = 0;
|
||||||
|
|
||||||
/* skips all whitespace anywhere.
|
/* skips all whitespace anywhere.
|
||||||
converts characters, four at a time, starting at (or after)
|
converts characters, four at a time, starting at (or after)
|
||||||
src from base - 64 numbers into three 8 bit bytes in the target area.
|
src from base - 64 numbers into three 8 bit bytes in the target area.
|
||||||
|
|
@ -187,18 +198,17 @@ b64_decode(const char *src,
|
||||||
return (tarindex);
|
return (tarindex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Process a rest request that requires (cookie) "authentication"
|
/*! HTTP basic authentication example (note hardwired)
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
* @param[in] arg Argument. Here: Fastcgi request handle
|
|
||||||
* @retval -1 Fatal error
|
* @retval -1 Fatal error
|
||||||
* @retval 0 Unauth
|
* @retval 0 Unauth
|
||||||
* @retval 1 Auth
|
* @retval 1 Auth
|
||||||
* @note: Three hardwired users: andy, wilma, guest w password "bar".
|
* @note: Three hardwired users: andy, wilma, guest w password "bar".
|
||||||
* Enabled by passing -- -a to the main function
|
* Enabled by passing -- -a to the main function
|
||||||
*/
|
*/
|
||||||
int
|
static int
|
||||||
example_restconf_credentials(clicon_handle h,
|
example_basic_auth(clicon_handle h,
|
||||||
void *arg)
|
void *arg)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *xt = NULL;
|
cxobj *xt = NULL;
|
||||||
|
|
@ -210,9 +220,6 @@ example_restconf_credentials(clicon_handle h,
|
||||||
size_t authlen;
|
size_t authlen;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* HTTP basic authentication not enabled, pass with user "none" */
|
|
||||||
if (basic_auth==0)
|
|
||||||
goto ok;
|
|
||||||
/* At this point in the code we must use HTTP basic authentication */
|
/* At this point in the code we must use HTTP basic authentication */
|
||||||
if ((auth = restconf_param_get(h, "HTTP_AUTHORIZATION")) == NULL)
|
if ((auth = restconf_param_get(h, "HTTP_AUTHORIZATION")) == NULL)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
@ -249,7 +256,7 @@ example_restconf_credentials(clicon_handle h,
|
||||||
clicon_debug(1, "%s user:%s", __FUNCTION__, user);
|
clicon_debug(1, "%s user:%s", __FUNCTION__, user);
|
||||||
if (clicon_username_set(h, user) < 0)
|
if (clicon_username_set(h, user) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
ok: /* authenticated */
|
/* authenticated */
|
||||||
retval = 1;
|
retval = 1;
|
||||||
done: /* error */
|
done: /* error */
|
||||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||||
|
|
@ -265,6 +272,53 @@ example_restconf_credentials(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! SSL client cert authentication.
|
||||||
|
* @param[in] h Clixon handle
|
||||||
|
* @param[in] arg
|
||||||
|
* @retval -1 Fatal error
|
||||||
|
* @retval 0 Unauth
|
||||||
|
* @retval 1 Auth
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
example_client_certs(clicon_handle h,
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
char *cn;
|
||||||
|
|
||||||
|
/* Check for cert subject common name (CN) */
|
||||||
|
if ((cn = restconf_param_get(h, "SSL_CN")) == NULL)
|
||||||
|
goto fail;
|
||||||
|
if (clicon_username_set(h, cn) < 0)
|
||||||
|
goto done;
|
||||||
|
/* authenticated */
|
||||||
|
retval = 1;
|
||||||
|
done: /* error */
|
||||||
|
return retval;
|
||||||
|
fail: /* unauthenticated */
|
||||||
|
retval = 0;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Authentication callback
|
||||||
|
* @param[in] h Clixon handle
|
||||||
|
* @param[in] arg
|
||||||
|
* @retval -1 Fatal error
|
||||||
|
* @retval 0 Unauth
|
||||||
|
* @retval 1 Auth
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
example_restconf_credentials(clicon_handle h,
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
if (basic_auth)
|
||||||
|
return example_basic_auth(h, arg);
|
||||||
|
else if (ssl_client_certs)
|
||||||
|
return example_client_certs(h, arg);
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Local example restconf rpc callback
|
/*! Local example restconf rpc callback
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
|
|
@ -338,11 +392,14 @@ clixon_plugin_init(clicon_handle h)
|
||||||
return NULL;
|
return NULL;
|
||||||
opterr = 0;
|
opterr = 0;
|
||||||
optind = 1;
|
optind = 1;
|
||||||
while ((c = getopt(argc, argv, "a")) != -1)
|
while ((c = getopt(argc, argv, RESTCONF_EXAMPLE_OPTS)) != -1)
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'a':
|
case 'a': /* basic authentication */
|
||||||
basic_auth = 1;
|
basic_auth = 1;
|
||||||
break;
|
break;
|
||||||
|
case 's': /* ssl client certs */
|
||||||
|
ssl_client_certs = 1;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -100,8 +100,3 @@
|
||||||
* clixon-4.4
|
* clixon-4.4
|
||||||
*/
|
*/
|
||||||
#define STATE_ORDERED_BY_SYSTEM
|
#define STATE_ORDERED_BY_SYSTEM
|
||||||
|
|
||||||
/*! Dir for Public Key Infrastructure (PKI) X.509 certificates
|
|
||||||
*/
|
|
||||||
#define CLIXON_SERVER_CERT "server"
|
|
||||||
#define CLIXON_PKI_DIR "/etc/pki/clixon"
|
|
||||||
|
|
|
||||||
|
|
@ -97,3 +97,17 @@ For example, in FreeBSD, add:
|
||||||
make=gmake
|
make=gmake
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## https
|
||||||
|
|
||||||
|
If you use evhtp with `configure --with-restconf=evhtp`, you can prepend the tests with RCPROTO=https which will run all restconf tests with SSL https and server certs.
|
||||||
|
|
||||||
|
Ensure the server keys are in order, as follows.
|
||||||
|
|
||||||
|
If you already have server certs, ensure CLICON_SSL_SERVER_CERT and CLICON_SSL_SERVER_KEY points to them.
|
||||||
|
|
||||||
|
If you do not have them, generate self-signed certs, eg as follows:
|
||||||
|
```
|
||||||
|
openssl req -x509 -nodes -newkey rsa:4096 -keyout /etc/ssl/private/clixon-server-key.pem -out /etc/ssl/certs/clixon-server-crt.pem -days 365
|
||||||
|
```
|
||||||
|
|
||||||
|
There are also client-cert tests, eg test_ssl*.sh
|
||||||
|
|
|
||||||
4
test/config.sh.in
Executable file
4
test/config.sh.in
Executable file
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Generated from autotools
|
||||||
|
|
||||||
|
WITH_RESTCONF=@with_restconf@
|
||||||
17
test/lib.sh
17
test/lib.sh
|
|
@ -26,6 +26,14 @@ version=4
|
||||||
|
|
||||||
>&2 echo "Running $testfile"
|
>&2 echo "Running $testfile"
|
||||||
|
|
||||||
|
# Generated config file from autotools / configure
|
||||||
|
if [ -f ./config.sh ]; then
|
||||||
|
. ./config.sh
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
return -1 # error
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Site file, an example of this file in README.md
|
# Site file, an example of this file in README.md
|
||||||
if [ -f ./site.sh ]; then
|
if [ -f ./site.sh ]; then
|
||||||
. ./site.sh
|
. ./site.sh
|
||||||
|
|
@ -139,10 +147,11 @@ if [ ! -G $dir ]; then
|
||||||
sudo chgrp $u $dir
|
sudo chgrp $u $dir
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# If you bring your own backend BE=0 (it is already started),the backend may
|
# If you bring your own backend BE=0 (it is already started), the backend may
|
||||||
# have created some files (eg unix socket) in $dir and therefore cannot
|
# have created some files (eg unix socket) in $dir and therefore cannot
|
||||||
# be deleted
|
# be deleted.
|
||||||
if [ $BE -ne 0 ]; then
|
# Same with RC=0
|
||||||
|
if [ $BE -ne 0 -a $RC -ne 0 ]; then
|
||||||
rm -rf $dir/*
|
rm -rf $dir/*
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -232,7 +241,7 @@ wait_backend(){
|
||||||
start_restconf(){
|
start_restconf(){
|
||||||
# Start in background
|
# Start in background
|
||||||
if [ $RCPROTO = https ]; then
|
if [ $RCPROTO = https ]; then
|
||||||
EXTRA="-s"
|
EXTRA="-s" # server certs
|
||||||
else
|
else
|
||||||
EXTRA=
|
EXTRA=
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ cat <<EOF > $cfg
|
||||||
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||||
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
||||||
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
||||||
<CLICON_NACM_CREDENTIALS>none</CLICON_NACM_CREDENTIALS>
|
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,6 @@ cat <<EOF > $cfg
|
||||||
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||||
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
||||||
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
||||||
<CLICON_NACM_CREDENTIALS>none</CLICON_NACM_CREDENTIALS>
|
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,6 @@ cat <<EOF > $cfg
|
||||||
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||||
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
||||||
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
||||||
<CLICON_NACM_CREDENTIALS>none</CLICON_NACM_CREDENTIALS>
|
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,6 @@ cat <<EOF > $cfg
|
||||||
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||||
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
||||||
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
||||||
<CLICON_NACM_CREDENTIALS>none</CLICON_NACM_CREDENTIALS>
|
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ cat <<EOF > $cfg
|
||||||
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
|
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
|
||||||
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
||||||
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
||||||
<CLICON_NACM_CREDENTIALS>none</CLICON_NACM_CREDENTIALS>
|
|
||||||
<CLICON_XMLDB_FORMAT>$format</CLICON_XMLDB_FORMAT>
|
<CLICON_XMLDB_FORMAT>$format</CLICON_XMLDB_FORMAT>
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
EOF
|
EOF
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,6 @@ cat <<EOF > $cfg
|
||||||
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||||
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
||||||
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
||||||
<CLICON_NACM_CREDENTIALS>none</CLICON_NACM_CREDENTIALS>
|
|
||||||
</clixon-config>
|
</clixon-config>
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
|
||||||
263
test/test_ssl_certs.sh
Executable file
263
test/test_ssl_certs.sh
Executable file
|
|
@ -0,0 +1,263 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Restconf+NACM openssl functionality using server and client certs
|
||||||
|
# The test creates certs and keys:
|
||||||
|
# A CA, server key/cert, user key/cert for two users
|
||||||
|
# Can we try illegal certs?
|
||||||
|
|
||||||
|
# Magic line must be first in script (see README.md)
|
||||||
|
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||||
|
|
||||||
|
# Only works with evhtp and https
|
||||||
|
if [ "${WITH_RESTCONF}" != "evhtp" -o "$RCPROTO" = http ]; then
|
||||||
|
if [ "$s" = $0 ]; then exit 0; else return 0; fi # skip
|
||||||
|
fi
|
||||||
|
|
||||||
|
APPNAME=example
|
||||||
|
|
||||||
|
# Common NACM scripts
|
||||||
|
. ./nacm.sh
|
||||||
|
|
||||||
|
# force it (or check it?)
|
||||||
|
RCPROTO=https
|
||||||
|
|
||||||
|
fyang=$dir/example.yang
|
||||||
|
|
||||||
|
cfg=$dir/conf.xml
|
||||||
|
certdir=$dir/certs
|
||||||
|
|
||||||
|
srvkey=$certdir/srv_key.pem
|
||||||
|
srvcert=$certdir/srv_cert.pem
|
||||||
|
cakey=$certdir/ca_key.pem # needed?
|
||||||
|
cacert=$certdir/ca_cert.pem
|
||||||
|
users="andy guest" # generate certs for some users in nacm.sh
|
||||||
|
|
||||||
|
# Whether to generate new keys or not (only if $dir is not removed)
|
||||||
|
# Here dont generate keys if restconf started stand-alone
|
||||||
|
genkeys=true
|
||||||
|
if [ $RC -eq 0 ]; then
|
||||||
|
genkeys=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
test -d $certdir || mkdir $certdir
|
||||||
|
|
||||||
|
# Use yang in example
|
||||||
|
|
||||||
|
cat <<EOF > $cfg
|
||||||
|
<clixon-config xmlns="http://clicon.org/config">
|
||||||
|
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
|
||||||
|
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
||||||
|
<CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR>
|
||||||
|
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
|
||||||
|
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
|
||||||
|
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
|
||||||
|
<CLICON_BACKEND_REGEXP>example_backend.so$</CLICON_BACKEND_REGEXP>
|
||||||
|
<CLICON_RESTCONF_DIR>/usr/local/lib/$APPNAME/restconf</CLICON_RESTCONF_DIR>
|
||||||
|
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
||||||
|
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
||||||
|
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
||||||
|
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
|
||||||
|
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||||
|
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||||
|
<CLICON_MODULE_LIBRARY_RFC7895>true</CLICON_MODULE_LIBRARY_RFC7895>
|
||||||
|
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
||||||
|
<CLICON_SSL_SERVER_CERT>$srvcert</CLICON_SSL_SERVER_CERT>
|
||||||
|
<CLICON_SSL_SERVER_KEY>$srvkey</CLICON_SSL_SERVER_KEY>
|
||||||
|
<CLICON_SSL_CA_CERT>$cacert</CLICON_SSL_CA_CERT>
|
||||||
|
</clixon-config>
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat <<EOF > $fyang
|
||||||
|
module example{
|
||||||
|
yang-version 1.1;
|
||||||
|
namespace "urn:example:example";
|
||||||
|
prefix ex;
|
||||||
|
import ietf-netconf-acm {
|
||||||
|
prefix nacm;
|
||||||
|
}
|
||||||
|
leaf x{
|
||||||
|
type int32;
|
||||||
|
description "something to edit";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Two groups: admin allow all, guest allow nothing
|
||||||
|
RULES=$(cat <<EOF
|
||||||
|
<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
|
||||||
|
<enable-nacm>false</enable-nacm>
|
||||||
|
<read-default>permit</read-default>
|
||||||
|
<write-default>deny</write-default>
|
||||||
|
<exec-default>deny</exec-default>
|
||||||
|
|
||||||
|
$NGROUPS
|
||||||
|
|
||||||
|
<rule-list>
|
||||||
|
<name>guest-acl</name>
|
||||||
|
<group>guest</group>
|
||||||
|
<rule>
|
||||||
|
<name>deny-ncm</name>
|
||||||
|
<module-name>*</module-name>
|
||||||
|
<access-operations>*</access-operations>
|
||||||
|
<action>deny</action>
|
||||||
|
<comment>
|
||||||
|
Do not allow guests any access to the NETCONF
|
||||||
|
</comment>
|
||||||
|
</rule>
|
||||||
|
</rule-list>
|
||||||
|
|
||||||
|
$NADMIN
|
||||||
|
|
||||||
|
</nacm>
|
||||||
|
<x xmlns="urn:example:example">0</x>
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
if $genkeys; then
|
||||||
|
# Create certs
|
||||||
|
# 1. CA
|
||||||
|
cat<<EOF > $dir/ca.cnf
|
||||||
|
[ ca ]
|
||||||
|
default_ca = CA_default
|
||||||
|
|
||||||
|
[ CA_default ]
|
||||||
|
serial = ca-serial
|
||||||
|
crl = ca-crl.pem
|
||||||
|
database = ca-database.txt
|
||||||
|
name_opt = CA_default
|
||||||
|
cert_opt = CA_default
|
||||||
|
default_crl_days = 9999
|
||||||
|
default_md = md5
|
||||||
|
|
||||||
|
[ req ]
|
||||||
|
default_bits = 2048
|
||||||
|
days = 1
|
||||||
|
distinguished_name = req_distinguished_name
|
||||||
|
attributes = req_attributes
|
||||||
|
prompt = no
|
||||||
|
output_password = password
|
||||||
|
|
||||||
|
[ req_distinguished_name ]
|
||||||
|
C = SE
|
||||||
|
L = Stockholm
|
||||||
|
O = Clixon
|
||||||
|
OU = clixon
|
||||||
|
CN = ca
|
||||||
|
emailAddress = olof@hagsand.se
|
||||||
|
|
||||||
|
[ req_attributes ]
|
||||||
|
challengePassword = test
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Generate CA cert
|
||||||
|
openssl req -x509 -days 1 -config $dir/ca.cnf -keyout $cakey -out $cacert
|
||||||
|
|
||||||
|
cat<<EOF > $dir/srv.cnf
|
||||||
|
[req]
|
||||||
|
prompt = no
|
||||||
|
distinguished_name = dn
|
||||||
|
req_extensions = ext
|
||||||
|
[dn]
|
||||||
|
CN = www.clicon.org # localhost
|
||||||
|
emailAddress = olof@hagsand.se
|
||||||
|
O = Clixon
|
||||||
|
L = Stockholm
|
||||||
|
C = SE
|
||||||
|
[ext]
|
||||||
|
subjectAltName = DNS:clicon.org
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Generate server key
|
||||||
|
openssl genrsa -out $srvkey 2048
|
||||||
|
|
||||||
|
# Generate CSR (signing request)
|
||||||
|
openssl req -new -config $dir/srv.cnf -key $srvkey -out $certdir/srv_csr.pem
|
||||||
|
|
||||||
|
# Sign server cert by CA
|
||||||
|
openssl x509 -req -extfile $dir/srv.cnf -days 1 -passin "pass:password" -in $certdir/srv_csr.pem -CA $cacert -CAkey $cakey -CAcreateserial -out $srvcert
|
||||||
|
|
||||||
|
# create client certs
|
||||||
|
for name in $users; do
|
||||||
|
cat<<EOF > $dir/$name.cnf
|
||||||
|
[req]
|
||||||
|
prompt = no
|
||||||
|
distinguished_name = dn
|
||||||
|
[dn]
|
||||||
|
CN = $name
|
||||||
|
emailAddress = $name@foo.bar
|
||||||
|
O = Clixon
|
||||||
|
L = Stockholm
|
||||||
|
C = SE
|
||||||
|
EOF
|
||||||
|
# Create client key
|
||||||
|
openssl genrsa -out "$certdir/$name.key" 2048
|
||||||
|
|
||||||
|
# Generate CSR (signing request)
|
||||||
|
openssl req -new -config $dir/$name.cnf -key $certdir/$name.key -out $certdir/$name.csr
|
||||||
|
|
||||||
|
# Sign by CA
|
||||||
|
openssl x509 -req -extfile $dir/$name.cnf -days 1 -passin "pass:password" -in $certdir/$name.csr -CA $cacert -CAkey $cakey -CAcreateserial -out $certdir/$name.crt
|
||||||
|
done
|
||||||
|
|
||||||
|
fi # genkeys
|
||||||
|
|
||||||
|
if [ $BE -ne 0 ]; then
|
||||||
|
new "kill old backend"
|
||||||
|
sudo clixon_backend -zf $cfg
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
err
|
||||||
|
fi
|
||||||
|
sudo pkill -f clixon_backend # to be sure
|
||||||
|
|
||||||
|
new "start backend -s init -f $cfg"
|
||||||
|
start_backend -s init -f $cfg
|
||||||
|
fi
|
||||||
|
|
||||||
|
new "wait for backend"
|
||||||
|
wait_backend
|
||||||
|
|
||||||
|
if [ $RC -ne 0 ]; then
|
||||||
|
new "kill old restconf daemon"
|
||||||
|
stop_restconf_pre
|
||||||
|
|
||||||
|
new "start restconf daemon -c means client certs, -- -s means ssl client cert authentication in example"
|
||||||
|
start_restconf -f $cfg -c -- -s
|
||||||
|
|
||||||
|
fi
|
||||||
|
#new "wait for restconf"
|
||||||
|
#wait_restconf XXX
|
||||||
|
|
||||||
|
new "auth set authentication config"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><edit-config><target><candidate/></target><config>$RULES</config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "commit it"
|
||||||
|
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "enable nacm"
|
||||||
|
expectpart "$(curl -sSik --key $certdir/andy.key --cert $certdir/andy.crt -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:enable-nacm": true}' $RCPROTO://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/1.1 204 No Content"
|
||||||
|
|
||||||
|
new "admin get x"
|
||||||
|
expectpart "$(curl -sSik --key $certdir/andy.key --cert $certdir/andy.crt -X GET $RCPROTO://localhost/restconf/data/example:x)" 0 "HTTP/1.1 200 OK" '{"example:x":0}'
|
||||||
|
|
||||||
|
new "guest get x"
|
||||||
|
expectpart "$(curl -sSik --key $certdir/guest.key --cert $certdir/guest.crt -X GET $RCPROTO://localhost/restconf/data/example:x)" 0 "HTTP/1.1 403 Forbidden" '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}}'
|
||||||
|
|
||||||
|
if [ $RC -ne 0 ]; then
|
||||||
|
new "Kill restconf daemon"
|
||||||
|
stop_restconf
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $BE -eq 0 ]; then
|
||||||
|
exit # BE
|
||||||
|
fi
|
||||||
|
|
||||||
|
new "Kill backend"
|
||||||
|
# Check if premature kill
|
||||||
|
pid=$(pgrep -u root -f clixon_backend)
|
||||||
|
if [ -z "$pid" ]; then
|
||||||
|
err "backend already dead"
|
||||||
|
fi
|
||||||
|
# kill backend
|
||||||
|
stop_backend -f $cfg
|
||||||
|
|
||||||
|
rm -rf $dir
|
||||||
|
|
@ -23,6 +23,11 @@
|
||||||
# Magic line must be first in script (see README.md)
|
# Magic line must be first in script (see README.md)
|
||||||
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||||
|
|
||||||
|
# Only works with fcgi and http
|
||||||
|
if [ "${WITH_RESTCONF}" != "fcgi" -o "$RCPROTO" = https ]; then
|
||||||
|
if [ "$s" = $0 ]; then exit 0; else return 0; fi # skip
|
||||||
|
fi
|
||||||
|
|
||||||
APPNAME=example
|
APPNAME=example
|
||||||
: ${clixon_util_stream:=clixon_util_stream}
|
: ${clixon_util_stream:=clixon_util_stream}
|
||||||
NCWAIT=10 # Wait (netconf valgrind may need more time)
|
NCWAIT=10 # Wait (netconf valgrind may need more time)
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,7 @@ if [ $BE -ne 0 ]; then
|
||||||
start_backend -s init -f $cfg
|
start_backend -s init -f $cfg
|
||||||
fi
|
fi
|
||||||
|
|
||||||
new "waiting"
|
new "wait backend"
|
||||||
wait_backend
|
wait_backend
|
||||||
|
|
||||||
if [ $RC -ne 0 ]; then
|
if [ $RC -ne 0 ]; then
|
||||||
|
|
@ -168,7 +168,7 @@ if [ $RC -ne 0 ]; then
|
||||||
new "start restconf daemon"
|
new "start restconf daemon"
|
||||||
start_restconf -f $cfg
|
start_restconf -f $cfg
|
||||||
|
|
||||||
new "waiting"
|
new "wait restconf"
|
||||||
wait_restconf
|
wait_restconf
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,10 @@ module clixon-config {
|
||||||
revision 2020-06-17 {
|
revision 2020-06-17 {
|
||||||
description
|
description
|
||||||
"Added: CLICON_CLI_LINES_DEFAULT
|
"Added: CLICON_CLI_LINES_DEFAULT
|
||||||
Added enum HIDE to CLICON_CLI_GENMODEL";
|
Added enum HIDE to CLICON_CLI_GENMODEL
|
||||||
|
Added CLICON_SSL_SERVER_CERT
|
||||||
|
Added CLICON_SSL_SERVER_KEY
|
||||||
|
Added CLICON_SSL_CA_CERT";
|
||||||
}
|
}
|
||||||
revision 2020-04-23 {
|
revision 2020-04-23 {
|
||||||
description
|
description
|
||||||
|
|
@ -377,6 +380,27 @@ module clixon-config {
|
||||||
Setting this value to false makes restconf return not pretty-printed
|
Setting this value to false makes restconf return not pretty-printed
|
||||||
which may be desirable for performance or tests";
|
which may be desirable for performance or tests";
|
||||||
}
|
}
|
||||||
|
leaf CLICON_SSL_SERVER_CERT {
|
||||||
|
type string;
|
||||||
|
default "/etc/ssl/certs/clixon-server-crt.pem";
|
||||||
|
description
|
||||||
|
"SSL server cert for restconf https. This is not required if you use
|
||||||
|
--with-restconf=fcgi, ie a reverse-proxy based such as nginx over fcgi";
|
||||||
|
}
|
||||||
|
leaf CLICON_SSL_SERVER_KEY {
|
||||||
|
type string;
|
||||||
|
default "/etc/ssl/private/clixon-server-key.pem";
|
||||||
|
description
|
||||||
|
"SSL server private key for restconf https. This is not required if you use
|
||||||
|
--with-restconf=fcgi, ie a reverse-proxy based such as nginx over fcgi";
|
||||||
|
}
|
||||||
|
leaf CLICON_SSL_CA_CERT {
|
||||||
|
type string;
|
||||||
|
default "/etc/ssl/certs/clixon-ca_crt.pem";
|
||||||
|
description
|
||||||
|
"SSL CA cert for client authentication. This is not required if you use
|
||||||
|
--with-restconf=fcgi, ie a reverse-proxy based such as nginx over fcgi";
|
||||||
|
}
|
||||||
leaf CLICON_CLI_DIR {
|
leaf CLICON_CLI_DIR {
|
||||||
type string;
|
type string;
|
||||||
description
|
description
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue