浅谈云原生安全建设

背景

我想总结一下我理解的云原生安全,内容源于我的工作经历、我看到的公开分享、最近面试时被问到的。

kubernetes官方提出”你可以分层去考虑安全性,云原生安全的4个C分别是云(Cloud)、集群(Cluster)、容器(Container)和代码(Code)”,我也是从这个”4c模型”来理解云原生安全的。

kubernetes描述的4c模型 https://kubernetes.io/zh-cn/docs/concepts/security/overview/

下面就向你介绍我用”4c模型”怎么理解云原生安全的,在每一层描述风险和对应的缓解措施。

第一个C:代码(Code)

代码风险有两种,编码和第三方依赖,缓解这两种安全风险也有很多手段。

其中我觉得安全编码最重要的是在web框架提供统一的默认安全能力,就比如说orm框架预编译避免sql注入、react/vue等前端框架能将数据和代码分离来避免xss。通过检查业务代码有没有使用框架提供的不安全api来收敛风险。

第二个C:容器(Container)

kubernetes的命名空间并没有网络隔离的效果,默认情况下pod之间能互相访问、pod能访问宿主机、pod能访问vpc。当pod被getshell、pod服务存在ssrf漏洞时,就能攻击其他服务。

容器网络中也可以arp欺骗,有师傅分享过实战案例,可以见 https://github.com/knownsec/KCon/tree/master/2019/25日/针对Docker容器网络的ARP欺骗与中间人攻击.pdf

镜像中的基础镜像、安装的软件有可能版本比较低,存在历史漏洞。这种风险不一定能变成漏洞利用,提供镜像的服务商可能更关注这类风险。风险更大的场景是镜像中存在研发运维留在镜像中的敏感信息,比如pod中的应用想要和云服务通信时需要有ak/sk来签名或者sts,所以反编译应用或者查看环境变量后能看到ak/sk或者sts。

身份认证

pod中的进程可以用/var/run/secrets/kubernetes.io/serviceaccount/token文件中的服务账号作为集群中的身份,这里的风险是token文件是明文存储的,并且也没有办法确保所有pod中的应用api都实现认证鉴权。

Kubernetes 下零信任安全架构分析 文章中提到蚂蚁k8s集群中api服务认证和授权的设计,在服务网格场景中,通过sidecar、pod label、证书或者jwt来在请求中带上身份信息、通过sidecar校验身份和授权。

对于这个设计我有很多不明白的地方,比如 “在pod里curl其他应用,sidecar也会带上身份信息?这样不就相当于伪造身份了吗”。

## 网络隔离

风险是什么呢?

网络隔离是我认为”投入产出比最高、优先级最高”的一个事情。

默认情况下,pod是可以访问”k8s集群网络”、”宿主机网络”:

  • 可以访问宿主机上的服务
  • 可以访问”宿主机所在网络”的服务
  • 可以访问集群service、pod,并且不受”kubernetes namespace”限制

kubernetes namespace不是内核的namespace,而是”项目”的概念。一个项目应该属于一个 kubernetes namespace。

从pod攻击集群有很多手段,举两个例子。

如果集群部署在云虚机上,在容器中就可以访问特殊网段的”元数据服务”。在18年黑掉DigitalOcean的k8s服务案例中,攻击者通过metadata中的etcd凭证拿下k8s集群。在Shopify的hackerone报告中,攻击者通过ssrf漏洞获取谷歌云metadata中的集群证书信息。

在容器中也可以攻击宿主机上的服务。举一个我以前报告的漏洞为例,通过”容器中挂载宿主机根目录到容器后写入ssh key,然后在容器中连接宿主机ssh”我逃逸到了宿主机。当时我想如果业务方”禁止容器访问宿主机ssh服务”,逃逸过程就会受影响。

挂载宿主机目录后,也可以用static pod、cron服务来做攻击。怎么预防和发现这种利用手段是另外一个话题。

