跳转至内容

systemd/Sandboxing (沙箱)

来自 ArchWiki

systemd 使您能够加固和沙盒化系统服务单元。由于技术限制以及(讽刺的是)安全原因,用户单元无法被正确加固或沙盒化,因为这会使权限提升问题成为可能。这不会影响使用 User= 指令的系统单元。

提示 Firejail 可以为以非特权且明确允许的用户身份运行的应用程序创建沙盒,但它使用一个 SUID 二进制文件,该文件理论上容易受到权限提升漏洞的攻击。

由于其他单元类型的性质,只有服务单元可以被传统意义上加固/沙盒化。有关更多信息,请参阅 systemd.exec(5)systemd.resource-control(5)

常规

由于加固/沙盒化有效地限制了应用程序,因此无法使用所有沙盒指令。例如,Web 服务器不应使用 PrivateNetwork=true,因为它通常需要网络访问。

systemd-analyze security unit 为该单元生成一个分数,显示所有使用的指令,这有助于确定下一步尝试哪些设置。

注意 该分数略有误导性。只有一个简单的 Hello world 可以获得近乎完美的分数。没有应用程序可以使用所有沙盒设置。

不幸的是,systemd 在与沙盒化相关的错误配置方面的错误消息有时含糊不清和/或具有误导性。暂时将日志级别设置为 debug 可能有助于获取真正相关的信息。

# systemctl log-level debug

功能

capabilities(7) 用于授予进程特定的提升权限。例如,CAP_NET_BIND_SERVICE 可用于允许一个非特权进程绑定低于 1024 的端口,从而无需以 root 权限启动。另一个值得注意的例子是 CAP_DAC_READ_SEARCH,用于授予备份程序对所有位置的无限制读取访问权限。

提示
  • 始终同时使用 CapabilityBoundingSetAmbientCapabilities 是一个好习惯。
  • systemd-analyze capability 提供了一个可与 systemd 一起使用的能力列表。如果您运行的是添加了新能力的实验性内核,这会很有用。

在服务单元中,您可以通过使用 AmbientCapabilities= 来授予其能力,并使用 CapabilityBoundingSet= 来确保不会授予超出预期范围的任何内容。

example.service
[Service]
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE

常用指令

其中大多数指令可以应用于大多数应用程序而不会引起太多问题。

注意 "影响" 和 "潜在的损坏" 都是相对的,因为它们取决于环境和单元。

无需特殊配置

简单的布尔设置,可以启用或不启用。它们不能被配置。

指令 影响1 损坏2 备注
LockPersonality 中等
MemoryDenyWriteExecute 中等 中等 与运行时动态生成的代码不兼容,包括 JIT、可执行栈、C 编译器代码“trampoline”。可以通过 SystemCallFilter 增强。
NoNewPrivileges
PrivateDevices 中等 /dev/null 和类似的设备仍然存在。
PrivateNetwork 非常高 禁止任何网络访问。
PrivateTmp 中等
PrivateUsers
RestrictRealtime 可能会阻止拒绝服务情况的发生。
RestrictSUIDSGID 中等 最好与 NoNewPrivileges 一起使用。
  1. 指令的有效性
  2. 指令可能损坏某项内容的可能性

可配置指令

其中大多数指令功能强大,影响很大。建议至少考虑使用其中的一些。

指令 影响1 损坏2 备注
ProtectSystem 严格 非常高 非常高 通常与 ReadWritePaths= 一起使用
完整 中等 可能会破坏例如使用 ACME 续订其自身密钥的 Web 服务器,而这些密钥可能位于 /etc 中。
true 中等 理论上很少有应用程序会写入 /boot/usr
ProtectHome true 中等 某些应用程序可能需要在 XDG_CONFIG_HOME3 中存储持久数据。
tmpfs 中等 主目录包含大量敏感数据,使用 tmpfstrue 可以防止泄露。4
只读 非常适合备份服务
ProtectProc5 不可见 中等
无访问权限 中等 中等
RestrictAddressFamilies 例如 AF_UNIX AF_INET AF_INET6 接受一个空格分隔的地址族列表,请参阅 address_families(7)
  1. 指令的有效性
  2. 指令可能损坏某项内容的可能性
  3. StateDirectory= 可用于缓解某些负面影响。
  4. 这也会使 /run/user/ 无法访问,从而防止通过 IPC 套接字泄露。理论上,其他地方也可能存在套接字,例如 /tmp
  5. 如果省略该指令,则默认为 /proc 挂载的 hidepid 值,该值通常是 0(无限制)。

高级指令

这些指令对大多数单元没有用,因此使用较少。

