如何在没有 root 权限的情况下在 systemd 下运行 init.d 服务(已更新)

如何在没有 root 权限的情况下在 systemd 下运行 init.d 服务(已更新)

我的公司在旧版 SLES 上使用 init.d 设置了一些自己开发的软件,使其作为服务运行。我们最近开始在 Amazon EC2 中的 SLES 12 实例上设置新环境,发现 SLES 12 现在使用的是 systemd 而不是 init.d。但是,看起来 init.d 脚本(即 /etc/init.d)中定义的服务仍可正常启动,因为 systemd 在后台执行了一些神奇的操作来处理预先存在的 init.d 脚本,这很棒。

但是我们不想以 root 用户身份运行我们的服务 - 相反,我们创建一个新用户来运行我们的服务。问题是,当我们尝试以非 root 用户身份启动我们的服务时,无论 systemd 为运行旧的 init.d 服务做了什么准备,都会请求 root 身份验证,我们认为这不是必要的。在较旧的 SLES 环境(使用实际的 init.d)上,我们可以毫无困难地以非 root 用户身份启动服务。据我所知,我们的非 root 用户权限和文件所有权在两个环境中是相同的。

我认为这是由于 systemd 在后台处理旧的 init.d 内容造成的,原因是启动我们服务的脚本在实际启动/停止服务命令之前会回显一行文本,但该行不会输出,即我们在回显语句之前被要求进行 root 身份验证。systemd 中隐藏的某个包/模块需要身份验证,但我现在记不起它的名字了。

请注意,如果可能的话,我们希望避免为我们的服务创建特定于服务的 .service 文件 - 我们希望坚持使用我们已有的 init.d 脚本并以非 root 用户身份运行它。此外,我们希望在不使用 sudo 的情况下运行该服务(在旧环境中我们不需要 sudo)。

抱歉,这个问题缺乏具体细节——一旦我回到办公室并能获得具体细节等,我会用更具体的细节来更新它。这似乎是一个常见的问题,所以我希望有人能对此作出解释。

具体来说:我们想以非 root 用户身份在 SLES 12 上使用 systemd 运行使用旧的 init.d 脚本定义的服务。

(更新 1)尝试运行我们的脚本 /etc/init.d/my_service stop 的确切结果是:

/etc/init.d/my_service 停止
重定向到 systemctl stop electrum-gateway-main.service
==== 正在对 org.freedesktop.systemd1.manage-units 进行身份验证 === 需要进行身份验证才能停止“my_service.service”。
身份验证为:root
密码:

当我们尝试停止该服务时,它并未运行,但这与身份验证问题无关。

我对我们的脚本做了更多的挖掘,脚本在要求授权之前到达了以下行:

. /etc/rc.status

问题就出在这里。调用 /etc/rc.status 需要 root 权限吗?

答案1

在 systemd(以及其他现代 init 系统)中,服务启动严格分为两个步骤:

  1. 用户工具(例如 systemctl)远程要求 init(pid 1)启动特定服务。
  2. Init 读取服务的配置,设置环境(包括切换到所需的用户帐户),并运行可执行文件。

由于这种间接性,无论谁启动服务、如何启动服务,都可以保证服务始终具有相同的环境。(以前,用户环境(如语言环境、路径或 SELinux 上下文)泄漏到服务中是一个常见问题。)

(对于 init.d 脚本,发行版的 lsb-functions 文件包含到“systemctl start”的魔术重定向,因此它们也接收相同的间接。)

这也意味着你不能“以同一用户身份”启动服务——你必须在相关的 systemd .service 文件中配置一个特定的用户名(如果没有,你确实应该写一个)。

“启动服务”调用通常是有特权的,但您可以编写一个 polkit 规则,允许每个用户或每个服务执行该调用(如果 systemd 版本足够新):

/* /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;
        }
    }
});

或者,可能可以选择退出 init.d 脚本中的间接寻址,但这样您也会完全失去 systemd 的服务跟踪——您的守护进程看起来就像一个常规的用户进程。

答案2

以非 root 用户身份运行服务是个好主意。除了另一个答案中的 Polkit 建议外,还有几种更常规的方法可以不以 root 身份启动服务:

  1. 使用 sudo。sudo 规则可以允许特定用户运行特定服务的startrestart命令,但不以 root 身份执行任何操作,从而提供相同类型的有限权限。
  2. 使用 systemduser模式.systemd 为用户提供了通过每个用户的 systemd 实例来管理用户控制下的服务的能力,使用户能够启动、停止、启用和禁用他们自己的单元。

我认为 systemd 用户模式可能更多地适用于桌面用例。对于服务器,我建议允许使用该sudo解决方案。在 systemd.service文件中,您还可以设置User=指令来指定服务以哪个用户身份运行——可能与您能够启动该服务的用户相同。

“polkit” 解决方案是一种远程操作解决方案——似乎很难理解是什么让用户启动了该服务。但是,使用 时,sudo您显然是在调用sudo,而使用 时systemd user mode,您显然是在使用该功能。

如果我处于设置了 Polkit 解决方案的环境中,除非同事留下一些相关文档,否则我将无法查明启动该服务为何能够正常工作。

相关内容