点击上方蓝色“Linux News搬运工”关注我们~
By Jonathan Corbet
Kernel内部的BPF虚拟机在过去几年经历了很多修改,与此同时,也有越来越多的kernel子系统开始使用BPF。过去这些patch的其中一个目标就是能允许非特权用户(unprivileged)来加载某些类型的BPF program到kernel,并且不破坏系统的安全性。最近的一次讨论最终明确下来需要放弃这个不可能实现的目标,今后所有这方面的patch都不会被BPF维护者接受。
BPF verifiery已经做了很多工作来尽量确保加载进kernel的BPF program是不会导致安全问题的。包括检查memory的访问,以及模拟执行流程来确保program会在有限时间内结束,等等。其中很多检查都有助于确保program是安全的,能避免某些类型的bug,其他的检查都是专门为了排查恶意program的,如果kernel允许接受非特权用户的BPF program的话,这些检查都是必须的。
大多数这类恶意program的检查工作都是在2015年的4.4 kernel里实现的。尤其是其中很多工作是为了阻止BPF program把kernel指针值泄露给user space。这些指针对攻击者会很有用,因为他们可以被用来推算出某些特定的数据结构或者代码的位置。所以我们一定要避免被非特权的进程拿到这些指针数据。在kernel 4.7里面增加了"Constant blinding",这个机制本质上会对program里面的常量值同一个随机数进行特定的OR操作,这个操作在运行时每次用到这个常量值的时候都会进行一次,可以避免攻击者把BPF代码假扮成常量值来进行攻击。其他还有一些patch是用来避免BPF program里的预测执行(speculative-execution)类型的攻击。
所以这些工作都完成之后,还是有个地方希望非特权用户能加载BPF program的,那就是在网络socket上增加filter。在2015年,BPF维护者Alexei Starovoitov宣布说“我认为现在已经可以让eBPF不再依赖CAP_SYS_ADMIN了”。不过4年过去了,这个目标还没有达到,近期的工作目标已经转为了希望能给系统管理员更多控制选项,来控制谁可以加载BPF program,例如尚未合入的/dev/bpf改动就是一个例子。
一句话来说,Starovoitov已经不再继续为非特权用户添加支持了。其他人还是没有完全放弃。事情是这样的:
Andy Lutomirski最近提出一组patch希望能让BPF更加适合这种用法,主要是实现了对BPF maps的访问权限控制,增加了一个方法能专门标记哪些BPF函数需要特权用户才能访问,以及允许特权用户加载所有类型的BPF program。他提出“这组patch可以让你在test mode下面运行program了,其他情况下不能执行program,所以它肯定是安全的”。这组patch还没有任何review意见。
不过,在一直持续进行的/dev/bpf改动的相关讨论中,Starovoitov说了一句让大家有些意外的话:“非特权用户的bpf其实是应该废弃的一个工作”。然后不出意料,Lutomirski站出来反对这个说法:“不应该啊,现在就有一些setsockopt用户场景有需要,还有今后的seccomp肯定也需要。尤其是container里面运行bpf的话肯定是非特权用户的场景,因为在大多数情况下,host都不信任container容器。”。
Starovoitov的回应是“Linux事实上已经成为了一个单用户系统”,任何人都能运行任何代码从而破出限制并且获取root权限。他认为这整个非特权用户BPF的概念都是一个错误:“我们在提到unprivileged bpf的时候,我们指的其实希望运行各种恶意bpf program而不出问题。这会一直带来各种问题。之前做的constant blinding, randomization, verifier speculative analysis,包括spectre v1, v2, v4修复,都对系统带来了更多负面影响,不值得。这些代码都非常复杂而且没有多少人想用它。我都想不到哪怕一个使用场景,会有人希望能允许恶意bpf program来加载执行。”
Lutomirski回复了几封邮件,他认为确实有这样的使用场景。他提到了seccomp()现在仍然在用"classical BPF"而不是作为近几年开发热点的"extended BPF" (eBPF),有些开发者希望能在seccomp()过滤条件里面加上eBPF的支持。还有per-user systemd instance(针对每个用户使用不同的systemd实例)也是另一个例子,systemd现在也在用BPF,如果BPF能供非特权用户使用的话肯定会更加方便。如果kernel能支持的话,应该会有更多应用场景出现。他认为:“现在没有太多非特权用户使用eBPF,是因为kernel尚未支持。”
Starovoitov明确说明他并没有被说服:“我觉得这些方案都没法达到目的。”他再次重复了他的主张,目前还没有非特权BPF的使用场景。而他期望能看到的,是“less priviledged BPF"(权限略微受限的BPF),假如一个进程拥有了CAP_BPF能力(或者有权访问/dev/bpf文件),从而能够加载BPF program,但是不会拥有其他特权操作权限。这种方案他认为能够增强现有的application的安全性,而不用花功夫来支持他认为根本不存在的那些非特权用户的应用场景。
这里讨论基本上陷入僵局。这个问题的核心,是Linux系统是不是对非特权用户能真正完成加固(harden)。如果答案是“No”,那么就没有多少必要在BPF子系统里面维护那么多目的是加固系统的复杂代码。不过如果接受这种回答的话,就无异于是在说Linux特权模型(privilege model)根本没法达到安全的目的,软件bug和硬件受攻击的风险组合在一起之后总是能破坏Linux特权模型,我们干脆可以放弃了。这个结论肯定是会让大家感到很失望的。
全文完
LWN文章遵循CC BY-SA 4.0许可协议。
极度欢迎将文章分享到朋友圈
长按下面二维码关注:Linux News搬运工,希望每周的深度文章以及开源社区的各种新近言论,能够让大家满意~