TOC

系统安全

    对于Kubernetes来讲,etcd存储了当前整个集群当中的每一个资源相关的用户定义的期望的状态以及当前在集群之上的资源状态,他们分别保存于资源的spec和status当中,当然也有个别资源没有spec字段,比如像ConfigMap当中只有data,Secret也只有data,但是事实上也代表了spec,只不过其过于简单,没有使用spec来进行嵌套而已,这些信息,都保存于etcd的存储系统当中,如果说etcd宕机那么整个集群的所有状态就丢失了,因此,一般来说,我们必须要对etcd做高可用,而etcd本身是一个有状态应用,如果它运行于Kubernetes上,那我们应该使用StatefulSet或者etcd的Operator来管理;
   Kubernetes之上运行的一个应用程序或者说一个进程kube-apiserver是整个etcd存储集群的存储服务的访问入口,也就意味着整个集群当中的所有状态数据都必须保存在etcd当中,但是任何客户端都不能直接写etcd,也就是说的客户端也只有一个那就是kube-apiserver,用户要想创建一个Pod资源,通常会使用kubeadm这样的工具与APIServer进行通信,将数据先提交给APIServer,由于APIServer再存储etcd,由此看出APIServer是我们介入整个集群的网关;
    一旦用户提交需要创建一个Pod资源,这个Pod需要被调度到集群中的某一个Work Node之上,由这个Node之上的kubelet负责调用docker将其运行起来,调度器kube-sceduler这个守护进程,它也是APIServer的客户端之一,一旦有kubectl提交了一个新的Pod定义给APIServer,APIServer自己把etcd的watch机制通知给注册监视相关资源的程序,而kube-sceduler就像APIServer注册监视了每一个没有没调度结果的相关信息,而kube-sceduler其实也是APIServer的客户端,它需要通过APIServer完成Pod信息的查询,甚至修改,比如把调度的结果保存给etcd当中,它不能够直接操作etcd,而必须要经过APIServer;
    同样的我们的controller manager当中运行了很多的controller,在运行着我们的controller loop我们也称之为控制循环或者和解循环,而这个和解循环的相关操作必要时需要去获取到当前集群中对应的每一个资源的真实状态,并将这个状态更新并保存至我们的kube-apiserver中,然后我们的kube-apiserver再存储至etcd集群中,一旦调度完成了,kubelet是运行在每一个Work Node之上的代理程序,它需要到APIServer上加载对应Pod的定义,并调用本地的docker引擎运行为容器,但是在Kubernetes之上,它表现为Pod,kubele也一样需要注册监听自己相关的Pod的相关信息,所以从这个角度来讲kubelet在调度并创建完之后,它也需要把创建并运行的结果,比如启动成功与否,保存于etcd,也需要经过APIServer来进行etcd的操作,对于我们的CoreDns的守护进程也需要在APIServer上注册并监视者资源的变动;
    对于Kubernetes来说,如果有用户提交了所有关于Service的定义,都会被kube-proxy所捕获,并读取到本地转换为Iptables或者Ipvs规则,而Service的定义也需要通过APIServer进行获取,它读取完成并且创建相关规则以后这个创建的结果就结束了,这个结果就是当前状态,也需要保存于etcd,它也需要经过APIServer保存于etcd当中,所以etcd就是一个通用键值存储系统,但是在Kubernetes之上,它不允许随意存取数据,所有能够操作或使用数据的行为,一定是在APIServer内部定义好了的;
    kube-sceduler负责只是调度它也只应该修改Pod上关于调度结果的字段,controller manager负责更新当前状态,并确保当前状态一定要吻合于用户期望的状态的,它负责修改status相关的信息,而kubelet负责创建Pod并负责更新Pod当前状态信息,kube-proxy负责创建Service修改Service并负责更新Service相关的信息,从这个角度来说,我们就没有必要,kube-sceduler拥有controller manager、kube-proxy等相关的权限,还有如果kube-sceduler随便连接到APIService上能随便完成各种调度,那么任何一个人使用kube-sceduler都可以完成集群上的所有操作了,这就乱套了,安全性不言而喻,因此Kubernetes必须对运行其上的所有资源在访问上构建出一个安全的访问机制,或者说访问控制模型来;
    在Kubernetes之上APIServer是整个集群的网关,因此它的访问控制功能机会都是在APIServer上实现的,所以不同的应用程序我们应该给它组织出不同的权限来,为此Kubernetes在APIServer通过三类插件分别完成这样的功能,对于客户端来讲,他们去连入APIServer的时候,第一步就要做认证,Kubernetes需要认证这个客户端是不是我们所认可的访问者,没有合理的帐号是不允许使用APIServer中的相关功能的,所以第一步叫做认证,第二步对于认证成功的人来基于所谓的资源管理逻辑实现合理的权限指派,读操作到第二步就结束了,所有的写操作,还需要经过第三步,就是准入控制,比如一个用户来了,认证通过也有权限创建Pod,于是它请求创建了Pod,而我们事先又规定说,这个用户(客户端)所创建的任何资源请求,它的CPU的核心数一定不能超过2个核心,而这个客户端发起的请求是4个,所以这一步是检查相关的细节,所以即使这个客户端在第二步进行权限校验之后发现有创建的权限也不一定就可以创建了,所以最后一关是检查用户的行为是否是合理的,也就是说你所请求的目标本身是合乎规范与否,还有一种情形,在创建Pod时,Pod的spec字段中有很多内建字段,但是我们在创建Pod时却可以只给一个字段,containers而里面只有一个容器的简单定义,其他资源都没给,那剩下的没给的字段也需要进行补全它也是靠第三步来完成的,所以第三步做的操作大概有两种,第一种检查你所定义的资源是否合理,第二种就是检查请求资源时的语法是否合乎规范,如何不合乎规范,缺失了某些字段,那就给他进行补齐,这种我们称之为便易型的准入控制,还有一种就是检查是否合乎规范,不符合就拒绝,这种属于合法性炎症的一种准入控制器,就像用户请求的CPU数量超过了给这个用户的范围,那么这个时候Admiiission Contol中的某一个Contol就可用拒绝它,他们都内嵌到kube-Apiserver这个守护进程的内部,也就意味着kube-Apiserver准入控制功能就可以工作了;

    对于kube-Apiserver来讲,认证是一种功能,这个功能kube-Apiserver是通过插件机制引入的,准入控制也是利用插件引入的,也就意味着,我们想在APIServer中使用某一种认证功能,认证有多种认证机制,每一种机制都要一个认证插件,可以选择在kube-Apiserver启动时,激活哪一个插件,来做认证插件, 甚至于还可以同时激活多个插件,都可以生效,授权、准入控制也一样。但是只有激活的才生效,未激活的不会生效,如果某一个用户访问请求来了,这么多认证插件也是有先后顺序的,对顺序并不是特别关键,因为它的认证逻辑很特别,叫一票通过,比如一个用户进来了,发现这个用户名是cce,第一个插件不认识cce,它没允许但是也没拒绝,就会直接跳到下一个插件,只要一个通过就OK,后面也不再检查,插件对于不认识的用户不允许也不拒绝;
    Kubernetes的授权的逻辑有点独特,叫做许可授权,意思是一个用户来访问了只要APIServer允许就允许,如果没给定允许,那就是不认可,那就下一个插件检查,如果插件给定的结果是允许,那就允许了,自上而下一票通过,拒绝是默认值,没有显示告知允许,那就拒绝;
    准入控制的插件也可以有多个,准入控制插件的逻辑有点独特,它是一票否决的,只要有一票否定了,那么就拒绝了,也是自上而下,第一个检查通过,继续检查第二个不通过就不通过,如果一直通过就直接检查到最后一个,每一个都成功结果才是成功;

