Unix socket support for sshd

Daniel Kahn Gillmor dkg at fifthhorseman.net
Fri Feb 5 04:31:12 AEDT 2016


On Thu 2016-02-04 11:00:27 -0500, David Goulet wrote:
> On 04 Feb (10:46:55), Daniel Kahn Gillmor wrote:
>> On Thu 2016-02-04 07:40:39 -0500, David Goulet wrote:
>> 
>> > I would like to know if adding support for Unix socket to sshd would be a
>> > feature that would be consider to be added upstream? (ListenAddress).
>> 
>> fwiw, i think this is a good idea, but i wouldn't implement it as an
>> explicit ListenAddress option: i'd rather have sshd be able to listen on
>> an inherited file descriptor.  This would allow generic socket
>> activation, regardless of socket type.
>
> Hrm... not sure I fully understand here. How would sshd inherited an fd? And
> what do you mean by "allow generic socket activation"? If I understand it,
> wouldn't that require a wrapper over sshd?
>
> Let's assume I set up an sshd and want it to use Unix socket in
> /foo/bar/ssh.sock, how would that work without me being able to specify
> somewhere the path?

The most common toolchain for doing this sort of thing today on
GNU/Linux systems is systemd, which acts as pid 1.

systemd's pid 1 instance is configured to know which sockets will be
listened for by all the daemons on the host, and it pre-opens all of
those sockets.  Then, when a connection is made to a socket that it
knows should belong to a daemon, if that daemon isn't already running,
it spawns (fork/execs) it, handing over the relevant socket(s) to the
daemon as a file descriptor during startup.

There are several advantages to this:

 * if the daemon can be run in a non-privileged mode, it can start in
   the non-privileged mode from the beginning.  no need for each daemon
   to need to start privileged to grab the relevant socket and then drop
   privileges.  we've seen many bugs around daemons not properly
   dropping privileges

 * no race for grabbing the socket.  In the event that multiple daemons
   are want to listen on the same socket, there's a predictable arbiter
   (pid 1) who decides where the socket gets passed to, and other
   daemons that try to listen on that socket get rejected, because the
   socket is already busy.

 * before any daemons are running, the sockets are opened for incoming
   connections.  This means there will be much smaller (possibly
   non-existent) window of time during system startup or daemon restart
   where the socket itself is closed.  Connections made to these sockets
   before the associated daemon starts up will of course hang until the
   daemon gets around to responding, but the system won't reject the
   connections.

 * socket activation also enables the OS to selectively start services
   on an as-needed basis.  If daemon X depends on daemon Y operationally
   (because X needs to talk to Y over Y's listening socket), there is no
   need for explicit representation of this startup dependency --
   rather, the daemons are spawned as the first connections come in.

Note that the daemon management service (what systemd's pid 1 provides)
needs some way to communicate to the spawned daemon which file
descriptors it has inherited should be treated as listeners.  for
systemd, this is a contiguious block of file descriptors starting at fd
3 (SD_LISTEN_FDS_START), and the enviornment variable LISTEN_FDS is a
decimal number indicating how many file descriptors to use for listening
(see sd_listen_fds(3) from libsystemd for more detail).

A reasonable approach that would avoid linking in libsystemd might be to
just specify something like ListenFDs N:M (meaning file descriptors N
through M are sockets you should listen on).  (perhaps -L N:M on the
command line, so that the config file could stay static and the spawning
daemon manager could dynamically adjust the invocation as sockets were
added or removed)

      --dkg


More information about the openssh-unix-dev mailing list