以用户身份运行 systemd 时出错 — 无法连接到总线:未定义 $DBUS_SESSION_BUS_ADDRESS 和 $XDG_RUNTIME_DIR

以用户身份运行 systemd 时出错 — 无法连接到总线:未定义 $DBUS_SESSION_BUS_ADDRESS 和 $XDG_RUNTIME_DIR

我有一个以 root 身份运行的脚本,并尝试让它设置一个用户服务来运行 IPFS 守护进程。问题是我需要以用户身份而不是 root 身份启用该服务。它通常在重启后起作用,但如果可以的话,我想避免这种情况。

服务脚本位于 ~/.config/systemd/user/ipfs.service

它包含了:

[Unit]
Description=IPFS daemon

[Service]
# Environment="IPFS_PATH=/data/ipfs"  # optional path to ipfs init directory if not default (\$HOME/.ipfs)
ExecStart=/usr/local/bin/ipfs daemon
Restart=on-failure

[Install]
WantedBy=default.target

(我从这里获取了这段代码:https://github.com/ipfs/go-ipfs/tree/master/misc

如果我以用户身份运行这些命令,它可以正常工作:

systemctl --user enable ipfs
systemctl --user start ipfs

问题是我的脚本以 root 身份运行,我不知道如何让它以用户身份运行。到目前为止,我尝试过以下方法:

    # Enable linger so IPFS can run at boot
    loginctl enable-linger $USER_ACCOUNT

    # Enable the service to run at boot
    sudo -u $USER_ACCOUNT systemctl --user enable ipfs

    # Start the service now
    sudo -u $USER_ACCOUNT systemctl --user start ipfs

不幸的是,该服务无法启动,当我收到以下错误消息时:

无法连接到总线:$DBUS_SESSION_BUS_ADDRESS 和 $XDG_RUNTIME_DIR 未定义(考虑使用[电子邮件保护]--用户连接到其他用户的总线)

脚本运行完成后,我重新启动,服务就可以正常启动,但如果可以的话,我想避免用户重新启动。如能得到任何帮助,我将不胜感激。

答案1

快速解决方案

假设someuser用途狂欢作为登录 shell,添加以下内容出口~someuser/.profile[1]:

export XDG_RUNTIME_DIR="/run/user/$UID"
export DBUS_SESSION_BUS_ADDRESS="unix:path=${XDG_RUNTIME_DIR}/bus"

然后,使用 root/须藤特权用户可以someuser通过将命令包装成以下内容来与 systemd进行交互runuser

sudo runuser someuser -l -c "systemctl --user enable ipfs"
sudo runuser someuser -l -c "systemctl --user start ipfs"

runuser someuser -l -c "printenv"可以帮助解决这些和其他导出的环境变量的问题。


[1]: 将这些导出放入~someuser/.bashrc可能会无效,因为默认情况下,.bashrc当检测到正在运行时,通常会提前退出-交互方式;参见https://unix.stackexchange.com/a/257613/20230

清洁的解决方案

一般来说,手动/明确地设置XDG_RUNTIME_DIRDBUS_SESSION_BUS_ADDRESS是一种解决方法;更好的方法是适当地以用户身份登录someuser适当地登录将自动设置这些环境变量,并避免someuser来自原始用户的设置污染的环境变量。

可以使用以下命令完成正确的登录machinectl,例如为了Ubuntu 22.04 LTS 卡米可通过 apt 包获取systemd-container

有了machinectl可用的,恰当的从任何用户登录someuser到他们的 shell,并正确设置所有环境变量:

sudo machinectl shell someuser@

答案2

这些变量是特定于用户的。它们由 systemd 的用户实例在用户登录时设置。如果您的脚本作为系统服务运行,那么它当然无权访问特定用户的此变量。

如果您使用,systemctl --user --global enable则该服务将为所有用户启用。

答案3

我在安装 docker-desktop 后遇到了这个问题https://docs.docker.com/desktop/install/ubuntu/在 Ubuntu 22.04 LTS 上。

以下解决了该问题。首先检查“下载”目录中下载的 .deb 文件的权限,或者直接设置它:

sudo chmod 755 docker-desktop-<version>-<arch>.deb

接下来将您的用户添加到 docker 组:

sudo usermod -aG docker $USER

激活对组的更改:

newgrp docker

现在尝试执行命令来启动 docker-desktop:

systemctl --user start docker-desktop

要从 docker-desktop 修复 docker-hub 登录问题:

https://docs.docker.com/desktop/get-started/#credentials-management-for-linux-users

参考:https://docs.docker.com/engine/install/linux-postinstall/

答案4

虽然其他答案要好得多,但以“批准”的方式处理几乎所有可能的情况(阅读:systemd以原有的方式使用设计为了完整起见,这里是我自己的方法,这个方法是在我甚至没有意识到systemctl与...合作任何用户,而不仅仅是在启动时(和作为 root)。

假设如下:

  1. 您已经为 IPFS 设置了一个专门为此目的创建的用户,通常ipfs(如您的 OP 中所述);
  2. ipfs将其主目录(兼作 IPFS 守护进程的工作目录)设置为/data/ipfs/(以阐明您自己的示例)。

然后你就可以拥有一个“常规”的系统范围配置文件,其中/etc/systemd/system/ipfs.service包含以下内容(确保全部路径是绝对的!):

[Unit]
Description=InterPlanetary File System (IPFS)
After=syslog.target
After=network.target
# Uncomment the following line if you use nginx as your reverse proxy to IPFS:
# After=nginx.service

[Service]
Type=simple
User=ipfs
Group=ipfs
WorkingDirectory=/data/ipfs/
ExecStart=/usr/local/bin/ipfs daemon
Restart=always
# IPFS usually consumes all resources it can grab; try to get it to play nicer with the rest of the system:
Nice=19
Environment=USER=ipfs HOME=/data/ipfs/

[Install]
WantedBy=multi-user.target

允许 IPFS 在ipfs:ipfsUID/GID 下运行的“技巧”是在配置文件中明确说明。这与对某些服务所做的操作相同(例如,ipfs.service如果您安装了 Redis,请在 上检查其配置)。/etc/systemd/system/redis.service

现在,上述配置假定这就是了“真实”用户登录ipfs——它纯粹是一个“维护”用户,例如mail,,,等等。在这种情况下postfixbind“真实”的意思是:“通过控制台或远程 SSH 连接登录并因此需要完整工作环境的人”;就我个人而言——对于 IPFS 更是如此!——我会完全阻止用户ipfs存在于外部/etc/passwd(设置为nologinshell),上述配置文件足以允许这样做。

那里不过,拥有“功能性”ipfs用户有一个相当好的理由;例如,它将用于获取 Go 源代码并在本地进行编译,从而保证与 IPFS 相关的所有内容 - 不仅是数据,甚至是代码本身! - 完全隔离在ipfs用户的主目录中。

在这种情况下,您很可能没有ipfs打开二进制文件/usr/local/bin/ipfs,但更有可能在~ipfs/go/bin安装编译的二进制文件的某个地方。只需记住确保使用绝对它的路径!

值得重复的是,在这种情况下,其他答案对如何systemd在特定(非特权)用户下正确配置给出了更好的建议。然而,在一些案例,采用混合方法可能是可取的,例如阻止用户直接ipfs管理systemd自己的目的,但仍允许它远程访问数据目录中的文件,甚至从源本地编译 IPFS。我在这种情况下所做的是将位于主目录ipfs.service中的某个位置ipfs(在我的情况下,我避免使用与用户systemd配置,否则一切都会变得难以管理:)),例如~ipfs/systemd/ipfs.service

然后,从sudoer,我只需从“主”systemd到本地单元文件建立一个链接:

sudo systemctl link ~ipfs/systemd/ipfs.service
sudo systemctl enable ipfs

进而...

sudo systemctl start ipfs
sudo journalctl -u ipfs -f
... etc ...

因为ipfs用户更改本地文件 — 即使它是在“主”的控制之下systemd而不是用户特定的systemd,也许值得一提的是,sudo systemctl daemon-reload每次ipfs.service使用ipfs用户更改文件时都要记住执行此操作。

尽管我很清楚以上都是不是做事的好方法,工作,我在处理单个用户主目录内的项目时广泛使用了它(而不是在,比如说,/usr/share/.../usr/local/...下),为了方便起见,我在本地编辑单元服务文件,然后将所有内容上传到运行这些服务的远程服务器,但使用非特权用户(从而避免需要 SSH 允许 root 帐户远程登录,即使只是通过 SFTP)。这也意味着任何仅在该项目上工作的合作者都不需要访问除非特权用户之外的任何其他用户(只要有人代表sudo systemctl daemon-reload他们执行此操作)。

最后,请注意,由于在这种情况下,非特权用户可能不是为它们分配一个 shell(出于安全原因),这意味着所有的 whole.profile.bashrcmagic 可能不是对他们来说,这一切都是为了限制潜在恶意黑客的攻击面。就我个人而言,我有 20 多个项目在这种情况下运行,每个项目都有自己的用户,没有任何其中有一个 shell 帐户,更不用说密码了;对这些帐户的身份验证仅通过 SSH 证书进行,这些帐户无论如何只具有使用 SFTP 的权限。

顺便提一下,我所有的这些项目——并非巧合——都是用 PHP(可以在任何地方运行)或 Go 编写的,因为后者可能是最简单的交叉编译器,它可以将代码完全编译为本机二进制文件,用于强类型编程语言。任何架构。项目开发人员只需在本地进行交叉编译(如果他们没有在桌面上使用 Ubuntu,那就是说;大多数人没有),然后将服务器原生可执行文件上传给该特定用户。剩下的就交给他们自己去systemd解决吧。

我希望以上所有内容对那些在 Google 上搜索此问题答案的人有用 :)

相关内容