查看原文
其他

小白都能看懂的 Kubernetes安全之API-server安全

麦田 K8S中文社区 2019-12-18


目录

  • API-server的作用

  • API-server提供的安全机制

  • 认证方式之——TLS

  • 授权方式之——node

  • 授权方式之——RBAC

  • 准入控制之——NodeRestriction

  • 自定义准入插件

API-server的作用

在kubernetes集群中API-server的作用有以下几点:
  • 提供了集群管理的REST API接口;
  • 提供其他模块之间的数据交互和通信的枢纽);
  • 是资源配额控制的入口;
  • 提供一定的集群安全机制.
通俗来讲,API-server就是整个集群的入口,任何用户和程序对集群资源的增删改查操作都必须经过API-server。为了保证集群的安全,API-server提供了完备的安全机制。

API-server提供的安全机制

API-server的本质就是一个web 服务器,底层代码是基于go-restful这个web框架搭建的。传统的web服务器在安全方面一般都会提供认证/授权机制,以过滤器的方式实现。在API-server中也以类似于过滤器的方式提供了认证、授权机制,不同的是API-server还提供了准入检查。下面一一讲解。
认证(Authentication),识别用户身份。认证的方式有很多,比如:HTTP base,HTTP token,TLS,Service Account,OpenID Connect等。高版本的kubernetes默认的认证方式是TLS。在TLS认证方案中,每个用户都拥有自己的X.509客户端证书,API服务器通过配置的证书颁发机构(CA)验证客户端证书。
授权(Authorization),识别是否有相应操作权利。授权者,通过组合属性(用户属性,资源属性,实体)的策略向用户授予访问权限。授权的方式也有很多,比如:AlwaysDeny,AlwaysAllow,ABAC,RBAC,node等,高版本的kubernetes默认的授权方式是RBAC 和 node。
准入(Admission Controller),判断你的操作是否符合集群的要求,这是一种更灵活的管控机制,用户还可以根据自己的需求定义准入插件来管理集群。kubernetes 中将准入模块分为三种,validating(验证型),mutating(修改型)以及两者兼有,准入的默认配置是NodeRestriction。
我理解的准入和授权的区别主要有以下几点:
  • 运行时机不同。认证/授权模块相当于是web开发中的filter,是针对请求头和证书进行分析操作,运行在具体handle处理逻辑之前,而准入模块可以操作具体的请求体,是在具体的处理逻辑中。
  • 管控范围不同。授权模块只管控用户有没有操作的权限,具体操作的细节并不关注。准入模块可以管控用户申请的资源大小(LimitRanger),可以细粒度地划分用户权限(NodeRestriction),可以检查用户创建的命名空间是否存在(NamespaceLifecycle)等等。
  • 运行逻辑不同。准入模块之间是逻辑与的关系,设定之后用户的任何请求必须满足所有的准入检查,如果一个插件不满足就会拒绝请求。认证/授权模块之间是逻辑或的关系,只要满足一个请求就可以通过。

认证方式之——TLS

TLS是安全传输层协议,包括两部分:TLS记录协议和TLS握手协议。TLS记录协议主要保证传输过程中信息传输的完整性和私密性,这一部分通过协商后的密钥来加密数据。TLS握手协议主要是为了认证对方的身份、协商密钥。在握手协议中主要涉及到身份认证,下面结合kubernetes说明。

什么是证书

常见的证书格式是X509格式。格式如下:

这个证书里面主要包含了主体的公钥信息,主体的身份信息,证书授权中心,签名信息等等,观察图可知,这个用户组就是system:nodes,用户名是system.node.slave1,授权中心是kubernetes,当需要验证这个证书的真伪时,就需要使用证书授权中心的公钥去解析签名(因为签名是由证书授权中心对证书信息经过消息摘要再使用私钥加密得到的),当解析出来的信息和证书的信息匹配时就认证通过了。
证书授权中心(Certificate Authority)可以自建也可以依赖于第三方机构,本质就是一对密钥,对认证过的身份信息经过私钥加密,公钥公开,任何用户都可以使用公钥解析证书得到被认证者的身份信息,这样就达到了身份验证的目的。

API-server与kubelet-client的证书验证

