On-demand SSH Socks proxy through systemd user units with socket-activation doesn't restart as wished
- Can /usr/bin/ssh really not accept systemd-passed sockets?
I think that's not too surprising, considering:
- OpenSSH is an OpenBSD project
- systemd only supports the Linux kernel
- systemd support would need to be explicitly added to OpenSSH, as an optional/build-time dependency, so it would probably be a hard sell.
- Can only units of root use the
BindTodevice
option?
User systemd instances are generally pretty isolated, and e.g. can not communicate with the main pid-0 instance. Things like depending on system units from user unit files are not possible.
The documentation for BindToDevice
mentions:
Note that setting this parameter might result in additional dependencies to be added to the unit (see above).
Due to the above-mentioned restriction, we can imply that the option doesn't work from user systemd instances.
- Why is my proxy service not respawning correctly on first new connection after the old tunnel dies?
As I understand, the chain of events is as follows:
SocksProxyHelper.socket
is started.- A SOCKS client connects to localhost:8118.
- systemd starts
SocksProxyHelper.service
. - As a dependency of
SocksProxyHelper.service
, systemd also startsSocksProxy.service
. systemd-socket-proxyd
accepts the systemd socket, and forwards its data tossh
.ssh
dies or is killed.- systemd notices, and places
SocksProxy.service
into a inactive state, but does nothing. SocksProxyHelper.service
keeps running and accepting connections, but fails to connect tossh
, as it is no longer running.
The fix is to add BindsTo=SocksProxy.service
to SocksProxyHelper.service
. Quoting its documentation (emphasis added):
Configures requirement dependencies, very similar in style to
Requires=
. However, this dependency type is stronger: in addition to the effect ofRequires=
it declares that if the unit bound to is stopped, this unit will be stopped too. This means a unit bound to another unit that suddenly enters inactive state will be stopped too. Units can suddenly, unexpectedly enter inactive state for different reasons: the main process of a service unit might terminate on its own choice, the backing device of a device unit might be unplugged or the mount point of a mount unit might be unmounted without involvement of the system and service manager.When used in conjunction with
After=
on the same unit the behaviour ofBindsTo=
is even stronger. In this case, the unit bound to strictly has to be in active state for this unit to also be in active state. This not only means a unit bound to another unit that suddenly enters inactive state, but also one that is bound to another unit that gets skipped due to a failed condition check (such asConditionPathExists=
,ConditionPathIsSymbolicLink=
, … — see below) will be stopped, should it be running. Hence, in many cases it is best to combineBindsTo=
withAfter=
.
- Is this the right way to set-up an "on-demand ssh socks proxy"? If, not, how do you do it?
There's probably no "right way". This method has its advantages (everything being "on-demand") and disadvantages (dependency on systemd, the first connection not getting through because ssh hasn't begun listening yet). Perhaps implementing systemd socket activation support in autossh would be a better solution.