认证Authentication

    Kubernetes支持很多种认证插件,如Client Certificates(客户端证书认证)、Passwd、Plain Tokens(令牌):Bootstrap Tokens(引导令牌,引导集群启动,或者加入集群使用的令牌) 以及 JWT Tokens(JSON的web Token,基于HTTP协议携带json格式的令牌);
    在Kubernetes之上认证是双向的,也就是说kubectl接入APIServer时,双方要互相认证,kubectl会把自己的证书提交给APIServer并请求APIServer的证书,此时kubectl是客户端证书APIServer是服务端证书,他们互相做双向认证,kubectl要检查这个证书是不是自己认可的信任的机制颁发的,这个证书持有者所声明的身份要和证书中的身份一直,证书还需要在有效期内,一旦条件不满足,认证就失败,集群需要很多证书,但是一般Kubernetes集群所有的证书都不会使用一样的,认证通过之后还需要知道这个认证是谁来认证的,如果这个证书颁发给某一个用户了,那么这个证书里面的subject里面的CN就是哪个用户(举例),很显然,每个用户都需要不同的证书,来进行用户身份判断,这样才能做到分用户或分客户端进行不同的授权;
    Kubernetes对于权限的检查,还需要知道哪个用户的哪个权限,按上面的例子来说用户就是subject里面的CN,所以我们创建的每一个证书的CN的名字还要和Kubernetes内建的用户名一致,subject里面还有一个字段叫做ower,O是用户所属的部门,O就相当于用户的组,CN是用户名,所以我们就可以在Kubernetes上利用Auth Policies对一个组授权,加入到这个组中的所有用户都有同一个权限,所以必要的时候多个用户要拥有通一个权限就可以在同一个组的方式来实现权限控制;
        Client Certificates:类似就是公私密钥认证的;
        Passwd:用户名和密码认证,APIServer是一个https服务器,用户名和密码只能在http协议的首部传输过去的, 把用户名和密码通过base64编码之后通过header进行传输;
        Plain Tokens: 就说一段随机字符,称之为令牌,没有用户名;
        Bootstrap Tokens: 主要是用于构建集群时使用的,在构建k8s集群的时候一般先要创建master,而后还需要把每一个node加入master,node是合法的也确实允许加入集群这是靠令牌来实现的,因为一般来讲所有节点的证书应该是不一样的,如果会带来问题,如果证书过期了,那么整个集群中的node都会出现问题,所以Kubernetes在controller manager内部内建了一个控制器, 这个控制器能够实现让一个节点加入的时候是没有证书的,因为这个kube-apiserver能够帮node节点在加入进来的时候,为它的kubelet生成一个证书签署请求,并且通过APIServer自己信任的这个CA自动签署好并且发送给kubelet ,然后kubelet才接入APIServer还有一种用户,就是Anonymomus匿名用户,没有被任何插件允许,处于安全考虑,Kubernetes一般禁用匿名用户;;

