Rpcd service script call from nonpriv user with ubus

I'm trying to implement a local daemon that can be called via ubus through
rpcd. My goal is to have this daemon run all the tasks needed to be run as
root (e.g. activate new config or reboot or sysupgrade...) but all the rest
should be run as a nopriviliged user that doesn't even have shell access (like
curl calls..):

example user:
testuser:x:1000:1000:testuser:/var:/bin/false

This works fine as long as I call ubus with root privs.

/usr/libexec/rpcd/example

#!/bin/sh

# The RPCd interfaces with commands via two methods: list and call.
case "$1" in
        list)
                # List method must return the list of methods and parameters that the daemon will accept. Only methods listed here will available to call.
                echo '{ "show": { } }'
        ;;
        call)
                # The way rpcd calls the methods is by calling your script like this: <script-name> call <method-name> << <json-input>
                # So, in order to retrieve the called method, you need to read $2
                case "$2" in
                        show)
                                echo -n '{ "content": "hello world" }'
                        ;;
                esac
        ;;
esac
root@97bf8f21ef32:/etc/init.d# sudo -u root /bin/ubus -v list example
'example' @089e4ba9
        "show":{}

But for nonroot user testuser this doesn't work. Ubus is silent or fails.

root@97bf8f21ef32:/etc/init.d# sudo -u testuser /bin/ubus -v list example
root@97bf8f21ef32:/etc/init.d# sudo -u testuser /bin/ubus call banner show
Command failed: Not found

is there a way to locally allow user testuser to make specific calls via ubus.
I don't want an service like uhttpd involved so the remote ubus method is not
an option for me. I would also like to prevent writing a excessive sudoers file
to allow all kinds of calls for the user. Is there any other good mothod to
implement this kind of privsep in openwrt?

thanks 1000x,
Mischa

Hi, you need to stage an ubus ACL. You can find an example in the LuCI repo which is deployed to allow the nobody UNIX user to call the ubus system.board and system.info methods:

So in your case, you would need an /usr/share/acl.d/my-app.json (filename does not matter) with a content like this:

{
  "user": "testuser",
  "access": {
    "example": {
      "methods": [ "show" ]
    }
  }
}

Hi,

unfortunately this doesn't work. From the docs I read:

When you're accessing the /ubus url in uhttpd however, uhttpd first queries whether your call is allowed, and whoever is providing the ubus session.* namespace is in charge of implementing the access control:

so could it be that this won't work from the command line as it doesn't hit that codepath?
Thx,
Mischa

The information your quoted only applies to ubus HTTP access, not to cli calls.

Maybe you confused /usr/share/acl.d/ with /usr/share/rpcd/acl.d/. The former is used by ubus itself and consulted when doing CLI calls. The latter only applies to HTTP requests made via uhttpd-mod-ubus

@jow I had to restart ubusd so that it would actually pic up the acl.

Thanks so much for your help - very much appreciated! Do you think this would be worth mentioning in the wiki?

Yes, it would definitely be worth mentioning. And it somehow needs to be made clear that there's two different ACL layers involved, the ubus native ACL stuff in /usr/share/acl.d/ and the rpcd specific ACL layer (which is used by uhttpd-mod-ubus through session.access) in /usr/share/rpcd/acl.d/

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.