第十届全球云计算大会长亭演讲实录:云原生安全的困境与技术实践

阅读量118317

发布时间 : 2022-07-21 15:37:30

Shield Icon of Cyber Security Digital Data, Technology Global Network Digital Data Protection, Future Abstract Background Concept. 3D Rendering

 

随着全社会加快数字化转型的步伐,尤其是云计算成为驱动数字经济发展的重要力量,云原生凭借快速部署、弹性、可扩展性等特性,在越来越多的领域落地应用。然而,云原生在改变云端应用的设计、开发、部署和运行模式的同时,也带来了新的安全要求和挑战。

云原生安全成为了不可忽视的问题:镜像分发带来了供应链安全问题,集群管理使得编排平台本身变成了攻击面,容器逃逸则更让人闻之色变。

在6月30日-7月1日举办的第十届全球云计算大会·中国站“云计算技术与安全论坛”中,长亭科技安全研究员陈靖远,以“云原生安全的困境与技术实践”为题,与参会企业共同分享了云时代中的安全困境与实践。

(以下为演讲内容,PPT可关注长亭技术沙盒后回复“问脉”领取)

 

云原生安全的困境

特征一:更短的生命周期

sysdig 2021年的容器安全报告中指出,49%的容器存活时间不超过五分钟。如果你对安全足够敏感的话,那你就会知道,这意味着攻击者的攻击成本会明显提高。

假设我们现在通过一个 java 反序列化的漏洞,拿到了某个 pod 的权限,可能这个 pod 在短短几分钟之内,因为运维策略而被 K8S 这类编排平台销毁。这个时间对于攻击者来说,甚至都不足以做信息收集。

假设你是攻击者,你会怎么办?很显然,供应链投毒的“性价比”会更高,只要我能往你的基础镜像里面加东西,那我就有非常多的办法进行进一步的这个渗透。

所以,云原生安全的第一个特征,更短的生命周期,而这个更短的生命周期,导致了安全风险相比于传统安全,更彻底的贯穿到整个生命周期。

特征二:复杂的基础设施

类似于 harbor 这种镜像仓库,大家知道市面上有多少种吗。

这里的截图是 CNCF 一部分盖章项目的截图,而这也只是云原生基础设施的冰山一角。

云原生安全的痛点

综合特征一和特征二,不难发现,对于云原生安全建设来说,在广度上相比于传动的主机安全,涉及的面要多得多。

一是因为周期延长,二则是针对不同用途,不同功能的基础设施如雨后春笋般涌出,导致了可能在传统的主机安全领域,我们可能只需要关注进程,文件或者网络,而对于云原生来说,从开发一直到运营阶段,都有不同的风险面,粒度也不尽相同。

举个例子,持续集成 CI/CD 基本上是现在每一个研发都会使用的功能。因此,在 CI/CD 中进行安全检测,也是非常常见的一个功能。

但是市面上的 CI/CD,百花缭乱,常见的比如 Jenkins, Gitlab CI,不常见的则有类似 CircleCI, Bitbucket Pipelines 这种。

虽然说他们在实际的技术实现层面,都大同小异。但是因为各自不同的一些 feature,导致研发仍然需要针对不同的 CI 进行针对性的适配。

如果说 CI/CD 是小问题的话,那容器运行时的适配则是大问题,因为容器运行时相当于容器的底座,这个不仅是针对 CI/CD,针对其他场景也是适用的。

 

解决困境的技术实践

抽象容器运行时

因此这个时候我们萌生了第一个大胆的想法,我们能不能将不同的容器运行时,在研发层面,抽象成同一种容器运行时。

这是什么意思,这意味着,对于不管是 docker 还是 containerd 来说,研发都只需要调用同一个 API 就可以实现自己的功能需求。

举个例子,每一个镜像都有自己的镜像 ID,这个镜像 ID 在不同的容器运行时下都是相对唯一的。因此,对于安全研发来说,他其实不需要关心,这个到底是 A 容器运行时还是 B 容器运行时,他只需要将对应的 镜像 ID 传入函数 OpenImageByID 即可。

如果能做到这一点,很明显有两个好处

第一,研发不需要关心不同容器运行时的适配问题,适配问题由专门的 SDK 解决。

第二,所有代码都只需要写一次,针对不同的 runtime,代码的处理是相同的。

