Socket activation support

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.

To be clear, I was not talking about Linux namespaces/containers. We can carve out abstract socket ‘paths’ that follow snappy conventions and enforce that with apparmor, in a similar fashion like we do now with the filesystem. We just have to decide how we want to carve that up (glossing over implementation details of making it work with systemd).

@niemeyer @jdstrand thanks for the clarification. I’ll reject abstract sockets for now in the listen-stream validation.

I’m also wondering what should be allowed in terms of TCP addresses; can we just limit to a port (which would be the same as allowing [::]:<port>) or should it be possible to specify addreses to (so that an app can, for example, bind only localhost)?

I don’t have an opinion on the actual question, but have a related question. You mention “bind only localhost” (emphasis mine); aiui systemd is merely going to listen on these itself, start the application then hand over the fd to the service, but the service is free to open and listen on any number of other ports (or named sockets or abstract sockets) as is desired. As such, the ‘only’ here refers to the fact that the port will be open on the localhost, but not eth0. All of this is fine AFAIC. Fine-grained network mediation is on the roadmap, so at some point we will be able to enforce “you may only this port on this interface and no others”.

I ask because I want to make sure we don’t try to use ‘Socket Activated OS Containers’ (see http://0pointer.de/blog/projects/socket-activated-containers.html) since those won’t work right with snapd.

Reading https://www.freedesktop.org/software/systemd/man/systemd.socket.html I see no reason why we could not include abstract sockets at this time. Like with named sockets, we simply limit their naming so different snaps can’t clobber each other. Eg, like I said before (feel free to suggest other ideas):

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

snapd ensures the abstract sockets match our naming convention when generating the systemd unit and we add a couple of apparmor glob rules to the default template that allow unconfined to access the socket and for anything in the snap to access it. Anything beyond that is out of scope for this PR (but other snap access is obviously implemented via interfaces).

1 Like

If that’s the case, I think @$SNAP_COMMON/whatever should be also supported (to match what would be allowed for filesystem-based unix sockets)