cdxy.me
Cyber Security / Data Science / Trading

1 K8s Secrets

K8集群中管理敏感信息的概念叫做Secrets,比如SSH key、云产品AK、OAuth token等信息,会在Secrets存储与管理,在业务运行时由Pod访问Secrets对象进行调用。

比如说一个Pod运行了WEB服务,需要访问云上的数据库服务RDS,其AK就保存在K8s Secrets对象中。

在云原生应用渗透场景中,企业敏感数据分布在各种SaaS服务中,拿到宿主机的root shell只是过程,最终目标是拿下K8s Secrets。

2 Secrets在哪里

K8s master node与worker node的结构如下:

左下角etcd是一个k-v数据库,用于持久化K8s集群数据,其中包含K8s自身user认证的token,用户的Secrets也存在这里。拿下这个数据库,就相当于拿下了K8s集群。

等等,我们的安全知识里,密码不是加密存储的吗?难道Secrets里面的AK不会被加密吗?

  • K8s 1.13版本之后可以对etcd里面的Secrets进行加密,之前(默认)是base64编码(相当于明文)存储的。
  • Secrets包括cluster-admin token,可以直接通过认证接管k8s集群。

因此,拿下etcd=拿下K8s

3 从etcd中窃取Secrets

这里分为两种情况讨论:

  1. 老版本K8s或用户错误配置导致etcd免密登录
  2. 从主机层通过token认证的方式攻击etcd

3.1 免密登录场景

etcd位于K8s master node 对内暴露2379端口。默认配置下本地127.1可免认证访问,其他地址要带--endpoint参数和cert进行认证。

但是,SSRF漏洞或网络转发会扩大攻击面,组合利用或可能导致etcd沦陷。 K8s曾爆出过一个SSRF漏洞:CVE-2020-8555

• https://github.com/kubernetes/kubernetes/issues/91542

通过patch分析可以看出,K8s只是屏蔽了SSRF的回显,并未根除SSRF的利用链。从安全设计角度来讲,这种风险是存在的。

ETCD V2和V3是两套不兼容的API,K8s用V3,通过环境变量设置API V3:

export ETCDCTL_API=3

检查是否正常连接

etcdctl endpoint health 
127.0.0.1:2379 is healthy: successfully committed proposal: took = 939.097µs

查看K8s secrets

etcdctl get / --prefix --keys-only | grep /secrets/

获取集群中保存的云产品AK,横向移动:

etcdctl get /registry/secrets/default/acr-credential-518dfd1883737c2a6bde99ed6fee583c

3.2 认证登录场景

云厂商提供K8s服务存在多种架构,阿里云ACK提供的产品如下:

各种架构下,用户与云厂商的安全边界不同。在左下角【其他集群-标准专有集群】中,允许用户通过ECS层管理K8s的master节点。这样一来,K8s节点所在ECS可能存在暴力破解/凭证泄露攻击,攻击者登录ECS后,可以在本地找到etcd的登录证书,登录etcd:

尝试读取etcd数据

etcdctl get / --prefix --keys-only
Error: dial tcp 127.0.0.1:2379: getsockopt: connection refused

结果返回本地2379连接失败,netstat看下发现监听的是172段,这种情况下需要指定endpoint带cert进行访问,认证失败会返回Error: context deadline exceeded。

[root@iZbp13l0dv5x8ke1jmrpihZ cert]# netstat -antp | grep LISTEN
tcp        0      0 127.0.0.1:10248         0.0.0.0:*               LISTEN      2917/kubelet
tcp        0      0 127.0.0.1:10249         0.0.0.0:*               LISTEN      4801/kube-proxy
tcp        0      0 172.16.0.112:2379       0.0.0.0:*               LISTEN      3222/etcd
tcp        0      0 172.16.0.112:2380       0.0.0.0:*               LISTEN      3222/etcd
tcp        0      0 127.0.0.1:10253         0.0.0.0:*               LISTEN      4628/cloud-controll
tcp        0      0 127.0.0.1:10257         0.0.0.0:*               LISTEN      4134/kube-controlle
tcp        0      0 127.0.0.1:10259         0.0.0.0:*               LISTEN      4150/kube-scheduler
tcp        0      0 127.0.0.1:33941         0.0.0.0:*               LISTEN      2917/kubelet
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      3465/sshd

带cert访问etcd

