* Changed config and install options for Restconf

* clixon_restconf daemon is installed in /usr/local/sbin (as clixon_backend), instead of /www-data
    * `configure --with-wwwdir=<dir>` remains but only applies to fcgi socket and log
    * New option `CLICON_RESTCONF_INSTALL_DIR` is set to where clixon_restconf is installed, with default `/usr/local/sbin/`
  * Restconf drop privileges user is defined by `CLICON_RESTCONF_USER`
    * `configure --with-wwwuser=<user>` is removed
  * clixon_restconf drop of privileges is defined by `CLICON_RESTCONF_PRIVILEGES` option
* New clixon-restconf@2020-05-20.yang revision
  * Added: restconf `log-destination`
This commit is contained in:
Olof hagsand 2021-05-21 15:12:06 +02:00
parent c3e26b004c
commit c20c672d83
32 changed files with 410 additions and 221 deletions

View file

@ -49,6 +49,7 @@ datarootdir = @datarootdir@
exec_prefix = @exec_prefix@
bindir = @bindir@
libdir = @libdir@
sbindir = @sbindir@
mandir = @mandir@
libexecdir = @libexecdir@
localstatedir = @localstatedir@
@ -56,9 +57,6 @@ sysconfdir = @sysconfdir@
includedir = @includedir@
HOST_VENDOR = @host_vendor@
# XXX why is not wwwdir under prefix?
wwwdir = @wwwdir@
wwwuser = @wwwuser@
# one of fcgi or native:
with_restconf = @with_restconf@
@ -79,6 +77,8 @@ LIBDEPS = $(top_srcdir)/lib/src/$(CLIXON_LIB)
LIBS = -L$(top_srcdir)/lib/src $(top_srcdir)/lib/src/$(CLIXON_LIB) @LIBS@
CPPFLAGS = @CPPFLAGS@
ifeq ($(LINKAGE),dynamic)
CPPFLAGS += -fPIC
endif
@ -149,12 +149,8 @@ distclean: clean
# Put config file in etc/
# Also a rule for letting www-dir be owned by www-data, which only works for sudo
install: install-lib $(APPL)
ifeq ($(shell whoami),root)
install -d -m 0755 -o $(wwwuser) -g $(wwwuser) $(DESTDIR)$(wwwdir)
else
install -d -m 0755 $(DESTDIR)$(wwwdir)
endif
install -m 0755 $(INSTALLFLAGS) $(APPL) $(DESTDIR)$(wwwdir)
install -d -m 0755 $(DESTDIR)$(sbindir)
install -m 0755 $(INSTALLFLAGS) $(APPL) $(DESTDIR)$(sbindir)
install-lib: $(MYLIB)
install -d -m 0755 $(DESTDIR)$(libdir)
@ -169,7 +165,7 @@ install-include: clixon_restconf.h
install -m 0644 $^ $(DESTDIR)$(includedir)/clixon
uninstall:
rm -f $(DESTDIR)$(wwwdir)/$(APPL)
rm -f $(DESTDIR)$(sbindir)/$(APPL)
rm -f $(DESTDIR)$(libdir)/$(MYLIBLINK)*
.SUFFIXES:

View file

