Added options for Restconf evhtp setting default bind socket address and ports CLICON_RESTCONF_IPV4_ADDR, CLICON_RESTCONF_IPV6_ADDR, CLICON_RESTCONF_HTTP_PORT, CLICON_RESTCONF_HTTPS_PORT

This commit is contained in:
Olof hagsand 2020-08-21 13:10:42 +02:00
parent 25f67d1eb9
commit 0e4df0e8fc
7 changed files with 279 additions and 35 deletions

View file

@ -31,7 +31,7 @@ Expected: September 2020
Users may have to change how they access the system
* New clixon-config@2020-08-17.yang revision
* Added `CLICON_RESTCONF_ADDRESS` for setting evhtp bind address
* Added options for Restconf evhtp setting default bind socket address and ports `CLICON_RESTCONF_IPV4_ADDR`, `CLICON_RESTCONF_IPV6_ADDR`, `CLICON_RESTCONF_HTTP_PORT`, `CLICON_RESTCONF_HTTPS_PORT`
### Corrected Bugs

View file

@ -81,9 +81,14 @@
/* Command line options to be passed to getopt(3) */
#define RESTCONF_OPTS "hD:f:l:p:d:y:a:u:o:P:sc"
/* See see listen(5) */
#define SOCKET_LISTEN_BACKLOG 16
/* Need global variable to for signal handler XXX */
static clicon_handle _CLICON_HANDLE = NULL;
/*! Signall terminates process
*/
static void
@ -594,7 +599,7 @@ main(int argc,
cvec *nsctx_global = NULL; /* Global namespace context */
size_t cligen_buflen;
size_t cligen_bufthreshold;
uint16_t defaultport = 80;
uint16_t defaultport;
uint16_t port = 0;
evhtp_t *htp = NULL;
struct event_base *evbase = NULL;
@ -602,7 +607,9 @@ main(int argc,
int dbg = 0;
int use_ssl = 0;
int ssl_verify_clients = 0;
char *restconf_address = NULL;
char *restconf_ipv4_addr = NULL;
char *restconf_ipv6_addr = NULL;
int i;
/* In the startup, logs to stderr & debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
@ -661,6 +668,12 @@ main(int argc,
if (clicon_options_main(h) < 0)
goto done;
// stream_path = clicon_option_str(h, "CLICON_STREAM_PATH");
/* Start with http default port, but change this later if -s is set to https default port */
if ((i = clicon_option_int(h, "CLICON_RESTCONF_HTTP_PORT")) < 0){
clicon_err(OE_CFG, EINVAL, "CLICON_RESTCONF_HTTP_PORT not found");
goto done;
}
defaultport = (uint16_t)i;
/* Now rest of options, some overwrite option file */
optind = 1;
@ -703,7 +716,12 @@ main(int argc,
}
case 's': /* ssl: use https */
use_ssl = 1;
defaultport = 443; /* unless explicit -P ? */
/* Set to port - note can be overrifden by -P */
if ((i = clicon_option_int(h, "CLICON_RESTCONF_HTTPS_PORT")) < 0){
clicon_err(OE_CFG, EINVAL, "CLICON_RESTCONF_HTTPS_PORT not found");
goto done;
}
defaultport = (uint16_t)i;
break;
case 'c': /* ssl: verify clients */
ssl_verify_clients = 1;
@ -722,6 +740,10 @@ main(int argc,
/* port = defaultport unless explicitly set -P */
if (port == 0)
port = defaultport;
if (port == 0){
clicon_err(OE_DAEMON, EINVAL, "Restconf bind port is 0");
goto done;
}
/* Check server ssl certs */
if (use_ssl){
/* Init evhtp ssl config struct */
@ -757,6 +779,7 @@ main(int argc,
clicon_err(OE_UNIX, errno, "evhtp_new");
goto done;
}
/* Here the daemon either uses SSL or not, ie you cant seem to mix http and https :-( */
if (use_ssl){
if (evhtp_ssl_init(htp, ssl_config) < 0){
clicon_err(OE_UNIX, errno, "evhtp_new");
@ -787,19 +810,49 @@ main(int argc,
evhtp_set_gencb(htp, cx_gencb, h);
/* bind to a socket, optionally with specific protocol support formatting
* If port is proteced must be done as root?
*/
if ((restconf_address = clicon_option_str(h, "CLICON_RESTCONF_ADDRESS")) == NULL){
clicon_err(OE_CFG, EINVAL, "Missing clixon option: CLICON_RESTCONF_ADDRESS");
restconf_ipv4_addr = clicon_option_str(h, "CLICON_RESTCONF_IPV4_ADDR");
restconf_ipv6_addr = clicon_option_str(h, "CLICON_RESTCONF_IPV6_ADDR");
if ((restconf_ipv4_addr == NULL || strlen(restconf_ipv4_addr)==0) &&
(restconf_ipv6_addr == NULL || strlen(restconf_ipv6_addr)==0)){
clicon_err(OE_DAEMON, EINVAL, "There are no restconf IPv4 or IPv6 bind addresses");
goto done;
}
if (evhtp_bind_socket(htp, /* evhtp handle */
restconf_address, /* string address, eg ipv4:<ipv4addr> */
port, /* port */
16 /* backlog flag, see listen(5) */
) < 0){
clicon_err(OE_UNIX, errno, "evhtp_bind_socket");
goto done;
if (restconf_ipv4_addr != NULL && strlen(restconf_ipv4_addr)){
cbuf *cb;
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
cprintf(cb, "ipv4:%s", restconf_ipv4_addr);
if (evhtp_bind_socket(htp, /* evhtp handle */
cbuf_get(cb), /* string address, eg ipv4:<ipv4addr> */
port, /* port */
SOCKET_LISTEN_BACKLOG /* backlog flag, see listen(5) */
) < 0){
clicon_err(OE_UNIX, errno, "evhtp_bind_socket");
goto done;
}
if (cb)
cbuf_free(cb);
}
if (restconf_ipv6_addr != NULL && strlen(restconf_ipv6_addr)){
cbuf *cb;
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
cprintf(cb, "ipv6:%s", restconf_ipv6_addr);
if (evhtp_bind_socket(htp, /* evhtp handle */
cbuf_get(cb), /* string address, eg ipv6:<ipv6addr> */
port, /* port */
SOCKET_LISTEN_BACKLOG /* backlog flag, see listen(5) */
) < 0){
clicon_err(OE_UNIX, errno, "evhtp_bind_socket");
goto done;
}
if (cb)
cbuf_free(cb);
}
/* Drop privileges to WWWUSER if started as root */
if (restconf_drop_privileges(h, WWWUSER) < 0)

View file

@ -17,4 +17,7 @@
<CLICON_CLI_LINESCROLLING>0</CLICON_CLI_LINESCROLLING>
<CLICON_STARTUP_MODE>init</CLICON_STARTUP_MODE>
<CLICON_NACM_MODE>disabled</CLICON_NACM_MODE>
<CLICON_MODULE_LIBRARY_RFC7895>false</CLICON_MODULE_LIBRARY_RFC7895>
<CLICON_RESTCONF_IPV4_ADDR>127.0.0.1</CLICON_RESTCONF_IPV4_ADDR>
<CLICON_RESTCONF_IPV6_ADDR>::1</CLICON_RESTCONF_IPV6_ADDR>
</clixon-config>

167
test/test_nacm_datanode_paths.sh Executable file
View file

@ -0,0 +1,167 @@
#!/usr/bin/env bash
# NACM data node rules
# Other NACM datanode tests assume a fixed NACM PATH ruleset
# These tests add and changes datanode rules in paths
# There is problems with namespace for paths
# See test_nacm_datanode_read.sh for original RULES for limit which are here added dynamically
#
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
APPNAME=example
# Common NACM scripts
. ./nacm.sh
cfg=$dir/conf_yang.xml
fyang=$dir/nacm-example.yang
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_DIR>$dir</CLICON_YANG_DIR>
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
<CLICON_FEATURE>ietf-netconf:startup</CLICON_FEATURE>
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
<CLICON_RESTCONF_DIR>/usr/local/lib/$APPNAME/restconf</CLICON_RESTCONF_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_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
<CLICON_NACM_CREDENTIALS>none</CLICON_NACM_CREDENTIALS>
</clixon-config>
EOF
cat <<EOF > $fyang
module nacm-example{
yang-version 1.1;
namespace "urn:example:nacm";
prefix ex;
import ietf-netconf-acm {
prefix nacm;
}
container table{
container parameters{
list parameter{
key name;
leaf name{
type string;
}
leaf value{
type string;
}
}
}
}
}
EOF
# Set initial NACM rules in startup enabling admin and a single param config
cat <<EOF > $dir/startup_db
<config>
<table xmlns="urn:example:nacm">
<parameters>
<parameter>
<name>a</name>
<value>72</value>
</parameter>
</parameters>
</table>
<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
<enable-nacm>true</enable-nacm>
<read-default>deny</read-default>
<write-default>deny</write-default>
<exec-default>permit</exec-default>
$NGROUPS
$NADMIN
</nacm>
</config>
EOF
new "test params: -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 -s startup -f $cfg"
start_backend -s startup -f $cfg
fi
new "waiting"
wait_backend
if [ $RC -ne 0 ]; then
new "kill old restconf daemon"
sudo pkill -u $wwwuser -f clixon_restconf
new "start restconf daemon (-a is enable basic authentication)"
start_restconf -f $cfg -- -a
new "waiting"
wait_restconf
fi
new "admin read OK"
expectpart "$(curl -u andy:bar -siS -X GET http://localhost/restconf/data/nacm-example:table/parameters/parameter=a)" 0 'HTTP/1.1 200 OK' '{"nacm-example:parameter":\[{"name":"a","value":"72"}\]}'
new "Fail limit read"
expectpart "$(curl -u wilma:bar -siS -X GET http://localhost/restconf/data/nacm-example:table/parameters/parameter=a)" 0 'HTTP/1.1 404 Not Found' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}'
# Add NACM read rule
new "Add NACM read path rule XML"
expectpart "$(curl -u andy:bar -siS -X POST http://localhost/restconf/data/ietf-netconf-acm:nacm -H 'Content-Type: application/yang-data+xml' -d '<rule-list xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm"><name>limited-acl</name><group>limited</group><rule><name>table</name><module-name>*</module-name><access-operations>read</access-operations><path xmlns:ex="urn:example:nacm">/ex:table</path><action>permit</action></rule></rule-list>')" 0 "HTTP/1.1 201 Created"
new "Read NACM rule"
expectpart "$(curl -u andy:bar -siS -X GET http://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl)" 0 "HTTP/1.1 200 OK" '{"ietf-netconf-acm:rule-list":\[{"name":"limited-acl","group":"limited","rule":\[{"name":"table","module-name":"\*","path":"/ex:table","access-operations":"read","action":"permit"}\]}\]}'
new "limit read OK"
expectpart "$(curl -u wilma:bar -siS -X GET http://localhost/restconf/data/nacm-example:table/parameters/parameter=a)" 0 'HTTP/1.1 200 OK' '{"nacm-example:parameter":\[{"name":"a","value":"72"}\]}'
new "Delete NACM read rule"
expectpart "$(curl -u andy:bar -siS -X DELETE http://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl)" 0 "HTTP/1.1 204 No Content"
new "Fail limit read"
expectpart "$(curl -u wilma:bar -siS -X GET http://localhost/restconf/data/nacm-example:table/parameters/parameter=a)" 0 'HTTP/1.1 404 Not Found' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}'
new "Add NACM read path rule JSON"
expectpart "$(curl -u andy:bar -siS -X POST http://localhost/restconf/data/ietf-netconf-acm:nacm -H 'Content-Type: application/yang-data+json' -d '{"ietf-netconf-acm:rule-list":[{"name":"limited-acl","group":"limited","rule":[{"name":"table","module-name":"*","path":"/ex:table","access-operations":"read","action":"permit"}]}]}')" 0 "HTTP/1.1 201 Created"
new "Read NACM rule"
expectpart "$(curl -u andy:bar -siS -X GET http://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl)" 0 "HTTP/1.1 200 OK" '{"ietf-netconf-acm:rule-list":\[{"name":"limited-acl","group":"limited","rule":\[{"name":"table","module-name":"\*","path":"/ex:table","access-operations":"read","action":"permit"}\]}\]}'
if false; then
new "Fail limit read"
# XXX: No namespace found for prefix: ex
# See [Cannot create or modify NACM data node access rule with path using JSON encoding #129](https://github.com/clicon/clixon/issues/129)
expectpart "$(curl -u wilma:bar -siS -X GET http://localhost/restconf/data/nacm-example:table/parameters/parameter=a)" 0 'HTTP/1.1 404 Not Found' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}'
fi
if [ $RC -ne 0 ]; then
new "Kill restconf daemon"
stop_restconf
fi
if [ $BE -ne 0 ]; then # Bring your own backend
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
fi
rm -rf $dir

View file

@ -22,12 +22,13 @@ APPNAME=example
fyang=$dir/example.yang
cfg=$dir/conf.xml
certdir=$dir/certs
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)
@ -53,7 +54,8 @@ cat <<EOF > $cfg
<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_RESTCONF_ADDRESS>127.0.0.1</CLICON_RESTCONF_ADDRESS>
<CLICON_RESTCONF_IPV4_ADDR>127.0.0.1</CLICON_RESTCONF_IPV4_ADDR>
<CLICON_RESTCONF_IPV6_ADDR>::1</CLICON_RESTCONF_IPV6_ADDR>
<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>

View file

@ -10,7 +10,7 @@ set -eux #
if [ $# -ne 1 -a $# -ne 2 ]; then
echo "usage: $0 <box> [destroy]\n <box> as defined in https://vagrantcloud.com/search"
exit -1
exit 255
fi
box=$1 # As defined in https://vagrantcloud.com/search

View file

@ -44,7 +44,8 @@ module clixon-config {
*/
revision 2020-08-17 {
description
"Added: CLICON_RESTCONF_ADDRESS";
"Added: CLICON_RESTCONF_IPV4_ADDR, CLICON_RESTCONF_IPV6_ADDR,
CLICON_RESTCONF_HTTP_PORT, CLICON_RESTCONF_HTTPS_PORT";
}
revision 2020-06-17 {
description
@ -388,39 +389,57 @@ module clixon-config {
Setting this value to false makes restconf return not pretty-printed
which may be desirable for performance or tests";
}
leaf CLICON_RESTCONF_ADDRESS {
leaf CLICON_RESTCONF_IPV4_ADDR {
type string;
default "ipv4:0.0.0.0";
default "0.0.0.0";
description
"RESTCONF outward address.
Applies to native http (eg evhtp), not proxy solutions (eg fcgi).
This is essentially from libevhtp: Bind to a socket, optionally with specific protocol
support formatting. The addr can be defined as one of the following:
ipv6:<ipv6addr> for binding to an IPv6 address.
unix:<named pipe> for binding to a unix named socket
ipv4:<ipv4addr> for binding to an ipv4 address
If not given, the addr is assumed to be ipv4.";
"RESTCONF IPv4 socket binding address.
Applies to native http by config option --with-restconf=evhtp.";
}
leaf CLICON_RESTCONF_IPV6_ADDR {
type string;
default "::";
description
"RESTCONF IPv6 socket binding address.
Applies to native http by config option --with-restconf=evhtp.";
}
leaf CLICON_RESTCONF_HTTP_PORT {
type uint16;
default 80;
description
"RESTCONF socket binding port, non-ssl
In the restconf daemon, it can be overriden by -P <port>
Applies to native http only by config option --with-restconf=evhtp.";
}
leaf CLICON_RESTCONF_HTTPS_PORT {
type uint16;
default 443;
description
"RESTCONF socket binding port, ssl
In the restconf daemon, this is the port chosen if -s is given.
Note it can be overriden by -P <port>
Applies to native http by config option --with-restconf=evhtp.";
}
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";
"SSL server cert for restconf https.
Applies to native http only by config option --with-restconf=evhtp.";
}
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";
"SSL server private key for restconf https.
Applies to native http only by config option --with-restconf=evhtp.";
}
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";
"SSL CA cert for client authentication.
Applies to native http only by config option --with-restconf=evhtp.";
}
leaf CLICON_CLI_DIR {
type string;