[root@iZbp13l0dv5x8ke1jmrpihZ cert]# ls
172.16.0.112-name-1.csr      172.16.0.114-name-3.csr      ca.pem               etcd-server.pem
172.16.0.112-name-1-key.pem  172.16.0.114-name-3-key.pem  etcd-client.csr      peer-ca-config.json
172.16.0.112-name-1.pem      172.16.0.114-name-3.pem      etcd-client-key.pem  peer-ca.csr
172.16.0.113-name-2.csr      ca-config.json               etcd-client.pem      peer-ca-key.pem
172.16.0.113-name-2-key.pem  ca.csr                       etcd-server.csr      peer-ca.pem
172.16.0.113-name-2.pem      ca-key.pem                   etcd-server-key.pem
[root@iZbp13l0dv5x8ke1jmrpihZ cert]# etcdctl --insecure-skip-tls-verify --insecure-transport=true --endpoints=https://172.16.0.112:2379 --cacert=ca.pem --key=etcd-client-key.pem --cert=etcd-client.pem endpoint health
https://172.16.0.112:2379 is healthy: successfully committed proposal: took = 2.084526ms

3.3 通过etcd接管集群

  • 参考旧文 https://www.cdxy.me/?p=827

4 etcd secerts本地静态加密

K8s 1.13版本之后支持at rest encryption:

  • https://kubernetes.cn/docs/tasks/administer-cluster/encrypt-data/

本地加密的流程如下:在API Server中控制Secrets的加解密,而etcd中只存储密文,仅在授权使用时解密。如此etcd被攻破后攻击者无法窃取Secrets保存的数据。

其中api-server启动时,可以通过命令行参数指定用于加解密的key文件(yaml)。

此架构仅解决了etcd数据泄露风险。但攻破node root后,可以在本地拿到key,仍然意味着可以接管整个集群的数据。

5 密钥管理服务KMS plugin

本地静态加密保护能力较弱,K8s提供了更优方案——接入密钥管理服务:

  • K8s官方文档 https://kubernetes.io/docs/tasks/administer-cluster/kms-provider/
  • 阿里云ACK Pro的实现 https://help.aliyun.com/document_detail/177372.html

KMS 加密驱动使用封套加密模型来加密 etcd 中的数据。 数据使用数据加密秘钥(DEK)加密;每次加密都生成一个新的 DEK。 这些 DEK 经一个秘钥加密秘钥(KEK)加密后在一个远端的 KMS 中存储和管理。 KMS 驱动使用 gRPC 与一个特定的 KMS 插件通信。这个 KMS 插件作为一个 gRPC 服务器被部署在 Kubernetes 主服务器的同一个主机上,负责与远端 KMS 的通信。

以下介绍Kubernetes Secret密钥进行加密和解密的过程:

  • 当一个业务密钥需要通过Kubernetes Secret API存储时,数据会首先被API Server生成的一个随机的数据加密密钥加密,然后该数据密钥会被指定的阿里云KMS密钥加密为一个密文密钥存储在etcd中。
  • 解密Kubernetes Secret密钥时,系统会首先调用阿里云KMS服务的解密OpenAPI进行密文密钥的解密,然后使用解密后的明文密钥对Secret数据解密并最终返回给用户。

我们简化这个过程如下:

这个设计将加解密的核心部分与数据分离,进一步缓解Secret泄露。

6 Matser Node内存窃密

以上设计仍有缺陷,由于DEK、KEK、Secrets明文 均可在kube-apiserver和kms-plugin内存中获得,如果hacker登录到master node通过gdb dump这两个服务的内存

7 内存窃密的防护-可信执行环境TEE

TEE(Truested Execution Environment, TEE, aka. Enclave) - 保护程序运行时内存数据不被其他应用窃取。

CPU通过内存映射手段给每个进程营造一个单独的地址空间来隔离多个进程的代码和数据,通过内核空间和用户空间不同的特权级来隔离操作系统和用户进程的代码和数据。

但由于内存中的代码和数据都是明文,容易被同处于内存中的其它应用偷窥,因此出现了可信执行环境 ,应用将加密数据送往安全模块,由安全模块处理完后再返回结果给相应的应用。

TEE本质上是解决信任与权限的冲突:

敏感数据(目标)存储在APP中,APP运行在VM中,VM(服务商提供的)运行在物理设备中。

从信任关系来讲,APP->VM->Hardware 从上层到底层是不信任的,但是从权限来看,Hardware->VM->APP 底层却有上层更大的控制权限。

用户想守住APP里面的数据,一方面需要防范直接针对APP的攻击,另一方面也要考虑从底层向上的攻击。

为解决这个问题,可以通过TEE(Intel SGX)与K8s结合保护APP Secrets,以下实践来自蚂蚁的议题:

  • https://v.qq.com/x/page/r3130pcs4y3.html

下文仅根据原意对议题内容进行概括。

8 使用TEE保护K8s Secrets

从Secrets生产、消费、传输 三个过程分别讨论其防护方案。

8.1 Secrets传输过程中的防护