这是我们的想法,但是距离实施还差了很多东西,比如除了容器运行时之外,其他对象也是需要高度抽象的,比如镜像,比如容器。

拓展能力边界

所以我们在刚才的设想上,再继续添加一点想法。

不仅要抽象化容器运行时,还要抽象化镜像、容器。而针对镜像和容器内部,如镜像内部的文件系统,容器内部的进程事件源,则作为更细粒度的组件抽象到镜像或者容器这种云原生对象中。

这里虽然镜像和容器是不同的对象,但是他们所实现的 FileSystem 接口是统一的,这也让安全研发,在针对文件进行操作时,可以屏蔽容器和镜像的区别。

在这套 SDK 的最外层,我们设计了一套 API,但是这套 API 并不直接对外提供使用。原因是我们希望这套 SDK 能够被更多不同的人使用的。

那么不同公司,不同架构,不同业务,技术栈都有可能不一样。这就导致了,假设我们只提供了一个 c 的版本,那可能绝大多数的研发都用不了。

但是相反,我们基于统一的API做不同语言的 binding,那么就解决了业务场景覆盖率的问题。

目前我们开源的 SDK 是做了 golang 和 python 两个版本的 binding。

快速检测镜像中的弱口令

那假设我们现在要根据这套 SDK 快速的检测镜像中的弱口令,应该怎么做呢。

首先,在 Linux 当中,系统账号密码实际都是以哈希的形式存在 /etc/shadow 这个文件当中的。

因此,如果需要检测镜像中的弱口令,那么我们就需要获取到镜像中该文件的内容。

综合PPT前面提到的内容,我们先获取一个抽象化的容器运行时,将它赋值到 client 变量。

调用容器运行时的 open_image_by_id 函数,获取实例化的镜像对象,赋值到 image 变量。

调用镜像的 open 函数,打开 /etc/shadow 文件获取句柄,然后再进行哈希匹配即可检测弱口令。

不难发现,SDK 的出现,极大降低了研发成本,我觉得上面这段代码,只要你稍微有一点计算机基础,就能在很短的时间内掌握编写方法。

集成CI/CD

回到刚才提到的 CI/CD 的场景,再来看看根据之前想法所设计的方案,能不能满足 CI/CD 的需求。

首先针对 CI/CD 来说,它是多阶段的,从 build 到 deploy 。其次则是多实现,就像刚才提到的,可能是 jenkins,可能是 gitlab,也可能是其他。

这两个特性,并不影响我们使用 SDK 对 pipeline 进行检测。真正影响的是第三点,多环境,绝大多数的 CI/CD 都是跑在容器当中的。

因此,如果我们需要真正的集成 CI/CD ,则必须支持以平行容器的模式运行。

平行容器

那首先,什么是平行容器呢。

平行容器,通俗易懂的来讲,就是以为容器的形式启动一个特殊的探针,这个探针可以监控并且检测其他容器。

那平行容器是怎么实现的呢?其实有三个核心点

第一是文件系统,我们需要通过一些特殊的挂载手段,将容器运行时相关的文件系统挂载到这个平行容器当中,比如针对 docker 来说,它需要挂载的则是 /var/lib/docker。注意这里我们需要控制它只读,降低安全风险。

第二则是取消部分 namespace 的限制,为什么呀,因为 namespace 会将网络进程等等的东西隔离开,导致了平行容器没有办法去检测其他容器是否属于一个安全的状态。

第三则是需要提供一个接口让其和容器运行时的服务进行通信,比如说 /var/run/docker.sock。基于这些设置,我们就能以容器的形式启动一个具备安全能力的探针。

那当然,刚才上面所说的一切也是被我们设计到了 SDK 里面,通过 SDK 做检测时,既支持宿主运行的模式,也支持平行容器的模式。

远程仓库扫描的问题

那接下来有一个新问题,针对镜像来说,我们除了需要做本地的扫描之外。我们也需要做远程的仓库镜像扫描。

但是远程扫描,这件事情它说白了,它还是需要将镜像下载到本地进行扫描的。那假设现在我们基于SDK编写了两个工具

一个是针对镜像的漏洞扫描,一个是针对镜像的后门扫描。那假设现在他们都要扫描同一个镜像,来自官方仓库的 nginx:latest。

我们现在有两种做法,一种是分别在漏洞扫描和后门扫描的工具中,增加远程镜像下载的功能,这样的好处是不同工具之间互相独立,坏处则是代码需要重复编写。所以 A 方案我们是否掉的。

