Support for "daemon: dbus"?


#1

Recently, I’ve been working on adding support for bus activated D-Bus services, and noticed that the existing systemd code supported dbus as a daemon type.

This generates a systemd .service file using Type=dbus, where the service is considered to have finished starting when it registers a particular well known name on the message bus. I imagine this is the mode most snapped D-Bus services would want to use.

However, Snapcraft complains that dbus is not a valid value for the daemon attribute on an app. I was able to build a snap using passthrough with something like this:

apps:
  system:
    command: ...
    passthrough:
      daemon: dbus
      bus-name: io.snapcraft.SnapDbusService

This produced a working snap with the right mode in the systemd unit, but it looks like review-tools also trips up on dbus type daemons:

$ review-tools.snap-review test-snapd-dbus-service_1.0_amd64.snap 
Errors
------
...
 - lint-snap-v2:daemon:system
    invalid daemon: 'dbus'
Warnings
--------
 - lint-snap-v2:apps_unknown:system
    unknown fields for app 'system': 'bus-name'

Is this just an oversight, or is there a particular reason to discourage use of this daemon type?


#2

This used to be supported by the review-tools but almost two years ago it was removed because it was not properly supported in snapd. This is bug https://bugs.launchpad.net/snappy/+bug/1649399. If ‘daemon: dbus’ is now properly supported in snapd, happy to add it back to the review-tools.


#3

It certainly seems to be working in current snapd master, but I guess it is worth discussing whether it’s a syntax we want to keep and/or encourage the use of. As I’ve been working on D-Bus service activation, here are a few points to keep in mind:

  1. A daemon must be able to acquire the bus name systemd is using to check that the daemon is active. If we’re only considering strict confinement, then this means the daemon must have a dbus slot matching the bus name. With classic snaps, it is quite possible for a daemon to register a name without a matching slot (although with installation of service activation files tied to the interface, they probably want one anyway).

  2. If a daemon is an activatable dbus service, then the dbus daemon type is the most appropriate method to signal that it is active. If the daemon doesn’t claim the name then it is obviously broken, and once it does claim the name it needs to be able to serve requests from clients.

  3. A daemon can potentially claim multiple bus names. It should be considered active only once it owns both names. In general, this means bus-name should be the last name it tries to claim.

    As an example close to home, snap userd tries to register both io.snapcraft.Launcher and io.snapcraft.Settings. By inspecting the source code I can see that it registers io.snapcraft.Settings last, so that is the name systemd should be watching for.

For simple cases it might be possible to infer the details, but (3) requires knowledge of how the daemon initialises itself. So maybe the existing syntax is good enough and just needs to be supplemented with review-tools checks. The following might be a good start:

  1. If a snap is using strict confinement and has an app with daemon: dbus, then bus-name must match one of the app’s dbus interface slots.
  2. If an app is a system daemon, then it should not have any dbus interface slots that specify bus: session.
  3. Once my user daemons PR is merged, any app that is a user daemon must not have any dbus interface slots that specify bus: system.
  4. If an app has a dbus interface slot with activatable: true, and sets daemon: simple then issue a warning suggesting that they use daemon: dbus.

#4

IIRC, this was one of the sticking points. It was somewhat awkward that in the snap.yaml you had to declare two different things (daemon: dbus and the dbus slot, with attributes). There was some talk of trying to unify this, but the way that the backends are all rather independent of one another made this problematic. Conceptually, I have no problem and would encourage snapd to grow the (as described or equivalent) functionally of daemon: dbus. At this point, we probably just need to live with the fact that we need two different things declared, but snapcraft and the review-tools can check for this; your suggestions for the review-tools seem reasonable to me.

I suggest asking @mvo his thoughts (since iirc, he was part of the older discussion) as well as signoff from @pedronis and/or @niemeyer on the path forward.


#5

@jamesh @jdstrand there are two issues here:

  • “bus-name” was ported over (but never agreed upon) from pre slots/plugs snappy, it would be more natural for the “daemon: dbus” app entry to actually designate a “dbus-slot” now
  • we haven’t really deeply discussed what roles plugs/slots should play for classic snaps, while they relate deeply to confinement for things like dbus and because now we have interface hooks they cannot probably be just ignored

#6

Referencing a type: dbus slot rather than the bus name directly seems like a decent improvement: it would remove the repetition of the bus name, and tie in closer to the interfaces system.

Re. dbus plugs/slots and classic snaps, I think you’re right that they’re probably already needed. For example, if you wanted a strict confined client app to talk to a classic confined D-Bus service, you need a slot on the classic snap for the strict snap to connect to, otherwise the policy for the strict snap will block communication. So that’s another point towards depending on slot definitions.

As a simple extension, we could infer the value of dbus-slot if an app has only a single dbus type slot. This way it would only be needed in cases where there are multiple slots (a situation where we can’t determine the correct value without code inspection).


#7

To be clear, you aren’t talking about the dbus interface are you? It is quite simplistic and doesn’t cover “all things dbus” (esp wrt dbus bus policy). If we wanted to change this interface, yes, we would need to deeply think about and design what the improvements should be to the dbus interface so that on the whole it is coherent (even if immediately we don’t implement everything). If you are talking about something else, that’s potentially fine.

This is a complicated topic in part because classic snaps run under a security policy (which is wide open), so we need to be careful not to accidentally break interactions between snaps. The most complicated bit is the mount namespace for things like the content interface since with classic, they intentionally run in the global mount namespace.


#8

I’m talking about this (I hoped it was clear):

apps:
  system:
    command: ...
    passthrough:
      daemon: dbus
      bus-name: io.snapcraft.SnapDbusService

#9

This is missing configuring the dbus bus policy for the snap and therefore this would be incomplete for arbitrary snaps. For systems where the default dbus bus policy is default closed, then the lack of snapd providing a policy means the default bus policy would block its use. On systems where the default dbus policy is default open, then the lack of snapd providing a policy means the default policy would allow its use to all users, when it is likely desired for system services that that the bus policy be locked down to, for example, root.

The dbus interface does provide a simplistic bus policy (root is allowed but all others users are not) that is not currently configurable. I had always hoped that specifying the dbus interface slot would somehow allow opting into systemd type=dbus and BusName=... but we don’t yet since, aiui, there wasn’t a convenient way for an interface slot to influence the systemd backend. IIRC, we aren’t allowing bus-name now precisely because it introduces an awkwardness in the snap.yaml of having to specify bus-name in the command and name in the dbus slot.

That said, if we implement what I always hoped for, that is likely only reasonable for Core and not classic, since I suspect we’ll need more flexibility with defining the dbus bus policy on classic systems.


#10

I fear we are bit talking at cross purposes here, what I said is that we shouldn’t specify a “bus-name” but a slot (with dbus interface) with the daemon. So

apps:
  system:
    command: ...
    daemon: dbus
    dbus-slot: my-service-dbus-slot