13 访问K8s集群的安全策略
你好,我是雪飞。
前面课程带你了解了 K8s 集群的大部分常用资源对象,现在你已经可以独立完整的部署各种应用、配置环境变量、网络访问以及存储资源。今天开始,我们进入到 K8s 高级篇的学习。我会带你了解 K8s 的安全相关策略和 Pod 的稳定性策略,掌握如何使用资源统计与日志来监控集群和排查故障,学习如何备份和恢复 etcd 数据以及升级 K8s 集群。
K8s 集群中承载着企业的关键应用和各种数据,它的安全性至关重要。在 CKA 考试中,常考两个安全策略,一个是访问 K8s 集群的安全策略,另一个是 Pod 相互访问的安全策略。这节课,我们先了解一下访问 K8s 集群的安全策略。
访问 K8s 集群的过程
K8s 集群是通过 API Server 组件提供外部访问接口,我们使用 kubectl 命令操作集群时,实际上是向 K8s 集群发起一个 HTTPS 请求。请求中包含了发起方、操作行为和资源对象。例如,执行一条简单的 “kubectl get pod” 命令,其中 get 是操作行为,Pod 是要操作的资源对象,而操作发起方就是集群管理员。
既然所有对集群的访问都要通过 API Server,那么我们只需要对访问 API Server 的请求进行控制就可以保证访问集群的安全性。因此,当请求到达 API Server 时,K8s 设计了一个安全请求流程,经历 3 个过程:身份认证、鉴权和准入控制,然后才能操作各种资源对象。
访问集群操作资源对象的过程,类似于进入公司财务部门修改账本数据。首先,进入公司大楼时需要经过“身份认证”,就像 K8s 中的身份认证组件一样,“身份认证”保安会查看你的员工卡来确认你是否有权限进入大楼。之后,当你找到财务室并准备进入时,会遇到“鉴权”保安,他会检查你的授权权限,从而确定你是否有权进入财务部修改账本。最后,即使你通过了前两项检查,还会有一个“准入控制”保安在你修改账本之前进行最终审查,确保你的操作不会违反规定,防止作假。只有通过全部 3 次检查,你才能修改账本。下面,我们就分别介绍一下怎么通过这3个检查。
身份认证
K8s 中有两种身份:一种是人类用户,另一种是服务账号。
人类用户就是通过个人账号访问和操作集群的真实用户,例如集群管理员、运维人员、研发人员等等。服务账号(ServiceAccount,简称 SA)是一个代表应用程序 Pod 的账号,用于让集群内的 Pod 安全访问 K8s 的 API Server。例如当 Pod 中的应用需要使用 K8s 的 API Server 提供的接口时,它就会使用服务账号的身份认证信息来建立安全的连接。
对于人类用户的身份认证,主要是通过证书认证。而对于服务账号,主要是通过 Token 认证。
证书认证
证书认证是基于 SSL/TLS 证书,类似 HTTPS 请求证书,请求发起方和 K8s 集群通过证书来相互验证身份,确保只有拥有有效证书的访问才能与 K8s 的 API Server 进行安全通信。
人类用户在使用 kubectl 命令访问和操作集群资源对象时,会使用一个 kubeconfig 配置文件,这个配置文件中包含了集群信息、证书信息以及上下文信息。你可能在想,自己在 Master 节点上使用 kubectl 命令的时候也没有用到 kubeconfig 配置文件呀,为啥就能直接访问集群?
其实在搭建集群的时候,我们在管理节点上执行过 3 条命令,就是把初始化集群时默认生成的管理员配置文件(/etc/kubernetes/admin.conf)复制到了 .kube 隐藏目录下保存为 config 文件,这样在你执行 kubectl 命令时默认会使用这个管理员的 kubeconfig 文件,所以我们就不用再加上任何参数来指定配置文件了。你可以去 .kube 目录下看一下这个文件。
[root@k8s-master ~]# cd .kube/
[root@k8s-master .kube]# cat config
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0...
server: https://192.168.1.11 :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: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0...
client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQp...
从 config 文件中看出,它包含三部分:clusters 属性中是集群的相关信息;contexts 属性中是上下文信息;users 属性中是个人证书相关信息。
除了 K8s 的管理员配置文件,你也可以为其他同事创建 kubeconfig 配置文件,之后这些同事就可以使用自己的 kubeconfig 配置文件来访问集群了。但是有了 kubeconfig 配置文件,只是可以通过身份认证检查,并不代表这个用户就有操作集群的权限,我们还需要给他分配权限。
Token 认证
对于 K8s 另一种身份,服务账号没有 kubeconfig 配置文件,所以需要使用 Token 作为访问 API Server的身份认证。
在业务 Pod 使用服务账号时会生成身份认证的 Token 令牌,Pod 中的应用可以通过读取挂载目录下的文件获取 Token 令牌,并使用它来与 API Server 进行身份认证。
当创建一个新的命名空间时,K8s 会自动在这个命名空间下创建一个默认的服务账号(通常名为default)。当然我们也可以手动创建一个服务账号,使用 “kubectl create sa” 命令创建一个名称为 my-sa 的服务账号。
[root@k8s-master ~]# kubectl create sa my-sa -n default
serviceaccount/my-sa created
[root@k8s-master ~]# kubectl get sa
NAME SECRETS AGE
default 0 12d
my-sa 0 4s
在 Pod 的 YAML 文件中,通过在 spec 属性里增加 serviceAccountName 属性,可以指定 Pod 要使用的服务账号,如果不指定,则会使用该命名空间下的默认服务账号(default)。
当 Pod 部署启动时,K8s 会自动将服务账号的 Token 令牌挂载到容器的文件系统中,挂载目录通常是 “/var/run/secrets/kubernetes.io/serviceaccount”。Pod 容器中的应用就可以通过读取这些文件来获取令牌,从而与 API Server 进行安全通信。
在 Pod 中指定服务账号之前,我们也可以使用 kubectl 命令来测试服务账号的权限。通过增加 “–as=system:serviceaccount:default:my-sa” 参数来模拟使用服务账号 “my-sa” 的身份来执行 kubectl 命令,其中 system:serviceaccount 表示系统服务账号身份;default 表示 my-sa 的命名空间。
[root@k8s-master ~]# kubectl get pod -n default --as=system:serviceaccount:default:my-sa
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:default:my-sa" cannot list resource "pods" in API group "" in the namespace "default"
返回结果可以看到虽然创建了 my-sa,但是没有分配任何权限,因此访问 Pod 是 Forbidden 禁止的。所以,下面我们就来了解如何给用户和服务账号分配适当的角色和权限。
鉴权
K8s 支持多种鉴权方式,其中最常用的是 RBAC。RBAC 意思是基于角色的访问控制(Role-Based Access Control),通过 RBAC 机制,你可以创建角色来定义哪些用户和服务账号可以执行哪些操作,例如查看、创建、修改或删除特定的资源对象,这个过程叫做授权,授权之后的用户和服务账号就有了角色所定义的权限,在 K8s 的鉴权过程中,就会根据权限来放行操作。
使用 RBAC 授权需要了解几个概念。
- Subject: 主体,即赋予权限的对象。可以是用户(User)、用户组(Group)或服务账号(ServiceAccount)。
- Role: 角色。用于定义可以对 K8s 哪些资源对象操作的一组权限规则(Rule),其中包括资源对象所属的 API Groups、可操作的资源对象列表(Resources),以及资源对象的操作方式列表(verbs)。
- RoleBinding: 将定义好的角色和要授权的主体对象进行绑定。
- ClusterRole 和 ClusterRoleBinding: Role 和 RoleBinding 只是授权特定命名空间的访问权限。而 ClusterRole 和 ClusterRoleBinding 是针对集群所有命名空间的授权,不受命名空间 NameSpace 的限制。
所以在使用 RBAC 时,首先确定授权主体,然后创建一个适合该主体对象的角色,角色里包含对资源对象操作的权限规则,最后将该角色与主体对象绑定,从而完成授权流程。
现在我带你动手配置一个角色和绑定关系,从而熟悉一下授权流程。对用户授权和对服务账号授权流程是一样的,只是最后绑定的主体不同,所以我这里就使用刚才已经定义好的 my-sa 服务账号作为主体对象,给它授权读取默认命名空间(default)下的 Pod 以及管理 Deployment 的权限。
使用 Role 和 RoleBinding
角色和绑定关系有两种,一种是针对特定命名空间的 Role 和 RoleBinding,另一种是针对全部命名空间的 ClusterRole 和 ClusterRoleBinding。我们先来配置针对默认命名空间下的资源对象的操作权限控制。
1. 创建角色
通过 YAML 文件(deployment-pod-manager-role.yaml)来定义默认命名空间下的角色 Role。我们定义了一个 “deployment-pod-manager” 角色,包含两个权限规则:一个允许查看和列出 Pod,另一个允许查看、创建、更新和删除 Deployment 资源对象。
# dep-pod-manager-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: deployment-pod-manager
namespace: default # 角色的权限限制在默认命名空间下
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "create", "update", "delete"]
在规则 rules 属性里,可以配置多条规则,每条规则由以下属性组成:
- apiGroups: 表示资源对象所属的 API 组。通过 “kubectl api-resources” 命令可以查看资源对象的 API VERSION ,去掉 “/” 之后的版本号就是资源对象所属的 API Group。如果多个资源对象所属多个 apiGroups,可以在 [] 里写多个,例如:[“”, “apps”]。
- resources: 表示要操作的资源对象。可以在 [] 里写多个,例如:[“pods”, “deployments”],注意资源对象要使用复数形式。
- verbs: 表示对资源的操作列表。这些操作可以应用于上面资源对象列表中的所有资源对象,可以在 [] 里写多个,例如:[‘get’, ‘list’, ‘create’, ‘update’, ‘edit’, ‘delete’]。
部署角色,然后可以使用 “kubectl describe” 命令检查角色的规则是否正确。
[root@k8s-master ~]# kubectl apply -f dep-pod-manager-role.yaml
role.rbac.authorization.k8s.io/deployment-pod-manager created
[root@k8s-master ~]# kubectl describe role deployment-pod-manager
Name: deployment-pod-manager
Labels: <none>
Annotations: <none>
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
deployments.apps [] [] [get list create update delete]
pods [] [] [get list]
2. 绑定主体和角色
角色创建完成后,就可以使用 RoleBinding 绑定主体对象和角色了,我们创建一个 RoleBinding 的 YAML 文件(sa-role-binding.yaml),把 deployment-pod-manager 角色和主体 my-sa 这个服务账号绑定。
# sa-role-binding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: sa-deployment-pod-manager-rolebinding
namespace: default
subjects:
- kind: ServiceAccount
name: my-sa
namespace: default # my-sa的命名空间
roleRef:
kind: Role
name: deployment-pod-manager
apiGroup: rbac.authorization.k8s.io
- subjects:表示要绑定的主体对象,可以将多个对象同时绑定到一个角色中。如果主体是人类用户,只需要把 subjects 属性下的 kind 属性改为 User,name 属性改为用户证书中的名称,然后去掉 namespace 属性,改为 apiGroup: rbac.authorization.k8s.io,就可以绑定用户主体了。
- roleRef:表示要绑定的角色。
部署角色的绑定关系,然后可以使用 “kubectl describe” 命令检查绑定关系。
[root@k8s-master ~]# kubectl apply -f sa-role-binding.yaml
rolebinding.rbac.authorization.k8s.io/sa-deployment-pod-manager-rolebinding created
[root@k8s-master ~]# kubectl describe rolebinding sa-deployment-pod-manager-rolebinding
Name: sa-deployment-pod-manager-rolebinding
Labels: <none>
Annotations: <none>
Role:
Kind: Role
Name: deployment-pod-manager
Subjects:
Kind Name Namespace
---- ---- ---------
ServiceAccount my-sa default
3. 验证权限
最后,我们就可以使用 “–as” 参数模拟 my-sa 来操作 Pod 和 Deployment。
[root@k8s-master ~]# kubectl create deployment sa-create-dep --image=nginx --replicas=3 --as=system:serviceaccount:default:my-sa
deployment.apps/sa-create-dep created
[root@k8s-master ~]# kubectl get pod --as=system:serviceaccount:default:my-sa
NAME READY STATUS RESTARTS AGE
sa-create-dep-8df44697f-5nb94 1/1 Running 0 108m
sa-create-dep-8df44697f-9xwwt 1/1 Running 0 108m
sa-create-dep-8df44697f-fh8xs 1/1 Running 0 108m
[root@k8s-master ~]# kubectl get pod -n kube-system --as=system:serviceaccount:default:my-sa
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:default:my-sa" cannot list resource "pods" in API group "" in the namespace "kube-system"
根据返回结果看到,我们模拟 my-sa 服务账号可以在默认命名空间下创建 Deployment,同时可以查看默认命名空间下的 Pod。这说明我们对 my-sa 的授权流程成功了,所以在 my-sa 访问集群操作资源对象的时候,K8s 的鉴权机制根据该账号的绑定的角色权限来决定是否放行操作。
但是,当我们想查看命名空间为 “kube-system” 下的 Pod 时,就没有权限了。因为使用 Role 和 RoleBinding 时,授权仅限于特定命名空间。若要授权查看所有的命名空间,需要使用 ClusterRole 和 ClusterRoleBinding。
使用 ClusterRole 和 ClusterRoleBinding
ClusterRole 和 ClusterRoleBinding 的用法和 Role、RoleBinding 非常类似,只需要分别在角色和角色绑定的 YAML 文件中修改 kind 的类型为 ClusterRole 和 ClusterRoleBinding,然后去掉 metadata 属性中的 “namespace: default”。这样角色和绑定关系就是对应所有的命名空间了,你可以自己尝试一下。
通过 kubectl 命令创建角色和绑定关系
上面是使用 YAML 文件方式来创建角色和绑定关系,也可以使用 kubectl 命令,熟悉这种方式在 CKA 考试中更利于快速解答问题。
# 创建集群角色
kubectl create clusterrole <角色名称> --verb=<操作列表(逗号分隔)> --resource=<资源对象列表(逗号分隔)>
# 将服务账号绑定角色
kubectl create clusterrolebinding <绑定关系名称> --serviceaccount=<命名空间>:<sa 名称> --clusterrole=<角色名称>
# 将用户绑定角色
kubectl create rolebinding <绑定关系名称> --user=<用户名称> --role=<角色名称>
命令中的 clusterrole 和 clusterrolebinding 可以换成 role 和 rolebinding。
准入控制
鉴权成功后,访问 API Server 的最后一关就到了准入控制。准入控制是一种安全机制,在资源对象被创建或修改之前,提供验证和修改这些操作的方法,目的是确保所有对集群资源对象的更改都符合预定的策略和标准。
准入控制是通过准入控制器(Admission Controllers)插件来实现的,每个准入控制器都可以实施不同的策略,例如验证请求的合法性、限制资源配额、执行安全检查或自动修复配置问题。K8s 提供了一些默认的准入控制器,我们不需要做任何修改。
小结
今天,我给你介绍了访问 K8s 的 API Server 的过程。要想通过 API Server 提供的接口访问和操作集群资源对象,需要经过身份认证、鉴权和准入控制三个阶段。
第一个过程是身份认证,K8s 支持证书认证和 Token 令牌认证两种方式,对于人类用户的身份认证,主要是通过证书认证。而对于服务账号,主要是通过 Token 认证。在业务 Pod 使用服务账号时会生成身份认证的 Token 令牌,应用可以通过该令牌来与 API Server 进行身份认证。
第二个过程是基于 RBAC 的鉴权,通过 RBAC 机制来给不同用户或服务账号分配权限,通过定义角色(Role/ClusterRole)和角色绑定关系(RoleBinding/ClusterRoleBinding)来控制用户和服务账号的访问权限,确保遵循最小权限原则。我也带你动手完成一个实验,给服务账号配置了访问 Pod 和管理 Deployment 的权限,并且通过 “–as” 参数来模拟访问集群。
最后一个过程是准入控制,API Server 启动时加载准入控制器插件,在资源对象创建或修改前提供验证和修改的方法,确保符合集群安全策略。
思考题
这就是今天的全部内容,在 CKA 中也会考到相关的知识点。我给你留一道练习题。
创建一个服务账号 my-sa,分配只能创建 deployment、daemonset 的权限,并通过 “–as” 参数测试你的配置结果。
相信经过动手实践,会让你对知识的理解更加深刻。