另外一种是将两个工具合二为一,好处是只需要下载一次,但是坏处是,随着功能的增多,这个工具的维护成本就会不断提高。

同时如果两个功能都塞到一个可执行文件中,也会变得没有办法热更新。所以 B 方案肯定是不行的。

New Idea:插件系统

于是我们又又又有了一个新的大胆的想法,能不能基于刚才的场景需求,设计一套插件系统出来。首先我们将注意力放到 cli wrapper 上面,这里的 cli wrapper,其实顾名思义,是给类似于 cobra 这样的 go 的 cli 框架做封装。这样做的原因是什么呢,最重要的原因是我们需要固定部分参数的输入和输出,具体固定哪些参数呢?后面会讲。

接下来我们往下走,我们看到拆成了两大块,第一块是 host,第二块也是 plugin。这里的 host,其实就是宿主的意思,而 plugin,则是插件的意思,那按照刚才的场景。对于漏洞扫描这个功能来说,它会抽象成一个插件,而对于远程仓库的认证和交互,则会放到宿主当中。

那么这个时候出现了第一个问题,就是宿主是怎么发现插件的?刚才我们有提到 cli wrapper对吧,这个其实就是帮助了我们去做插件发现。我们通过定义,一个类似于身份识别的命令,能够让宿主快速的判定插件的名称,插件的用途,以及插件的使用方式等。其实就是固定了 manifest 的格式。

那接下来,宿主只需要根据文件目录,遍历对应的二进制文件并进行识别即可。

当然,仅仅是调度是不够的,因为宿主和探针的数据流向是双向的,而不是单相的。举个例子,当我进行完漏洞扫描之后,事件是需要上报的对不对。那这个时候,插件需要怎么上报给宿主呢,这就引出了另一个机制,Service 机制。

大家如果对 grpc 这个东西熟悉的话,那应该会对我们设计的这个 Service 机制比较熟悉,用一句简单的话来概括的话,它其实就是用于进程间通信的 “grpc”。我们在使用宿主的时候,向注册中心注册我们提供的服务,这里的粒度其实是一个 function,函数的粒度。那注册完之后,插件就可以调用我们在宿主注册的函数。这里我举了两个例子,一个是 Log ,我们知道任何程序都会有 Log 的功能,但是换到我们刚才的这个场景里面,我们会发现插件的 Log 宿主并不能收到,因此这个时候让宿主去定义对应的 Log 服务以及函数,插件调用宿主的 Log 函数,这样就能在宿主收到对应的日志,那report其实也是一样的。

那至于这个东西具体是怎么实现的,我就不展开讲了。最后提及一下,其实进程间通信的方式不止一种,那我们在设计这套 SDK 的时候,进程间通信的方式其实是和插件机制解耦,你既可以选择管道,也可以选择 unix domain socket,这样也保证了他能兼容更多不同的业务场景

Easy Plugin

这里还是以 python 为例,我们看一下增加了插件机制后应该怎么使用。

这里我们打算编写一个判定镜像是否存在可执行文件的脚本。

首先我们在最上面,调用 command 这个包的 set_manifest 函数,设置对应的 manifest 信息,这里我们的名称是 hello-plugin,版本是 1.0.0

接下来我们使用了 python 的一个 feature,装饰器,通过这个装饰器,实际上我们的 SDK 会给他包裹一层,那包裹了之后 SDK 会将进程间通信的部分给屏蔽掉。

这样插件不需要主动去实现相关调用 Service 机制的代码,它只需要实现一个很简单的 fucntion,就是 scan(image) ,里面传参是一个 image 对象。

插件的开发者,只需要知道怎么处理这个 image 就好了,其他的都不需要关心。

那再往下走就是实际的处理逻辑了,就是遍历文件判定是否是一个 ELF。那这里的 walk 也是 image 实现了 FileSystem 的接口,这个其实是前面提到的。

所以其实到现在这一步,这个插件已经非常好用了。

基于插件机制实现镜像阻断

那我们实现了插件机制之后,面对场景的灵活性其实也提高了。

举个例子,除了刚才的远程仓库镜像扫描之外,类似于 docker 提供的 authzplugin 的机制,也是可以适配的。

首先,docker 它的这个机制有点像 webhook,只不过它通信的方式是一般是基于本地的 socket。

