做者 | 匡大虎 阿里巴巴技术专家node
本文整理自《CNCF x Alibaba 云原生技术公开课》第 27 讲,点击直达课程页面。docker
关注“阿里巴巴云原生”公众号,回复关键词**“入门”**,便可下载从零入门 K8s 系列文章 PPT。api
**导读:**访问控制是云原生安全的一个重要组成部分,也是 K8s 集群在多租环境下必要且基本的安全加固手段。在 K8s 体系中,访问控制又分为三个重要的组成部分,请求认证,鉴权和运行时刻的 admission 准入控制。在本文中,做者将带领你们了解这 3 部分的基本定义和使用方法,并给出多租环境下安全加固的相关最佳实践。安全
你们都知道访问控制是云原生安全中的一个重要组成部分。也是一个 Kubernetes 集群在多租户环境下必需要采起的一个基本的安全防御手段。app
那么在概念上能够抽象的定义为谁在何种条件下能够对什么资源作什么操做。这里的资源就是在 Kubernetes 中咱们熟知的:Pod、 ConfigMaps、Deployment、Secrets 等等这样的资源模型。less
由上图来介绍一下 Kubernetes API 的请求从发起到其持久化入库的一个流程。微服务
首先看一下请求的发起,请求的发起分为两个部分:工具
当咱们的 apiserver 收到请求后,就会开启访问控制流程。这里面分为三个步骤:学习
Authentication 认证阶段:判断请求用户是否为可以访问集群的合法用户。若是用户是个非法用户,那 apiserver 会返回一个 401 的状态码,并终止该请求;测试
若是用户合法的话,咱们的 apiserver 会进入到访问控制的第二阶段 Authorization:鉴权阶段。在该阶段中 apiserver 会判断用户是否有权限进行请求中的操做。若是无权进行操做,apiserver 会返回 403 的状态码,并一样终止该请求;
若是用户有权进行该操做的话,访问控制会进入到第三个阶段:AdmissionControl。在该阶段中 apiserver 的 admission controller 会判断请求是不是一个安全合规的请求。若是最终验证经过的话,访问控制流程才会结束。
此时咱们的请求将会转换为一个 Kubernetes objects 相应的变动请求,最终持久化到 ETCD 中。
对于认证来讲,首先咱们要肯定请求的发起方是谁。并最终经过认证过程将其转换为一个系统可识别的用户模型用于后期的鉴权,那么先来看一下 Kubernetes 中的用户模型。
什么是用户管理能力呢?咱们没法像操做 Pod 同样,经过 API 的方式建立删除一个用户实例。同时咱们也没法在 ETCD 中找到用户对应的存储对象。
在 Kubernetes 的访问控制流程中用户模型是如何产生的呢?答案就在请求方的访问控制凭证中,也就是咱们平时使用的 kube-config 中的证书,或者是 Pod 中引入的 ServerAccount。通过 Kubernetes 认证流程以后,apiserver 会将请求中凭证中的用户身份转化为对应的 User 和 Groups 这样的用户模型。在随后的鉴权操做和审计操做流程中,apiserver 都会使用到改用户模型实例。
该认证方式下,管理员会将 Username 和 Password 组成的白名单放置在 apiserver 读取的静态配置文件上面进行认证,该方式通常用于测试场景,在安全方面是不推荐且不可拓展的一种方式。
该方式是 apiserver 中相对应用较多的使用方式,首先访问者会使用由集群 CA 签发的,或是添加在 apiserver Client CA 中授信 CA 签发的客户端证书去访问 apiserver。apiserver 服务端在接收到请求后,会进行 TLS 的握手流程。除了验证证书的合法性,apiserver 还会校验客户端证书的请求源地址等信息。开启双向认证,X509 认证是一个比较安全的方式,也是 Kubernetes 组件之间默认使用的认证方式,同时也是 kubectl 客户端对应的 kube-config 中常用到的访问凭证。
该方式的 Tokens 是通用的 JWT 的形式,其中包含了签发者、用户的身份、过时时间等多种元信息。它的认证方式也是常见的私钥加签,公钥验签的一个基本流程。基于 Token 的认证使用场景也很普遍,好比 Kubernetes Pod 应用中常用到的 Service Account,其中就会自动绑定一个签名后的 JWT Token 用于请求 apiserver。
另外 apiserver 还支持基于 OpenID 协议的 Token 认证,能够经过对 apiserver 的配置链接一个指定的外部 IDP,同时能够经过 Keycloak,Dex 这样的开源服务来管理 IDP,请求者能够按照本身熟悉的方式在原身份认证服务上进行登陆认证,并最终返回一个相应的 JWT token,为了后面的 apiserver 的鉴权流程。
除此以外,还可使用 Webhooks 的方式,将请求的 Token 发送到指定外部服务进行 Token 的验签。
对于一个集群证书体系来讲,认证机构 (CA) 是一个很是重要的证书对。它会被默认放置在集群 Master 节点上的 /etc/Kubernetes/pki/ 目录下。集群中全部组件之间的通信用到的证书,其实都是由集群根 CA 来签发的。在证书中有两个身份凭证相关的重要字段:一个是 CN,一个是 O。
另外能够经过 openssl 命令来进行证书的解析。上图右侧能够看到,经过 Subject 中的 O 和 CN 字段能够查看对应的信息。
上面每个组件证书都有本身指定的 Common Name 和 Organization 用于特定角色的绑定。这样的设置可使各系统组件只绑定自身功能范围内的角色权限。从而保证了每一个系统组件自身权限的最小化。
Kubernetes 集群自己就提供了证书签发的 API,而在集群的建立过程当中,像 kubeadm 这样的集群安装工具,会基于不一样的 CSR 签发请求调用 apiserver 对应接口。此时 apiserver 会根据请求,以这种 csr 资源模型的形式建立对应的签发请求实例。刚开始建立的签发实例都会处于 pending 的状态,直到有权限的管理员进行审批后,这个 csr 才会处于 approved 的状态,请求对应的证书就会被签发。
经过上图右侧中的命令能够来查看相应的证书内容信息。
首先开发人员需用经过 openssl 等证书工具生成私钥,而后建立对应的 x509 csr 请求文件,须要在 subj 字段中指定用户 user 和组 group,最后经过 API 建立 K8s csr 实例并等待管理员审批。
对于集群管理员,他能够直接读取集群根 CA,并经过 x509 的 csr 请求文件签发证书,因此它无需定义或审批 csr 实例。上图中最后一行是一个 openssl 签发示例,命令中须要指明 csr 和 ca.crt 的文件路径,以及签发证书的过时时间信息。
另外各个云厂商也会基于登陆用户和目标集群一键化生成对应的集群访问凭证,方便用户的使用。
除了证书认证以外,Service Account 也是 apiserver 中应用比较普遍的一种方式。对于 Service Account 来讲,它是 K8s 中惟一可以经过 API 方式管理的 APIService 访问凭证,其余特性在上图中能够看到。
图中也给出了一些使用 kubectl 进行 Service Account API 相关增删改查的示例,同时咱们能够为已经存在的 serviceaccount 手动建立其 token 对应的 secret,有兴趣的同窗能够在 Kubernetes 集群中操做执行一下。
接着看一下 Service Account 的使用。
首先能够经过 get secret –oyaml 命令查看 serviceaccount 对应的指定 secret,其中 token 字段即为通过了 base64 编码的 JWT 格式的认证 token。
在部署一个应用时,咱们能够经过 template -> spec -> containers 中的 serviceAccountName 字段声明须要使用的 Service Account 名称。注意若是是在 Pod 建立过程当中,发现制定的 ServiceAccount 不存在,则该 Pod 建立过程会被终止。
在生成的 pod 模板中能够看到指定 serviceaccount 对应的 secret 中的 ca,namespace 和认证 token 会以文件的形式挂载到容器中的指定目录下。另外对于已经建立的 Pod,咱们不能更新其已经挂载的 ServiceAccount 内容。
kubeconfig 是用户本地链接 Kubernetes 集群使用的重要访问凭证,接着来介绍一下 kubeconfig 的配置和使用。
当一个请求在完成 apiserver 认证后,能够认为它是一个合法的用户,那么如何控制该用户在集群中的哪些 namespace 中访问哪些资源,对这些资源又能进行哪些操做呢?
这就由访问控制的第二步 Kubernetes 鉴权来完成。apiserver 自己支持多种鉴权方式,在本节内容中,咱们主要介绍在安全上推荐的鉴权方式 RBAC。
这里举个例子,假设有个经过合法认证的用户 Bob,他请求 list 某个 namespace下的 Pods,改请求的鉴权语义记为:Can Bob list pods ?其中 Bob 即为请求中的 Subject,list 为对应的请求动做 Action,而 pods 为对应的请求资源 Resource。
上面介绍了 RBAC 角色模型的三要素,在整个 RBAC 策略定义下,还须要将这个角色绑定到一个具体的控制域内。这就是 Kubernetes 你们熟悉的命名空间。经过 namespace 能够将 Kubernetes api 资源限定在不一样的做用域内。从而帮助咱们在一个多租户集群中,对用户进行逻辑上的隔离。
上面的事例能够改成 User A can create pods in namespace B。这里须要注意的是,若是不进行任何的权限绑定,RBAC 会拒绝全部访问。
一般 RBAC 会进行对 apiserver 的细粒度访问控制,可是这个细粒度是个相对的概念,RBAC 是面向模型级别的绑定。它不能绑定到 namespace 中的一个具体的 object 实例,更不能绑定到指定资源的任意一个 field。
RBAC 对访问权限的控制粒度上,它能够细化到 Kubernetes api 的 subresources 级别。好比针对一个访问者,咱们能够控制其在指定 namespace 下对 nodes/status 模型的访问。
接着介绍 RBAC 具体的绑定权限和对象。
首先是角色 Role,它定义了用户在指定的 Kubernetes 命名空间资源上能够进行哪些操做。好比能够定一个 namespace 中 pod 的只读权限,同时还能够定义一个 namespace 管理员权限,它具备对这个命名空间下全部对象资源的全部操做权限。
如上图所示,是一个 Role 的定义模板编排文件,其中 resource 字段定义了这个角色能够访问哪些资源,verbs 字段定义了这个角色有哪些操做的权限。在 apiGroups 中,须要指定目标资源的 apiGroups 名称,这里能够经过官方 API 文档查询,若是指定的 Group 是 core,那么在角色模板中的 apiGroups 可置为空。
当咱们完成了一个 namespace 下的角色定义以后,还须要创建其与使用这个角色的主体之间在 namespace 下的绑定关系,这里须要一个 RoleBinding 模型。使用 RoleBinding 能够将 Role 对应的权限模型绑定到对应的 Subject 上。
好比这里能够将名为 test 的 namespace 中的 pod 只读权限同时绑定给用户 test1 和 test2 以及 proc1。也能够将 namespace test 只读权限绑定 tech-lead group 中的 test1 用户,这样用户 test2 和 proc1 是没有 get namespace 权限的。
接着看一下对应的 RoleBinding 编排文件模板。
其中 roleRef 字段中声明了咱们须要绑定的角色,一个绑定只能指定惟一的 Role。在 subject 字段中定义了咱们要绑定的对象,这里能够是 User,Group 或者是 Service Account。它同时支持绑定多个对象。
除了定义指定 namespace 中的权限模型,也能够经过 ClusterRole 定义一个集群维度的权限模型。在一个 Cluster 实例中,能够定义集群维度的权限使用权限,好比像 PV、Nodes 在 namespace 中不可见的资源权限,能够在 ClusterRole 中定义,而操做这些资源的动做一样是以前 Role 中支持的增删改查和 list、watch 等操做。
下图为 ClusterRole 编排文件模板:
ClusterRole 编排文件几乎和 Role 是如出一辙的,惟一不一样的地方是 ClusterRole 中是全部集群维度的权限定义,不支持 namespace 的定义。
一样在 ClusterRole 的基础上,能够将其绑定在对应的 Subject 主体上。而 ClusterRoleBinding 模型实例能够帮助咱们在集群全部命名空间上将 ClusterRole 绑定到具体的 Subject 对象上。
好比这里能够将全部 namespace 的 list 权限绑定给 group 为 sre 或者 devops 的管理员 admin1 和 admin2。
相比较于 RoleBinding,ClusterRoleBinding 模板定义也只是在 namespace 和 roleRef 中的权限对象模型定义上有不一样,其余的定义格式是同样的。
经过上文的学习,咱们知道在不进行任何权限的绑定下,RABC 会拒绝全部的访问。那么咱们的系统组件之间是如何互相请求呢?
其实在集群建立的时候,处理系统各组件的客户端证书,它们各自的角色和环境对象也会被建立出来,以知足组件业务之间交互必须的权限要求。
下面看几个预置的集群角色:
经过以上对 RBAC 的学习,你们应该对 Kubernetes 中 RBAC 中的模型定义有了必定的了解,可是在某些复杂的多租户业务场景下,如何在权限模板中针对各个 API 模型定义相应的动做策略,仍是须要必定的理论和实践基础的。而对一个应用开发人员来讲,kubectl 可能更为直观和熟悉些,这里也给出了一些 kubectl 操做和 RBAC 中的对应关系。
好比当咱们但愿 edit 一个 deploy 的时候,须要在相应的角色模板中增长对 Deployment 资源的 get、patch 这样的权限。若是但愿 exec 到一个 pod 中,须要在相应的角色模板中增长对 pod 的 get 权限,以及针对 pod/exec 模型的 create 权限。
经过 docker hub 上的统计结果能够看到,主流的业务镜像有 82.4% 是以 root 用户来启动的。经过这个调查能够看到对 Security Context 的相关使用是不容乐观的。
通过对上面的分析结果能够看出来,若是咱们可以对业务容器配置足够安全的运行时刻参数,其实攻击者很难有可乘之机。那么咱们究竟应该在部署 Kubernetes 集群中的业务容器作哪些 runtime 运行时刻的安全加固呢?
因为 PSP 策略相对复杂一些,这里介绍一下使用注意事项。
首先能够经过 API 直接操做 PSP 的安全策略实例,如上图左侧是 PSP 策略支持的配置参数,包括特权容器,系统capabilities,运行时刻用户 id 和文件系统权限等多种配置。你们也能够在官方文档找到各个参数的详细说明。
而 PSP 的做用正是在业务容器运行前,基于这个策略校验安全参数配置,若是不知足该安全策略,则禁止该 Pod 运行。
最后在 PSP 的使用上,咱们须要注意几点,如上图右侧所示。
最后在对多租环境下,如何利用 Kubernetes 下原生的安全能力作安全加固作一个最佳实践的小结。
本文主要内容就到此为止了,这里为你们简单总结一下要点:
“阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,作最懂云原生开发者的公众号。”