LWN: 针对驱动程序设计的防火墙!
关注了就能看到更多这么棒的文章哦~
A firewall for device drivers
By Jonathan Corbet
August 13, 2021
DeepL assisted translation
https://lwn.net/Articles/865918/
设备驱动程序(device drivers)以及它们所控制的硬件,长期以来一直被认为是系统中的完全可以信赖的一部分。不过,近年来这种信任已经被逐步破坏了,而且在某些情况下甚至变得完全不可信了,比如 VM 虚拟机不信任它们所运行的 host 系统。最近报道的 virtio-hardenning 工作就是针对这种情况所采取的一些措施,但它只解决了典型内核配置中内置进来的一小部分驱动。其余的部分该怎么处理呢?来自 Kuppuswamy Sathyanarayanan 的驱动过滤 patch 就展示了一种可能的处理方法:把它们完全禁用掉。
虚拟机通常只能直接访问很少的物理硬件,甚至完全不可以访问物理硬件。相应地,它们会通过主机提供的 emulated 设备来与外界互动。这使得 host 系统处于一种特权地位,因为它完全控制了这些虚拟设备的行为。如果在编写驱动程序时没有考虑到它所管理的设备可能是有恶意的,那么很有可能这个驱动程序就会被利用来入侵 guest 并窃取数据,甚至哪怕客户在运行时使用了 host 一般来说无法访问的加密过的内存数据也有可能出现数据泄露。
上述的 virtio 改动对少数 virtio 驱动进行了加固,防止它们在 host 不按规则出牌时产生错误行为。这方面的工作非常多(但至今仍未达到可以合入 mainline 的程度),而且漏洞是完全有可能真实存在的。即使 virtio 的工作完美完成了也是不够的,毕竟内核包含了成千上万的其他各种驱动程序,其中大多数都没有得到过类似程度的关注和分析。可以猜想得到,这些驱动程序绝大多数都不够强大,无法完全抵御恶意设备发起的攻击。如果 host 可以说服 guest 加载这样一个恶意设备的驱动程序,那么系统的安全就完全无法得到保障了。
针对这个问题,一个可能的解决方案是来逐个检查并加固所有这些当前仍被被忽视的驱动程序。这样做,肯定可以将内核变得更完美,但千万不要指望事情能按照这个方向发展。哪怕能够找到一位开发者来愿意实现这样的开发工作,可是也还有大量代码需要用海量设备来进行测试,其中相当多的设备在很多年前就已经很少有人使用了。因此,现实一点的话,必须接受这样的事实:许多驱动程序永远不会按这个方式来完成加固。
另一种方法是直接让这些驱动程序不可用。毕竟一个完全无法运行的驱动程序不太可能会损害系统了。大多数虚拟机只需要少量的驱动程序,其余的代码都是危险的无价值代码。显而易见,如果编译生成的内核里面只带有它所需要的驱动程序,那么系统不仅变得更加安全了,同时 size 也更小。这个想法有一个问题,那就是发行版提供商很讨厌提供多个采用不同的 kernel config 来编译生成的内核。因为每多提供一种内核,都会增加相应的编译、测试和支持的工作量,而且只需要几个 config 选项的差异就会导致大量的内核映像文件。因此,如果可能的话,发行版提供商会尽力确保只提供单一的内核镜像。
这样一来 Sathyanarayanan 的 patch set 就有用处了。它为系统管理员提供了一种方法来控制哪些驱动程序可以被允许运行。具体来说是增加了两个新的 cmdline 选项(filter_allow_drivers 和 filter_deny_drivers)用来进行这方面的控制,可以使用 "bus:driver" 符号来将指定驱动程序添加到两个 list 中。字符串 "ALL" 可以匹配所有选项。因此,举例来说,在启动一个系统时如果使用下面的 cmdline 参数:
filter_allow_drivers=ALL:ALL
那么就会允许所有驱动程序正常运行,也就是缺省配置。allow list 会首先被使用,并优先于 deny list,所以如果像下面这样来配置的话:
filter_allow_drivers=pci:e1000 filter_deny_drivers=ALL:ALL
就会允许 e1000 网络适配器的驱动程序正常运行,但会阻止其他任何驱动程序。在 sysfs 中还有一个新增的 driver attribute(名为 allowed),也可以用它在运行时来改变一个驱动程序的状态。
驱动程序子系统的维护者 Greg Kroah-Hartman 对这个提案很不以为然。他建议要么编译生成一个特殊的内核镜像,要么使用一些现有的机制来阻止那些不需要的设备驱动程序。比如在系统的 modprobe 配置中拒绝,或者使用 sysfs 中的开关来解除驱动与设备的绑定关系。但正如 Andi Kleen 后来的解释所说,这些机制并不能完全满足要求。配置 modprobe 的话,无法影响内置的驱动程序,而且,无论如何,起初的目的是为了防止不受信任的驱动得以运行。在用户空间可以手动解除某个驱动程序的绑定关系时,它已经在内核中完成了配置,没准可能已经在试图驱动某个恶意设备了。
Kleen 补充说,还可以换一个角度来看待这种情况,那就是把在可能有恶意的 host 上运行的 guest 系统看作是互联网上的服务器。服务器几乎必定会运行一个防火墙程序,将所有访问限制在已知安全(或至少是希望是安全的)的端口上。他开发的 driver filter 就相当于 guest 系统的防火墙。这个方案把加固问题简化一下之后,真正变得可行了。
这些论点是否能说服 Kroah-Hartman 还有待观察。交流在没有得出任何明确结论的情况下就沉默了下来。不过,推动这项工作的出发点似乎是真实的需求。如果当前的解决方案没有被采纳的话,我们很可能会在未来看到其他类似方案的尝试。设备已经不再是隐藏在内核后面而已了,它们已经变成了内核受攻击面(attach surface)的一部分。注重安全的开发者自然希望尽可能地减少这种受攻击面。
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~