安全

Kubernetes 作为一个分布式集群的管理工具,保证集群的安全性是其一个重要的任务。API Server 是所有服务访问统一入口,所有的组件都只和APIServer交互,除此之外并不两两交互,因此API Server 是集群内部各个组件通信的中介,也是外部控制的入口。所以 Kubernetes 的安全机制基本就是围绕保护 API Server 来设计的

Kubernetes 使用了认证(Authentication)、鉴权(Authorization)、准入控制(Admission
**Control)**三步来保证 API Server 的安全

认证

认证方式
  • HTTP Base 认证:通过 用户名+密码 的方式认证。用户名+:+密码 用 BASE64 算法进行编码后的字符串放在 HTTP Request 中的 Heather。Authorization 域里发送给服务端,服务端收到后进行编码,获取用户名及密码;
  • HTTP Token 认证:通过一个 Token 来识别合法用户。HTTP Token 的认证是用一个很长的特殊编码方式的并且难以被模仿的字符串 Token 来表达客户的一种方式。Token 是一个很长的很复杂的字符串,每一个 Token 对应一个用户名存储在 API Server 能访问的文件中。当客户端发起 API 调用请求时,需要在 HTTP Header 里放入 Token;
  • 最严格的 HTTPS 证书认证:基于 CA 根证书签名的客户端身份认证方式。

k8s 采用的就是 HTTPS 的双向认证方式:

各组件的认证

需要认证的组件分两种类型:

  • Kubenetes 组件对 API Server 的访问:kubectl、Controller Manager、Scheduler、kubelet、kubeproxy;
  • Kubernetes 管理的 Pod 对容器的访问:Pod(dashborad 也是以 Pod 形式运行)

端口访问说明:

  • Controller Manager、Scheduler、kubectl、kubelet、kube-proxy访问API Server都需要证书进行HTTPS双向认证,只不过CM、Scheduler、kubectl使用的证书是放在对应的kubeconfig。
  • Pod访问API Server通过自动颁发的SA,是一种单向认证方式。
kubeconfig

kubeconfig 是一个文件类型,文件包含集群参数(CA证书、API Server地址),客户端参数(为某个用户生成的证书和私钥),集群 context 信息(集群名称、用户名),可以理解为一个认证函,里面包含了怎么访问服务的信息以及认证信息Kubenetes 组件(CM、Scheduler、kubectl)通过启动时指定一个 kubeconfig 文件来确定操作哪一个集群以及操作权限,以及访问API Server的证书等。

查看主节点下的 ~/.kube/config 的文件:

$ ls ~/.kube
cache
config:kubeconfig文件
http-cache

项目中通常有多个k8s集群,也就是环境/上下文:dev、testing、staging、prod,kubectl在多个环境中切换,操作Pod等资源对象。

ServiceAccount(SA)(给Pod颁发证书)

Service Account 用来访问 Kubernetes API,由 Kubernetes 自动创建,并且会自动挂载到 Pod/run/secrets/kubernetes.io/serviceaccount 目录中。并不是所有的Pod都能访问Kubernetes API,只有拥有Service Account的Pod才能访问。

# 1. 随便找一个需要访问 Kubernetes API 的 Pod
$ kubectl get pod -n kube-system
NAME                                   READY   STATUS    RESTARTS   AGE
kube-proxy-2pqkk                       1/1     Running   6          40d

# 2. 查看该 Pod 中 /run/secrets/kubernetes.io/serviceaccount 目录下的文件
$ kubectl exec kube-proxy-2pqkk -n kube-system -it -- ls /run/secrets/kubernetes.io/serviceaccount
ca.crt:访问 API Service 时的证书
namespace:名称空间
token:认证的密钥信息

ServiceAccount 中包含三个部分:

  • Token 是使用 API Server 私钥签名的 JWT。用于访问 API Server 时,Server 端认证;
  • ca.crt:根证书。用于 Client 端验证 API Server 发送的证书;
  • namespace,标识这个 service-account-token 的作用域名空间。

Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

默认情况下,每个 namespace 都会有一个 ServiceAccount,如果 Pod 在创建时没有指定 ServiceAccount,就会使用 Pod 所属的 namespace 的 ServiceAccount。