用户

    在Kubernetes中用户有两种,第一种是常规用户,Normal user,用户客户端操作的用户,第二类是服务账号,在我们的Kubernetes集群之上可能有一个Pod里面的服务程序也需要连接到APIServer,像这种是程序作为一个守护进程需要连入APIServer认证并获得相关授权时,它就需要附带着用户身份,这种用户就叫做服务帐号;
    二者还有一个非常大的区别是,常规用户是在Kubernetes上不存在的,客户端意思是说只要拿着证书发给APIServer,APIServer只要认可这个证书就可以, 只要认证通过那么这个证书里面的用户名是谁,那么这个客户端就可以拥有谁的权限;
    服务帐号是在Kubernetes系统上存在的帐号,它也是Kubernetes之上的一个标准资源,它通过secrets保存着这个用户帐号要接入APIServer时的密码,作为一个secrets资源存在;
启用证书
    APIServer自己内建了认证授权和准入控制功能,也内建了很多认证或授权或者准入控制插件,到底启用哪一个不启用哪一个可以在启用APIServer的时候,传递选项来定义,定义参数如下;
Client Certificates:--client-ca-file=SOMEFILE启用证书认证;
Static Token File:静态令牌认证,简单的明文令牌认证,--token-auth-file=SOMEFILE启用静态令牌认证;
Bootstrap Tokens:Bootstrap的令牌文件也需要指定,定义方式是为kube-apiserver使用--experimental-bootstrap-token-auth选项,为kube-controller-manager启用"--controllers=*,tokencleaner"选项;
ServiceAccount Tokens:--service-account-key-file用于为承载令牌进行数字前面的PEM格式的密钥,缺省使用APIServer的TLS私钥,--service-account-lookup是否吊销删除的令牌;
kubeconfig配置文件简介
    一个kubeconfig文件是 YAML格式的配置文件,它通常由以下几个配置段组成;
