systemd icon indicating copy to clipboard operation
systemd copied to clipboard

Get a shell in the environment/sandbox of an existing service

Open Lucus16 opened this issue 1 year ago • 11 comments

Component

systemd-run

Is your feature request related to a problem? Please describe

I'm often trying to debug a service where I don't understand what the environment of that service looks like. In that case I'd want to be able to spawn a shell with exactly the environment (variables, user, group, mounts, other sandbox features) specified by a service so I can try some commands in it and see the results directly. Especially when the service runs in a sandbox, I can't easily peek inside and I can't easily become the right dynamic user. Being able to spawn a shell in the same sandbox would help me a lot to find issues in the sandbox without needing to take the service offline.

I often have manual maintenance I want to do in the same environment as that of a service. I want to be sure I write files as the right user, don't accidentally write to paths I shouldn't, etc... That would all be trivial if I could start a shell in the same environment as the target service.

Describe the solution you'd like

Ideally, there's a dedicated systemd-shell foo.service command or a flag I can pass to systemd-run like systemd-run --shell --in-environment-of=foo.service. That would spawn a shell (either the one from the user corresponding to the service if it has a usable one, or a global one, or one explicitly passed as argument) which matches the runtime environment of the service as closely as possible. It would also use the working directory specified in the service. If the service was already running, it would use the same (possibly dynamic) user as the service and generally be in the same sandbox such that it can see everything the service sees. If the service is started after such a shell exists, the service would join and keep the sandbox of the shell.

Describe alternatives you've considered

I can run things as root and then manually patch up the owner and group files I create, but the odds of making mistakes are large. I can try to get closer to the environment of service unit by manually applying each of the many settings, e.g. starting with the user and group and all the environment variables, but it would still be quite different because systemd units can also set bind mounts and have various other sandbox features. It would also be a lot of work to reproduce manually. I don't know much about slices but it might be possible to give each service its own slice, define all sandboxing features in that slice and then use systemd-run --shell --slice=foo.slice but it would require me to rework all my services and to split their configuration between the slice and the service unit.

The systemd version you checked that didn't have the feature you are asking for

253.3

Lucus16 avatar Jun 26 '23 15:06 Lucus16

Have you tried nsenter?

bluca avatar Jun 27 '23 17:06 bluca

That does seem to help a lot. But it can't help me debug a systemd service that fails immediately and I'm not sure if it can replicate all parts of the environment that can be described in a service file.

Lucus16 avatar Jul 01 '23 22:07 Lucus16

for that use systemd-run

bluca avatar Jul 01 '23 22:07 bluca

I found the systemd-run --shell option but I don't see any option in the manual to base the environment on an existing service. For clarity, I do not mean just the environment variables, but also user, group, mounts and all the various sandbox features.

Lucus16 avatar Jul 01 '23 22:07 Lucus16

Just pass them with --property

bluca avatar Jul 01 '23 22:07 bluca

While that is possible, it can easily be 20 things I need to copy paste without making a mistake. The feature I'm requesting then is a single flag that takes a service name or filepath as argument in order to do that automatically. Call it --properties-from for example.

Lucus16 avatar Jul 01 '23 22:07 Lucus16

I second this request - this would be quite useful.

Have you tried nsenter?

nsenter brings one pretty close to emulating the sandbox the service processes are running in yes, but there are still gaps. For example, nsenter will not reproduce system call restrictions, capabilities, resource limits, to name just a few.

dechamps avatar Jul 16 '23 18:07 dechamps

A service equivalent of machinectl shell would be useful.

vcaputo avatar Jul 16 '23 18:07 vcaputo

I currently use a convenience script like:

#!/bin/bash
pid="$(systemctl show --property MainPID --value myapp.service)"
exec sudo nsenter -t "$pid" -a --wd=/opt/myapp -e -S follow -G follow bash

Two other nuances I found:

  • It inherits TERM=dumb, but maybe specific (hardcoded?) environment variables should be inherited from the parent process, instead of the target process.
  • It inherits environment variables from the target process that may not be set in the unit. This caused some unexpected behavior for me.

stephank avatar Feb 16 '24 10:02 stephank

Is this a duplicate of #9907? Being able to run a shell as if it were within the configued sandbox of a systemd service is pretty close to actually running a shell within the same cgroups of a running service.

And if you really did need to be in the exact same cgroups, nsenter is there.

nogweii avatar May 14 '24 18:05 nogweii

Passing the service file of the target service would be an okay solution for this. As mentioned before, nsenter won't help in the case where the service isn't running.

Lucus16 avatar May 14 '24 19:05 Lucus16