认证证书相关文件
序号证书作用类型
1etcd节点间通讯的证书服务器和客户端证书(因节点间互相访问)
2etcd向外提供服务使用服务器证书(因被访问)
3apiserver访问etcd使用客户端证书
4apiserver对外提供服务使用服务器证书
5kube-controller-manager访问apiserver使用客户端证书
6kube-scheduler访问 apiserver使用客户端证书
7kube-proxy访问apiserver使用客户端证书
8kubelet访问apiserver使用客户端证书
9kubectl访问apiserver使用客户端证书
10kubelet对外提供服务使用服务器证书
11apiserver访问kubelet使用客户端证书
12kube-controller-manager生成和验证service-accout token的证书并不需要证书,实际上使用的是公钥和私钥,k8s为server accout 生成JWT token,secret将此token加载到pod上,公钥分配到apiserver上用于验证,私钥分配到pod上用于数字签名
服务端证书

互联网上的证书需要由权威CA机构签发,同时还要缴纳一笔不菲的费用,但由于Kubernetes目的是要在集群内建立信任体系,因此使用自签CA就足够了。Kubernetes需要一个根CA,集群中的证书(除了kubelet的服务端证书)都需要由该根CA或者基于该CA的中间CA签发。使用kubeadm init创建控制平面时,kubeadm会为我们生成根CA,以及相关证书。kubeadm创建的集群的默认证书存放路径为/etc/kubernetes/pki

在**/etc/kubernetes/pki**文件夹下,保存了服务端认证证书的相关文件:

├── admin.conf
├── controller-manager.conf
├── kubelet.conf
├── manifests
│   ├── etcd.yaml
│   ├── kube-apiserver.yaml
│   ├── kube-controller-manager.yaml
├── pki
│   ├── apiserver.crt   //apiServer对外提供服务,server证书
│   ├── apiserver.key
│   ├── apiserver-etcd-client.crt   //apiServer请求etcd服务,client证书
│   ├── apiserver-etcd-client.key
│   ├── apiserver-kubelet-client.crt  //apiServer请求kubelet,client证书
│   ├── apiserver-kubelet-client.key
│   ├── ca.crt    //根证书
│   ├── ca.key    //根证书私钥
│   ├── etcd    
│   │   ├── ca.crt   //etcd根证书
│   │   ├── ca.key
│   │   ├── healthcheck-client.crt
│   │   ├── healthcheck-client.key
│   │   ├── peer.crt  //etcd各节点通信peer证书,server/client证书
│   │   ├── peer.key
│   │   ├── server.crt  //etcd对外提供服务,server证书
│   │   └── server.key
│   ├── front-proxy-ca.crt  //用于前端代理证书
│   ├── front-proxy-ca.key
│   ├── front-proxy-client.crt
│   ├── front-proxy-client.key
│   ├── sa.key   //用来生成sa的私钥和公钥,并不需要证书
│   └── sa.pub
├── scheduler.conf
└── tmp [error opening dir]

根CA需要在集群中所有的节点上都保存一份,因为集群的各组件都需要使用该CA证书来验证证书签名。

当然,除了这些证书外,还有kublet、 kube-proxy、 kube-controller-manager、kube-scheduler、kubectl访问apiserver的证书。

kublet相关证书

(1)kubelet访问kube-apiserver时需要带上客户端证书(TLS双向校验),证书的默认保存路径为/var/lib/kubelet/pki

[root@master-1 ~]# ls -lh /var/lib/kubelet/pki
总用量 12K
-rw-------. 1 root root 2.7K 98 22:59 kubelet-client-2022-08-08-22-59-01.pem
lrwxrwxrwx. 1 root root   59 98 22:59 kubelet-client-current.pem -> /var/lib/kubelet/pki/kubelet-client-2022-08-08-22-59-01.pem
-rw-r--r--. 1 root root 2.2K 98 22:58 kubelet.crt
-rw-------. 1 root root 1.7K 98 22:58 kubelet.key

其中kubelet-client-2022-08-08-22-59-01.pem表示的是客户端证书,kubelet-client-current.pem是证书的一个软链接。证书之所以带日期是因为kubelet的证书快过期时会自动更新,因此带上时间方便区分新旧证书。查看一下证书信息:

[root@master-1 ~]# openssl x509 -noout -text -in /var/lib/kubelet/pki/kubelet-client-current.pem
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 7265397798501666783 (0x64d3e0cdd398cfdf)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=kubernetes
        Validity
            Not Before: Sep  8 14:58:50 2021 GMT
            Not After : Sep  8 14:58:55 2022 GMT
        Subject: O=system:nodes, CN=system:node:master-1 # O表示Organization, CN表示Common Name
        Subject Public Key Info:
        ...

