以非特权用户身份运行服务
以 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=appsvcUser=appsvc 意味着进程以该账户受限的权限启动,而不是 root 的权限。这是在 systemd 下运行应用最常规、最成熟的方式,值得为您编写 unit 的每一个服务都这样做。
或者用 DynamicUser 彻底省去账户
systemd 还能更进一步,为您创建一个一次性的用户,它只在服务运行期间存在。设置 DynamicUser=yes,您就完全不必管理任何账户:
[Service]
ExecStart=/opt/myapp/bin/server
DynamicUser=yes
StateDirectory=myapp启动时,systemd 分配一个未被使用的用户 ID;停止时,再把它释放。这个服务还会得到一个私有的 /tmp、对大部分文件系统的只读视图,以及一个位于 /var/lib/myapp 下的可写状态目录,这个目录由 StateDirectory= 建立并交给它。对于一个自包含、只需要自己状态目录的服务来说,DynamicUser=yes 是获得强隔离最省事的方式,因为根本不存在一个长期账户供攻击者下手。
手写 unit 文件很繁琐,而把加固指令写对正是其中大部分的价值所在。systemd 服务与定时器指南 里的生成器可以替您填好这些选项,让 unit 一次就写对。
它如何与其他措施配合
最小权限只是其中一层,它与其他措施相互配合,而不是取而代之。默认拒绝的防火墙 控制什么能够到达服务;以非特权用户运行则控制服务一旦被攻破能做什么;而 加固后的 SSH 从一开始就把攻击者挡在机器之外。它们中的任何一层单独都不够,而合在一起就意味着一个服务的漏洞不会演变成整台服务器的失守。
在继续之前,请把整台机器过一遍加固清单,并生成一份供您使用的个性化副本:
FAQ
为什么不应该以 root 运行服务?
因为 root 在这台机器上可以做任何事,一个以 root 运行的服务一旦被利用,就会把整台服务器而不只是这个服务交到攻击者手里。以一个受限的非特权账户运行服务,能把损害限制在该账户可以访问的范围内。把 root 留给管理工作,让每一个长期运行的服务都以受限用户身份运行。
如何创建一个无法登录的用户?
运行 sudo useradd --system --no-create-home --shell /usr/sbin/nologin NAME。nologin shell 意味着即使凭据被盗,这个账户也无法打开一个交互式会话;--system 把它标记为服务账户;--no-create-home 跳过它并不需要的家目录。用 chown 让它只拥有自己的文件。
什么是 systemd 的 DynamicUser?
DynamicUser=yes 告诉 systemd 为服务创建一个临时用户,它只在服务运行期间存在,因此您永远不必管理一个长期账户。它还会给服务一个私有的 /tmp、一个大部分只读的文件系统视图,以及一个受管理的状态目录。这是让一个自包含服务以一次性的低权限身份运行最省事的方式。
以非 root 用户运行能取代防火墙吗?
不能。它们保护的是不同的东西。以非特权用户运行限制的是服务一旦被攻破能做什么,而防火墙限制的是什么能够到达服务。请把两者连同加固后的 SSH 一起使用,让每一层都覆盖其他层顾及不到的地方。
服务用户应该拥有哪些文件?
只拥有服务真正需要的文件,不多拥有一个。让账户拥有它自己的工作目录及其数据,其余的一切都保持由 root 拥有。一个不错的做法是对应用目录执行 sudo chown -R svc-app:svc-app /opt/svc-app,而 /etc 下的配置仍由 root 拥有、只对服务可读。目标是:万一进程被攻破,它能够改动的文件也仅限于它自己的数据,而不是系统的其余部分。