LWN: 利用eBPF实现的文件系统沙盒

Filesystem sandboxing with eBPF

By  Jake Edge

November 6, 2019


OSS EU


原文来源:https://lwn.net/Articles/803890/

译者注:

  1.  继续热点话题eBPF,同时hardening(加固)也是社区热点

  2. 文中提出的5点标准,以及基于这些标准来分析的现有安全方案优缺点,非常值得专业开发者参考。


我们平时说的sandbox沙盒机制,主要就是希望能以某种比较安全的方式来执行那些无法确认可信度的代码。在Open Source Summit Europe 2019会议上,Georgia Tech的博士生Ashish Bijlani介绍的就是一种沙盒机制。他采用了一种新颖的方式来允许没有特权权限的代码利用BPF实现沙盒策略,这些策略都由kernel来保证执行。


Background


有很多应用场景都需要能在确保不损害系统文件内容的前提下执行一些不确定可信的第三方代码。他举了两个例子,一个是从非官方网站下载的浏览器插件,另一个例子是某人可能希望尝试一下一种新的机器学习代码的时候。所有的代码中,除了确信可靠的代码和确信不可靠的代码以外,还有很多灰色地带,这些灰色地带被统称为unknown, untrusted code(未知的,未确认可靠的代码,后文译者称之为可疑代码)。确信可靠的代码,可以加入白名单,确信不可靠的代码,可以加入黑名单,而针对这些可疑代码,则需要采用沙盒机制运行。通常所说的沙盒,就是指一个独立、可控的执行环境。


Bijlani专注于某一类特定的沙盒:文件系统沙盒。想法是在执行这类可疑程序之时,限制对敏感数据的访问。这些策略必须是可以动态调整的,因为针对正在执行的各种程序,可能需要使用不同的限制策略。比如,我们可能需要限制访问~/.ssh/id_rsa*文件,或者干脆拒绝对某种类型文件的访问(例如只有PDF reader能访问*.pdf)。

LWN: 利用eBPF实现的文件系统沙盒_第1张图片


首先他介绍了一些现有的方案,指出他们的缺陷在哪里,从5个方面对他们进行了比较:允许动态调整策略,可以供非特权用户使用,提供细粒度的控制,满足执行可疑代码时的安全需求,并且避免引入过多性能损失。Unix的discretionary access control (DAC)——也就是文件权限——可以供非特权用户使用,不过绝大多数其他条件都没能满足。更重要的是,它完全不能阻止可疑代码访问正在执行此程序的用户本人拥有的那些文件。SELinux mandatory access control (MAC)满足了大多数的要求,不过没法供非特权用户使用。


Namespace(命名空间,其实就是chroot() )可以用来隔离文件系统或者文件系统内部的一小部分,不过没法设置安全策略。利用LD_PRELOAD来截获对文件系统的调用(例如open()或者write()等)倒是一种让非特权用户也能动态调整策略的方式,不过这种保护方式可以轻易被绕过,系统调用可以直接调用的,并不是必须要走函数库里的代码;还有一些文件是通过mmap()映射出来的,那么就可以不经过系统调用也能对文件进行I/O操作了。ptrace()也是类似的道理,并且它没法解决time-of-check-to-time-of-use(TOCTTOU)的竞态问题,因此这种安全保护也是能被绕过的。


ptrace()还有个问题,就是会导致性能损失(大概50%)。Bijlani最后介绍的Filesystem in Userspace (FUSE)也有类似的性能问题。FUSE文件系统可以满足他列出的所有要求,不过就是会多80%左右的性能损耗。他很希望能找到一个方案,仅仅增加5~10%的损耗。


因此他创建了SandFS。SandFS是一个可以叠加的文件系统,能够允许非特权用户对文件系统的访问设置限制策略。用户可以像下面这样调用:

    $ sandfs -s sandfs.o -d /home/user /bin/bash