可以看到kubelet的用户组是system:nodes, 用户名为system:node:master-1。有了这些信息,kube-apiserver就可以基于Node Authorizer来限制kubelet只能读取和修改本节点上的资源。

(2)类似的,kube-apiserver调用kubelet接口时(执行exec/logs命令),kubelet也会要求校验kube-apiserver的客户端证书,该证书保存路径为/etc/kubernetes/pki/apiserver-kubelet-client.crt。查看一下证书信息:

[root@master-1 ~]# openssl x509 -noout -text -in /etc/kubernetes/pki/apiserver-kubelet-client.crt
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 5903840273946896753 (0x51eea773033fe971)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=kubernetes
        Validity
            Not Before: Sep  8 14:58:50 2021 GMT
            Not After : Oct  5 12:36:44 2022 GMT
        Subject: O=system:masters, CN=kube-apiserver-kubelet-client
        ....

该证书的用户组是system:masters,这是Kubernetes内置的用户组,Kubernetes会自动为该用户组绑定集群管理员权限。

# ClusterRole/cluster-admin 包含了所有操作权限,当kube-apiserver启动时,会自动将该权限绑定给system:masters用户组。
[root@master-1 ~]# kubectl get clusterRoleBinding cluster-admin -owide
NAME            ROLE                        AGE   USERS   GROUPS           SERVICEACCOUNTS
cluster-admin   ClusterRole/cluster-admin   27d           system:masters
kube-controller-manager、kube-scheduler、kube-proxy

kube-controller-managerkube-scheduler访问kube-apiserver时也需要带上客户端证书,它们的客户端证书都保存在各自的kubeconfig凭证中。

[root@master-1 ~]# ls -ls /etc/kubernetes/controller-manager.conf /etc/kubernetes/scheduler.conf
8 -rw-------. 1 root root 5486 105 20:36 /etc/kubernetes/controller-manager.conf
8 -rw-------. 1 root root 5438 105 20:36 /etc/kubernetes/scheduler.conf

查看证书内容,会发现kube-controller-manager的用户名为system:kube-controller-managerkube-scheduler的用户名为system:scheduler。即使是内部组件,基于最小权限原则,Kubernetes依然会为这两个用户只绑定必要的权限。

#生成 kube-controller-manager.kubeconfig 文件
##查看,文件里会有密钥,这里忽略,server: 地址就是k8s master 地址
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: =
    server: https://192.168.100.170:6443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: kube-scheduler
    namespace: default
current-context: default
kind: Config
preferences: {}
users:
- name: kube-scheduler
  user:
    client-certificate-data: 
    client-key-data: =

👉 可以看看二进制部署K8S中的kube-scheduler:5、二进制安装K8s 之 部署kube-scheduler - 小兔几白又白 - 博客园 (cnblogs.com)

kube-proxy默认使用的是admin集群证书,类似,也是保存在在kubeconfig中。当kube-proxy启动的时候就会运行该配置:/etc/kubernetes/kube-proxy.kubeconfig。(我也不知道对不对,没有具体实验)

也可以为kube-proxy手动创建一个kubeconfig:修复kube-proxy证书权限过大问题 - 掘金 (juejin.cn)

apiVersion: v1
clusters:
- cluster:
    certificate-authority: ssl/ca.pem
    server: https://192.168.1.71:6443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: kube-proxy
  	namespace: default
current-context: default
kind: Config
preferences: {}
users:
- name: kube-proxy
  user:
    as-user-extra: {}
    client-certificate: ssl/kube-proxy-1-71.pem
    client-key: ssl/kube-proxy-1-71.key
kubectl相关证书(⭐)

kubectl的证书比较特别,当我们使用kubectl访问kube-apiserver时,也要提供客户端证书。kubectl使用的客户端证书存放在kubeconfig中:

