I’ve been working to upstream the snap support for xdg-desktop-portal
and while they are receptive to accepting the feature, they asked how it would fit in with the new D-Bus “app container” feature:
https://bugs.freedesktop.org/show_bug.cgi?id=100344
This is essentially an attempt to provide a confinement system independent way of labelling connections as originating from a container, and to impose some restrictions on those connections. It also provides a new API for reading the connection labels, that can then be used by D-Bus services that act as “trusted helpers” when talking to confined applications.
Even if we continue to rely on our AppArmor patches for D-Bus mediation, I think it is worth investigating whether we can plug into this system for the benefit of using trusted helpers like xdg-desktop-portal
with fewer snap-specific code paths. While some parts of the feature exist in dbus’s master branch, it is still in flux so now is probably the time to look at this.
Changes here will obviously have security implications (so @jdstrand may be interested), and will affect how the sandbox mount namespace is constructed (so @zyga-snapd may be interested). To handle the session/user bus, it probably also depends on user mounts. Below is my understanding of how this feature works in its current form.
Separate sockets for confined apps
Rather than having confined apps connect to the main dbus sockets, the idea is to create a new listening socket for each sandbox. Any connections accepted by this socket will be labelled as belonging to that “app container”.
For the system bus, this probably means mounting a tmpfs over /run/dbus
in the sandbox and creating a new socket at /run/dbus/system_bus_socket
. Alternatively, we could set $DBUS_SYSTEM_BUS_ADDRESS
and use a different location.
For the session bus, it is generally found at $XDG_RUNTIME_DIR/bus
, so it will depend on how we handle $XDG_RUNTIME_DIR
in the future. This might be something that is easier if we stop altering the value of $XDG_RUNTIME_DIR
inside the sandbox and bind mount over the normal contents.
Contained app metadata
When registering an extra listening socket, the confinement system provides some additional information:
-
container_type
: a string representing the confinement system (e.g.io.snapcraft.snapd
). -
app_identifier
: a string identifier representing how the confinement system identifies the app (e.g. the snap package name). -
metadata
: ana{sv}
dictionary of additional metadata.
This information will be associated with any connections made on the new listening socket, and made available to services acting as trusted helpers.
Lifetime of app container sockets
New app container sockets are created by the confinement system and then passed to dbus-daemon through the AddContainerServer
D-Bus method call. Of course, dbus-daemon then needs to know when it can close that socket and stop accepting new connections. In the current design, this is handled by passing an extra close_notification
file descriptor: this would generally be the read end of a pipe, with dbus-daemon closing the listening socket when it detects the write end of the pipe has been closed.
This particular design means there needs to be some process to hold on to the file descriptor and ensure it is closed at the right time. For system bus sockets, snapd
may be in the best position to handle this. For session bus sockets, this is going to tie into how we handle lifecycle of the user mount namespaces. Perhaps a daemon within the user session is the right choice here: maybe even snap userd
?
Access control
When registering a new container socket, the confinement system provides a set of access control rules dictating what connections made to this socket are allowed to do. These rules are checked in addition to the LSM checks, so we could install a broad access control list and continue relying on our existing AppArmor mediation.
Alternatively we could look at moving some of our D-Bus access control over to this system, which would benefit snaps running on host systems that haven’t adopted AppArmor or the dbus-daemon mediation support. If we are interested in using this system, then we should make sure if can handle everything we care about. From a read of the bug report, the main missing features I can see at this point are:
- no way to change the ACL of a container socket after creation. We would need this to support
snap connect
/snap disconnect
on an interface that changes D-Bus rules. - no way to specify rules that depend on the peer’s
app_identifier
. We currently generate AppArmor rules that can distinguish e.g. NetworkManager running on the host system vs. NetworkManager running as a snap, for instance. - can only allow method calls at the (bus_name, object_path, interface) granularity. In some of our existing snap interfaces we grant access to specific methods in an interface, but it would be worth deciding whether that is actually necessary.