如果我的应用程序可以从一开始就在较低级别运行,为什么我要使用 chroot 进行沙箱化以确保安全?

mur*_*mur 15 chroot daemon

我正在用 C 编写一个 HTTP 服务器守护进程(有原因),用 systemd 单元文件管理它。

我正在重写一个 20 年前设计的应用程序,大约在 1995 年。他们使用的系统是 chroot,然后是 setuid,以及标准程序。

现在在我之前的工作中,通常的策略是永远不要以 root 身份运行任何进程。您为它创建一个用户/组并从那里运行。当然,系统确实以root身份运行了一些东西,但是我们可以不以root身份完成所有业务逻辑处理。

现在对于 HTTP 守护进程,如果我不在应用程序中 chroot,我可以在没有 root 的情况下运行它。那么应用程序永远不会以 root 身份运行不是更安全吗?

从一开始就以 mydaemon-user 身份运行它不是更安全吗?不是用 root 启动它,chrooting,然后 setuid 到 mydaemon-user?

Jde*_*eBP 29

似乎其他人已经错过了你的观点,这不是为什么使用改变的根的原因,当然你已经清楚地知道,也不是你可以做些什么来限制守护进程,当你也清楚地知道在非特权用户帐户;但是为什么要在应用程序内部做这些事情。实际上有一个相当恰当的例子来说明原因。

考虑一下httpdDaniel J. Bernstein 的 publicfile 包中守护程序的设计。它所做的第一件事是将 root 更改为使用命令参数告诉它使用的根目录,然后将特权删除到在两个环境变量中传递的非特权用户 ID 和组 ID。

Dæmon 管理工具集有专门的工具,用于更改根目录和删除非特权用户和组 ID 等。Gerrit Pape 的 runit 有chpst. 我的 nosh 工具集有chrootsetuidgid-fromenv。Laurent Bercot 的 s6 有s6-chroots6-setuidgid。韦恩马歇尔的 Perp 有runtoolrunuid。等等。事实上,他们都有 M. Bernstein 自己的 daemontools 工具集setuidgid作为前提。

人们会认为可以从httpd这些专用工具中提取功能并使用它们。然后,如您所想,服务器程序的任何部分都不会以超级用户权限运行。

问题是,作为直接后果,必须做更多的工作来设置更改的根,这会暴露新的问题。

就伯恩斯坦而言httpd,根目录树中唯一的文件和目录是要向全世界发布的文件和目录。有没有别的树都没有。此外,该树中没有任何可执行程序映像文件存在的理由。

但移动的根目录改变了成链加载程序(或systemd),突然在节目图像文件httpd,任何共享库,它的负荷,并且在任何特殊的文件/etc/run以及/dev该程序加载或C运行时库访问在程序初始化(你可能会发现相当令人吃惊,如果你truss/ straceC或C ++程序),必须存在在改变根。否则httpd无法链接并且不会加载/运行。

请记住,这是一个 HTTP(S) 内容服务器。它可以潜在地提供更改后的根目录中的任何(世界可读)文件。这现在包括您的共享库、程序加载器以及操作系统的各种加载器/CRTL 配置文件的副本。如果某些(意外)意味着内容服务器可以访问写入内容,则受感染的服务器可能会为httpd自己获得对程序映像的写入访问权限,甚至是您系统的程序加载器。(请记住,你现在有两个平行套/usr/lib/etc/run,和/dev目录,以保持安全。)

这都不是httpd更改 root 并删除权限本身的情况。

因此,您交易了少量特权代码,这些代码相当容易审核,并且在httpd程序开始时运行,以超级用户权限运行;因为在更改的根目录中具有极大扩展的文件和目录攻击面。

这就是为什么它不像在服务程序外部做所有事情那么简单。

请注意,这仍然是httpd其自身功能的最低限度。执行诸如在操作系统的帐户数据库中查找用户 ID 和组 ID 以首先放入这些环境变量之类的所有代码都是httpd程序外部的,在简单的独立可审计命令中,例如envuidgid. (当然,它是一个 UCSPI 工具,因此它不包含任何用于侦听相关 TCP 端口或接受连接的代码,这些是诸如tcpservertcp-socket-listentcp-socket-accepts6-tcpserver4-socketbinder、等命令的域s6-tcpserver4d。)