[root@master-1 ~]# cat ~/.kube/config
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: <表示CA证书,很长的一段base64编码>server: https://192.168.33.220:6443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: kubernetes-admin
  name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
  user:
    client-certificate-data: <表示客户端证书,很长的一段base64编码> ②
    client-key-data: <表示客户端证书私钥,很长的一段base64编码>
  1. 由于是自签CA,所以kubeconfig里也要保存根CA的证书(certificate-authority-data),用于校验kube-apiserver的服务端证书;

  2. 客户端证书使用base64编码后保存在client-certificate-data字段中。查看一下客户端证书部分:

    [root@master-1 ~]# grep "client-certificate-data" ~/.kube/config \
    | sed 's/\s*client-certificate-data:\s*//' \
    | base64 -d | openssl x509 -noout -text
    Certificate:
        Data:
            Version: 3 (0x2)
            Serial Number: 368716738867724927 (0x51df1eba2f3827f)
        Signature Algorithm: sha256WithRSAEncryption
            Issuer: CN=kubernetes
            Validity
                Not Before: Sep  8 14:58:50 2021 GMT
                Not After : Sep  8 14:58:54 2022 GMT
            Subject: O=system:masters, CN=kubernetes-admin # O表示Organization, CN表示Common Name
            ....
    
  3. 可以看到,该kubeconfig的用户名为kubernetes-admin,用户组为system:masters,即拥有所有权限。示例中的kubeconfigkubeadm默认创建的集群管理员凭证,集群管理员可以通过签发任意的用户组和用户名的证书,制作成kubeconfig结合RBAC权限管理来控制权限

服务器证书时间修改

查看SA\CA证书到日期:

openssl x509 -in apiserver.crt -text -noout
openssl x509 -in ca.crt -text -noout

可以看到根证书的有效时间是10年,apiserver证书是1年。

在前面,我们说过,使用kubeadm init创建控制平面时,kubeadm会为我们生成根CA,以及相关证书,因此证书时间修改也就四步:

  • 下载kubeadm源码;
  • 修改kubeadm的证书时间代码;
  • 编译代码并替换旧的kubeadm;
  • 生成新的证书文件;

kubeadm是基于go语言编写的,因此要先安装go环境。

(1)go 环境部署

wget https://dl.google.com/go/go1.12.7.linux-amd64.tar.gz
tar -zxvf go1.12.1.linux-amd64.tar.gz -C /usr/local
vi /etc/profile
export PATH=$PATH:/usr/local/go/bin
source /etc/profile
go version

(2)下载源码

cd /data && git clone https://github.com/kubernetes/kubernetes.git
git checkout -b remotes/origin/release-1.15.1 v1.15.1

(3)修改 Kubeadm 源码更新证书策略

vim staging/src/k8s.io/client-go/util/cert/cert.go # kubeadm 1.14 版本之前
vim cmd/kubeadm/app/util/pkiutil/pki_helpers.go # kubeadm 1.14 至今  const duration365d = time.Hour * 24 * 365 * 10  NotAfter:time.Now().Add(duration365d).UTC(),

(4)编译并替换旧的文件

#  保存退出后编译
make WHAT=cmd/kubeadm GOFLAGS=-v

# 备份一下旧的并使用新的
cp output/bin/kubeadm /root/
cp /usr/bin/kubeadm /usr/bin/kubeadm.old
cp /root/kubeadm /usr/bin/
chmod a+X /usr/bin/kubeadm
# 备份一下旧的pki
cd /etc/kubernetes/
cp -r pki/ pki.old

(5)生成所有证书

# 重新生成所有证书文件
kubeadm alpha certs renew all --config=/usr/local/install-k8s/core/kubeadm-config.yaml

(6)查看证书

# 重新查看证书时间
cd /etc/kubernetes/pki
openssl x509 -in apiserver.crt -text -noout # 十年

(7)HA集群其余 mater 节点证书更新

#!/bin/bash
masterNode="192.168.66.20 192.168.66.21"
#for host in ${masterNode}; do
#  scp /etc/kubernetes/pki/{ca.crt,ca.key,sa.key,sa.pub,front-proxy-ca.crt,front-proxy-ca.key}
"${USER}"@$host:/etc/kubernetes/pki/
#  scp /etc/kubernetes/pki/etcd/{ca.crt,ca.key} "root"@$host:/etc/kubernetes/pki/etcd
#  scp /etc/kubernetes/admin.conf "root"@$host:/etc/kubernetes/
#done
for host in ${CONTROL_PLANE_IPS}; do
  scp /etc/kubernetes/pki/{ca.crt,ca.key,sa.key,sa.pub,front-proxy-ca.crt,front-proxy-ca.key}
"${USER}"@$host:/root/pki/
  scp /etc/kubernetes/pki/etcd/{ca.crt,ca.key} "root"@$host:/root/etcd
  scp /etc/kubernetes/admin.conf "root"@$host:/root/kubernetes/
done

授权

