Does ubus call uci set on the local socket use per-session staging?

I'm building a Salt extension that manages OpenWrt devices via both JSON-RPC (HTTPS) and SSH. I'm trying to confirm my understanding of how rpcd's per-session UCI staging works depending on the call path.

My understanding of the two paths:

  JSON-RPC:  curl -> uhttpd -> rpcd -> ubusd -> UCI handler
                                ^
                       rpcd injects ubus_rpc_session,
                       rpc_uci_set_savedir() sets
                       /var/run/rpcd/uci-<session_hex>/

  Local:     ubus call -> ubusd -> UCI handler
                          ^
                   no rpcd in path,
                   sid is NULL,
                   savedir falls back to /tmp/.uci/

Question: Is it correct that ubus call over the local Unix socket never goes through rpcd's session layer, so sid is always NULL and staging always lands in /tmp/.uci/? Or is there a case where the local socket path does populate ubus_rpc_session in the blob message?

In the rpcd repo in uci.c I found rpc_uci_set_savedir() with a check if(!sid) -- am I right that via ssh transport a "local" ubus call uci ... does not has a sid?

I am trying to achieve "rollback", view config drift and ideally human-in-the-loop review;
hence my ambition to fully understand the internals.

The r in rpcd is Remote. If you call ubus on the device, it uses the local socket. uci opts for /tmp/.uci/ by default. Poke around with ubus monitor and see what goes on for a deeper dive.

1 Like

Ah, ubus monitor was also new to me. But is insightful for sure!

Here is how I summarized my understanding in my

┌──────────────────────────┬──────────┬───────────┬─────────────────────┐
│        Transport         │   Auth   │   ACLs    │ Per-session staging │
├──────────────────────────┼──────────┼───────────┼─────────────────────┤
│ JSON-RPC (uhttpd → rpcd) │ password │ yes       │ yes                 │
├──────────────────────────┼──────────┼───────────┼─────────────────────┤
│ SSH (ubus CLI → ubusd)   │ SSH key  │ no (root) │ no (/tmp/.uci/)     │
└──────────────────────────┴──────────┴───────────┴─────────────────────┘

So:

  • SSH gives key-based auth (no password to manage on the device), but gets unrestricted root access to all ubus objects and shared staging in /tmp/.uci/.
  • JSON-RPC gives fine-grained ACLs and isolated per-session staging, but needs a password set on the device — which means either:
    • Manual passwd
    • A provisioning step that sets it automatically
    • The $p$ mechanism (rpcd verifies against /etc/shadow)

There's no way to get rpcd ACLs with SSH key auth — rpcd's session.login only accepts username+password. The session token that gates everything flows from that password-based login.

Hope that is correct! (If not, I will edit, to not confuse future readers)

Well, a good summary - although no password is required to be set on the device. The underlying assumption is that you ssh as root@. So largely correct, there.

1 Like