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


14

我正在用C语言编写HTTP服务器守护程序(有原因),并使用systemd单元文件对其进行管理。

我正在重写一个20年前设计的应用程序,大约在1995年。他们使用的系统是先创建chroot,然后设置setuid,再执行标准过程。

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

现在,对于HTTP守护程序,如果不在应用程序内部chroot,则可以在没有root的情况下运行它。那么,永远不要以root用户身份运行应用程序是否更安全?

从一开始就以mydaemon-user身份运行它是否更安全?不用以root身份启动,chroot,然后将setuid设置为mydaemon-user?


3
您需要以root用户身份运行才能使用端口80或443。否则,您可以执行tomcat和其他webapp / web-server软件所做的操作,并在更高的端口(例如8080、9090等)上运行,然后使用apache / nginx将代理连接到Web服务器软件,或使用系统防火墙将流量从NAT NAT /转发到端口80。如果不需要端口80或443,或者可以代理或转发连接,那么您就无需以root用户身份,在chroot中或其他方式运行。
SnakeDoc '18

3
Linux上的@SnakeDoc不再正确。多亏了capabilities(7)
0xC0000022L18年

@SnakeDoc可以使用authbind以及
阿卜杜勒·阿哈德

Answers:


27

似乎其他人已经错失了你的观点,这不是为什么要使用更改的根源(当然您已经很清楚地知道了这一点),或者当您也清楚地知道在以下情况下运行时,可以采取什么措施来限制恶魔?非特权用户帐户;但是为什么要在应用程序内部执行此操作。实际上,有一个相当重要的例子。

考虑httpdDaniel J. Bernstein的publicfile包中dæmon程序的设计。它要做的第一件事是将root更改为通过命令参数被告知要使用的根目录,然后将特权授予在两个环境变量中传递的非特权用户ID和组ID。

Dæmon管理工具集具有专用工具,可用于更改根目录以及删除非特权用户和组ID。杰里特·帕佩(Gerrit Pape)的特权chpst。我的nosh工具集具有chrootsetuidgid-fromenv。洛朗·贝科特(Laurent Bercot)的s6具有s6-chroots6-setuidgid。韦恩·马歇尔的《佩珀》有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更改根目录并放弃特权本身的情况下,都不存在这种情况。

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

这就是为什么它不像在服务程序外部执行所有操作那样简单。

请注意,这仍然是其内部功能的最低限度httpd。所有这一切做的事情,如看在用户ID和组ID投入摆在首位的环境变量操作系统的帐户数据库的代码外部httpd程序,在简单的独立审计的命令,如envuidgid。(当然它是一个UCSPI工具,所以它不含有的代码的相关TCP端口(一个或多个)或接受连接上听,它们是如命令的域tcpservertcp-socket-listentcp-socket-accepts6-tcpserver4-socketbinders6-tcpserver4d,等。)

进一步阅读


+1,有罪。我发现标题和最后一段含糊不清,如果您是对的,我会漏掉这一点。这个答案给出了非常实用的解释。我个人会明确指出,必须像这样构建chroot环境是一项额外的工作,大多数人都希望避免这样做。但是这里的两个安全点已经做好了。
sourcejedi

要记住的另一点是,如果服务器在处理任何网络流量之前就放弃了特权,那么特权代码就不会暴露给任何远程利用。
kasperd

5

我认为您问题的许多细节都可以同样适用于avahi-daemon,这是我最近查看的。(我可能已经错过了另一个不同的细节)。万一avahi-daemon被破坏,在chroot中运行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选项黑名单/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复制的代码。但是,系统单位选项应该明显更容易编写,并且由于它们是标准格式,因此应该更易于阅读和查看。

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


+1,特别是针对系统建议。
mattdm

我从您的答案中学到了很多,如果堆栈溢出允许多个答案,我也将接受您的答案。我想知道,如果使用systemd沙箱比更安全的chroot /的setuid /的umask / ...
MUR

@mur很高兴您喜欢它:)。这是对我的回答很自然的回答。因此,我再次对其进行了更新,以尝试回答您的问题。
sourcejedi

1

如果可以依靠systemd,那么将沙箱留给systemd确实更安全(也更简单!)。(当然,应用程序还可以检测它是否已由systemd沙箱启动,如果沙箱仍是root,则沙箱本身也可以。)您描述的服务的等效项是:

[Service]
ExecStart=/usr/local/bin/mydaemon
User=mydaemon-user
RootDirectory=...

但是我们不必到此为止。systemd还可以为您完成许多其他沙箱操作-以下是一些示例:

[Service]
# allocate separate /tmp and /var/tmp for the service
PrivateTmp=yes
# mount / (except for some subdirectories) read-only
ProtectSystem=strict
# empty /home, /root
ProtectHome=yes
# disable setuid and other privilege escalation mechanisms
NoNewPrivileges=yes
# separate network namespace with only loopback device
PrivateNetwork=yes
# only unix domain sockets (no inet, inet6, netlink, …)
RestrictAddressFamilies=AF_UNIX

请参阅man 5 systemd.exec以获取更多指令和更详细的描述。如果使守护程序套接字可激活(man 5 systemd.socket),甚至可以使用与网络相关的选项:服务与外界的唯一链接将是它从systemd接收到的网络套接字,它将无法连接到其他任何东西。如果它是只侦听某些端口并且不需要连接到其他服务器的简单服务器,则这可能会很有用。(RootDirectory在我看来,与文件系统相关的选项也可能过时,因此也许您无需再麻烦地设置带有所有必需的二进制文件和库的新根目录。)

较新的systemd版本(自v232起)也支持DynamicUser=yes,其中systemd仅在服务运行时自动为您分配服务用户。这意味着您无需注册该服务的永久用户,并且只要该服务未写入除,和(您还可以在单​​元文件中声明的文件系统)之外的任何文件系统位置StateDirectory,它就可以正常工作。再次参见,以及由哪个systemd管理,并小心地将它们正确分配给动态用户。LogsDirectoryCacheDirectoryman 5 systemd.exec

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.