进一步阅读


sou*_*edi 7

我认为您问题的许多细节同样适用于avahi-daemon我最近查看的 。(我可能错过了另一个不同的细节)。在 chroot 中运行 avahi-daemon 有很多优点,以防 avahi-daemon 受到威胁。这些包括:

  1. 它无法读取任何用户的主目录并泄露私人信息。
  2. 它不能通过写入 /tmp 来利用其他程序中的错误。至少有一类这样的错误。例如https://www.google.co.uk/search?q=tmp+race+security+bug
  3. 它无法打开 chroot 之外的任何 unix 套接字文件,其他守护程序可能正在侦听和读取消息。

当您使用 dbus 或类似工具时,第 3 点可能特别好......我认为 avahi-daemon 使用 dbus,因此它确保即使从 chroot 内部也能访问系统 dbus。如果您不需要在系统 dbus 上发送消息的能力,那么拒绝该能力可能是一个很好的安全功能。

使用 systemd 单元文件管理它

请注意,如果 avahi-daemon 被重写,它可能会选择依赖 systemd 来确保安全,并使用例如ProtectHome. 我提议对 avahi-daemon 进行更改,以将这些保护添加为一个额外的层,以及一些 chroot 无法保证的额外保护。您可以在此处查看我提出的完整选项列表:

https://github.com/lathiat/avahi/pull/181/commits/67a7b10049c58d6afeebdc64ffd2023c5a93d49a

如果 avahi-daemon使用 chroot 本身,我似乎可以使用更多限制,其中一些在提交消息中提到。我不确定这在多大程度上适用。

请注意,我使用的保护措施不会限制守护进程打开 unix 套接字文件(上面的第 3 点)。

另一种方法是使用 SELinux。但是,您可能会将您的应用程序绑定到 Linux 发行版的子集。我在这里积极考虑 SELinux 的原因是 SELinux 以细粒度的方式限制了进程对 dbus 的访问。例如,我认为您通常会期望它systemd不会出现在您需要能够向其发送消息的总线名称列表中:-)。

“我想知道,是否使用 systemd 沙箱比 chroot/setuid/umask/... 更安全”

总结:为什么不是两者兼而有之?让我们稍微解码一下上面的内容:-)。

如果您考虑第 3 点,使用 chroot 会提供更多限制。ProtectHome= 及其朋友甚至不会尝试像 chroot 那样严格。(例如,没有命名的 systemd 选项 blacklists /run,我们倾向于将 unix 套接字文件放在那里)。

chroot 表明限制文件系统访问可能非常强大,但并非Linux 上的所有内容都是文件:-)。有一些 systemd 选项可以限制其他东西,而不是文件。如果程序受到威胁,这很有用,您可以减少可用的内核功能,它可能会尝试利用其中的漏洞。例如 avahi-daemon 不需要蓝牙套接字,我猜您的 Web 服务器也不需要:-)。所以不要让它访问 AF_BLUETOOTH 地址族。只需使用该RestrictAddressFamilies=选项将AF_INET、AF_INET6 和 AF_UNIX 列入白名单。

请阅读您使用的每个选项的文档。有些选项与其他选项结合使用会更有效,而有些选项并非在所有 CPU 架构上都可用。(不是因为 CPU 不好,而是因为该 CPU 的 Linux 端口设计得不够好。我认为)。

(这里有一个一般原则。如果你能写出你想要允许的列表,而不是你想要拒绝的列表,那就更安全了。就像定义一个 chroot 给你一个你被允许访问的文件列表一样,这更健壮而不是说你想阻止/home)。

原则上,您可以在 setuid() 之前自己应用所有相同的限制。这只是您可以从 systemd 复制的代码。但是,systemd 单元选项应该更容易编写,并且由于它们采用标准格式,因此应该更易于阅读和查看。

因此,我强烈建议您阅读man systemd.exec目标平台上的沙盒部分。但是,如果你想要最安全的设计成为可能,我不会害怕尝试chroot(再落root权限)在你的程序,以及。这里有一个权衡。使用chroot会对您的整体设计施加一些限制。如果您已经有一个使用 chroot 的设计,并且它似乎可以满足您的需求,那听起来很棒。