作为Write for DOnations计划的一部分,作者选择了免费和开源基金来接受捐赠。
介绍
沙盒是一种计算机安全技术,专注于将程序或进程与在正常操作期间不需要与之交互的系统部分隔离。当一个新程序启动时,它具有运行它的用户的所有能力。这些能力通常比程序执行其功能所需的要多得多。当不良行为者操纵程序以访问其某些未使用的功能来执行程序通常不会执行的操作时,这可能会导致安全问题。
沙盒的目的是准确确定程序需要哪些能力和资源,然后阻止其他一切。
几乎所有主要的 Linux 发行版都使用systemd工具的系统管理套件来启动、停止和管理程序和进程。它有许多沙盒选项来限制它启动的进程访问主机系统的方式,使其更加安全。
本教程的目的不是尽可能创建最严格的沙箱环境,而是使用推荐且易于启用的设置来使您的系统更加安全。
在本教程中,您将通过实际演示,了解如何在 Ubuntu 20.04 上使用 systemd 的沙箱技术,以实现高效的工作流程来实施和测试这些技术。使用这些技术可以使在使用 systemd 的 Linux 系统上运行的任何进程更加安全。
先决条件
您将需要以下内容才能开始本指南:
- 一个 Ubuntu 20.04 系统。
- 具有 sudo 权限的非 root 用户。有关如何在 Ubuntu 20.04 上执行此操作的信息,请按照Ubuntu 20.04的初始服务器设置进行操作。
- 熟悉使用 systemd 管理进程。请参阅Systemd Essentials: Working with Services, Units 和 Journal指南以了解基础知识。
- 对 systemd 单元文件的基本了解会有所帮助。有关更多信息,请参阅了解 Systemd 单位和单位文件指南。
第 1 步 — 安装 lighttpd
在本教程中,我们将对lighttpd Web 服务器进行沙箱处理。选择 lighttpd 不是因为它比其他软件安全性低,而是因为它是一个小程序,具有单一功能,很容易被沙箱化。这使其成为学习应用程序的绝佳选择。
让我们更新系统开始:
- sudo apt update
在输入之前检查将在您的系统上升级的软件包y
:
- sudo apt upgrade
然后安装 lighttpd:
- sudo apt install lighttpd
此安装过程将自动为 lighttpd 安装和启用 systemd 服务文件。这将使 lighttpd 在系统重新启动时启动。
现在我们已经在我们的系统上安装并运行了 lighttpd,我们将熟悉在开始沙箱时将使用的 systemd 工具。
步骤 2 — 准备您的系统
在此步骤中,您将熟悉将使用的 systemd 命令并准备系统以使您能够有效地沙箱化进程。
systemd 是一套工具的总称,每个工具都有不同的名称。您将使用的两个是systemctl
和journalctl
。systemctl
管理进程及其服务文件,同时journalctl
与系统日志交互。
systemd 使用服务文件来定义如何管理进程。systemd 从文件系统中的多个位置加载这些文件。以下命令将显示活动服务文件的位置并显示正在使用的任何覆盖:
- sudo systemctl cat process.service
您需要替换process
为您正在处理的流程。这里使用了 lighttpd:
- sudo systemctl cat lighttpd.service
这是上一个命令的输出:
Output# /lib/systemd/system/lighttpd.service
[Unit]
Description=Lighttpd Daemon
After=network-online.target
[Service]
Type=simple
PIDFile=/run/lighttpd.pid
ExecStartPre=/usr/sbin/lighttpd -tt -f /etc/lighttpd/lighttpd.conf
ExecStart=/usr/sbin/lighttpd -D -f /etc/lighttpd/lighttpd.conf
ExecReload=/bin/kill -USR1 $MAINPID
Restart=on-failure
[Install]
WantedBy=multi-user.target
此输出显示服务文件位于/lib/systemd/system/lighttpd.service
并且没有正在使用的覆盖选项。覆盖选项添加或修改基本服务文件。您将使用覆盖对带有专用覆盖文件的 lighttpd 进行沙盒处理。
覆盖文件位于. systemd 有一个专门的命令,它会在正确的位置创建一个覆盖文件,并在保存和退出编辑器后运行。该指示systemd使用你写的任何新的配置。/etc/systemd/system/process.service.d/override.conf
edit
systemctl daemon-reload
systemctl daemon-reload
systemd 编辑命令具有以下形式:
- sudo systemctl edit process.service
当您运行此命令时,systemd 通常会选择您的默认 CLI 编辑器,但情况并非总是如此,您可能会发现自己在vi甚至ed 中。您可以通过设置SYSTEMD_EDITOR
shell 变量来配置 systemd 将使用的编辑器。
通过在~/.bashrc
文件中添加一行来设置此 shell 变量。用文本编辑器打开这个文件:
- nano ~/.bashrc
并添加以下行:
export SYSTEMD_EDITOR=editor
更改editor
为您首选的 CLI 编辑器。这是使用nano编辑器的行集:
export SYSTEMD_EDITOR=nano
注销后确认已设置并使用以下echo
命令重新登录:
- echo $SYSTEMD_EDITOR
此命令将打印您设置的编辑器的名称。
该SYSTEMD_EDITOR
shell变量只在您的用户的shell并没有设置root的 那得到由打开外壳sudo
。要将此变量传递给root 的shell 调用,请systemctl edit
使用sudo -E
:
- sudo -E systemctl edit process.service
最终建议将通过向您显示您的更改导致的任何错误,使您的沙箱调试更容易。这些错误将被系统日志记录,通过journalctl
命令访问。
在沙盒化期间,您将进行许多更改,这些更改会破坏您尝试沙盒化的过程。出于这个原因,打开第二个终端并将其专用于跟踪系统日志是个好主意。这将节省重新打开系统日志的时间。
通过运行以下命令在第二个终端中跟踪系统日志:
- sudo journalctl -f -u process.service
-f
:跟踪或跟踪系统日志,以便立即显示新行。-u process.service
:只显示process
您正在沙盒化的日志行。
以下是您需要运行以仅打印 lighttpd 的错误:
- sudo journalctl -f -u lighttpd.service
接下来,您将开始编辑override.conf
文件并开始对 lighttpd 进行沙箱处理。
第 3 步 – 强制执行用户和组
在这一步中,您将设置运行 lighttpd 的非 root 用户。
在其默认配置中,lighttpd 开始以root 用户身份运行,然后更改为www-data用户和组。这是一个问题,因为当 lighttpd 以root身份运行时, 它可以做任何root 可以做的事情——这是任何事情。
systemd 提供了以非 root 用户身份启动和运行进程的能力,从而避免了这个问题。
返回到您的第一个终端会话并通过运行以下命令开始编辑覆盖文件:
- sudo -E systemctl edit lighttpd.service
现在,添加以下几行:
[Service]
User=www-data
Group=www-data
[Service]
: 告诉 systemd 应该将以下选项应用于该[Service]
部分。User=www-data
:定义启动进程的用户。Group=www-data
:定义启动进程的组。
接下来,保存并退出编辑器并使用以下命令重新启动 lighttpd:
- sudo systemctl restart lighttpd.service
lighttpd 将无法启动,因为它正在使用root
权限将 PID 文件写入root拥有的位置。在www数据的用户不能够写入所拥有的目录根。这个问题会出现在第二个终端会话中的系统日志中:
journalctl error messageAug 29 11:37:35 systemd lighttpd[7097]: 2020-08-29 11:37:35: (server.c.1233) opening pid-file failed: /run/lighttpd.pid Permission denied
解决此问题遵循沙箱过程,即:
- 实施沙箱限制。
- 重新启动进程并检查错误。
- 修复任何错误。
接下来,您将解决 PID 文件问题,同时仍然强制执行您在本节中设置的用户和组限制。
步骤 4 — 管理 PID 文件
PID 文件是包含正在运行的进程的 PID 或进程标识号的文件。像 lighttpd 这样的长时间运行的程序使用它们来管理自己的进程。您在上一节中遇到的问题是 lighttpd 无法将其 PID 文件写入/run/lighttpd.pid
,因为/run/
属于root。
systemd 有RuntimeDirectory
解决此问题的选项,您将使用该选项为 lighttpd 提供一个可以写入其 PID 文件的位置。
该RuntimeDirectory
选项允许您指定一个目录/run/
,当 systemd 启动 lighttpd 时,该目录将使用您在步骤 3 中设置的用户和组创建。lighttpd 将能够将其 PID 写入此目录,而无需root 的权限。
首先,使用您在步骤 3 中使用的相同命令打开并编辑覆盖文件:
- sudo -E systemctl edit lighttpd.service
接下来,在您已添加到覆盖文件的两行下添加以下行:
RuntimeDirectory=lighttpd
保存并退出编辑器。
您没有添加到带有 的目录的完整路径RuntimeDirectory
,而只添加了/run/
. 在这种情况下,systemd 将创建的目录是/run/lighttpd/
.
您现在需要配置 lighttpd 将其 PID 文件写入新目录/run/lighttpd/
而不是/run/
.
用文本编辑器打开 lighttpd 的配置文件:
- sudo nano /etc/lighttpd/lighttpd.conf
更改以下行:
server.pid-file = "/run/lighttpd.pid"
至:
server.pid-file = "/run/lighttpd/lighttpd.pid"
保存并退出编辑器。
现在,重新启动 lighttpd:
- sudo systemctl restart lighttpd.service
它不会启动,因为它无法执行需要root 权限之一的操作。接下来,您将解决这个新问题。
第 5 步——借用 root 的能力
系统日志中的以下行解释了停止 lighttpd 启动的问题:
journalctl error messageAug 29 12:07:22 systemd lighttpd[7220]: 2020-08-29 12:07:22: (network.c.311) can't bind to socket: 0.0.0.0:80 Permission denied
只有root才能打开 number 以下的网络端口1024
。lighttpd 正在尝试打开 HTTP 端口80
,但被拒绝,因为www-data用户不能这样做。
这个问题是通过给 lighttpd 进程一小部分root 的权力来解决的——也就是说,打开下面的端口1024
。
根的“力量”被划分为称为“能力”的能力。在根用户有充分的能力,因此可以做任何事情。将root 的权力分解为能力意味着它们可以单独分配给非 root 进程。这允许该进程执行需要完整root用户才能执行的操作,但普通用户现在可以使用root 的其中一项功能执行操作。
为进程提供一项或多项root功能的 systemd 选项是AmbientCapabilities
选项。
打开覆盖文件:
- sudo -E systemctl edit lighttpd.service
然后在您已经添加的行下添加以下行:
AmbientCapabilities=CAP_NET_BIND_SERVICE
该CAP_NET_BIND_SERVICE
功能允许进程在1024
.
保存并退出文件。
lighttpd 现在可以启动了。
您现在拥有了一个比默认配置更安全的 lighttpd 网络服务器。systemd 提供了更多沙盒选项,您可以使用它们来使目标进程更加安全。我们将在以下各节中探讨其中的一些。
在下一步中,您将限制 lighttpd 在文件系统中可以访问的内容。
步骤 6 — 锁定文件系统
lighttpd 进程以www-data用户身份运行,因此可以访问系统上www-data有权读写的任何文件。在www-data的情况下,虽然不是很多,但仍然超过了 lighttpd 的需要。
第一个也是最简单的沙箱设置是ProtectHome
选项。此选项会阻止进程读取或写入/home/
. lighttpd 不需要访问任何内容,/home/
因此实施这将保护您的所有私人文件,而不会影响 lighttpd。
打开覆盖文件:
- sudo -E systemctl edit lighttpd.service
然后在文件底部添加以下行:
ProtectHome=true
保存并退出编辑器,然后重新启动 lighttpd 以使用以下命令检查它是否按预期工作:
- sudo systemctl restart lighttpd.service
您已经保护了/home/
,但仍然保留了文件系统的其余部分。这是由ProtectSystem
选项处理的,该选项会阻止进程写入文件系统的某些部分。
该ProtectSystem
选项具有三个设置,可提供更高级别的保护。它们如下:
true
:将以下目录设置为只读:/usr/
/boot/
/efi/
full
:将以下目录设置为只读:/usr/
/boot/
/efi/
/etc/
strict
:将以下目录设置为只读:- 整个文件系统
更高级别的保护更安全,因此通过将以下行添加到覆盖文件来设置ProtectSystem
选项strict
:
ProtectSystem=strict
保存并退出编辑器并使用以下命令重新启动 lighttpd:
- sudo systemctl restart lighttpd.service
lighttpd 将无法启动,因为它需要将其日志文件写入其中,/var/log/lighttpd/
而strict
设置禁止这样做。系统日志中的以下行显示了问题:
journalctl error messageAug 29 12:44:41 systemd lighttpd[7417]: 2020-08-29 12:44:41: (server.c.752) opening errorlog '/var/log/lighttpd/error.log' failed: Read-only file system
这个问题是 systemd 通过LogsDirectory
选项预料到的。它采用/var/log/
允许进程将其日志写入的目录的名称。
在您的第一个终端会话中再次打开覆盖文件:
- sudo -E systemctl edit lighttpd.service
lighttpd 日志目录是/var/log/lighttpd/
这样添加以下行到覆盖文件的底部:
LogsDirectory=lighttpd
保存并退出编辑器并重新启动 lighttpd:
- sudo systemctl restart lighttpd.service
lighttpd 现在将能够启动和运行。
注意:如果您对 lighttpd 以外的进程进行沙箱处理,并希望允许您的进程在/var/log/
使用ReadWritePaths选项之外对特定目录进行写访问。
在下一步中,您将通过限制允许进行的系统调用来限制 lighttpd 进程与系统其余部分的交互方式。
步骤 7 — 限制系统调用
一个系统调用是怎样一个程序从内核请求的东西。系统调用的数量非常大,包括读取、写入和删除文件等操作,以及挂载文件系统、生成进程、重新启动等硬件相关任务。
systemd 已经创建了一组系统调用,这些系统调用处理(如 lighttpd)通常会使用并排除它们不使用的调用。被阻塞的系统调用是诸如挂载文件系统和重启系统之类的事情,而 lighttpd 从来不需要这样做。
首先,打开覆盖文件:
- sudo -E systemctl edit lighttpd.service
将以下行添加到文件底部以使用SystemCallFilter
设置@system-service
组的选项:
SystemCallFilter=@system-service
保存并退出编辑器并重新启动 lighttpd:
- sudo systemctl restart lighttpd.service
在下一部分中,您将应用其余推荐的沙盒选项。
步骤 8 — 实施更多选项
systemd 文档建议为长时间运行的网络进程(如 lighttpd)启用以下选项。这些设置都是可选的,但每一项都使沙盒过程更加安全,如果可以,应该使用。
您应该一次启用这些选项并在每个选项之后重新启动您的过程。如果将它们全部添加一次,则调试问题会困难得多。
以下推荐的选项附有其功能的简要说明。将这些行添加到您已经添加的行下的覆盖文件中:
NoNewPrivileges=true
此选项会阻止沙盒进程及其任何子进程获得新权限。
ProtectKernelTunables=true
此选项会阻止进程更改任何内核变量。
ProtectKernelModules=true
此选项会阻止进程加载或卸载内核模块。
ProtectKernelLogs=true
此选项会阻止进程直接读取和写入内核日志。它必须使用系统日志应用程序来记录任何日志消息。
ProtectControlGroups=true
此选项会阻止进程修改系统控制组。
MemoryDenyWriteExecute=true
此选项会阻止进程修改系统内存中运行的任何代码。
RestrictSUIDSGID=true
此选项会阻止进程在文件或目录上设置 set-user-ID (SUID) 或 set-group-ID (SGID)。这种能力可以被滥用来提升特权。
KeyringMode=private
此选项阻止进程访问以同一用户身份运行的其他进程的内核密钥环。
ProtectClock=true
此选项阻止进程更改硬件和软件系统时钟。
RestrictRealtime=true
此选项会阻止进程启用可被滥用来使 CPU 过载的实时调度。
PrivateDevices=true
此选项会阻止进程访问连接到系统的物理设备,例如存储设备或 USB 设备。
PrivateTmp=true
此选项强制进程使用私有/tmp/
和/var/tmp/
目录。这会阻止进程读取存储在这些共享系统目录中的其他程序的临时文件。
ProtectHostname=true
此选项会阻止进程更改系统的主机名。
您沙箱化的进程现在比默认配置更安全。您现在可以采用这些技术并将它们用于您需要在 Linux 系统上保护的任何其他进程。
结论
在本文中,您通过使用 systemd 沙盒选项使 lighttpd 程序更加安全。您可以将这些技术用于 systemd 管理的任何进程,从而继续提高系统的安全性。
沙盒和其他安全选项的完整列表可在 systemd 的在线文档中找到。此外,请查看DigitalOcean 社区上的更多安全主题。