上面认证过程,只是确认通信的双方都确认了对方是可信的,可以相互通信。而鉴权是确定请求方有哪些资源的权限。API Server 目前支持以下几种授权策略 (通过 API Server 的启动参数 “–authorization-mode” 设置):

  • AlwaysDeny:表示拒绝所有的请求,一般用于测试;
  • AlwaysAllow:允许接收所有请求,如果集群不需要授权流程,则可以采用该策略;
  • ABAC(Attribute-Based Access Control):基于属性的访问控制,表示使用用户配置的授权规则对用户请求进行匹配和控制;
  • Webbook:通过调用外部 REST 服务对用户进行授权;
  • RBAC(Role-Based Access Control)(默认):基于角色的访问控制,现行默认规则
RBAC

RBAC(Role-Based Access Control)基于角色的访问控制,在 Kubernetes 1.5 中引入,现行版本成为默认标准。相对其它访问控制方式,拥有以下优势:

  • 对集群中的资源和非资源均拥有完整的覆盖;
  • 整个 RBAC 完全由几个 API 对象完成,同其它 API 对象一样,可以用 kubectl 或 API 进行操作;
  • 可以在运行时进行调整,无需重启 API Server

RBAC 引入了 4 个新的顶级资源对象:RoleClusterRoleRoleBindingClusterRoleBinding,4 种对象类型均可以通过 kubectl 与 API 操作。

  • Role 和 RoleBinding 是名称空间级别资源,ClusterRole 和 ClusterRoleBinding 是集群级别资源;
  • Role 与 RoleBinding 之间的关系如下图所示,Role 用来指定一些角色,每个角色对资源的权限不同,RoleBinding 将这些角色赋予给用户、组和SA 。

需要注意的是 Kubenetes 并不会提供用户管理,那么 UserGroupServiceAccount 指定的用户又是从哪里来的呢? Kubenetes 组件(kubectl、kube-proxy)或是其他自定义的用户在向 CA 申请证书时,需要提供一个json文件:

{
  "CN": "admin",	# Common Name(CN),公用名,一般是主机名+网站域名
  "hosts": [],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",		# Country Code(C):国家,只能是两个字母的国家码
      "ST": "HangZhou",			# State or Province(S),省名或者州名
      "L": "XS",				# Locality(L),城市名
      "O": "system:masters",	# Organization Name(O),单位名称
      "OU": "System"	# Organization Unit(OU),部门
    }
  ]
}
  • 这个json文件会用于生成证书,API Server 会把客户端证书的 CN 字段作为 User,把 names.O 字段作为 Group;

  • kubelet 使用 TLS Bootstaping 认证时,API Server 可以使用 Bootstrap Tokens 或者 Token authentication file 验证 =token,无论哪一种,Kubenetes 都会为 token 绑定一个默认的 User 和 Group;

  • Pod 使用 ServiceAccount 认证时,service-account-token 中的 JWT 会保存 User 信息。

有了用户信息,再创建一对 角色/角色绑定(集群角色/集群角色绑定)资源对象,就可以完成权限绑定了。

Role / ClusterRole

在 RBAC API 中,Role 表示一组规则权限,权限只会增加(累加权限),不存在一个资源一开始就有很多权限而通过
RBAC 对其进行减少的操作;Role 可以定义在一个 namespace 中,如果想要跨 namespace 则可以创建 ClusterRole

kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  namespace: default	# 名称空间
  name: pod-reader
rules:
  - apiGroups: [""] 	# api组,"" 为空代表的是 core 核心组
    resources: ["pods"]		# 资源对象
    verbs: ["get", "watch", "list"]		# 操作动作

ClusterRole 具有与 Role 相同的权限角色控制能力,不同的是 ClusterRole 是集群级别的,ClusterRole 可以用
于:

  • 集群级别的资源控制( 例如 node 访问权限 );
  • 非资源型 endpoints( 例如 /healthz 访问 );
  • 所有命名空间资源控制(例如 pods )。
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: secret-reader
rules:
  - apiGroups: [""]		# api 组
    resources: ["secrets"]		# 资源对象
    verbs: ["get", "watch", "list"]		# 操作动作
reource

Kubernetes 集群内一些资源一般以其名称字符串来表示,这些字符串一般会在 API 的 URL 地址中出现;同时某些资源也会包含子资源,例如 logs 资源就属于 pods 的子资源,API 中 URL 样例如下:

GET /api/v1/namespaces/{namespace}/pods/{name}/log

如果要在 RBAC 授权模型中控制这些子资源的访问权限,可以通过 / 分隔符来实现,以下是一个定义 pods 资源 logs 访问权限的 Role 定义样例

kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  namespace: default
  name: pod-and-pod-logs-reader
