Added validation of clixon-restconf.yang: server-key-path and server-cert-path must be present if ssl enabled.

This commit is contained in:
Olof hagsand 2021-01-21 12:11:26 +01:00
parent f1449a2542
commit 2d402b7ba5
6 changed files with 222 additions and 5 deletions

View file

@ -53,6 +53,8 @@ Users may have to change how they access the system
### Minor changes ### Minor changes
* Added validation of clixon-restconf.yang: server-key-path and server-cert-path must be present if ssl enabled.
* Only if `CLICON_BACKEND_RESTCONF_PROCESS` is true
* Experimental IPC API, `clixon_client`, to support a loose integration model * Experimental IPC API, `clixon_client`, to support a loose integration model
* Many systems using other tools employ such a model, and this API is an effort to make a usage of clixon easier * Many systems using other tools employ such a model, and this API is an effort to make a usage of clixon easier
* see https://clixon-docs.readthedocs.io/en/latest/overview.html#loose-integration * see https://clixon-docs.readthedocs.io/en/latest/overview.html#loose-integration
@ -338,7 +340,7 @@ Users may have to change how they access the system
Developers may need to change their code Developers may need to change their code
* Added yang-binding `yb` parameter to `xmldb_get0()` and all xmldb get functions. * Added yang-binding `yb` parameter to `xmldb_get0()` and all xmldb get functions.
* Simplified the _module-specific upgrade API. * Simplified the _module-specific_ upgrade API.
* The new API is documented here: [Module-specific upgrade](https://clixon-docs.readthedocs.io/en/latest/upgrade.html#module-specific-upgrade) * The new API is documented here: [Module-specific upgrade](https://clixon-docs.readthedocs.io/en/latest/upgrade.html#module-specific-upgrade)
* The change is not backward compatible. The API has been simplified which means more has to be done by the programmer. * The change is not backward compatible. The API has been simplified which means more has to be done by the programmer.
* In summary, a user registers an upgrade callback per module. The callback is called at startup if the module is added, has been removed or if the revision on file is different from the one in the system. * In summary, a user registers an upgrade callback per module. The callback is called at startup if the module is added, has been removed or if the revision on file is different from the one in the system.

View file

@ -474,6 +474,35 @@ restconf_pseudo_process_control(clicon_handle h)
return retval; return retval;
} }
/*! Restconf pseduo-plugin process validate
*/
static int
restconf_pseudo_process_validate(clicon_handle h,
transaction_data td)
{
int retval = -1;
cxobj *xtarget;
clicon_debug(1, "%s", __FUNCTION__);
xtarget = transaction_target(td);
/* If ssl-enable is true and (at least a) socket has ssl,
* then server-cert-path and server-key-path must exist */
if (xpath_first(xtarget, NULL, "restconf/enable[.='true']") &&
xpath_first(xtarget, NULL, "restconf/socket[ssl='true']")){
/* Should filepath be checked? One could claim this is a runtime system,... */
if (xpath_first(xtarget, 0, "restconf/server-cert-path") == NULL){
clicon_err(OE_CFG, 0, "SSL enabled but server-cert-path not set");
return -1; /* induce fail */
}
if (xpath_first(xtarget, 0, "restconf/server-key-path") == NULL){
clicon_err(OE_CFG, 0, "SSL enabled but server-key-path not set");
return -1; /* induce fail */
}
}
retval = 0;
return retval;
}
/*! Restconf pseduo-plugin process commit /*! Restconf pseduo-plugin process commit
*/ */
static int static int
@ -513,6 +542,7 @@ restconf_pseudo_process_reg(clicon_handle h,
if (clixon_pseudo_plugin(h, "restconf pseudo plugin", &cp) < 0) if (clixon_pseudo_plugin(h, "restconf pseudo plugin", &cp) < 0)
goto done; goto done;
cp->cp_api.ca_trans_commit = restconf_pseudo_process_commit; cp->cp_api.ca_trans_commit = restconf_pseudo_process_commit;
cp->cp_api.ca_trans_validate = restconf_pseudo_process_validate;
/* Register generic process-control of restconf daemon, ie start/stop restconf */ /* Register generic process-control of restconf daemon, ie start/stop restconf */
if (restconf_pseudo_process_control(h) < 0) if (restconf_pseudo_process_control(h) < 0)

View file

@ -530,8 +530,16 @@ cx_get_ssl_server_certs(clicon_handle h,
int retval = -1; int retval = -1;
struct stat f_stat; struct stat f_stat;
if (ssl_config == NULL || server_cert_path == NULL){ if (ssl_config == NULL){
clicon_err(OE_CFG, EINVAL, "Input parameter is NULL"); clicon_err(OE_CFG, EINVAL, "ssl_config is NULL");
goto done;
}
if (server_cert_path == NULL){
clicon_err(OE_CFG, EINVAL, "server_cert_path is not set but is required when ssl is enabled");
goto done;
}
if (server_key_path == NULL){
clicon_err(OE_CFG, EINVAL, "server_key_path is not set but is required when ssl is enabled");
goto done; goto done;
} }
if ((ssl_config->pemfile = strdup(server_cert_path)) == NULL){ if ((ssl_config->pemfile = strdup(server_cert_path)) == NULL){
@ -695,7 +703,6 @@ restconf_socket_init(clicon_handle h,
// return evhtp_bind_sockaddr(htp, sa, sin_len, SOCKET_LISTEN_BACKLOG); // return evhtp_bind_sockaddr(htp, sa, sin_len, SOCKET_LISTEN_BACKLOG);
} }
/*! 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

166
test/test_client.sh Executable file
View file

@ -0,0 +1,166 @@
#!/usr/bin/env bash
# Advanced Client api test
# Compile and run a client
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
# Which format to use as datastore format internally
: ${format:=xml}
APPNAME=example
cfg=$dir/conf_yang.xml
fyang=$dir/example-client.yang
cfile=$dir/example-client.c
pdir=$dir/plugin
app=$pdir/example-api
if [ ! -d $pdir ]; then
mkdir $pdir
fi
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_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_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
<CLICON_BACKEND_DIR>$pdir</CLICON_BACKEND_DIR>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
<CLICON_XMLDB_FORMAT>$format</CLICON_XMLDB_FORMAT>
$RESTCONFIG
</clixon-config>
EOF
cat <<EOF > $fyang
module clixon-client {
yang-version 1.1;
namespace "urn:example:clixon-client";
prefix exc;
description
"Clixon client example yang";
revision 2021-01-14 {
description "Added table/paramater/value as the primary data example";
}
/* Generic config data */
container table{
list parameter{
key name;
leaf name{
type string;
}
leaf value{
type uint32;
}
}
}
}
EOF
cat<<EOF > $cfile
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h> /* sockaddr_in */
#include <arpa/inet.h> /* inet_addr */
#include <clixon/clixon_client.h>
#define CLIXONCONF "$cfg"
int
main(int argc,
char **argv)
{
int s;
void *h = NULL; /* clixon handle */
if ((h = clixon_client_init("server", stderr, 0, CLIXONCONF)) == NULL)
return -1;
if ((s = clixon_client_connect(h)) < 0){
return -1;
}
/* Here are read functions depending on an example YANG
* (Need an example YANG and XML input to confd)
*/
{
uint32_t u = 0;
if (clixon_client_get_uint32(s, &u, "urn:example:clixon-client", "/table/parameter[name='a']/value") < 0)
return -1;
}
clixon_client_close(s);
clixon_client_terminate(h);
return 0;
}
EOF
new "compile $cfile -> $app"
echo "$CC -g -Wall -I/usr/local/include $cfile -o $app -lclixon"
expectpart "$($CC -g -Wall -I/usr/local/include $cfile -o $app -lclixon)" 0 ""
exit
new "test params: -s init -f $cfg"
if [ $BE -ne 0 ]; then
new "kill old backend"
sudo clixon_backend -zf $cfg
if [ $? -ne 0 ]; then
err
fi
new "start backend"
start_backend -s init -f $cfg
fi
new "waiting"
wait_backend
if [ $RC -ne 0 ]; then
new "kill old restconf daemon"
stop_restconf_pre
new "start restconf daemon"
start_restconf -f $cfg
new "waiting"
wait_restconf
fi
XML='<c xmlns="urn:example:api"><y3><k>2</k></y3><y3><k>3</k></y3><y3><k>5</k><val>zorro</val></y3><y3><k>7</k></y3></c>'
# Add a set of entries using restconf
new "PUT a set of entries"
expectpart "$(curl $CURLOPTS -X PUT -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example-api:c -d "$XML")" 0 "HTTP/1.1 201 Created"
new "Check entries"
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/example-api:c -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' "$XML"
new "Send a trigger"
expectpart "$(curl $CURLOPTS -X POST $RCPROTO://localhost/restconf/operations/example-api:trigger -H 'Accept: application/yang-data+json')" 0 'HTTP/1.1 204 No Content'
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
# unset conditional parameters
unset format
rm -rf $dir

View file

@ -106,7 +106,6 @@ fi
# Restconf test routine with arguments: # Restconf test routine with arguments:
# 1. proto:http/https # 1. proto:http/https
# 2: addr: 127.0.0.1/::1 # IPv4 or IPv6 # 2: addr: 127.0.0.1/::1 # IPv4 or IPv6
# 3: config: local / backend config (evhtp only)
testrun() testrun()
{ {
proto=$1 # http/https proto=$1 # http/https

View file

@ -262,6 +262,14 @@ new "check status RPC off"
pid=$(testrpc status 0) pid=$(testrpc status 0)
if [ $? -ne 0 ]; then exit -1; fi if [ $? -ne 0 ]; then exit -1; fi
# Negative validation checks of clixon-restconf / socket
new "netconf edit config invalid ssl"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><restconf xmlns=\"http://clicon.org/restconf\" nc:operation=\"replace\" xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><enable>true</enable><socket><namespace>default</namespace><address>0.0.0.0</address><port>80</port><ssl>true</ssl></socket></restconf></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
new "netconf validate should fail"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>SSL enabled but server-cert-path not set</error-message></rpc-error></rpc-reply>]]>]]>$"
if false; then # Work in progress - namespace if false; then # Work in progress - namespace
#------------------------------- #-------------------------------
# Now in a separate network namespace # Now in a separate network namespace
@ -314,5 +322,10 @@ sudo ip netns delete $netns
fi # namespaces fi # namespaces
unset pid
sleep $DEMWAIT # Lots of processes need to die before next test
endtest
rm -rf $dir rm -rf $dir