Skip to content

Latest commit

 

History

History
117 lines (47 loc) · 6.8 KB

关于特权容器的一些事.md

File metadata and controls

117 lines (47 loc) · 6.8 KB

前言

凌晨一点睡不着,在企微上和我同事说:“我在学习,我要卷死你们”,便有了这个随笔,也正好探讨一下最近遇到的问题。

最近负责公司云安全相关的面试,在云原生相关的问题里,我最常问的问题之一就是让面试者们讲讲容器逃逸相关的东西,他们往往都能说的头头是道,一些攻击手法,一些漏洞都能讲得出来,其中在他们口中出现频率最高的就是特权容器这个词。

但是当我去问面试者特权容器和普通容器的区别是什么?或者什么是特权容器?时候,结果就不怎么令人满意了。

不少面试者都会提到privileged=true这个配置,少数略有研究的面试者会提到capability,但是更多的面试者却仅能支支吾吾的说出特权容器root权限,所以能执行很多root的指令这样的回答出来,若是我再去追问登入普通的容器中的root特权容器root有什么区别时候,就只能摇摇头表明确实没有在意这个东西。

那这个问题的答案就是capability的区别吗?其实并非如此,但是从一个面试官的角度,却可以姑且将其算是一个回答。

要说这个问题的答案究竟是什么,可能得先说一下我为什么要问这些问题。

容器的现状

在现如今公司都在大规模上k8s做快发布的背景下,容器安全是一个优先级被提的很高的事情,而其中关于特权容器更是重中之重。不管是有没有认真的研究过容器,研究过k8s,都能或多或少知道只要生产环境中存在着特权容器,那就等于是埋了雷,指不定哪天就会炸了,因此就有了一个简单的安全公式出来:

  • 特权容器 == 不安全

在这样的认知下,不少安全的规章制度,甚至是业务方本身都会刻意地避免在生产环境中使用到特权容器。甚至是云原生社区本身都在尝试推动rootless的容器,但是当业务实际跑起来,真的能够避免特权容器的出现吗?

当然若是你连特权容器都搞不明白,又怎么去搞明白rootless容器

很显然,答案是不能的,因为特权容器是为程序释放枷锁的最高效的方式,请记住这句话,因为在我看来,解除枷锁才是特权容器的本质。

现在市面上有不少的安全产品,比如PA的twistlock,比如青藤云的蜂巢,比如火山云的容器防护平台,将他们部署在业务域中,包准能够扫出一堆的特权容器出来,但是当你实际去排查时候就会发现,一部分是k8s自身的组件,一部分是基础架构或者运维们引入的云原生的组件,一部分是内部开发的用于管理集群的一些容器,最终仅剩下可怜的一丢丢是业务容器

就只是这些一丢丢去整改时候,都会遇到业务反馈:非特权容器程序就无法跑起来,那很现实的问题就来了,是业务让步还是安全让步?

其实这个问题并没有那么两极分化,因为在实际场景中遇到这样的事情,最后往往都是通过诸如确认网络隔离,验证程序本身没有安全风险这样的方式来达成虽然这是一个特权容器,但是无法被利用,因此并不存在安全风险

这样的做法当然好,业务不用做什么大的变更,安全只需要配一个白名单,一个原本被视作洪水猛兽的问题就如此轻松愉快的被解决了,高效,简洁。

其实连我自己都是这么处理的,笑 : )

但是特权容器还是特权容器,他还是静静地跑在生产环境里,直到有一天被下掉,或是炸掉。

大家心里清楚,特权容器总归还是要被解决的,这些人里包括我,包括底层架构,包括运维,包括提供安全产品的乙方。

那么在这个尝试解决的过程里,就会有聪明人尝试去研究特权容器,虽不够那么深入,却能提出看似合理的解决方案出来,比如基础架构给业务提供了自由编辑capability的能力。

不清楚如何编辑的业务还是乖乖的要求特权容器,乖乖的网络隔离,乖乖的进行安全验证,而就碰巧有那么一些懂一些的业务,就选择了全新功能,一个看似不是特权容器的容器就顺利跑起来了。

这样的容器需要被安全关注吗?关注的理由又是什么?

那更多其他的解决方案,又需要安全去关注什么?

当探求到这一步的时候,其实你就已经能回答面试时候我提出的那个问题了。

分割线

看到这就行了,因为下面的内容就有些枯燥了


特权容器

好,回到特权容器本身这个话题来,特权容器到底特殊在什么地方,为什么一个普通容器跑不起来的业务,在特权容器里就能跑起来。

要想听明白这个,其实需要提前知道不少东西,比如容器怎么创建,linux的权限逻辑,一些安全机制等等等等。

可惜我并不想在这长篇大论这些,那就以一个略懂的人的视角来看特权容器

前面提到过,面试者在回答特权容器时候,或多或少都会提到一个privileged的参数,用docker作为例子的话就是

docker run --privileged

在常规状态下,容器的最底层实现都是runc,而runc本身是没有特权相关的参数的,反而是有rootless的参数,那么privileged就显然是上层的参数,最后是需要表现在runc的配置当中。

那就很简单了,直接观察一个特权容器非特权的底层配置就不难发现,有关键区别的就只有这些:

  • capabilities
  • mounts
  • devices
  • seccomp

在我这个版本,非特权容器只有14个capabilities,而特权容器则是全部放开的,这个能力是linux下为了限制root的权限,或者说为了部分授权而诞生的机制,无需赘述。

mounts里面最大的区别就是针对一些虚拟文件系统目录的ro权限,因为这儿按照以前的经验都是一些越权的高发地带,所以在常规容器中都增加了只读限制。

devices中的设置决定了容器总能够看到宿主机上的设备的情况,为什么经常就有人说特权容器就能挂载宿主机的目录呢?那是因为在特权容器里,宿主机的设备几乎是全开放的,那只要在容器中挂载设备,就相当于挂载了宿主机的整块硬盘了,那不是想看什么看什么。

最后就是seccomp,这是一个系统调用沙盒,用于限制一个进程的系统调用特权容器中没有该设置,而非特权容器中则是个很长的白名单,限制了调用的情况,甚至在某些系统调用上限制了参数的输入

所以特权容器也就只有这么多的变化