使用登录会话以另一个用户身份运行 Systemd 服务

使用登录会话以另一个用户身份运行 Systemd 服务

问题

如何在启动时以任意用户身份运行任意服务,并使用/run/user/uid为此用户创建的登录会话(特别包括 下的所有好东西)而无需显式以该用户身份登录?

背景

我正在转换 docker-compose 部署以与无根 podman + systemd 服务一起使用。我已经弄清楚了 podman 的大部分内容,但我特别努力让它通过 systemd 运行。

在之前的部署中,我为每个服务创建了一个用户帐户,并将容器配置为以该用户身份运行。从文件系统控制和维护的角度来看,这非常方便,并且确实是我希望保留的主要属性。

为此,我发现askubuntu 问题使我能够将用户直接嵌入到我的 systemd 作业中并让它作为所述用户执行。完美的。

现在问题来了:Fedora 迁移到了 cgroups v2,现在由 systemd 处理,事实上,这也是这项工作的首要原因。因此,podman 需要能够与 systemd 对话,而 systemd 又基本上需要由 logind 设置登录会话(如果我理解的话)链接正确)。我不是 systemd 专家,所以请纠正我的任何问题。

我确实找到了关于等待用户会话的另一个问题这接近我所追求的,但似乎表明这是不可能的。我的情况在两个主要方面有所不同:

  • 我不想等待任何会话(例如,这应该在启动时运行,无需交互)。
  • 我的解决方案需要足够强大,能够承受手动干预,从而使服务启动和关闭。

因此,如上所述,我确实正在寻找任何方法让 systemd 服务以特定用户身份运行,并具有足够的登录环境,以便服务应用程序连接到 systemd(或者至少是其中的 cgroups v2 部分) )。

最后,下面是其中一项服务的示例 systemd 单元文件,该服务按预期工作,直到需要连接到 systemd。此外,当以我的登录用户身份手动执行时,podman 调用完全可以工作。

[Unit]
Description=Emby Podman Container

[Service]
User=emby
Group=emby
Restart=on-failure
ExecStartPre=/usr/bin/rm -f /%t/%n-pid /home/emby/%n-cid
ExecStart=/usr/bin/podman run --conmon-pidfile /%t/%n-pid --cidfile /home/emby/%n-cid -d --name=emby --cgroup-manager=systemd -e TZ="$TZ" -p 8096:8096 -p 8920:8920 -v /opt/docker/storage/emby:/config -v /media/media/:/media emby/embyserver
ExecStop=/usr/bin/sh -c "/usr/bin/podman rm -f `cat /home/emby/%n-cid`"
KillMode=none
Type=forking
PIDFile=/%t/%n-pid

[Install]
WantedBy=multi-user.target

答案1

因此,经过一番苦思冥想,我找到了一个可行的解决方案,结果证明它比我想象的更简单,尽管不够优雅。让我们切入正题,看看功能单元文件:

[Unit]
Description=Emby Podman Container
[email protected]
[email protected]

[Service]
User=emby
Group=media
Restart=on-failure
ExecStartPre=/usr/bin/rm -f /home/emby/%n-pid /home/emby/%n-cid
ExecStartPre=-/usr/bin/podman rm emby
ExecStart=/usr/bin/podman run --conmon-pidfile /home/emby/%n-pid --cidfile /home/emby/%n-cid \
          --name=emby --rm --cgroup-manager=systemd \
          -e TZ="$TZ" \
          -p 8096:8096 -p 8920:8920 \
          -v /opt/docker/storage/emby:/config \
          -v /media/media/:/media \
          emby/embyserver
ExecStop=/usr/bin/sh -c "/usr/bin/podman rm -f `cat /home/emby/%n-cid`"
KillMode=none
Type=forking
PIDFile=/home/emby/%n-pid

[Install]
WantedBy=multi-user.target

这里的关键见解是让 systemd 管理用户会话本身(而不是让您做任何其他事情)。在此配置中使用BindsToAfter是必不可少的,因为它们将强制在 emby 服务实际启动之前完全创建 emby 用户的用户会话。此外,这使得管理员(读:我)不必登录每个用户来启用会话,随着更多服务和用户的添加,这应该会有所帮助。

有关设置的其他一些有用的注释:

  • -d标志已从 podman 中删除,以便可以通过查看 stdout journalctl -fu emby _TRANSPORT=stdout。方便测试和验证。
  • 根据[电子邮件受保护]手册页,用户服务必须使用 UID 而不是名称进行实例化(在我的系统 emby == 1012 上)。我还没有找到在任何非Exec命令中进行扩展的方法,因此目前这是硬编码的。如果有人知道如何清理它,我很想听听。
  • PID 文件已被移动到用户的主目录中,因为/run它不可字写(也很好)。

我尝试过的其他一些方法没有工作:

  • 直接在 中启动用户服务ExecPre。该单元以用户身份运行,因此无法为该用户启动 systmd(先有鸡还是先有蛋)。
  • 自动登录用户。注意:这可能是可以工作,但对于完全登录的用户来说存在安全隐患。
  • 通过 su 更改用户。 systemd 维护者对 su 的看法相当悲观并拒绝修复它。抛开宗教争论不谈,su plain 是行不通的。

附加参考资料:


编辑:

当然,我一发布这篇文章,我就设法找到了神奇的搜索字符串,它引导我找到了一个文章这消除了我的方法。

为了简洁起见,上面文章的重点涉及以 root 身份运行服务并使用--uidmap/--gidmap将容器的 root 用户映射到所需的系统用户。

然而,这个解决方案是 podman 特定的,因此我将把上述内容保留给那些非 root、非 podman 进程需要访问 systemd 的其他人。

最后我思考我的解决方案更加安全,因为如果攻击者破坏了容器运行时,范围仍然仅限于我的非特权用户。不过,这可能会被运行的另一个用户服务的攻击面增加所抵消,所以,也许,这是一次洗礼。

相关内容