Add ca_reset plugin also when backend starts as -s none

This commit is contained in:
Olof hagsand 2021-01-31 19:40:59 +01:00
parent 200e258ded
commit f65efa3e5e
4 changed files with 375 additions and 33 deletions

View file

@ -55,6 +55,7 @@ Users may have to change how they access the system
### Minor changes
* Add ca_reset plugin also when backend starts as `-s none`
* Corrected client session handling to make internal IPC socket persistent
* Applies to cli/netconf/restconf/client-api code
* Previous behaviour:

View file

@ -529,6 +529,24 @@ restconf_pseudo_process_commit(clicon_handle h,
return retval;
}
static int
restconf_pseudo_reset(clicon_handle h,
const char *db)
{
int retval = -1;
cxobj *xt = NULL;
/* Get data as xml from db1 */
if (xmldb_get(h, (char*)db, NULL, "/restconf[enable='true']", &xt) < 0)
goto done;
if (xt && xml_child_nr(xt))
if (clixon_process_operation(h, RESTCONF_PROCESS, "start", 0, NULL) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Register start/stop restconf RPC and create pseudo-plugin to monitor enable flag
* @param[in] h Clixon handle
*/
@ -541,6 +559,7 @@ restconf_pseudo_process_reg(clicon_handle h,
if (clixon_pseudo_plugin(h, "restconf pseudo plugin", &cp) < 0)
goto done;
cp->cp_api.ca_reset = restconf_pseudo_reset;
cp->cp_api.ca_trans_commit = restconf_pseudo_process_commit;
cp->cp_api.ca_trans_validate = restconf_pseudo_process_validate;
@ -1078,13 +1097,19 @@ main(int argc,
}
/* Merge extra XML from file and reset function to running
*/
if (status == STARTUP_OK && startup_mode != SM_NONE){
if (status == STARTUP_OK){
if (startup_mode == SM_NONE){
if (clixon_plugin_reset_all(h, "running") < 0)
goto done;
}
else {
if ((ret = startup_extraxml(h, extraxml_file, cbret)) < 0)
goto done;
if (ret2status(ret, &status) < 0)
goto done;
/* if status = STARTUP_INVALID, cbret contains info */
}
}
if (status != STARTUP_OK){
if (cbuf_len(cbret))

View file

@ -66,18 +66,19 @@ EOF
err "$expect1" "$ret"
fi
# >&2 echo "ret:$ret" # debug
expect2="</pid></rpc-reply>]]>]]>"
match=$(echo "$ret" | grep --null -Go "$expect2")
if [ -z "$match" ]; then
err "$expect2" "$ret"
fi
new "check rpc $operation get pid"
pid=$(echo "$ret" | awk -F'[<>]' '{print $5}')
>&2 echo "pid:$pid" # debug
if [ -z "$pid" ]; then
err "Running process" "$ret"
fi
new "check restconf retvalue"
if [ $expectret -eq 0 ]; then
if [ $pid -ne 0 ]; then
@ -89,7 +90,7 @@ EOF
fi
fi
>&2 echo "pid:$pid" # debug
echo $pid # cant use return that only uses 0-255
}
@ -123,8 +124,8 @@ fi
# Get pid of running process and check return xml
new "Get rpc status"
pid0=$(testrpc status 1)
if [ $? -ne 0 ]; then exit -1; fi
pid0=$(testrpc status 1) # Save pid0
if [ $? -ne 0 ]; then echo "$pid0";exit -1; fi
new "check restconf process running using ps pid0:$pid0"
ps=$(ps -hp $pid0)
@ -134,16 +135,16 @@ if [ -z "$ps" ]; then
fi
new "stop restconf RPC"
pid1=$(testrpc stop 0)
if [ $? -ne 0 ]; then exit -1; fi
pid=$(testrpc stop 0)
if [ $? -ne 0 ]; then echo "$pid";exit -1; fi
new "Get rpc status stopped"
pid2=$(testrpc status 0)
if [ $? -ne 0 ]; then exit -1; fi
pid=$(testrpc status 0)
if [ $? -ne 0 ]; then echo "$pid";exit -1; fi
new "Start rpc again"
pid3=$(testrpc start 1)
if [ $? -ne 0 ]; then exit -1; fi
pid3=$(testrpc start 1) # Save pid3
if [ $? -ne 0 ]; then echo "$pid3";exit -1; fi
new "check restconf process running using ps"
ps=$(ps -hp $pid3)
@ -159,20 +160,20 @@ new "kill restconf"
stop_restconf_pre
new "start restconf RPC"
pid4=$(testrpc start 1)
if [ $? -ne 0 ]; then exit -1; fi
pid=$(testrpc start 1)
if [ $? -ne 0 ]; then echo "$pid";exit -1; fi
new "check status RPC on"
pid5=$(testrpc status 1)
if [ $? -ne 0 ]; then exit -1; fi
pid5=$(testrpc status 1) # Save pid5
if [ $? -ne 0 ]; then echo "$pid5";exit -1; fi
new "restart restconf RPC"
pid6=$(testrpc restart 1)
if [ $? -ne 0 ]; then exit -1; fi
pid=$(testrpc restart 1)
if [ $? -ne 0 ]; then echo "$pid";exit -1; fi
new "Get restconf status rpc"
pid7=$(testrpc status 1)
if [ $? -ne 0 ]; then exit -1; fi
pid7=$(testrpc status 1) # Save pid7
if [ $? -ne 0 ]; then echo "$pid7";exit -1; fi
if [ $pid5 -eq $pid7 ]; then
err "A different pid" "$pid7"
@ -199,9 +200,42 @@ if [ $BE -ne 0 ]; then
fi
# kill backend
stop_backend -f $cfg
fi
fi
# So far, no restconf config enable flag has been true. Now change enable flag.
# Restconf is enabled and restconf was running but was killed by stop ^.
# Start backend with -s none should start restconf too via ca_reset rule
new "Restart backend -s none"
if [ $BE -ne 0 ]; then
new "kill old backend"
sudo clixon_backend -z -f $cfg
if [ $? -ne 0 ]; then
err
fi
new "start backend -s none -f $cfg"
start_backend -s none -f $cfg
new "waiting"
wait_backend
fi
new "Get restconf (running) after restart"
pid=$(testrpc status 1)
if [ $? -ne 0 ]; then echo "$pid"; exit -1; fi
if [ $BE -ne 0 ]; then
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
#--------------------------
# So far, restconf config enable flag has been true. Now change enable flag.
new "ENABLE false"
# Second basic operation with restconf enable is false
@ -232,15 +266,15 @@ fi
new "check status RPC off"
pid=$(testrpc status 0)
if [ $? -ne 0 ]; then exit -1; fi
if [ $? -ne 0 ]; then echo "$pid";exit -1; fi
new "start restconf RPC"
pid=$(testrpc start 0)
if [ $? -ne 0 ]; then exit -1; fi
if [ $? -ne 0 ]; then echo "$pid";exit -1; fi
new "check status RPC off"
pid=$(testrpc status 0)
if [ $? -ne 0 ]; then exit -1; fi
if [ $? -ne 0 ]; then echo "$pid";exit -1; fi
new "Enable restconf"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><edit-config><default-operation>merge</default-operation><target><candidate/></target><config><restconf xmlns=\"http://clicon.org/restconf\"><enable>true</enable></restconf></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
@ -250,7 +284,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "
new "check status RPC on"
pid=$(testrpc status 1)
if [ $? -ne 0 ]; then exit -1; fi
if [ $? -ne 0 ]; then echo "$pid";exit -1; fi
new "Disable restconf"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><edit-config><default-operation>merge</default-operation><target><candidate/></target><config><restconf xmlns=\"http://clicon.org/restconf\"><enable>false</enable></restconf></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
@ -260,7 +294,7 @@ expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "
new "check status RPC off"
pid=$(testrpc status 0)
if [ $? -ne 0 ]; then exit -1; fi
if [ $? -ne 0 ]; then echo "$pid";exit -1; fi
# Negative validation checks of clixon-restconf / socket
@ -270,6 +304,20 @@ expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><edit-config><target><ca
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>]]>]]>$"
# stop backend
if [ $BE -ne 0 ]; then
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
#Start backend -s none should start
unset pid
sleep $DEMWAIT # Lots of processes need to die before next test

