我正在用 C 编写一个 HTTP 服务器守护进程(有原因),用 systemd 单元文件管理它。
我正在重写一个 20 年前设计的应用程序,大约在 1995 年。他们使用的系统是 chroot,然后是 setuid,以及标准程序。
现在在我之前的工作中,通常的策略是永远不要以 root 身份运行任何进程。您为它创建一个用户/组并从那里运行。当然,系统确实以root身份运行了一些东西,但是我们可以不以root身份完成所有业务逻辑处理。
现在对于 HTTP 守护进程,如果我不在应用程序中 chroot,我可以在没有 root 的情况下运行它。那么应用程序永远不会以 root 身份运行不是更安全吗?
从一开始就以 mydaemon-user 身份运行它不是更安全吗?不是用 root 启动它,chrooting,然后 setuid 到 mydaemon-user?
Jde*_*eBP 29
似乎其他人已经错过了你的观点,这不是为什么使用改变的根的原因,当然你已经清楚地知道,也不是你可以做些什么来限制守护进程,当你也清楚地知道在非特权用户帐户;但是为什么要在应用程序内部做这些事情。实际上有一个相当恰当的例子来说明原因。
考虑一下httpd
Daniel J. Bernstein 的 publicfile 包中守护程序的设计。它所做的第一件事是将 root 更改为使用命令参数告诉它使用的根目录,然后将特权删除到在两个环境变量中传递的非特权用户 ID 和组 ID。
Dæmon 管理工具集有专门的工具,用于更改根目录和删除非特权用户和组 ID 等。Gerrit Pape 的 runit 有chpst
. 我的 nosh 工具集有chroot
和setuidgid-fromenv
。Laurent Bercot 的 s6 有s6-chroot
和s6-setuidgid
。韦恩马歇尔的 Perp 有runtool
和runuid
。等等。事实上,他们都有 M. Bernstein 自己的 daemontools 工具集setuidgid
作为前提。
人们会认为可以从httpd
这些专用工具中提取功能并使用它们。然后,如您所想,服务器程序的任何部分都不会以超级用户权限运行。
问题是,作为直接后果,必须做更多的工作来设置更改的根,这会暴露新的问题。
就伯恩斯坦而言httpd
,根目录树中唯一的文件和目录是要向全世界发布的文件和目录。有没有别的树都没有。此外,该树中没有任何可执行程序映像文件存在的理由。
但移动的根目录改变了成链加载程序(或systemd),突然在节目图像文件httpd
,任何共享库,它的负荷,并且在任何特殊的文件/etc
,/run
以及/dev
该程序加载或C运行时库访问在程序初始化(你可能会发现相当令人吃惊,如果你truss
/ strace
C或C ++程序),也必须存在在改变根。否则httpd
无法链接并且不会加载/运行。
请记住,这是一个 HTTP(S) 内容服务器。它可以潜在地提供更改后的根目录中的任何(世界可读)文件。这现在包括您的共享库、程序加载器以及操作系统的各种加载器/CRTL 配置文件的副本。如果某些(意外)意味着内容服务器可以访问写入内容,则受感染的服务器可能会为httpd
自己获得对程序映像的写入访问权限,甚至是您系统的程序加载器。(请记住,你现在有两个平行套/usr
,/lib
,/etc
,/run
,和/dev
目录,以保持安全。)
这都不是httpd
更改 root 并删除权限本身的情况。
因此,您交易了少量特权代码,这些代码相当容易审核,并且在httpd
程序开始时运行,以超级用户权限运行;因为在更改的根目录中具有极大扩展的文件和目录攻击面。
这就是为什么它不像在服务程序外部做所有事情那么简单。
请注意,这仍然是httpd
其自身功能的最低限度。执行诸如在操作系统的帐户数据库中查找用户 ID 和组 ID 以首先放入这些环境变量之类的所有代码都是httpd
程序外部的,在简单的独立可审计命令中,例如envuidgid
. (当然,它是一个 UCSPI 工具,因此它不包含任何用于侦听相关 TCP 端口或接受连接的代码,这些是诸如tcpserver
、tcp-socket-listen
、tcp-socket-accept
、s6-tcpserver4-socketbinder
、等命令的域s6-tcpserver4d
。)
httpd
. 公用档案。cr.yp.to.httpd
. Daniel J. Bernstein 的软件集于一身。软件。乔纳森·德·博因·波拉德。2016 年。gopherd
. Daniel J. Bernstein 的软件集于一身。软件。乔纳森·德·博因·波拉德。2017 年。我认为您问题的许多细节同样适用于avahi-daemon
我最近查看的 。(我可能错过了另一个不同的细节)。在 chroot 中运行 avahi-daemon 有很多优点,以防 avahi-daemon 受到威胁。这些包括:
当您不使用 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
不会出现在您需要能够向其发送消息的总线名称列表中:-)。
总结:为什么不是两者兼而有之?让我们稍微解码一下上面的内容:-)。
如果您考虑第 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 的设计,并且它似乎可以满足您的需求,那听起来很棒。
归档时间: |
|
查看次数: |
1783 次 |
最近记录: |