我们可以看到右边这张图,我们知道平时我们使用 docker 的命令,其实我们操作的是一个 client,也就是客户端。

这个 client,会以 http 的形式,将对应的操作请求发送给 docker daemon,docker daemon 再根据实际的指令进行操作。

比如说你告诉它这是一个 push,那其实 push 执行的对是 docker daemon。

那我们再来看 authzplugin 这个机制,它其实很简单,就是你 client 发给 daemon 的请求,会被 daemon 原封不动的转发给实际的插件,插件会去判断这个请求是否有问题,如果不 ok ,它会告诉 daemon,最终 daemon 不执行这个操作。那我们在拥有了插件机制之后,我们可以非常轻松的,将 authzplugin 的功能集成到我们的宿主当中。

就像左图所示,我们启动了宿主,这个宿主会监听本地套接字的请求,然后在 dockerd 的配置文件中增加设置相关信息。

最后,当我们需要 push 一个 挖矿镜像到仓库的时候, xmrig/xmrig ,请求会转发到我们的监听服务中,监听服务发现这是一个 push 的请求,

于是启动检测的插件,检测本地 xmirg/xmrig 镜像是否有问题,最终在镜像中发现了对应的恶意文件,并且阻断。

所以可以发现,当宿主和插件解耦了之后,我们可以适配很多不同的场景,包括一些镜像仓库的 webhook,持续集成的 CI/CD,都能非常完美的适配。

风险面和安全策略

在 PPT 的最后,分享一点安全策略建设方面的想法。

其实云原生安全,相比于传统安全来说,它的粒度会更丰富。

我举个例子,比如说 NIDS,对于 NIDS 这种设备来说,那它只需要关心流量就够了。对于 RASP 这种防护手段来说,它只需要关心代码就够了。

市场上很多的安全产品,它的对象都是非常收敛的。但是云原生不一样,那这里我给它做了一个归类。分别是 镜像/容器/基础设施/以及 IaC

首先针对镜像来说,其实它的细分粒度主要是文件,那文件,会牵涉到,漏洞扫描,敏感信息,弱口令,恶意文件等等。

接下来则是容器,容器的话也是有文件粒度的,这个文件粒度和镜像是相同的,不同的是什么呢,有这网络,有进程,以及会有相关的容器配置。

那网络更多的是会做一些审计的策略,比如说,有没有外连矿池地址呀,有没有 dns 解析到 一些外部的 dnslog 平台呀。那进程的话,更多的就是命令审计,以及容器逃逸的检测。那容器配置的话,主要检测的是挂载以及权限是否恰当。

那文件/网络/进程, 这个本身也是可以去做行为的学习的。因为对容器来说,它的很多行为都是相对固定的,所以是能够去打造那么一个白名单的基线的。

再让我们看一下左边,分别是 IaC 以及 基础设施,IaC 全称其实是 Infrastructure as Code,基础设施即代码,比如 dockerfile,比如 k8s 的配置文件。

很多时候安全风险其实在这个配置文件里面已经暴露了,比如你把 k8s 一个高权限的 secret 配给了一个普通的 web 服务容器,那这种完全可以在配置文件的维度进行检测。

最后就是基础设施了,基础设施我这里列出了两大块,一个是这个容器运行时,一个则是编排平台。对编排平台来说,也会有针对这个粒度的安全建设,比如一直再说的 K8S 的微隔离。那针对容器运行时和编排平台,我们都能针对它做基线检测。

回馈开源社区——问脉

最后的最后,这个也是大家喜闻乐见的。刚才所说的部分能力,其实已经在 github 上开源出来了,包括 SDK 的部分以及检测工具的部分。

这个开源品牌叫做问脉,寓意“容器安全见筋脉,望闻问切治病害”。希望能够成为云原生安全领域的一剂良方。

扫描二维码,添加问脉小助手。可以加入讨论群进行讨论,只要是在群里讨论云原生相关的话题,我们都非常的支持

本文由长亭科技原创发布

转载,请参考转载声明,注明出处: https://www.anquanke.com/post/id/276332

安全客 - 有思想的安全新媒体

分享到:微信
+10赞
收藏
长亭科技
分享到:微信

发表评论

内容需知
合作单位
  • 安全客
  • 安全客
Copyright © 北京奇虎科技有限公司 三六零数字安全科技集团有限公司 安全客 All Rights Reserved 京ICP备08010314号-66