View file

@ -0,0 +1,268 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2019 Olof Hagsand
Copyright (C) 2020-2021 Olof Hagsand and Rubicon Communications, LLC (Netgate)
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
* Create stream socket, connect to remote address, then exec sshd -e that takes over the
* tcp connection.
device client
+-----------------+ tcp 4321 +-----------------+
| util_netconf_ssh| <----------------> | xxx |
| | | +-----------------+
| exec v | 4322 | tcp
| | ssh +-----------------+
| sshd -e | <----------------> | ssh |
+-----------------+ +-----------------+
| stdio | stdio
+-----------------+
| clixon_netconf |
+-----------------+
|
+-----------------+
| clixon_backend |
+-----------------+
Example sshd-config (-c option):n
Port 2592
UsePrivilegeSeparation no
TCPKeepAlive yes
AuthorizedKeysFile ~.ssh/authorized_keys
Subsystem netconf /usr/local/bin/clixon_netconf
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define NETCONF_CH_SSH 4334
#define SSHDBIN_DEFAULT "/usr/sbin/sshd"
#define UTIL_OPTS "hD:f:a:p:s:c:"
static int
callhome_connect(struct sockaddr *sa,
size_t sa_len,
int *sp)
{
int retval = -1;
int s;
if ((s = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) {
perror("socket");
goto done;
}
if (connect(s, sa, sa_len) < 0){
perror("connect");
close(s);
goto done;
}
*sp = s;
retval = 0;
done:
return retval;
}
static int
exec_sshd(int s,
char *sshdbin,
char *configfile)
{
int retval = -1;
char **argv = NULL;
int i;
int nr;
if (s < 0){
errno = EINVAL;
perror("socket s");
goto done;
}
if (sshdbin == NULL){
errno = EINVAL;
perror("sshdbin");
goto done;
}
if (configfile == NULL){
errno = EINVAL;
perror("configfile");
goto done;
}
nr = 7; // XXX
if ((argv = calloc(nr, sizeof(char *))) == NULL){
perror("calloc");
goto done;
}
i = 0;
argv[i++] = sshdbin;
argv[i++] = "-i"; /* Specifies that sshd is being run from inetd(8) */
argv[i++] = "-d";
argv[i++] = "-e";
argv[i++] = "-f";
argv[i++] = configfile;
argv[i++] = NULL;
assert(i==nr);
if (setreuid(0, 0) < 0){
perror("setreuid");
goto done;
}
if (dup2(s, STDIN_FILENO) < 0){
perror("dup2");
return -1;
}
if (dup2(s, STDOUT_FILENO) < 0){
perror("dup2");
return -1;
}
if (execv(argv[0], argv) < 0) {
perror("execv");
exit(1);
}
/* Should reach here */
retval = 0;
done:
return retval;
}
static int
usage(char *argv0)
{
fprintf(stderr, "usage:%s [options]\n"
"where options are\n"
"\t-h \tHelp\n"
"\t-D <level> \tDebug\n"
"\t-f ipv4|ipv6 \tSocket address family(ipv4 default)\n"
"\t-a <addrstr> \tIP address (eg 1.2.3.4) - mandatory\n"
"\t-p <port> \tPort (default 4334)\n"
"\t-c <file> \tSSHD config file - mandatory\n"
"\t-s <sshd> \tPath to sshd binary, default %s\n"
,
argv0, SSHDBIN_DEFAULT);
exit(0);
}
int
main(int argc,
char **argv)
{
int retval = -1;
int c;
char *family = "ipv4";
char *addr = NULL;
struct sockaddr *sa;
struct sockaddr_in6 sin6 = { 0 };
struct sockaddr_in sin = { 0 };
size_t sin_len;
int debug = 0;
uint16_t port = NETCONF_CH_SSH;
int s = -1;
char *sshdbin = SSHDBIN_DEFAULT;
char *configfile = NULL;
optind = 1;
opterr = 0;
while ((c = getopt(argc, argv, UTIL_OPTS)) != -1)
switch (c) {
case 'h':
usage(argv[0]);
break;
case 'D':
debug++;
break;
case 'f':
family = optarg;
break;
case 'a':
addr = optarg;
break;
case 'p':
port = atoi(optarg);
break;
case 'c':
configfile = optarg;
break;
case 's':
sshdbin = optarg;
break;
default:
usage(argv[0]);
break;
}
if (port == 0){
fprintf(stderr, "-p <port> is invalid\n");
usage(argv[0]);
goto done;
}
if (addr == NULL){
fprintf(stderr, "-a <addr> is NULL\n");
usage(argv[0]);
goto done;
}
if (configfile == NULL){
fprintf(stderr, "-c <file> is NULL\n");
usage(argv[0]);
goto done;
}
if (strcmp(family, "ipv6") == 0){
sin_len = sizeof(struct sockaddr_in6);
sin6.sin6_port = htons(port);
sin6.sin6_family = AF_INET6;
inet_pton(AF_INET6, addr, &sin6.sin6_addr);
sa = (struct sockaddr *)&sin6;
}
else if (strcmp(family, "ipv4") == 0){
sin_len = sizeof(struct sockaddr_in);
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
sin.sin_addr.s_addr = inet_addr(addr);
sa = (struct sockaddr *)&sin;
}
else{
fprintf(stderr, "-f <%s> is invalid family\n", family);
goto done;
}
if (callhome_connect(sa, sin_len, &s) < 0)
goto done;
if (exec_sshd(s, sshdbin, configfile) < 0)
goto done;
if (s >= 0)
close(s);
retval = 0;
done:
return retval;
}