Restarting systemd service only as a specific user?

To achieve that the user techops can control the service publicapi.service without giving a password, you have different possiblities. Which one is suitable for you cannot be answered as you have to choose on your own.


The classical sudo approach is maybe the most used, as it is there for a long time. You would have to create e.g. the file as follows.
Note that the drop-in directory /etc/sudoers.d is only active when #includedir /etc/sudoers.d is set in /etc/sudoers. But that should be the case if you are using a modern Ubuntu distribution. As root execute:

cat > /etc/sudoers.d/techops << SUDO
techops ALL= NOPASSWD: /bin/systemctl restart publicapi.service
techops ALL= NOPASSWD: /bin/systemctl stop publicapi.service
techops ALL= NOPASSWD: /bin/systemctl start publicapi.service
SUDO

Now you should be able to run the systemctl commands as user techops without giving a password by prepending sudo to the commands.

sudo systemctl start publicapi.service
sudo systemctl stop publicapi.service
sudo systemctl restart publicapi.service

The second method would be to use PolKit (was renamed from PolicyKit) to allow the user techops to control systemd services. Depending on the version of polit, you can give normal users control over systemd units.
To check the polkit version, just run pkaction --version.

  • with polkit version 0.106 and higher, you can allow users to control specific systemd units.
    To do so, you could create a rule as root:

    cat > /etc/polkit-1/rules.d/10-techops.rules << POLKIT
    polkit.addRule(function(action, subject) {
        if (action.id == "org.freedesktop.systemd1.manage-units" &&
            action.lookup("unit") == "publicapi.service" &&
            subject.user == "techops") {
            return polkit.Result.YES;
        }
    });
    POLKIT
    
  • with polkit version 0.105 and lower: you can allow users to control systemd units. This unfortunately includes all systemd units and you might not want to do this. Not sure if there is a way to limit access to specific systemd units with version 0.105 or lower, but maybe someone else can clarify.
    To enable this, you could create a file as root:

    cat > /etc/polkit-1/localauthority/50-local.d/org.freedesktop.systemd1.pkla << POLKIT
    [Allow user techops to run systemctl commands]
    Identity=unix-user:techops
    Action=org.freedesktop.systemd1.manage-units
    ResultInactive=no
    ResultActive=no
    ResultAny=yes
    POLKIT
    

In both cases you can run systemctl [start|stop|restart] publicapi.service as user techops without giving a password. In the latter case ( polkit <= 0.105 ) the user techops could control any systemd unit.


A third option would be to make the service a user service, which does not need sudo or polkit configurations. This puts everything under the control of the user and only works if your actual service that is started with /home/techops/publicapi start can run without root privileges.

First you have to enable lingering for the user techops. This is needed to startup the user service on boot. As root execute:

loginctl enable-linger techops

Next you have to move the systemd unit file into the techops user directory. As user techops execute the commands as follows.

mkdir -p ~/.config/systemd/user

cat > ~/.config/systemd/user/publicapi.service << UNIT
[Unit]
Description=public api startup script

[Service]
Type=oneshot
RemainAfterExit=yes
EnvironmentFile=-/etc/environment
WorkingDirectory=/home/techops
ExecStart=/home/techops/publicapi start
ExecStop=/home/techops/publicapi stop

[Install]
WantedBy=default.target
UNIT

Note that the WantedBy has to be default.target as there is no multi-user.target in the user context.

Now reload the configuration and enable the service. Again as user techops execute the commands.

systemctl --user daemon-reload
systemctl --user enable publicapi.service
systemctl --user start publicapi.service

In general you should place your systemd units in /etc/systemd/system/ not directly in /etc/systemd/system/multi-user.target.wants. When you execute systemctl enable publicapi.service a symbolic link will be created in etc/systemd/system/multi-user.target.wants or whatever target is specified for that unit.

As already mentioned, if the service/process itself can be run without root privileges you should consider adding User=techops to your unit file to run the process with a non-privileged user account.


First of all, you don't put a unit file in the directory /etc/systemd/system/multi-user.target.wants. This directory is maintained by systemd and the systemctl commands. Put the unit file in /etc/systemd/system instead and then enable it with

sudo systemctl enable publicapi.service

This will create a symlink below multi-user.target.wants (or wherever, systemctl knows better) to honor the lines

[Install]
WantedBy=multi-user.target

in the unit.

Next, create a sudoers file below /etc/sudoers.d:

sudo visudo -f /etc/sudoers.d/techops

with the following content:

Cmnd_Alias HANDLE_PUBLICAPI = \
    /bin/systemctl start   publicapi.service, \
    /bin/systemctl stop    publicapi.service, \
    /bin/systemctl restart publicapi.service

techops ALL = (root) NOPASSWD: HANDLE_PUBLICAPI

Do not use a regular editor (e.g. sudo vim /etc/sudoers.d/techops) to edit that file because if you put any syntax errors in it you won't be able to run sudo again. visudo on the other hand checks the syntax of the file before leaving the editor.

Now the user techops can run

sudo systemctl start publicapi.service

and the other two without supplying a password. Note that you must type the command and parameters exactly as given in the sudoers file (except for the /bin/systemctl part which can be shortened to just systemctl).

For instance, sudo /bin/systemctl start publicapi (whithout .service) would ask for a password.