指令 影响 损坏 备注
DynamicUser 布尔值,例如 true 中等 中等 需要与 StateDirectory=RuntimeDirectory=CacheDirectory=LogsDirectory=ConfigurationDirectory= 结合使用。[1]
ProtectClock1,2 布尔值 中等 一些用户报告说,启用此设置后 smartctl 无法运行,但这应该相对安全。
ProtectControlGroups1 布尔值 中等
ProtectHostname1,2 布尔值
ProtectKernelLogs1 布尔值 所有官方内核都将 SECURITY_DMESG_RESTRICT 设置为 y,但这仍然是深度防御。
ProtectKernelModules1 布尔值 中等
ProtectKernelTunables1 布尔值
RestrictFileSystems 例如 ext4 tmpfs 中等 中等 接受一个空格分隔的文件系统列表或一个集合,例如 @network。请参阅 systemd-analyze filesystems 以获取完整列表。
SystemCallArchitectures 例如 native 另请参阅 #禁用非原生系统调用 - 最好使用此项将单元从系统默认中排除。
SystemCallFilter 例如 @system-service 中等 请参阅 systemd.exec(5) § SYSTEM CALL FILTERING - 忘记一个系统调用都会导致您的应用程序在可能不合时宜的时候发生段错误。
SocketBindAllow/Deny 例如 SocketBindAllow=ipv4:22 SocketBindDeny=any 中等 中等 最好与 CAP_NET_BIND_SERVICE 结合使用,以确保特权上下文只能绑定到所需的端口。
  1. 如果服务单元没有以提升的权限运行(例如通过使用 AmbientCapabilities= 或以 root 身份运行),则指定这些选项是多余且不必要的。如果服务单元以其他普通且非特权的 User= 身份运行,则这些设置完全是多余的,可以安全地省略。
  2. 仅限制发出相应的系统调用,但并不限制对 systemd 提供的 IPC 服务的访问(即 systemd-timedatedsystemd-hostnamed)。[2] 如果要求绝对安全,则必须小心阻止涉及的 D-Bus/Varlink 方法。

chroot 监狱

警告 虽然这在实践中可行,但它部分依赖于指令的未文档化副作用,并且可能在新版本的 systemd 中随时失效。因此,systemd 的开发者不将其视为受支持的功能。已提交功能请求以使其成为预期和受支持的功能。[3]

可以通过指定 TemporaryFileSystem=/:ro 并将所需路径挂载到这个类 chroot 的监狱中来严格限制进程的可见性。RootDirectory 需要一个存在的目录,而 TemporaryFileSystem 则不需要,并且可以无缝地覆盖 /。两者(特别是后者)似乎都是安全的类 chroot 指令,不容易被破解,因为它们不使用 chroot 系统调用。

警告 ProtectSystemProtectHomeTemporaryFileSystem=/:ro 不兼容,并且会导致后者失效,使 / 重新可见。但是,由于路径将被白名单化,因此不需要这些指令。请参阅上面的警告,发生这种情况是因为这些指令之间的交互从未被考虑/支持。

所有必需的路径都必须通过 BindReadOnlyPathsBindPaths 挂载到此监狱中。

example_jailed_unit.service
[Unit]
Description=Example unit

[Service]
ExecStart=/home/someuser/executable
User=someuser
Group=someuser
TemporaryFileSystem=/:ro
PrivateTmp=true
BindReadOnlyPaths=/usr/lib /lib64 /lib
BindPaths=/home/someuser/executable

这是一个最小化的示例,大多数应用程序将需要更多路径的白名单。一些常见路径包括:

  • /etc/ca-certificates, /etc/ssl
  • /etc/resolv.conf
  • /usr/share/zoneinfo
  • 您需要的任何套接字,例如 /var/run/mysqld/mysqld.sock

首次尝试沙盒化单元时,很可能在某个时候需要进行调试。如果单元根本无法启动并以 status=203/EXEC 失败,则表示可执行文件本身或所需的库不可访问。从广泛的路径开始(例如允许整个 /usr),然后逐步缩小范围,也可以有所帮助。

system.conf

/etc/systemd/system.conf 的更改是全局的,因此它们会影响所有单元。请参阅 systemd-system.conf(5)

禁用非原生系统调用

非原生二进制文件,在几乎所有情况下都是 32 位二进制文件,可能部分损害系统安全,因为它们无法获得更多加固。曾发生过一些相对较小的漏洞,例如CVE-2009-0835,这些漏洞影响了非原生系统调用。

注意破坏 32 位二进制文件。尝试执行此类二进制文件将导致文件未找到错误。
/etc/systemd/system.conf
SystemCallArchitectures=native

这在大多数系统上运行良好,但如果例如使用了 multilib,则需要至少部分禁用它。特别是使用 Wine 进行游戏可能会受到影响。使用 systemd-run 或修改会话切片以覆盖 SystemCallArchitectures 可以部分禁用限制。

启用更多单元统计信息

systemd 默认不跟踪单元的所有资源使用情况。启用 Default*Accounting 以在 systemctl status 输出和 journal 中获得更多统计信息。这严格来说不是一个安全设置,但它肯定会使调试更容易,并可以提供有用的资源使用情况洞察。

/etc/systemd/system.conf
DefaultCPUAccounting=yes
DefaultIOAccounting=yes
DefaultIPAccounting=yes
DefaultMemoryAccounting=yes
DefaultTasksAccounting=yes