users:用户帐号及其认证信息列表;
clusters:目标集群列表;
contexts:以哪个user接入哪个clustere的连接组合;
curren-context:当前使用的context;

    users主要用于定义用户及其帐号信息,它是一个列表,可以有多个用户,一个用户可以接入多个集群,一对多的关系,cluster可以定义目标集群信息,contexts定义了上下文,它就是把用户和集群建立一一映射的组合关系,比如user1可以接入cluster1,user2可以接入cluster1和cluster2,每一个contexts就叫做上下文,但是当有很多上下文的时候到底用哪一个取决于clurren-context,它是表示当前客户端使用什么上下文,它可以切换的,所以使用一个配置文件可以连接一个或者多个集群,只需要切换一下curren-context就可以了;
授权配置管理
    至于切换上下文,设置users设置contexts等都可以使用命令行的方式来定义,其主要配置的是/etc/kubernetes/admin.conf;
语法:kubectl config subcommand [option]
参数:
    set-cluster:添加集群;
    set-context:添加一个上下文;
    set-credentials:添加用户信息;
    set:添加集群、添加用户、添加上下文;
    use-context:指定当前使用的上下文;
    view:查看当前配置;
    ...
配置介绍
[root@node1 ~]# kubectl --kubeconfig=/etc/kubernetes/admin.conf config view
apiVersion: v1
clusters:  # cluster配置段
- cluster: # 集群配置的标识,因为可以有多个所有得有所区分
    certificate-authority-data: DATA+OMITTED # 集群的CA证书,APIServer所信任的CA的证书
    server: https://172.16.1.2:6443 # 集群Server地址
  name: kubernetes # 集群名称
contexts: # contexts配置段
- context: # 上下文的表示,因为可以有多个所有得有所区分
    cluster: kubernetes # 这个上下文接入的cluster
    user: kubernetes-admin # 这个上下文使用的user
  name: kubernetes-admin@kubernetes # 上下文名称
current-context: kubernetes-admin@kubernetes # 当前使用的上下文
kind: Config
preferences: {}
users: # users配置段
- name: kubernetes-admin # 定义一个users,名为kubernetes-admin
  user: # 该user的信息
    client-certificate-data: REDACTED # 公钥
    client-key-data: REDACTED # 私钥
    要想能够认证到Kubernetes集群上, 可以使用证书认证、令牌认证、密码认证而如果要使用用户名和密码认证,就必须要修改kube-apiserver在启动时,加载的账号密码文件,因为默认是没有加载任何账号密码文件的,同样的如果使用令牌得加载令牌文件,最简单的可以后期定制的不用重新加载的可以使用它所信任的私有CA签署一个客户端证书即可,客户端只需要拿着证书,那么就能够认证成功,而Kubernetes所信任的CA就在我们的/etc/kubernetes/pki里面他们分别是ca.crt和ca.key,那么我们就自己生成一个私钥,手动用这个ca去签署得到一个证书,那拿着这个客户端签署的证书就恶意直接接入kube-apiserver,CN代表用户名,能够认证通过,对应的这个获得了哪些权限它就能拥有哪些权限,而且这个用户名不需要在Kubernetes之上存在,系统上有一个组叫做masters,这是一个管理员组,如果创建一个cce的用户加入这个组,那么cce就是管理员;
Kubernetes证书
整个Kubernetes之上要使用的证书有以下组件:
    etcd:etcd保存着整个Kubernetes集群之上的资源,所以etcd也一定不能非授权访问,它的认证方式和APIServer的方式一样,默认使用证书认证,etcd是服务端,APIServer是etcd的客户端,为了保证etcd不被非授权访问,要做一个专门的CA,让etcd各节点之间通信要使用一个证书,自己做个CA证书,只用于节点之间通信,这叫做节点证书,还需要为每一个节点发送一个Server端证书,而后再给APIServer发生一个Client证书,APIServer与etcd通信也需要拿着Client证书才可以;