为了方便用户访问kube-apiserver,推荐配置方式是采用kubeconfig文件。这个配置文件里面描述了集群、上下文和用户信息。我们来看kubelet默认的kubeconfig文件。
首先kubelet的kubeconfig如下所示:

上图users.user.client-certificate 字段就是用户的证书地址,这个证书由kubernetes签发,我们使用kubernetes的公钥来验证这个文件。

结果显示验证成功,验证成功后得到用户的身份信息。在我们自建CA时,一定要保存好CA的私钥。如果私钥丢失那么攻击者可以制作证书,从而认证通过。

授权方式之——node

节点授权方式是专门为kubelet发出的请求的一种授权方式。从上小节可以看处kubelet经过认证以后得到的用户组时system:nodes,用户名是system:node:slave1,这个满足节点授权的用户名格式system:node:<nodename>,于是这个用户被赋予以下的权限。
操作资源
services,endpoints,nodes,pods,secrets ···
node,pod,events
可见这个节点授权赋予每个节点的权利很大。可以对整个集群的pod、node等资源进行操作。如果没有设置准入控制NodeRestriction,那么节点授权是很危险的。

授权方式之——RBAC

RBAC是一种基于角色的授权方式,这里涉及到两类资源,一类是RoleBinding、ClusterRoleBinding,另一类是Role、ClusterRole,角色是对用户拥有权利的抽象,角色绑定是将角色绑定到用户(user,group或者service account)。在使用RBAC创建自己的角色时,要秉持最小权限原则,防止操作范围过大而影响集群安全。


在kubernetes中已经默认建立了很多角色,在创建自己的角色之前可以考虑默认的角色。

准入控制之——NodeRestriction

这个插件的作用是限制每一个kubelet只能操作自己node的资源。在集群部署中,最好设置NodeRestriction,如果没有设置这个准入插件不法分子可以通过攻克一个子节点获得kubelet的证书和密钥,进而远程控制整个集群的资源。

自定义准入插件

kubernetes准入模块强大的地方在于提供了 External Admission Webhook,可以基于这个功能自定义准入插件。这里涉及到两个特殊的准入控制器:
MutatingAdmissionWebhook和ValidatingAdmissionWebhook。这两个控制器将发送准入请求到外部的 HTTP 回调服务并接收一个准入响应。如果启用了这两个准入控制器,Kubernetes 管理员可以在集群中创建和配置一个 admission webhook。API server发送一个AdmissionReview的json数据到webhook,其中主要包含一个AdmissionRequest的请求,以及需要处理的kubernetes资源的详细信息。webhook在接收到该请求之后会根据自定义逻辑进行处理,并返回处理结果AdmissionResponse。
自定义准入插件的示例代码可以在github中找到,这个项目最主要的就是main.go文件。
下面是main.go 文件中的admit代码。

  1. // 验证的主要逻辑



  2. func admit(data []byte) *v1alpha1.AdmissionReviewStatus {



  3. ar := v1alpha1.AdmissionReview{}



  4. if err := json.Unmarshal(data, &ar); err != nil {



  5. glog.Error(err)



  6. return nil



  7. }



  8. podResource := metav1.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}



  9. if ar.Spec.Resource != podResource {



  10. glog.Errorf("expect resource to be %s", podResource)



  11. return nil



  12. }




  13. raw := ar.Spec.Object.Raw



  14. pod := v1.Pod{}



  15. if err := json.Unmarshal(raw, &pod); err != nil {



  16. glog.Error(err)



  17. return nil



  18. }



  19. reviewStatus := v1alpha1.AdmissionReviewStatus{}



  20. for _, container := range pod.Spec.Containers {



  21. if !strings.Contains(container.Image, "gcr.io") {



  22. reviewStatus.Allowed = false



  23. reviewStatus.Result = &metav1.Status{



  24. Reason: "can only pull image from grc.io",



  25. }



  26. return &reviewStatus



  27. }



  28. }



  29. reviewStatus.Allowed = true



  30. return &reviewStatus



  31. }


这里面核心的结构体就是AdmissionReview。AdmissionReview里面的AdmissionRequest中封装了可用于逻辑判断的所有请求信息,用户可以根据自己的要求自定义验证逻辑。当创建好自己的镜像之后按照项目中的步骤部署运行。

推荐阅读

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存