How to run init.d service under systemd without root auth (updated)

initservicessusesystemd

My company set up some software it develops to run as a service using init.d on an older version of SLES. We recently created started setting up a new environment on a SLES 12 instance in Amazon's EC2 and discovered that SLES 12 now uses systemd rather than init.d. However it looks as though services defined in init.d scripts (i.e. in /etc/init.d) can still be started as normal because systemd performs some magic in the background to handle pre-existing init.d scripts which is great.

However we don't want to run our service as the root user – instead we create a new user to run our service. The problem is that when we try to start our service as the non-root user then whatever systemd does to prepare itself to run an old init.d service requests root authentication which we don't think should be necessary. On the older SLES environment (using actual init.d) we can start the service as the non-root user without trouble. As far as I can tell our non-root user permissions and file ownerships are the same in both environments.

The reason I think this is in the under-the-hood handling of old init.d stuff by systemd is that the script to start our service echoes a line of text immediate before the actual command to start/stop the service but that line isn't output i.e. we're being asked for root auth before the echo statement. The authentication is required for a certain package/module buried in systemd but I can't remember its name at the moment.

Note that we'd like to avoid creating a service specific .service file for our service if possible – we want to stick with the init.d script we have and just run it as the non-root user. Also, we'd like to run the service without the use of sudo (we don't need sudo in the old environment).

Apologies for the lack of specifics in this question – I'll update it with more specific details once I get back to the office and can grab the specific details etc. This seems like it might be a common problem though so I'm hoping someone can shed some light on this.

Specifically: we want to run a service defined with an old init.d script as a non-root user using systemd on SLES 12.

(update 1)
The exact result of trying to run our script /etc/init.d/my_service stop is:

/etc/init.d/my_service stop
redirecting to systemctl stop electrum-gateway-main.service
==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units === Authentication is required to stop 'my_service.service'.
Authenticating as: root
Password:

The service wasn't running when we tried to stop it but that's not relevant to the auth issue.

I've done a little more digging in our script and the script reaches the following line before asking for auth:

. /etc/rc.status

So this is where the trouble lies. Should one need root privileges to call /etc/rc.status?

Best Answer

In systemd (and other modern init systems), service startup is strictly separated into two steps:

  1. User tools (e.g. systemctl) remotely ask init (pid 1) to start a particular service.
  2. Init reads the service's configuration, sets up environment (including switching to the desired user account), and runs the executable.

Due to this indirection, services are guaranteed to always have the same environment regardless of who and how started them. (Previously, user environment like locale, path, or SELinux contexts leaking into services used to be a common problem.)

(For init.d scripts, the distro's lsb-functions file contains the magic redirects to 'systemctl start', so they also receive the same indirection.)

This also means that you cannot start a service "as the same user" – you must configure a specific username in the relevant systemd .service file (and if there is none, you really should write one).

The 'start service' call is normally privileged, but you can write a polkit rule allowing it per-user or per-service (if the systemd version is recent enough):

/* /etc/polkit-1/rules.d/allow-whatever.rules */

polkit.addRule(function(action, subject) {
    if (action.id == "org.freedesktop.systemd1.manage-units") {
        var verb = action.lookup("verb");
        var unit = action.lookup("unit");
        if (subject.user == "manager"
            && unit == "app.service"
            && (verb == "start" || verb == "stop" || verb == "restart"))
        {
            return polkit.Result.YES;
        }
    }
});

Alternatively, it might be possible to opt out of indirection in the init.d script, but then you would also lose systemd's service tracking entirely – your daemon will look like a regular user process.