rules:
  - apiGroups: [""]
    resources: ["pods/log"]
    verbs: ["get", "list"]
RoleBinding / ClusterRoleBinding

RoloBinding 可以将角色中定义的权限授予用户或用户组,RoleBinding 包含一组权限列表(subjects),权限列表中包含有不同形式的待授予权限资源类型(users, groups, SA);RoloBinding 同样包含对被 Bind 的 Role 引用;RoleBinding 适用于某个命名空间内授权,而 ClusterRoleBinding 适用于集群范围内的授权

  • RoleBinding可以将user、groups、sa绑定一个role和clusterrole,虽然能绑定clusterrole,但是还是作用于RoleBinding指明的命名空间;
  • ClusterRoleBinding可以将user、groups、sa绑定一个clusterrole,作用于整个集群。

将 default 命名空间的 pod-reader Role 授予 jane 用户,此后 jane 用户在 default 命名空间中将具有 pod-reader 的权限:

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: read-pods
  namespace: default	# 名称空间
subjects:			# 权限列表
  - kind: User		# User 类型
    name: jane		# 用户名
    apiGroup: rbac.authorization.k8s.io		# api 组
roleRef:		# 角色
  kind: Role	# 类型
  name: pod-reader		# Role 名字
  apiGroup: rbac.authorization.k8s.io		# api组

RoleBinding 同样可以引用 ClusterRole 来对当前 namespace 内用户、用户组或 ServiceAccount 进行授权,这种操作允许集群管理员在整个集群内定义一些通用的 ClusterRole,然后在不同的 namespace 中使用 RoleBinding 来引用

例如,以下 RoleBinding 引用了一个 ClusterRole,这个 ClusterRole 具有整个集群内对 secrets 的访问权限;但是其授权用户 dave 只能访问 development 空间中的 secrets(因为 RoleBinding 定义在 development 命名空间):

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: read-secrets
  namespace: development # 名称空间,只授予 development 名称空间内的权限
subjects:
  - kind: User
    name: dave
    apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

使用 ClusterRoleBinding 可以对整个集群中的所有命名空间资源权限进行授权;以下 ClusterRoleBinding 样例展示了授权 manager 组内所有用户在全部命名空间中对 secrets 进行访问:

kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: read-secrets-global
subjects:
  - kind: Group		# 绑定给一个组
    name: manager	# 组名
    apiGroup: rbac.authorization.k8s.io # api接口
roleRef:
  kind: ClusterRole	# ClusterRole
  name: secret-reader # 名称
  apiGroup: rbac.authorization.k8s.io # api接口
Subjects

RoleBinding 和 ClusterRoleBinding 可以将 Role 绑定到 Subjects;Subjects 可以是 groups、users 或者 service accounts

Subjects 中 Users 使用字符串表示,它可以是一个普通的名字字符串,如 “alice”;也可以是 email 格式的邮箱地址,如 “xxx@163.com”;甚至是一组字符串形式的数字 ID 。但是 Users 的前缀 system: 是系统保留的,集群管理员应该确保普通用户不会使用这个前缀格式。(比如kubelet就是属于system:node组,名字可以是system.node:master-1)

Groups 书写格式与 Users 相同,都为一个字符串,并且没有特定的格式要求;同样 system: 前缀为系统保留。

准入控制

准入控制是 API Server 的插件集合,通过添加不同的插件,实现额外的准入控制规则。甚至于 API Server 的一些主要的功能都需要通过 Admission Controllers 实现,比如 ServiceAccount。

官方文档上有一份针对不同版本的准入控制器推荐列表,其中最新的 1.16 的推荐列表是:

NamespaceLifecycle, LimitRanger, ServiceAccount, TaintNodesByCondition, Priority, DefaultTolerationSeconds, 
DefaultStorageClass, StorageObjectInUseProtection, PersistentVolumeClaimResize, MutatingAdmissionWebhook,
ValidatingAdmissionWebhook, RuntimeClass, ResourceQuota

列举几个插件的功能:

  • NamespaceLifecycle: 防止在不存在的 namespace 上创建对象,防止删除系统预置 namespace,删除 namespace 时,连带删除它的所有资源对象;
  • LimitRanger:确保请求的资源不会超过资源所在 Namespace 的 LimitRange 的限制;
  • ServiceAccount: 实现了自动化添加 ServiceAccount;
  • ResourceQuota:确保请求的资源不会超过资源的 ResourceQuota 限制。