sandfs这个程序本身执行不需要特权,可以由任何人调用。上述的例子会执行bash程序,不过对/home/user目录的访问都会受沙盒限制。其中沙盒策略定义在sandfs.o里面,这个sandfs.o是由C语言写成,利用LLVM编译成BPF二进制代码。


他简要介绍了一下BPF是什么以及如何使用。他称BPF为SandFS之所以能实现出来所主要依赖的技术。BPF maps提供了让user space和kernel内部BPF program之间进行通讯的机制,并且在SandFS里面有重要用途。BPF的更多细节请参考LWN 2017年的文章:https://lwn.net/Articles/740157/


Architecture


接下来他介绍了SandFS的架构,包含了好几个组件,例如SandFS daemon守护进程,SandFS的user space函数库。sandfs程序会跟守护进程通讯,而函数库则是供大家自己开发安全策略的时候用的。还有一个改动过的Wrapfs,主要用来截获已经mount的文件系统的操作。kernel里还有一组SandFS BPF handler(处理函数)用来针对SandFS截获的每个文件系统操作进行安全检查,这都是基于Wrapfs文件系统的。


简单来说,sandfs程序会把BPF代码发给守护进程,让它把BPF program加载进kernel。假如BPF verifier检查通过了,那么下一步就是把SandFS给mount到指定的目录上去(上文例子中的/home/user)。任何文件系统操作都会被SandFS截获下来,然后调用到之前加载过得BPF program来进行决策。SandFS本身不会执行I/O操作,仅仅会把过滤过的任何文件操作都传递给底层文件系统(例如ext4或者XFS)去。


具体的策略会从BPF maps而来,而BPF maps是可以由user space来写入更改的,这样就能支持动态调整策略了。从user space传入的BPF program可以会从BPF maps中查找信息,例如路径名,来决定是否允许这次访问。还有可能来改变这次操作的参数(例如让所有的open()操作都变成以只读方式打开)。SandFS会直接利用内核对象来决策,而不是直接使用user space传入的参数,因此就避免了TOCTTOU问题。


在介绍中,他给出了两个例子来解释如何使用BPF program进行访问限制。第一个例子会从BPF map这边查找进行lookup()操作时用到的路径,如果发现map中有这个路径,就直接返回-EACCES,这样就能让user space利用来限制对沙盒里文件的访问。第二个例子则是检查open()调用的mode参数,禁止O_CREAT这种参数,并且把其他参数全都改为O_RDONLY。


接下来他展示了各种文件系统操作的性能数据,对比ext4和SandFS的性能表现。利用4.17内核来创建一个.tar.gz文件这种场景看到的额外耗时最小(4.57%,具体时间是61.05s vs. 63.84s)。解压缩并展开tar文件的动作看到增加的损耗最多(9.75%,5.13s vs 5.63s)。进行kernel编译时(make -j 4 tinyconfig),看到9.28%的额外耗时(27.15s vs. 29.67s)。


SandFS框架可以有多种用法。例如用来限制对SSH秘钥等个人私有信息的访问,也可以用来把像网页浏览器这样的复杂程序的某些操作分隔开来,其中文件和多媒体格式的处理可以放到一个单独的沙盒进程去。还有containter-management system(容器管理系统)也能够叠加多层SandFS检测来确保container内部发起的对外文件系统访问得到安全加固(harden)。


总结时,他指出SandFS代码已经在GitHub上可以下载了,同时他也撰写了一篇学术论文。此外他还建议大家看看他2018年在OSS North America会议上介绍的演示文稿和2018 Linux Plumbers Conference的Youtube录播。


[I would like to thank LWN's travel sponsor, the Linux Foundation, for travel assistance to attend Open Source Summit Europe in Lyon, France.]


全文完

LWN文章遵循CC BY-SA 4.0许可协议。


长按下面二维码关注:Linux News搬运工,希望每周的深度文章以及开源社区的各种新近言论,能够让大家满意~


640?wx_fmt=jpeg

你可能感兴趣的:(LWN: 利用eBPF实现的文件系统沙盒)