8.1.1 保证信封加密key不被窃取

TEE技术我们理解成一个保护内存的enclave(图中蓝色部分),这部分的内存不会被master node其他进程窃取。

首先把KMS Plugin扔到TEE里,这里就可以保护信封加密的KEK和DEK不被窃取。

但问题是:KEK和DEK本质上是完成加解密动作的,攻击者不需要窃取直接调用KMS-plugin接口即可完成加解密动作。

8.1.2 Intex SGX Attestation技术

这个技术解决的问题是:两个内存保护的enclave,在互相通信时如何相信对方是安全的。

实现这种方式最直接的方法是英特尔公司为每一个SGX CPU生成一对公私密钥对,将私钥烧录在不可删改的硬件上面。我们假设英特尔的CPU能够实现复杂的密码原语,比如进行数字签名。英特尔公司还需要提供一个公钥证书认证中心(Certificate Authority)为每一个SGX CPU和它的公钥关联起来,这个跟互联网上面的公钥认证中心是一个道理,目的是提供公钥的认证,让其它人能够确认其所持有或使用的某一个公钥是属于切确的团体或者个人的。比如我希望使用A的公钥加密一个消息然后发给A,A使用他的私钥就能解密和查看这个消息,但是在加密前我希望能够确定这是A的公钥,怎么确定呢?就需要到公钥认证中心去查询了。如果我使用的是黑客调包后的公钥来加密我的消息,那么黑客在得到密文后就可以直接解密查看这消息了。这些条件具备之后,在enclave完成初始化之后,enclave程序根据初始化状态信息生成一个哈希摘要,然后让SGX CPU签名,即数字签名(跟现实中的签名是同一个意思,表示签名者认同了所签署的文件),然后将签名和哈希摘要一同发给Bob。Bob收到之后,利用英特尔提供的Attestation service来认证enclave发来的数据,确信远程的enclave确实是运行在可信的Intel SGX GPU上面。

一个攻击者是不能够伪造签名的,因为:

  1. 攻击者不能够获得烧录在硬件上面的CPU私钥;
  2. 只有enclave程序才能够让CPU数字签名;
  3. 不同的enclave程序的哈希摘要是不一样的,一个攻击者不能够伪造enclave的哈希摘要。

因为上面的原因,如果对方enclave发来的数据能够通过认证,那么相信他的enclave程序在安全运行。

接下来,两个enclave通信的原理跟现在浏览器的HTTPS的TLS差不多。先使用密钥交换算法(Diffie-Hellmann key exchange)协商一个对称加密算法的密钥,然后通信双方都使用这个密钥来加密消息进行通信。

简而言之此技术可以达到两点需求:

  1. 保证接口无法被不可信的使用者调用。
  2. 保证数据在传输过程中不被窃取。

8.1.3 使用Attestation技术保证KMS plugin不被恶意调用

将API Server中负责调用KMS plugin的部分(KMS provider)拆分出来,使用TEE保护,然后在KMS provider和KMS plugin两个enclave通信间使用SGX Attestation技术保护通信的可信、机密性。

8.2 Secrets生产过程的防护

一般来讲开发者在配置K8s Secrets会通过client将Secrets写入K8s etcd。这一过程往往发生在运维的PC机或跳板机,如 kubectl apply xxx.yaml命令。此时如果kubectl所在机器被入侵,或者kube-config被泄露(如github),攻击者可以接管包括Secrets在内的整个K8s集群。

这时可以考虑把client用TEE enclave掉,这样保证攻击者无法在宿主机内存dump到kubeconfig,同时Attestation机制保证了被泄露的kubeconfig无法被攻击者在其他机器上使用。

8.5 Secrets消费过程的防护

Pod在使用Secrets时,由Kubelet与API Server通信,调用KMS provider来获取Secrets。一旦Node被攻破,攻击者同样可以从kubelet内存中dump出Secrets明文。

同样的,使用TEE对node kubelet创建enclave并使用Attestation技术保证通信安全。

以上就是K8s 核心数据Secrets在 生产-传输-消费 全过程的保护方案。

核心优势:

  1. 使用内存加密与硬件强隔离TEE技术保护了内存中的明文数据不被窃取。
  2. 使用Attestation机制确保各组件接口被可信程序调用,且传输过程安全。

Ref

  • https://kubernetes.io/docs/tasks/administer-cluster/kms-provider/
  • https://help.aliyun.com/document_detail/177372.html
  • https://zhuanlan.zhihu.com/p/107281508
  • https://blog.csdn.net/kouryoushine/article/details/89966837
  • https://v.qq.com/x/page/r3130pcs4y3.html
  • https://www.pianshen.com/article/453656620/