我有一个以 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_DIR
和DBUS_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)。
假设如下:
- 您已经为 IPFS 设置了一个专门为此目的创建的用户,通常
ipfs
(如您的 OP 中所述); 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:ipfs
UID/GID 下运行的“技巧”是在配置文件中明确说明。这与对某些服务所做的操作相同(例如,ipfs.service
如果您安装了 Redis,请在 上检查其配置)。/etc/systemd/system/redis.service
现在,上述配置假定这就是了不“真实”用户登录ipfs
——它纯粹是一个“维护”用户,例如mail
,,,等等。在这种情况下postfix
,bind
“真实”的意思是:“通过控制台或远程 SSH 连接登录并因此需要完整工作环境的人”;就我个人而言——对于 IPFS 更是如此!——我会完全阻止用户ipfs
存在于外部/etc/passwd
(设置为nologin
shell),上述配置文件足以允许这样做。
那里是不过,拥有“功能性”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
或.bashrc
magic 可能不是对他们来说,这一切都是为了限制潜在恶意黑客的攻击面。就我个人而言,我有 20 多个项目在这种情况下运行,每个项目都有自己的用户,没有任何其中有一个 shell 帐户,更不用说密码了;对这些帐户的身份验证仅通过 SSH 证书进行,这些帐户无论如何只具有使用 SFTP 的权限。
顺便提一下,我所有的这些项目——并非巧合——都是用 PHP(可以在任何地方运行)或 Go 编写的,因为后者可能是最简单的交叉编译器,它可以将代码完全编译为本机二进制文件,用于强类型编程语言。任何架构。项目开发人员只需在本地进行交叉编译(如果他们没有在桌面上使用 Ubuntu,那就是说;大多数人没有),然后将服务器原生可执行文件上传给该特定用户。剩下的就交给他们自己去systemd
解决吧。
我希望以上所有内容对那些在 Google 上搜索此问题答案的人有用 :)