SSD Nodes Learn Hosting plans →
指南 Matt Connor作者: Matt Connor · 更新于 2026-08-22

如何让服务以非特权用户运行

以 root 运行服务会让漏洞直接升级为整台服务器失陷。为每个服务使用专用系统账户,或在 systemd 中设置 DynamicUser,限制文件与系统访问权限。

为什么不要直接以 root 身份运行所有程序

root 可以对计算机执行任何操作:读取所有文件、修改任意设置、删除整个系统。以 root 身份运行服务时,您会将这些权限全部授予该服务。如果服务存在可被攻击者利用的漏洞,攻击者获得的不只是该服务的权限,而是 root 权限;而 root 权限意味着可以控制整台服务器。以非特权用户身份运行服务可以限制损害范围。以受限账户运行的服务即使存在漏洞,攻击者也只能访问该账户有权操作的内容,而这些内容通常应当极少。

这就是最小权限原则:为系统的每个部分仅授予其完成工作所需的权限,不授予其他权限。这是限制服务器遭入侵后影响范围最有效的做法。在现代服务器上,实施这一原则几乎不需要额外成本。

每个服务使用专用账户

经典做法是为每个服务创建单独的系统用户。该用户只拥有对应服务的文件,并且不能登录。Web 应用的系统账户可以这样创建:

sudo useradd --system --no-create-home --shell /usr/sbin/nologin appsvc

每个标志都有作用。--system表示这是服务账户,而不是供人登录的账户。--no-create-home跳过创建该账户不需要的主目录。--shell /usr/sbin/nologin表示即使攻击者通过某种方式取得该账户,也无法使用它打开 shell。该账户只用于拥有进程及其文件。

然后,仅向该用户授予它所需的文件权限,不要授予更多权限:

sudo chown -R appsvc:appsvc /opt/myapp

现在,该服务只能读写自己的目录,无法访问磁盘上的其他位置。如果服务遭到利用,攻击者能够修改的文件仅限于 /opt/myapp;该账户仍可读取所有其他用户都能读取的文件,但无法修改系统的其余部分。

让 systemd 以该用户身份运行服务

创建账户后,让 systemd 以该账户身份运行服务。在 unit 文件中添加一行即可:

[Service]
ExecStart=/opt/myapp/bin/server
User=appsvc
Group=appsvc

User=appsvc表示进程以该账户的受限权限启动,而不是使用 root 的权限。这是让应用在 systemd 下运行的标准做法。为每个编写 unit 文件的服务执行此操作都很有必要。不过,如果 systemd 监控的是错误的进程,降低权限也无法解决问题。如果守护进程已经静默退出,而 unit 仍报告为 active,请检查是否已根据进程的启动方式选择正确的 Type=

或者使用 DynamicUser,完全跳过创建账户

systemd 还可以更进一步,为您创建一个临时用户。该用户仅在服务运行期间存在。设置 DynamicUser=yes 后,您无需管理任何账户:

[Service]
ExecStart=/opt/myapp/bin/server
DynamicUser=yes
StateDirectory=myapp

服务启动时,systemd 会分配一个未使用的用户 ID;服务停止时,systemd 会释放该 ID。服务还会获得私有的 /tmp、大部分文件系统的只读视图,以及位于 /var/lib/myapp 下的可写状态目录。StateDirectory= 会创建该目录并将其交给服务使用。在 SysV init 下无法实现这些功能,因为降权操作由各服务自己的启动脚本决定。这也是 各发行版最初改用 systemd 的原因 的重要组成部分。对于只需要使用自身状态目录的独立服务,DynamicUser=yes 是实现强隔离的最低成本方式,因为系统中根本不存在可供攻击者长期针对的账户。

手动编写单元文件较为繁琐,而正确设置加固指令才是其中最有价值的部分。systemd 服务和计时器指南中的生成器可以替您填充这些选项,确保单元文件首次配置就正确。

这与其他措施的关系

最小权限只是其中一层,应与其他措施配合使用,而不是取代它们。默认拒绝防火墙控制哪些流量可以到达服务;以非特权用户运行服务,可以限制服务在遭受入侵后能够执行的操作;加固 SSH则可在攻击者进入服务器之前将其拦截。单独使用其中任何一项都不够。将这些措施结合起来,可以避免某个服务中的漏洞演变为整台服务器遭到入侵。托管用于保护机密信息的服务时,可以看出这些措施的边界:受限账户可以限制被入侵进程能够访问的内容,但自行托管的密码管理器(如 Vaultwarden)是否安全,仍取决于您如何保护其管理令牌和备份文件;用户隔离无法覆盖这两项内容。

继续操作前,请先完成整台服务器的加固检查,并生成一份个性化副本作为工作基础:

ToolVPS hardening checklist

FAQ

为什么不应以 root 身份运行服务?

因为 root 可以对系统执行任何操作。以 root 身份运行的服务一旦被利用,攻击者就能控制整个服务器,而不只是该服务。使用受限的非特权账户运行服务,可将影响限制在该账户能够访问的范围内。将 root 仅用于管理操作,并让所有长期运行的服务使用受限用户。

如何创建无法登录的用户?

运行 sudo useradd --system --no-create-home --shell /usr/sbin/nologin NAMEnologin shell 表示即使凭据被盗,该账户也无法打开交互式会话;--system 将其标记为服务账户;--no-create-home 跳过创建该账户不需要的主目录。使用 chown 仅将其自己的文件所有权授予该账户。

什么是 systemd DynamicUser?

DynamicUser=yes 告诉 systemd 为服务创建临时用户。该用户仅在服务运行期间存在,因此无需管理长期存在的账户。它还会为服务提供私有的 /tmp、基本只读的文件系统视图,以及由 systemd 管理的状态目录。这是让自包含服务以临时低权限身份运行的最省事方式。

以非 root 用户运行服务可以替代防火墙吗?

不可以。两者保护的对象不同。使用非特权用户运行服务,可以限制服务被攻破后能够执行的操作;防火墙则限制能够访问该服务的网络流量。应同时使用两者,并加固 SSH,使每一层都能弥补其他层无法覆盖的风险。

服务用户应拥有哪些文件?

只应拥有服务实际需要的文件,不要授予更多权限。将服务自己的工作目录和数据的所有权授予该账户,其余文件继续由 root 拥有。一个合理的做法是使用 sudo chown -R svc-app:svc-app /opt/svc-app 作为应用目录,同时让 /etc 下的配置文件继续由 root 拥有,并仅允许服务读取这些文件。目标是:如果进程遭到入侵,它能够修改的文件仅限于自身数据,而不是系统的其他部分。

#安全#least-privilege#systemd#users#hardening#linux