操作:

  1. 启用 NamespaceLifecycle 和 LimitRanger 准入控制插件:

    kube-apiserver --enable-admission-plugins=NamespaceLifecycle,LimitRanger
    
  2. 禁用插件:

    kube-apiserver --disable-admission-plugins=PodNodeSelector,AlwaysDeny ...
    
  3. 查看默认启用的插件:

    kube-apiserver -h | grep enable-admission-plugins
    

实践:创建一个 devuser 用户只能管理 dev 空间

主要就是两步:

  1. 创建linux用户;
  2. 创建用户的证书;
  3. 配置用户载入的kubeconfig;
  4. 创建RoleBinding;
  5. 切换上下文/环境。
创建用户
useradd devuser
passwd devuser
创建证书

(1)创建一个证书请求json文件:

$ vim /home/userdev/cert/devuser-csr.json
	{
	  "CN": "devuser",
	  "hosts": [],
	  "key": {
	    "algo": "rsa",
	    "size": 2048
	  },
	  "names": [
	  {
	    "C": "CN",
	    "ST": "BeiJing",
	    "L": "BeiJing",
	    "O": "k8s",	# 组名
	    "OU": "System"
	  }]
	}
  • CN: Common Name,浏览器使用该字段验证网站是否合法,一般写的是域名,浏览器使用该字段验证网站是否合法,K8S中用作用户名
  • hosts:可以使用的主机,不写的话代表所有;
  • key:生成证书的算法;
  • names:一些其它的属性:
    • C:Country, 国家;
    • ST:State,州,省;
    • L: Locality,地区,城市;
    • O:Organization Name,组织名称,公司名称;(K8S中用作组名)
    • OU:Organization Unit Name,组织单位名称,公司部门。

(2)生成该用户的证书和私钥(使用cfssl工具):

# 1. 下载证书生成工具
wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
mv cfssl_linux-amd64 /usr/local/bin/cfssl
wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
mv cfssljson_linux-amd64 /usr/local/bin/cfssljson
wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
mv cfssl-certinfo_linux-amd64 /usr/local/bin/cfssl-certinfo
chmod a+x /usr/local/bin/cfssl*

# 2. 生成证书
$ cd /etc/kubernetes/pki
$ cfssl gencert -ca=ca.crt -ca-key=ca.key -profile=kubernetes /home/devuser/cert/devuser-csr.json | cfssljson -bare devuser

# 查看生成的证书
$ ll dev*
-rw-r--r-- 1 root root  997 1117 19:53 devuser.csr
-rw------- 1 root root 1679 1117 19:53 devuser-key.pem
-rw-r--r-- 1 root root 1237 1117 19:53 devuser.pem
  • cfssl gencert: 生成新的 key (密钥)和签名证书
    • -ca:指明 ca 的证书
    • -ca-key:指明 ca 的私钥文件
    • -profile:根据 config 中的 profile 段来生成证书的相关信息
  • -bare:指定文件名

ca.crt和ca.key证书是服务器的根证书和私钥,使用kubeadm init创建控制平面时,kubeadm会为我们生成根CA,以及相关证书,保存在 /etc/kubernetes/pki文件夹下。

生成了三个文件,分别是:证书、私钥、证书的pem格式。

创建kubeconfig

(1)在 devuser home目录中创建 .kube 目录(config 文件一般是放在 ~/.kube 目录中的)

mkdir /home/devuser/.kube
cd /home/devuser/.kube

(2)设置集群参数:

export KUBE_APISERVER="https://192.168.66.10:6443"

kubectl config set-cluster kubernetes \
--certificate-authority=/etc/kubernetes/pki/ca.crt \
--embed-certs=true \
--server=${KUBE_APISERVER} \
--kubeconfig=config
  • kubernetes:集群名字
  • –certificate-authority:指定 CA 证书
  • –embed-certs:是否进行加密的认证
  • –server:指定 APIserver 的信息
  • –kubeconfig:指定 kubeconfig 的文件,没有会自动创建

(3)设置客户端认证参数:

kubectl config set-credentials devuser \
--client-certificate=/etc/kubernetes/pki/devuser.pem \
--client-key=/etc/kubernetes/pki/devuser-key.pem \
--embed-certs=true \
--kubeconfig=config
  • devuser:证书的用户名
  • --client-certificate:指定客户端证书
  • --client-key:指定客户端私钥
  • --embed-certs:是否开启证书认证
  • --kubeconfig:写入的文件,与上面相同,写入同一个文件

(4)设置上下文参数:

# 1. 创建 dev 的名称空间
kubectl create namespace dev

