One of the objections to adding support for user session systemd daemons and D-Bus services was the lack of control over package upgrades or removal. While system level daemons can be stopped and started by snapd, this isn’t the case for user session daemons: as snapd is running as root, it can not talk to the user session instance of systemd. Even if snapd tried to connect to the user’s D-Bus session bus, the connection will be refused due to mismatched user ID.
At the last engineering sprint earlier this year, we brainstormed some ideas for how we could solve this problem. I don’t think any work has been done on it since, and a concrete plan wasn’t written down. So here is my recollections, as a first step towards an implementation.
A “session agent” for snapd
Rather than have the root owned snapd try to poke around inside the user’s session, we would instead have an agent running as the user that could act on snapd’s behalf. Preferably this agent wouldn’t need to run constantly through out the session for the following reasons:
- If we can recover from the agent stopping, then crashes are far less critical.
- If snapd is upgraded mid-session, the agent can also be upgraded.
Systemd socket activation seems the best solution to this problem. It provides a reliable end-point for snapd to communicate with, and starts the agent on demand. If the service exits when idle (or when told to via an API), an upgraded version of the service will respond to the next API call.
The socket unit could look something like this:
[Unit]
Description=Socket activation for snap session agent
[Socket]
ListenStream=%t/snap-session.socket
[Install]
WantedBy=sockets.target
This will expand to /run/user/$uid/snap-session.socket
, so snapd can easily enumerate the available session agents with a simple glob.
The agent could either be a new process, or implemented as new functionality of snap userd
. If we make userd’s D-Bus service activation files use the SystemdService
option, we should be able to have it activate by either D-Bus or the unix socket.
What protocol should the agent speak?
At the sprint, @pedronis suggested using an HTTP/REST API similar to the system level snapd socket. This seems like a reasonable option. We’ve already got code in place to do SO_PEERCRED
checks to verify that the agent is talking to the root account, for instance.
One thing to keep in mind is that the session agent is untrusted code, from the perspective of snapd. While snapd should be talking to code we’ve written, there is nothing stopping me from killing the agent and writing my program listening on that socket. With this in mind, HTTP is a reasonable choice since most attacks related to a misbehaving server also apply to using HTTP to speak to random servers on the Internet.
We’d need to make sure any API calls use reasonable timeouts and response size limits.
What API should be offered?
We should be able to verify that the agent is working correctly with a simple “status” or “verson” API. But as for real world uses, I imagine we’d probably want:
- start and stop named user mode systemd units.
- perform a
daemon-reload
on the user instance of systemd. - post a notification asking the user to close an application (e.g. the user has Skype running but minimised, and an update has come through).
I’d appreciate any feedback on this (e.g. from @zyga-snapd or @pedronis), so we can move on to implementation.