Socket activation support

If the configuration format is agreed upon we (LXD) could work on implementing at least the minimal set of features that we need (what was there before + socket permissions), the rest could be added later.

According to https://www.freedesktop.org/software/systemd/man/sd_listen_fds.html, it seemssd_listen_fds() returns file descriptors in an arbitrary order. I’m not sure how you can ensure ordering with multiple sockets.

Looking at the systemd.socket man page, it seems that sockets defined in the same unit file will be provided in the order defined, but if a service depends on multiple socket units there is no guarantee which order those units will be provided in.

Maybe it isn’t as important as I thought.

you can go back from the fd to the name (with getsockname(2)) to know which is which , that’s what snapd itself does with its sockets for example

1 Like

I pushed a PR at https://github.com/snapcore/snapd/pull/3916 implementing the basic options (ListenStream and SocketMode) for socket activation

just for the laughs btw …

https://github.com/ogra1/packageproxy/blob/master/run-approx

using socat in a wrapper script also gets you nicely working socket activation :wink: (this is particulary using port activation though, but socket is possible as well)

Should we define each socket in its own .socket file or have a single file associated to the .service one?

The former allows finer control on each socket; for instance the LXD snap could disable the socket on 8443 if only the local socket is enabled.

If we go with a single .socket file, then there is no real use for the socket name (lxd-unix and lxd-tcp in the example), so we could make the sockets stanza just a list, but all sockets would be started/stopped together by systemd.

@ack, How would this feature work together with snap set to allow administrators to change the addresses the service listen on?

@ack Per comments in the review, your approach of multiple sockets sounds nice.

A few more details to figure, as described in the review.

@chipaca Good question… I think it’s on us to answer that.

I don’t know much about snap set, but the current implementation won’t interact.

Basically it lets you define a set of sockets that would cause the service to be started. What I mentioned about having separate socket files would possible let the service disable the .socket if it doesn’t actually listen on a specific port.
IOW you’d have to define all possible sockets that the service could listen on.

@ack Yeah, don’t worry too much about that. There are a few ideas to explore in terms of how to allow people and even the snap itself to tune the default value of this setting at runtime, but we can figure this out in a follow up and so it’s not a blocker for this to land.

The only real blocker we have now is figuring out how to define the snap filename in a safe way, per the review.

As a first pass can we just restrict the socket paths to be under the snap’s writable paths? That would effectively be either:

  • /var/snap/NAME/current/
  • /var/snap/NAME/common/

Both of those should always be entirely safe and avoid any potential conflict with the host. Having this initial restriction would unblock LXD (which binds /var/snap/lxd/common/lxd/unix.socket) and would be compatible with allowing other paths (/run or such) down the line either through interfaces or another mechanism.

@jdstrand you may have some opinions on this :slight_smile:

@stgraber That would be the best approach actually, as it would effectively allow systemd to create anything that the snap might create anyway. The question is just how to verify it easily from that location. The vague option we discussed was to have some sort of support on snap-exec to answer that question, and then do the typical snap run → snap-confine → snap-exec dance to get an answer. It feels a bit fiddly, but I can’t see good alternatives yet. If we go that route, systemd socket files have an ExecStartPre option that runs before the socket is created. Assuming it aborts the socket creation if that command fails, we might leverage it.

@stgraber Ah, sorry, I misunderstood your proposal. You mean allowing only $SNAP_DATA and $SNAP_COMMON. Yeah, that sounds fine as a first step. The implementation I mentioned above is likely what we should aim for, as it enables other use cases, but we can do that as a follow up and it would remain compatible with what you suggest too.

Right, that’s what I had in mind. Lets limit it to something we know is safe for now, then extend it later for more complex use cases.

@niemeyer should we disallow abstract unix sockets (as they’re not confined IIUC)?

Yes, sounds like a good idea for now. We also need to get a review from @jdstrand before the final PR goes in, so he has a chance to pick up any other issues around that sort of problem.

Actually, abstract sockets are mediated. Like so many other things, it is a matter of namespacing. Abstract sockets often look like this: @foo, but they can have the form: @/path/foo. I’m not saying the first iteration should support abstract sockets, but it could if the namespacing was right. Eg:

  • @snap.$SNAP_NAME.whatever (eg, @snap.foo.whatever)
  • @$SNAP_DATA/whatever (eg, @/var/snap/foo/x1/whatever)

Today, the default template does not allow creating abstract sockets, but this is only because we never decided on how to namespace them (it was discussed long ago, but never brought up again).

@jdstrand Not in this case, I suspect. It’s systemd that is opening up the socket. We have the same issue described above about writing of unix sockets.

Well, unless we do enforce it at the namespacing level rather than hoping for apparmor to catch it.