# 2. 设置上下文参数
kubectl config set-context kubernetes \
--cluster=kubernetes \
--user=devuser \
--namespace=dev \
--kubeconfig=config
  • kubernetes:Context 的名字
  • --cluster:指定集群名称
  • --user:指定用户名
  • --namespace:绑定到某个名称空间
  • --kubeconfig:写入的文件,与上面相同,写入同一个文件

(5)更改 config 文件所属的用户和组(都是devuser):

chown devuser:devuser /home/devuser/.kube/config

(6)查看 config 文件:

apiVersion: v1
clusters:	# 配置kubernetes集群
- cluster:
    certificate-authority-data: ...
    server: https://192.168.66.10:6443
  name: kubernetes
contexts:	# 配置访问kubernetes集群的具体上下文环境
- context:
    cluster: kubernetes
    namespace: dev
    user: devuser
  name: kubernetes
current-context: ""		# 配置当前使用的上下文环境
kind: Config
preferences: {}
users:	# 配置访问的用户信息,用户名以及证书信息
- name: devuser
  user:
    client-certificate-data: ...
    client-key-data: ...
创建RoleBinding

创建 RoleBindingdevuser 赋予权限:

kubectl create rolebinding devuser-admin-binding --clusterrole=admin --user=devuser --namespace=dev
  • devuser-admin-binding:RoleBinding 的名字
  • –clusterrole=admin:指定要绑定的 Clusterrole 名字,k8s 集群默认存在一个 admin 的 Clusterrole
  • –user=devuser:指定要绑定的用户名为 devuser
  • –namespace=dev:可以访问 dev 名称空间下的所有资源
切换上下文/环境(kubeconfig)

切换上下文也就是让 kubectl 读取到 config 中的配置信息:

kubectl config use-context kubernetes --kubeconfig=config
  • 这行命令设置了 config 文件中的 current-contextkubernetes
  • kubernetes:上下文环境名
  • --kubeconfig:指定 kubeconfig 文件

Java访问K8S API Server

以springboot为例:

(1)注入依赖:

<!-- java操作k8s-->
<dependency>
    <groupId>io.fabric8</groupId>
    <artifactId>kubernetes-client</artifactId>
    <version>4.0.0</version>
</dependency>

(2)配置yml文件,指明根证书、客户端证书、客户端私钥:

(3)K8S初始化:

import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.ConfigBuilder;
import io.fabric8.kubernetes.client.DefaultKubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * k8s初始化

 */
@Component
public class K8sInit {

    /**
     * 连接地址
     */
    @Value("${k8s.server}")
    private String apiHttps;

    /**
     * 安全证书相关
     */
    @Value("${k8s.certificate-authority-data}")
    private String caCert;

    @Value("${k8s.client-certificate-data}")
    private String clientCert;

    @Value("${k8s.client-key-data}")
    private String clientKey;

    /**
     *
     * k8s初始化
     * @return
     */
    public KubernetesClient getFabric8Connection() {
        Config config = new ConfigBuilder().withMasterUrl(apiHttps).withTrustCerts(true)
                .withCaCertData(caCert)
                .withClientCertData(clientCert)
                .withClientKeyData(clientKey)
                .build();
        KubernetesClient kubernetesClient = new DefaultKubernetesClient(config);
        return kubernetesClient;
    }
}

(4)具体操作

import io.fabric8.kubernetes.api.model.PodList;
/**
 * pod服务
 */
public interface PodService {
    /**
     * nameSpace不为空,获取指定命名空间下pod列表;nameSpace为空,获取所有
     * @param nameSpace
     * @return
     */
    public PodList getPodListByNamespace(String nameSpace);
}
import io.fabric8.kubernetes.api.model.PodList;
import io.fabric8.kubernetes.client.KubernetesClient;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * pod服务实现
 */
@Service
public class PodServiceImpl implements PodService {

    @Autowired
    private K8sInit k8sInit;

    /**
     * nameSpace不为空,获取指定命名空间下pod列表;nameSpace为空,获取所有
     *
     * @param nameSpace
     * @return
     */
    @Override
    public PodList getPodListByNamespace(String nameSpace) {
        KubernetesClient kubernetesClient = k8sInit.getFabric8Connection();
        PodList podList = new PodList();
        if(StringUtils.isNotBlank(nameSpace)){
            podList = kubernetesClient.pods().inNamespace(nameSpace).list();
        }else {
            podList = kubernetesClient.pods().list();
        }
        return podList;
    }
}
Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