攻击者也可以在容器中对vpc、内网、集群服务做漏洞扫描。更多的kubernetes下的攻击手法和案例可以阅读 neargle大佬的总结

怎么做网络隔离呢?

一个比较常见的网络隔离效果如下:

对pod的限制:

  • pod不能主动访问”宿主机所在的内网”
  • pod不能主动访问宿主机
  • pod不能主动访问虚机metadata
  • pod能主动访问外网
  • 同一个”k8s namespace”下pod网络可以互通,不同”k8s namespace”下pod网络不通

同时,不应该限制宿主机网络:

  • node可以访问master
  • node可以访问外网

kubernetes的NetworkPolicy 并不能完全实现上面要求的网络隔离效果,因为它的默认策略是拒绝,用户只能加白。所以我们还需要借助其他的手段来做网络隔离,比如iptables。

一个简单的例子如下:在宿主机上执行下面的iptables (假设pod网段是10.233.69.0/24)

1
2
3
4
5
6
7
8
9
10
iptables -I OUTPUT -m state --state NEW -s 10.233.69.0/24 -d 192.168.0.0/16 -j DROP   // pod不能主动访问"宿主机所在的内网"
iptables -I OUTPUT -m state --state NEW -s 10.233.69.0/24 -d 10.0.0.0/8 -j DROP
iptables -I OUTPUT -m state --state NEW -s 10.233.69.0/24 -d 172.16.0.0/12 -j DROP

iptables -I OUTPUT -m state --state NEW -s 10.233.69.0/24 -d 169.254.169.254/32 -j DROP // pod不能主动访问metadata(华为云/百度云/amazon/azure)
iptables -I OUTPUT -m state --state NEW -s 10.233.69.0/24 -d 100.100.100.200/32 -j DROP // pod不能主动访问metadata(阿里云)

iptables -I OUTPUT -d 10.233.69.0/24 -j ACCEPT // 容器网络加白

iptables -I OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

利用内核漏洞逃逸的检测和阻断

The Route to Host:从内核提权到容器逃逸 提到多种利用内核漏洞做容器逃逸的手段,比如修改容器进程task_struct数据结构的nsproxy、cred字段,修改命名空间和capability。

有针对类似利用手法做检测的方案,比如 lkrg项目,检查进程cred等字段是否改变。

https://i.blackhat.com/USA-22/Wednesday/US-22-Fournier-Return-To-Sender.pdf paper中提到针对内核漏洞rop利用手法,通过eBPF检查commit_creds(prepare_kernel_cred(0))函数调用栈中是否有来自用户态空间的地址,达到类似smep、smap的效果。

镜像裁剪和运行时监控

https://containerjournal.com/features/sysdig-adds-ability-to-make-container-runtimes-immutable 提到sysdig产品提供了一个能力,能让”容器只运行白名单程序”。

https://github.com/falcosecurity/falco/blob/35db0b4a24344bc6c24022555f7d8531ad925136/rules/falco_rules.yaml#L3018 可以看到规则。

包括两种告警策略:

  • 发现chmod时
  • 发现open创建的文件有执行权限时

如果一个镜像裁剪到只留下必要的可执行文件,再加上面的运行时监控,就能削减很大的攻击面、容易发现威胁。

第三个C:集群

蚂蚁集团针对 K8s 中 Secret 安全防护的实践与探索 中提到针对secret对象实施的数据安全方案,相比于只在etcd中加密secret,还保护了apiserver内存中没有secret。个人感觉大部分公司没有需求和动力做这个建设。

https://github.com/knownsec/KCon/blob/master/2021/kubernetes中的异常活动检测.pdf paper中提到通过kubernetes的审计机制、创建蜜罐账号来发现集群中的攻击行为。

总结

总结了code、container、cluster三层的风险和缓解措施,对于其中我自己觉得比较重要或者有意思的部分内容做了多一点说明。

kubernetes攻防矩阵 也是一个做云原生安全检测产品、云安全建设值得参考的模型。