@ -82,11 +82,11 @@ Start clixon backend daemon (if not already started)
Start clixon restconf daemon
```
sudo -u www-data -s /www-data/clixon_restconf -f /usr/local/etc/example.xml
sudo clixon_restconf -f /usr/local/etc/example.xml
```
On FreeBSD:
```
sudo -u www -s /www/clixon_restconf -f /usr/local/etc/example.xml
sudo clixon_restconf -f /usr/local/etc/example.xml
```
Make restconf calls with curl (or other http client). Example of writing a new interface specification:
@ -226,7 +226,7 @@ See (https://nchan.io/#eventsource) on more info on how to access an SSE sub end
Start the restconf fastcgi program with debug flag:
```
sudo su -c "/www-data/clixon_restconf -D 1 -f /usr/local/etc/example.xml" -s /bin/sh www-data
sudo clixon_restconf -D 1 -f /usr/local/etc/example.xml
```
Look at syslog:
```
@ -240,7 +240,7 @@ curl -G http://127.0.0.1/restconf/data/*
You can also run restconf in a debugger.
```
sudo gdb /www-data/clixon_restconf
sudo gdb clixon_restconf
(gdb) run -D 1 -f /usr/local/etc/example.xml
```
but you need to ensure /www-data/fastcgi_restconf.sock has the following access (may need to be done after restconf has started)

View file

@ -312,7 +312,7 @@ restconf_terminate(clicon_handle h)
xpath_optimize_exit();
restconf_handle_exit(h);
clixon_err_exit();
clicon_debug(1, "%s done", __FUNCTION__);
clicon_debug(1, "%s pid:%u done", __FUNCTION__, getpid());
clicon_log_exit(); /* Must be after last clicon_debug */
return 0;
}
@ -495,18 +495,18 @@ restconf_uripath(clicon_handle h)
/*! Drop privileges from root to user (or already at user)
* @param[in] h Clicon handle
* @param[in] user Drop to this level
* Group set to clicon to communicate with backend
*/
int
restconf_drop_privileges(clicon_handle h,
char *user)
restconf_drop_privileges(clicon_handle h)
{
int retval = -1;
uid_t newuid = -1;
uid_t uid;
char *group;
gid_t gid = -1;
char *user;
enum priv_mode_t priv_mode = PM_NONE;
clicon_debug(1, "%s", __FUNCTION__);
/* Sanity check: backend group exists */
@ -523,12 +523,19 @@ restconf_drop_privileges(clicon_handle h,
clicon_configfile(h));
goto done;
}
/* Get privileges mode (for dropping privileges) */
if ((priv_mode = clicon_restconf_privileges_mode(h)) == PM_NONE)
goto ok;
if ((user = clicon_option_str(h, "CLICON_RESTCONF_USER")) == NULL)
goto ok;
/* Get (wanted) new www user id */
if (name2uid(user, &newuid) < 0){
clicon_err(OE_DAEMON, errno, "'%s' is not a valid user .\n", user);
goto done;
}
/* get current backend userid, if already at this level OK */
/* get current userid, if already at this level OK */
if ((uid = getuid()) == newuid)
goto ok;
if (uid != 0){
@ -539,12 +546,22 @@ restconf_drop_privileges(clicon_handle h,
clicon_err(OE_DAEMON, errno, "setgid %d", gid);
goto done;
}
if (drop_priv_perm(newuid) < 0)
goto done;
/* Verify you cannot regain root privileges */
if (setuid(0) != -1){
clicon_err(OE_DAEMON, EPERM, "Could regain root privilieges");
goto done;
switch (priv_mode){
case PM_DROP_PERM:
if (drop_priv_perm(newuid) < 0)
goto done;
/* Verify you cannot regain root privileges */
if (setuid(0) != -1){
clicon_err(OE_DAEMON, EPERM, "Could regain root privilieges");
goto done;
}
break;
case PM_DROP_TEMP:
if (drop_priv_temp(newuid) < 0)
goto done;
break;
case PM_NONE:
break; /* catched above */
}
clicon_debug(1, "%s dropped privileges from root to %s(%d)",
__FUNCTION__, user, newuid);

View file

@ -81,7 +81,7 @@ int restconf_terminate(clicon_handle h);
int restconf_insert_attributes(cxobj *xdata, cvec *qvec);
int restconf_main_extension_cb(clicon_handle h, yang_stmt *yext, yang_stmt *ys);
char *restconf_uripath(clicon_handle h);
int restconf_drop_privileges(clicon_handle h, char *user);
int restconf_drop_privileges(clicon_handle h);
int restconf_authentication_cb(clicon_handle h, void *req, int pretty, restconf_media media_out);
int restconf_config_init(clicon_handle h, cxobj *xrestconf);
int restconf_socket_init(const char *netns0, const char *addrstr, const char *addrtype, uint16_t port, int backlog, int flags, int *ss);

View file

@ -88,7 +88,7 @@
#include "restconf_stream.h"
/* Command line options to be passed to getopt(3) */
#define RESTCONF_OPTS "hD:f:E:l:p:d:y:a:u:ro:"
#define RESTCONF_OPTS "hD:f:E:l:p:d:y:a:u:rW:o:"
/*! Convert FCGI parameters to clixon runtime data
* @param[in] h Clixon handle
@ -192,6 +192,7 @@ usage(clicon_handle h,
"\t-u <path|addr>\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-W <user>\tRun restconf daemon as this user, drop according to CLICON_RESTCONF_PRIVILEGES\n"
"\t-o \"<option>=<value>\" Give configuration option overriding config file (see clixon-config.yang)\n",
argv0
);
@ -225,7 +226,6 @@ main(int argc,
size_t cligen_buflen;
size_t cligen_bufthreshold;
int dbg = 0;
int drop_privileges = 1;
int ret;
cxobj *xrestconf1 = NULL; /* Local config file */
cxobj *xconfig2 = NULL;
@ -234,6 +234,7 @@ main(int argc,
cvec *nsc = NULL;
cxobj *xerr = NULL;
struct passwd *pw;
char *wwwuser;
/* In the startup, logs to stderr & debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
@ -272,6 +273,7 @@ main(int argc,
goto done;
break;
} /* switch getopt */
dbg=1;
/*
* Logs, error and debug to stderr or syslog, set debug level
*/
@ -324,7 +326,8 @@ main(int argc,
clicon_option_str_set(h, "CLICON_SOCK", optarg);
break;
case 'r':{ /* Do not drop privileges if run as root */
drop_privileges = 0;
if (clicon_option_add(h, "CLICON_RESTCONF_PRIVILEGES", "none") < 0)
goto done;
break;
}
case 'o':{ /* Configuration option */
@ -507,11 +510,12 @@ main(int argc,
goto done;
}
_MYSOCK = sock;
/* Change group of fcgi sock fronting reverse proxy to WWWUSER, the effective group is clicon
* which is backend. */
/* Change group of fcgi sock fronting reverse proxy to CLICON_RESTCONF_USER,
* the effective group is clicon which is backend. */
gid_t wgid = -1;
if (group_name2gid(WWWUSER, &wgid) < 0){
clicon_log(LOG_ERR, "'%s' does not seem to be a valid user group.", WWWUSER);
wwwuser = clicon_option_str(h, "CLICON_RESTCONF_USER");
if (group_name2gid(wwwuser, &wgid) < 0){
clicon_log(LOG_ERR, "'%s' does not seem to be a valid user group.", wwwuser);
goto done;
}
if (chown(sockpath, -1, wgid) < 0){
@ -525,11 +529,11 @@ main(int argc,
clicon_err(OE_UNIX, errno, "chmod");
goto done;
}
if (drop_privileges){
/* Drop privileges to WWWUSER if started as root */
if (restconf_drop_privileges(h, WWWUSER) < 0)
goto done;
}
/* Drop privileges if started as root to CLICON_RESTCONF_USER
* and use drop mode: CLICON_RESTCONF_PRIVILEGES
*/
if (restconf_drop_privileges(h) < 0)
goto done;
if (FCGX_InitRequest(req, sock, 0) != 0){
clicon_err(OE_CFG, errno, "FCGX_InitRequest");
goto done;

View file

@ -168,7 +168,7 @@
#include "restconf_native.h" /* Restconf-openssl mode specific headers*/
/* Command line options to be passed to getopt(3) */
#define RESTCONF_OPTS "hD:f:E:l:p:y:a:u:ro:"
#define RESTCONF_OPTS "hD:f:E:l:p:y:a:u:rW:o:"
/* If set, open outwards socket non-blocking, as opposed to blocking
* Should work both ways, but in the ninblocking case,
@ -1093,7 +1093,7 @@ restconf_connection(int s,
if (conn->bev != NULL){
struct evbuffer *ev;
size_t buflen0;
size_t buflen;
size_t buflen1;
char *buf = NULL;
if ((rc = conn->arg) == NULL){
@ -1102,31 +1102,34 @@ restconf_connection(int s,
}
if ((ev = bufferevent_get_output(conn->bev)) != NULL){
buflen0 = evbuffer_get_length(ev);
buflen = buflen0 - rc->rc_bufferevent_output_offset;
if (buflen == 0)
readmore = 1;
else if (buflen > 0){
buflen1 = buflen0 - rc->rc_bufferevent_output_offset;
if (buflen1 > 0){
buf = (char*)evbuffer_pullup(ev, -1);
/* If evhtp has print an output buffer, clixon whould not have done it
* Shouldnt happen
*/
clicon_debug(1, "%s Warning: evhtp printed output buffer, but clixon output buffer is non-empty %s",
__FUNCTION__, cbuf_get(rc->rc_outp_buf));
cbuf_reset(rc->rc_outp_buf);
if (cbuf_append_buf(rc->rc_outp_buf, buf, buflen) < 0){
if (cbuf_len(rc->rc_outp_buf)){
clicon_debug(1, "%s Warning: evhtp printed output buffer, but clixon output buffer is non-empty %s",
__FUNCTION__, cbuf_get(rc->rc_outp_buf));
cbuf_reset(rc->rc_outp_buf);
}
if (cbuf_append_buf(rc->rc_outp_buf, buf, buflen1) < 0){
clicon_err(OE_UNIX, errno, "cbuf_append_buf");
goto done;
}
/* XXX Cant get drain to work, need to keep an offset */
evbuffer_drain(ev, -1);
rc->rc_bufferevent_output_offset += buflen;
rc->rc_bufferevent_output_offset += buflen1;
}
}
if (cbuf_len(rc->rc_outp_buf) &&
buf_write(cbuf_get(rc->rc_outp_buf), cbuf_len(rc->rc_outp_buf), conn->sock, conn->ssl) < 0)
if (cbuf_len(rc->rc_outp_buf) == 0)
readmore = 1;
else {
if (buf_write(cbuf_get(rc->rc_outp_buf), cbuf_len(rc->rc_outp_buf), conn->sock, conn->ssl) < 0)
goto done;
cvec_reset(rc->rc_outp_hdrs); /* Can be done in native_send_reply */
cbuf_reset(rc->rc_outp_buf);
cvec_reset(rc->rc_outp_hdrs); /* Can be done in native_send_reply */
cbuf_reset(rc->rc_outp_buf);
}
}
else{
if (send_badrequest(h, s, conn->ssl, "application/yang-data+xml",
@ -1896,6 +1899,7 @@ usage(clicon_handle h,
"\t-a UNIX|IPv4|IPv6 Internal backend socket family\n"
"\t-u <path|addr>\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-W <user>\tRun restconf daemon as this user, drop according to CLICON_RESTCONF_PRIVILEGES\n"
"\t-o <option>=<value> Set configuration option overriding config file (see clixon-config.yang)\n"
,
argv0
@ -1913,7 +1917,6 @@ main(int argc,
clicon_handle h;
int dbg = 0;
int logdst = CLICON_LOG_SYSLOG;
int drop_privileges = 1;
restconf_native_handle *rh = NULL;
int ret;
cxobj *xrestconf = NULL;
@ -2012,10 +2015,14 @@ main(int argc,
usage(h, argv0);
clicon_option_str_set(h, "CLICON_SOCK", optarg);
break;
case 'r':{ /* Do not drop privileges if run as root */
drop_privileges = 0;
case 'r': /* Do not drop privileges if run as root */
if (clicon_option_add(h, "CLICON_RESTCONF_PRIVILEGES", "none") < 0)
goto done;
break;
case 'W': /* Run restconf daemon as this user (afetr drop) */
if (clicon_option_add(h, "CLICON_RESTCONF_USER", optarg) < 0)
goto done;
break;
}
case 'o':{ /* Configuration option */
char *val;
if ((val = index(optarg, '=')) == NULL)
@ -2068,12 +2075,12 @@ main(int argc,
/* Openssl inits */
if (restconf_openssl_init(h, dbg, xrestconf) < 0)
goto done;
/* Drop privileges after clixon and openssl init */
if (drop_privileges){
/* Drop privileges to WWWUSER if started as root */
if (restconf_drop_privileges(h, WWWUSER) < 0)
goto done;
}
/* Drop privileges if started as root to CLICON_RESTCONF_USER
* and use drop mode: CLICON_RESTCONF_PRIVILEGES
*/
if (restconf_drop_privileges(h) < 0)
goto done;
/* Main event loop */
if (clixon_event_loop(h) < 0)
goto done;