然后对于集群组件连接到APIServer还需要证书:
    这个时候还需要一套CA,然后给各组件通信到APIServer各一套证书,因为上面说过不同组件实现不同功能,组件也不能非授权访问,Kubernetes是没有用户这么一说的,用户的区别主要是以证书里面的信息来区分的,比如说CN就是用户名,所以每个组件的证书我们应该使用不一样的,而不能使用同一个证书,因为Kubernetes组件到APIServer是双向认证的,也就是说APIServer到组件也需要证书,也就是说APIServer->组件,组件->APIServer一套,每个组件两套证书,好在这个APIServer->组件的证书会自己生成;
APIServer
APIServer定义了当前资源可以使用哪几种资源类型,Pod、StatefulSet等,我们都称之为资源类型,如果我们认为Kubernetes内建的存储方案不够用,需要自定义一个,那有三种方案,第一把APIServer拿到修改go源码;
第二自建APIServer,自己写一个程序,这个程序引入新的资源类型,它自己可以以Pod方式跑在Kubernetes,这就叫自定义资源类型,类似我们的Operator,只不过我们要做的是APIServer,Operator是一个控制器,Kubernetes为了方便用户自定义开发自己的APIServer就设计了一个扩展接口,整个APIServer包含三个组件,第一个kube-aggregator主要用于反代,第二个kube-apiserver,第三个extension--apiserver,所以当我们需要自定义apisever的时候只需要配置我们的kube-aggregator就好了默认资源反代给kube-apiserver,自定义反代给extension--apiserver,所以kube-aggregator到extension--apiserver还需要一套证书;
APIServer相关所有的证书都在这里:~]# cat /etc/kubernetes/manifests/kube-apiserver.yaml

自建证书连接到Kubernetes

--kubeconfig:指定要配置的配置文件;
--embed-certs:证书和私钥打包存放;
--username:指定用户名,在此处无实际意义,因为用户主要是靠证书的CN来区分的,但是这里最好和证书里面的CN一致;
# 创建一个私钥
[root@node1 ~]# openssl genrsa -out cce.key 2048
# 为证书生成一个证书签署请求,并且使用kubernetes的CA来签署
[root@node1 ~]#  openssl req -new -key cce.key -out cce.csr -subj "/CN=cce/O=kubernetes"
# 利用kubernetes的CA来签署我们的证书
[root@node1 ~]#  openssl x509 -req -in cce.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out cce.crt -days 36500
[root@node1 ~]# mkdir /etc/kubernetes/cce
[root@node1 ~]# mv cce.crt cce.csr cce.key /etc/kubernetes/cce/
# 创建一个新的kubeconfig文件
[root@node1 ~]# kubectl config set-cluster cce-cluster --server=https://172.16.1.2:6443 --certificate-authority=/etc/kubernetes/pki/ca.crt --embed-certs=true --kubeconfig=/etc/kubernetes/cce.conf
# 设定用户配置信息
[root@node1 ~]# kubectl config set-credentials cce --client-certificate=/etc/kubernetes/cce/cce.crt --client-key=/etc/kubernetes/cce/cce.key --username=cce --kubeconfig=/etc/kubernetes/cce.conf --embed-certs=true
# 将user和cluster组合起来形成一个context
[root@node1 ~]# kubectl config set-context context-cce --cluster=cce-cluster --user=cce --kubeconfig=/etc/kubernetes/cce.conf
# 指定默认使用的context
[root@node1 ~]# kubectl config use-context --kubeconfig=/etc/kubernetes/cce.conf context-cce 
# 测试使用该配置连接Kubernetes集群
[root@node1 ~]# kubectl get pods --kubeconfig=/etc/kubernetes/cce.conf   # 权限被拒绝
Error from server (Forbidden): pods is forbidden: User "cce" cannot list resource "pods" in API group "" in the namespace "default

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注