本文主要是介绍LWN: 让container更加安全,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
点击上方蓝色“Linux News搬运工”关注我们~
Making containers safer
By Jake Edge
LSS-NA
在Linux Security Summit North America (LSS-NA)会议的第一天,Stéphane Graber和Christian Brauner做了一个联合演讲,介绍了container security (容器安全)的现状与未来。他们俩都在Canonical负责LXD项目相关的研发。Graber是项目领导人,Brauner是维护者(maintainer)。他们研究了kernel里面多种可以用来让container更加安全的技术,据此给出了一些建议。
Caring about container safety
Graber首先问道:“为什么我们会关注容器安全?” 他认为并不是所有人都关心的,不过Linux container project(包含LXD和LXC)已经在container领域钻研超过10年时间了。LXC和LXD用来创建“system containers”,可以运行各种无需专门修改的Linux发行版镜像,这一点同docker不一样,docker创建的是"application containers"。它的目的是让LXD用户在运行Linux发行版的时候就像是在一个虚拟机(VM)里面一样,也就是说,根本意识不到他们其实运行在container里。
system container的管理员通常会给用户SSH 访问container里的系统的权限,这些用户就可以自己在Linux里面做任意动作了。这就是为什么这个项目会很在乎安全。他们已经使用了所有能想到的技巧来让container更加安全,包括:namespaces,cgroup,seccomp filter,Linux security modules (LSMs),等等等等。目标就是能让这些container运行起来就像是个VM虚拟机。
因为这个项目是针对system container的,所以每天会针对18种Linux发行版编译出总共77种映像文件。其中也包括一些不太流行的发行版。还会编译Android映像文件。除此之外,LXD也包含在了Chromebook上Chrome OS里Linux桌面的一部分。Chrome OS里面每个用户都有一个单独的VM,Linux桌面发行版就运行在一个container里面,包含一些外部存储空间。得益于GPU passthrough等功能,桌面可以跟Chrome OS无缝集成起来。
无论使用上述哪种发行版,用户都可以在这些container里面执行任意代码。因此Linux container需要非常关注安全方面。
Privileged versus unprivileged
目前container可以分为两大类,一类是privileged,一类是unprivileged,不过Linux kernel并不会意识到有container这么个概念,container只是一个纯粹的user-space的、利用kernel提供的机制构建出来的东西。Privileged containers是指这样的一类container:container内部的root跟container外部的root是同一个用户(也就是UID都是0)。对unprivileged container来说则不同,container内部的UID 0用户其实是被映射到了container外部的某个unprivileged用户,这是通过user namespace来实现的。
可惜的是,今时今日在用的container,大多数都是privileged container。包括绝大多数Docker container以及Kubernetes里面运行的大多数container。这样做的主要问题是,如果攻击者能有办法攻破container的限制能触碰到host系统的话,它就会拥有host系统的root权限,这样整个系统就全部沦陷了。因为container的安全性很依赖于LSM,Linux capabilities,以及seccomp filter。而privileged container的用户权限没有隔离好,因此各种安全机制都很有可能会不保险。
LXD项目的假设就是privileged container永远无法做到安全,要想获得安全性,就不要使用privileged container。虽然LXD实现中想尽办法去堵住它所意识到的各种漏洞,不过仍然强烈不建议使用privileged container。
对于unprivileged container就好多了,因为container内部无法访问host系统上的UID 0用户,万一破出container了,虽然是一个重大风险,却比privileged container场景好得多,不会造成那么严重的破坏。并且还有一种工作模式,系统中各个LXD container都使用了host系统上互不重复的UID和GID范围,这样危害就更加有限了,哪怕破出了container,也只是拥有自己特有的UID和GID权限,而无权干涉其他container进程和host系统。
User namespace是kernel 3.12开始引入的功能,不过很少有其他container管理系统(CMS)会利用这个功能来对container做隔离。其中主要原因就是如果用了UID mapping之后,在各个container之间互相共享文件就会变得很麻烦。LXD则在Ubuntu系统上使用shiftfs来做各个container之间的UID转换,从而能实现大家共享host文件系统的一部分的功能。shiftfs暂时还没有合入upstream,希望不久之后mount API能拥有类似的功能。
The perils(严重危害) of privileges
接下来,Brauner接替了Graber继续进行演讲,首先他也问了一个问题:“privileged container真的那么不安全吗?”。他的回答是“绝对是的”。他列举了六七个“很严重”的CVE报告,是过去几年里面privileged container深受其害的。包括CVE-2019-5736,是二月份暴露的runc container-confinment破出(breakout)问题,为今年的container security领域开了个坏头。据他所知,所有这些CVE都无法攻破LXD创建的unprivileged container。一般人总以为把现有的安全机制都用上是件很容易的事情,不过其实不是。经常有一些攻击是可以用安全机制来阻止的,但是container管理员都因为这样那样的原因没有使用这些防护。有一些时候是因为安全机制的文档不完善,这是kernel开发者应该改进的地方。
首先以namespaces来说,他认为没有得到广泛使用。在application container世界里,namespace用的太少了,通常只用了mount namespace。其实所有各种namespace都是有安全防护价值的,能隔离一些资源避免受到系统其他部分被攻破之后的影响。最有用的namespace就是user namespace,可以把各个container的权限(privileges)都分隔开。
Namespace的API确实不太好用。kernel开发者应该想办法再改善一下。能在container启动的时候创建好各种namespace很有必要,除此之外没有什么办法能对把进程通过setns安全的(保持原子操作特性)绑定到所有namespace里去。Brauner说他有一些想法可以改善这部分。
接下来是seccomp filter,这是对privileged container来说必备的保护。比如,如果privileged container有全县能调用open_by_handle_at(),就相当于马上攻破了系统,拿到了本不该访问到的信息。Seccomp filter对unprivileged container来说也相当于增加了一层防护网,不过并不是必须的。因为通常来说unprivileged container都会维护一个system call黑名单,避免调用这些syscall。而privileged container则应该是创建一个白名单,只允许少数的安全系统调用。
LSM对于privileged container来说也是必须的。在procfs和sysfs里面不应该允许所有文件都能被访问,否则container可能会被攻破。container管理者最常用到的LSM就是SELinux和AppArmor。不过其他还有些次要一点的LSM(因为LSM支持叠加)也应该加进来一起保护系统。
Recent and future features
Brauner描述了一些近期加入kernel的安全功能,以及后续大家期望能加入的功能。Linux 5.0里面加入了让user space来做seccomp filter决策的功能,这样user space就能检查系统调用的参数而不出现race condition,这样像路径名这样的参数也就能审查了。LXD会用这个功能来允许container里的Linux发行版去调用mknod()创建某些设备节点(例如/dev/null),而不允许创建那些对container有安全风险的设备节点。此前要想做到这一点,就需要把host文件系统的安全设备mount进来。
他觉得这里让user space来决定,是一个非常精巧的设计。不过还是有一些问题,例如,这就导致user space去处理系统调用了,从而会引入一些需要小心处理的权限问题。如果系统调用审查之后是合理的,那么就需要在container用户的上下文、用这个用户的权限来执行,而不能用container管理器的权限。
这样,这个功能用起来就有些麻烦了。最好能有个办法告知kernel直接继续完成这个系统调用就好了。还有个问题是某些系统调用会有一些flag参数,例如clone3()这样,因为这些flag不是直接作为参数传递的,而是嵌入在一个结构里面,只把结构指针传递给了系统调用。这样依赖,kernel里的seccomp filter救没法拿到flag的值了,因为seccomp filter只能访问参数本身(在寄存器里)而不能访问指针指向的内容。他给ksummit-discuss mailing list发了一个seccomp的邮件,希望能在September的Kernel Summit开启这个议题,讨论一下这些麻烦的问题,看如何解决。
LXD项目也很希望能把这些主要的LSM(SELinux, AppArmor, 还有Smack)都堆叠起来使用。希望让host和container内部能使用不同LSM方案,例如Android container里面运行的是SELinux,而container可能是运行在Ubuntu系统(使用AppArmor)上的;或者可能要让一个Ubuntu container运行在Fedora(也是使用SELinux的)上。如果能实现就会很方便。
Linux 5.3里面包含了SafeSetID LSM,可以做到只允许白名单中的UID/GID转换。这个功能来自Chrome OS,对privileged container也会很有帮助。
新加的moutn API把mount()这个系统调用分成了好几个独立的调用,这样对于container管理器来说就可以多一些很高级的用法了。例如,现在允许anonymous mount,就是mount的内容没有对应到文件系统的路径上,不过仍然允许拥有这个mount descriptor的进程访问其中的文件。这可以用来做UID/GID shifting(加偏移值),从而不用再使用shiftfs了。
Brauner还提到了pidfd功能(process ID file descriptor)。pidfd是指向某个进程的一个文件描述符,发signal的时候就可以避免发错对象(此前,如果PID被废弃又被另一个进程很快重用了,给PID发signal就会发错对象)的问题。pidfd也能让进程拿到其他进程(不限于自己的子进程)的退出通知。pidfd在LXD里也有应用,后面他期待会有更多pidfd的高级用法。
最后总结到了,Graber认为其他container manager(管理器)可以从LXD的工作中学习这些优势。他认为应该尽快停止使用privileged container,开始使用user namespace,不过其他这些container manager确实需要会针对自己特定情况来决定方案。他的判断是,除非container能把权限隔离做好,否则container不可能做到真的把用户操作的影响局限在container内部。
[I would like to thank LWN's travel sponsor, the Linux Foundation, for funding to travel to San Diego for LSS-NA.]
全文完
LWN文章遵循CC BY-SA 4.0许可协议。
极度欢迎将文章分享到朋友圈
长按下面二维码关注:Linux News搬运工,希望每周的深度文章以及开源社区的各种新近言论,能够让大家满意~
这篇关于LWN: 让container更加安全的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!