目录
一、k8s 安全管理:认证、授权、准入控制概述
1.1 认证
认证基本介绍
授权基本介绍
准入控制基本介绍
为什么需要准入控制器呢?
k8s 客户端访问 apiserver 的几种认证方式
kubeconfig 文件
1.2 授权
Role、RoleBinding、ClusterRole 和 ClusterRoleBinding 的关系如下:
用户基于 rbac 授权有几种方案:
1.3 准入控制
查看 apiserver 启用的准入控制器有哪些?
二、UserAccount 和 ServiceAccount 介绍
三、ServiceAccount 使用案例介绍
3.1 创建 sa(ServiceAccount)并绑定到 pod
1)创建 sa
2)创建 pod
3)对 sa 做授权
4)再次请求
四、RBAC 认证授权策略
4.1 RBAC 介绍
4.2 Role:角色
4.3 ClusterRole:集群角色
4.4 RoleBinding:角色绑定、ClusterRolebinding:集群角色绑定
五、资源的引用方式
一、k8s 安全管理:认证、授权、准入控制概述
1.1 认证
认证基本介绍
kubernetes 主要通过 APIserver 对外提供服务,那么就需要对访问 apiserver 的用户做认证,如果任何人都能访问 apiserver,那么就可以随意在 k8s 集群部署资源,这是非常危险的,也容易被黑客攻击渗透,所以需要我们对访问 k8s 系统的 apiserver 的用户进行认证,确保是合法的符合要求的用户。
授权基本介绍
认证通过后仅代表它是一个被 apiserver 信任的用户,能访问 apiserver,但是用户是否拥有删除资源的权限,需要进行授权操作,常见的授权方式有 rbac 授权。
准入控制基本介绍
当用户经过认证和授权之后,最后一步就是准入控制了,k8s 提供了多种准入控制机制,它有点类似"插件",为 apiserver 提供了很好的"可扩展性"。请求 apiserver 时,通过认证、鉴权后、持久化("api 对象"保存到 etcd)前,会经过"准入控制器",让它可以做"变更和验证"。
为什么需要准入控制器呢?
如果我们创建 pod 时定义了资源上下限,但不满足 LimitRange 规则中定义的资源上下限,此时 LimitRanger 就会拒绝我们创建此 pod。
假如我们定义了一个名称空间叫做 test-aa,这个名称空间做下资源限制:限制最多可以使用10vCPU、10Gi 内存,在这个名称空间 test-aa 下创建的所有 pod,定义 limit 的时候,所有 pod 的 limit 值不能超过 test-aa 这个名称空间设置的 limit 上限。
k8s 客户端访问 apiserver 的几种认证方式
1)客户端认证:
客户端认证也称为双向 TLS 认证, kubectl 在访问 apiserver 的时候,apiserver 也要认证kubectl 是否是合法的,他们都会通过 ca 根证书来进行验证,如下图:
2)Bearertoken
Bearertoken 的方式,可以理解为 apiserver 将一个密码通过了非对称加密的方式告诉了kubectl,然后通过该密码进行相互访问,如下图:
Kubectl 访问 k8s 集群,要找一个 kubeconfig 文件,基于 kubeconfig 文件里的用户访问 apiserver。
3)Service account
上面客户端证书认证和 Bearertoken 的两种认证方式,都是外部访问 apiserver 的时候使用的方式,那么我们这次说的 Service account 是内部访问 pod 和 apiserver 交互时候采用的一种方式。Service account 包括了,namespace、token、ca,且通过目录挂载的方式给予 pod,当 pod运行起来的时候,就会读取到这些信息,从而使用该方式和 apiserver 进行通信。如下图:
kubeconfig 文件
在 K8S 集群当中,当我们使用 kubectl 操作 k8s 资源时候,需要确定我们用哪个用户访问哪个 k8s 集群,kubectl 操作 k8s 集群资源会去 /root/.kube 目录下找 config 文件,可以通过 kubectl config 查看 config 文件配置,如下:
[root@k8s-master01 ~]# kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: DATA+OMITTED
server: https://192.168.78.133:6443 # apiserver 的地址
name: kubernetes # k8s 集群的名字
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: REDACTED
client-key-data: REDACTED
在上面的配置文件当中,定义了集群、上下文以及用户。其中 Config 也是 K8S 的标准资源之一,在该配置文件当中定义了一个集群列表,指定的集群可以有多个;用户列表也可以有多个,指明集群中的用户;而在上下文列表当中,是进行定义可以使用哪个用户对哪个集群进行访问,以及当前使用的上下文是什么。
使用 kubeconfig 文件组织集群访问:使用 kubeconfig 文件组织集群访问 | Kubernetes
1.2 授权
用户通过认证之后,默认什么权限都没有,需要一些后续的授权操作,如对资源的增删改查等,kubernetes1.6 之后开始有 RBAC(基于角色的访问控制机制)授权检查机制。
Kubernetes的授权是基于插件形成的,其常用的授权插件有以下几种:
- Node(节点认证)
- ABAC (基于属性的访问控制)
- RBAC(基于角色的访问控制)
- Webhook(基于 http 回调机制的访问控制)
鉴权模块:鉴权概述 | Kubernetes
什么是 RBAC(基于角色的授权)?
让一个用户(Users)扮演一个角色(Role),角色拥有权限,从而让用户拥有这样的权限,随后在授权机制当中,只需要将权限授予某个角色,此时用户将获取对应角色的权限,从而实现角色的访问控制。如图:
在 k8s 的授权机制当中,采用 RBAC 的方式进行授权,其工作逻辑是,把对对象的操作权限定义到一个角色当中,再将用户绑定到该角色,从而使用户得到对应角色的权限。如果通过rolebinding 绑定 role,只能对 rolebingding 所在的名称空间的资源有权限,上图 user1 这个用户绑定到 role1 上,只对 role1 这个名称空间的资源有权限,对其他名称空间资源没有权限,属于名称空间级别的。
另外,k8s 为此还有一种集群级别的授权机制,就是定义一个集群角色(ClusterRole),对集群内的所有资源都有可操作的权限,从而将 User2 通过 ClusterRoleBinding 到 ClusterRole,从而使 User2 拥有集群的操作权限。
Role、RoleBinding、ClusterRole 和 ClusterRoleBinding 的关系如下:
- 用户基于 rolebinding 绑定到 role
限定在 rolebinding 所在的名称空间
- 用户基于 rolebinding 绑定到 clusterrole
上面我们说了两个种角色绑定:
(1)用户通过 rolebinding 绑定 role
(2)用户通过 rolebinding 绑定 clusterrole
rolebinding 绑定 clusterrole 的好处:
假如有 6 个名称空间,每个名称空间的用户都需要对自己的名称空间有管理员权限,那么需要定义 6 个 role 和 rolebinding,然后依次绑定,如果名称空间更多,我们需要定义更多的 role,这个是很麻烦的,所以我们引入 clusterrole,定义一个 clusterrole,对 clusterrole 授予所有权限,然后用户通过 rolebinding 绑定到 clusterrole,就会拥有自己名称空间的管理员权限了
注:RoleBinding 仅对当前名称空间有对应的权限。
- 用户基于 clusterrolebinding 绑定到 clusterrole
用户基于 rbac 授权有几种方案:
- 基于 rolebinding 绑定到 role 上;
- 基于 rolebinding 绑定到 clusterrole 上;
- 基于 clusterrolebinding 绑定到 clusterrole 上。
Role 和 ClusterRole:使用 RBAC 鉴权 | Kubernetes
RoleBinding 和 ClusterRoleBinding:使用 RBAC 鉴权 | Kubernetes
1.3 准入控制
在 k8s 上准入控制器的模块有很多,其中比较常用的有 LimitRanger、ResourceQuota、ServiceAccount、PodSecurityPolicy(k8s 1.25 废弃了)等等,对于前面三种准入控制器系统默认是启用的,我们只需要定义对应的规则即可;对于 PodSecurityPolicy(k8s 1.25 废弃了)这种准入控制器,系统默认没有启用,如果我们要使用,就必需启用以后,对应规则才会正常生效;这里需要注意一点,对应 psp 准入控制器,一定要先写好对应的规则,把规则和权限绑定好以后,在启用对应的准入控制器,否则先启用准入控制器,没有对应的规则,默认情况它是拒绝操作,这可能导致现有的 k8s 系统跑的系统级 pod 无法正常工作;所以对于 psp 准入控制器要慎用,如果规则和权限做的足够精细,它会给我们的 k8s 系统安全带来大幅度的提升,反之,可能导致整个 k8s 系统不可用。
查看 apiserver 启用的准入控制器有哪些?
[root@k8s-master01 ~]# cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep enable-admission-plugins
- --enable-admission-plugins=NodeRestriction
提示:
apiserver 启用准入控制插件需要使用 --enable-admission-plugins 选项来指定,该选项可以使用多个值,用逗号隔开表示启用指定的准入控制插件;这里配置文件中显式启用了NodeRestrication 这个插件;默认没有写在这上面的内置准入控制器,它也是启用了的,比如LimitRanger、ResourceQuota、ServiceAccount 等等;对于不同的 k8s 版本,内置的准入控制器和启用与否请查看相关版本的官方文档;对于那些没有启动的准入控制器,我们可以在上面选项中直接启用,分别用逗号隔开即可。
准入控制器参考:准入控制器参考 | Kubernetes
二、UserAccount 和 ServiceAccount 介绍
kubernetes 中账户分为:UserAccounts(用户账户) 和 ServiceAccounts(服务账户) 两种:UserAccount 是给 kubernetes 集群外部用户使用的,如 kubectl 访问 k8s 集群要用useraccount 用户,kubeadm 安装的 k8s,默认的 useraccount 用户是 kubernetes-admin。
k8s 客户端(一般用 kubectl) ------> APIServer
APIServer 需要对客户端做认证,使用 kubeadm 安装的 K8s,会在用户家目录下创建一个认证配置文件 /root/.kube/config 这里面保存了客户端访问 APIServer 的密钥相关信息,这样当用 kubectl访问 k8s 时,它就会自动读取该配置文件,向 APIServer 发起认证,然后完成操作请求。
# 用户名称可以在 kubeconfig 中查看
[root@k8s-master01 ~]# cat .kube/config | grep "\- name"
- name: kubernetes-admin
ServiceAccount 是 Pod 使用的账号,Pod 容器的进程需要访问 API Server 时用的就是ServiceAccount 账户;ServiceAccount 仅局限它所在的 namespace,每个 namespace 创建时都会自动创建一个 default service account;创建 Pod 时,如果没有指定 Service Account,Pod 则会使用 default Service Account。
用户账号与服务账号:管理服务账号 | Kubernetes
三、ServiceAccount 使用案例介绍
3.1 创建 sa(ServiceAccount)并绑定到 pod
1)创建 sa
[root@k8s-master01 ~]# kubectl create sa sa-test
serviceaccount/sa-test created
[root@k8s-master01 ~]# kubectl get serviceaccounts
NAME SECRETS AGE
default 0 68d
sa-test 0 17s
2)创建 pod
[root@k8s-master01 ~]# mkdir serviceaccount
[root@k8s-master01 ~]# cd serviceaccount/
[root@k8s-master01 serviceaccount]# vim pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: sa-test
namespace: default
labels:
app: sa
spec:
serviceAccountName: sa-test
containers:
- name: sa-nginx
ports:
- containerPort: 80
image: nginx
imagePullPolicy: IfNotPresent
[root@k8s-master01 serviceaccount]# kubectl apply -f pod.yaml
pod/sa-test created
[root@k8s-master01 serviceaccount]# kubectl get pods
NAME READY STATUS RESTARTS AGE
sa-test 1/1 Running 0 4s
# 进入容器内部访问 service
[root@k8s-master01 serviceaccount]# kubectl exec -it sa-test -- bash
root@sa-test:/# cd /var/run/secrets/kubernetes.io/serviceaccount/
root@sa-test:/var/run/secrets/kubernetes.io/serviceaccount# ls
ca.crt namespace token
root@sa-test:/var/run/secrets/kubernetes.io/serviceaccount# curl --cacert ./ca.crt -H "Authorization: Bearer $(cat ./token)" https://kubernetes/api/v1/namespaces/kube-system
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {},
"status": "Failure",
"message": "namespaces \"kube-system\" is forbidden: User \"system:serviceaccount:default:sa-test\" cannot get resource \"namespaces\" in API group \"\" in the namespace \"kube-system\"",
"reason": "Forbidden",
"details": {
"name": "kube-system",
"kind": "namespaces"
},
"code": 403
上面结果能看到 sa 能通过 https 方式成功认证 API,但是没有权限访问 k8s 资源,所以 code状态码是 403,表示没有权限操作 k8s 资源。
3)对 sa 做授权
[root@k8s-master01 ~]# kubectl create clusterrolebinding sa-test-admin --clusterrole=cluster-admin --serviceaccount=default:sa-test
4)再次请求
root@sa-test:/var/run/secrets/kubernetes.io/serviceaccount# curl --cacert ./ca.crt -H "Authorization: Bearer $(cat ./token)" https://kubernetes/api/v1/namespaces/kube-system
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {},
"status": "Failure",
"message": "namespaces \"kube-system\" is forbidden: User \"system:serviceaccount:default:sa-test\" cannot get resource \"namespaces\" in API group \"\" in the namespace \"kube-system\"",
"reason": "Forbidden",
"details": {
"name": "kube-system",
"kind": "namespaces"
},
"code": 403
}root@sa-test:/var/run/secrets/kubernetes.io/serviceaccount# curl --cacert ./ca.crt -H "Authorization: Bearer $(cat ./token)" https://kubernetes/api/v/namespaces/kube-system
{
"kind": "Namespace",
"apiVersion": "v1",
"metadata": {
"name": "kube-system",
"uid": "5d0a0f96-745a-4116-8fa5-464def457f38",
"resourceVersion": "15",
"creationTimestamp": "2022-10-22T05:31:58Z",
"labels": {
"kubernetes.io/metadata.name": "kube-system"
},
"managedFields": [
{
"manager": "kube-apiserver",
"operation": "Update",
"apiVersion": "v1",
"time": "2022-10-22T05:31:58Z",
"fieldsType": "FieldsV1",
"fieldsV1": {
"f:metadata": {
"f:labels": {
".": {},
"f:kubernetes.io/metadata.name": {}
}
}
}
}
]
},
"spec": {
"finalizers": [
"kubernetes"
]
},
"status": {
"phase": "Active"
}
}
# 访问 pod 资源
}root@sa-test:/var/run/secrets/kubernetes.io/serviceaccount# curl --cacert ./ca.crt -H "Authorization: Bearer $(cat ./token)" https://kubernetes/api/v/pods
通过上面可以看到,对 sa 做授权之后就有权限访问 k8s 资源了。
使用默认的服务账号访问 API 服务器:为 Pod 配置服务账号 | Kubernetes
四、RBAC 认证授权策略
4.1 RBAC 介绍
在 Kubernetes 中,所有资源对象都是通过 API 进行操作,他们保存在 etcd 里。而对 etcd 的操作我们需要通过访问 kube-apiserver 来实现,上面的 Service Account 其实就是 APIServer 的认证过程,而授权的机制是通过 RBAC:基于角色的访问控制实现。
RBAC 有四个资源对象,分别是 Role、ClusterRole、RoleBinding、ClusterRoleBinding。
4.2 Role:角色
一组权限的集合,在一个命名空间中,可以用其来定义一个角色,只能对命名空间内的资源进行授权。如果是集群级别的资源,则需要使用 ClusterRole。例如:定义一个角色用来读取 Pod的权限。
下面是一个位于 "rbac" 名字空间的 Role 的示例,可用来授予对 Pod 的读访问权限:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: rbac
name: pod-read
rules:
- apiGroups: [""]
resources: ["pods"]
resourceNames: []
verbs: ["get","watch","list"]
rules 中的参数说明:
-
apiGroups:支持的 API 组列表,例如:"apiVersion: apps/v1"等
-
resources:支持的资源对象列表,例如 pods、deployments、jobs 等
-
resourceNames: 指定 resource 的名称
-
verbs:对资源对象的操作方法列表。
4.3 ClusterRole:集群角色
ClusterRole 同样可以用于授予 Role 能够授予的权限。 因为 ClusterRole 属于集群范围,所以它也可以为以下资源授予访问权限:
-
集群范围资源(比如节点(Node))
-
非资源端点(比如
/healthz
) -
跨名字空间访问的名字空间作用域的资源(如 Pod)
比如,你可以使用 ClusterRole 来允许某特定用户执行
kubectl get pods --all-namespaces
下面是一个 ClusterRole 的示例,可用来为任一特定名字空间中的 Secret 授予读访问权限, 或者跨名字空间的访问权限(取决于该角色是如何绑定的):
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
# "namespace" 被忽略,因为 ClusterRoles 不受名字空间限制
name: secret-clusterrole
rules:
- apiGroups: [""]
# 在 HTTP 层面,用来访问 Secret 资源的名称为 "secrets"
resources: ["secrets"]
verbs: ["get", "watch", "list"]
Role 和 ClusterRole:使用 RBAC 鉴权 | Kubernetes
4.4 RoleBinding:角色绑定、ClusterRolebinding:集群角色绑定
角色绑定和集群角色绑定用于把一个角色绑定在一个目标上,可以是 User,Group,Service Account,使用 RoleBinding 为某个命名空间授权,使用 ClusterRoleBinding 为集群范围内授权。
例如:将在 rbac 命名空间中把 pod-read 角色授予用户 es
apiVersion: rbac.authorization.k8s.io/v1
# 此角色绑定允许 "es" 读取 "rbac" 名字空间中的 Pod
# 你需要在该命名空间中有一个名为 “pod-reader” 的 Role
kind: RoleBinding
metadata:
name: pod-read-bind
namespace: rbac
subjects:
# 你可以指定不止一个“subject(主体)”
- kind: User
name: es # "name" 是区分大小写的
apiGroup: rbac.authorization.k8s.io
roleRef:
# "roleRef" 指定与某 Role 或 ClusterRole 的绑定关系
kind: Role # 此字段必须是 Role 或 ClusterRole
name: pod-read # 此字段必须与你要绑定的 Role 或 ClusterRole 的名称匹配
apiGroup: rbac.authorizatioin.k8s.io
要跨整个集群完成访问权限的授予,你可以使用一个 ClusterRoleBinding。 下面的 ClusterRoleBinding 允许 "manager" 组内的所有用户访问任何名字空间中的 Secret。
apiVersion: rbac.authorization.k8s.io/v1
# 此集群角色绑定允许 “manager” 组中的任何人访问任何名字空间中的 Secret 资源
kind: ClusterRoleBinding
metadata:
name: read-secrets-global
subjects:
- kind: Group
name: manager # 'name' 是区分大小写的
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io
五、资源的引用方式
多数资源可以用其名称的字符串表示,也就是 Endpoint 中的 URL 相对路径,例如 pod 中的日志是 GET /api/v1/namaspaces/{namespace}/pods/{podname}/log。如果需要在一个RBAC对象中体现上下级资源,就需要使用“/”分割资源和下级资源。
例如:若想授权让某个主体同时能够读取 Pod 和 Pod log,则可以配置 resources 为一个数组:
# 创建 test 名称空间
[root@k8s-master01 serviceaccount]# kubectl create ns test
# 创建 role
[root@k8s-master01 serviceaccount]# vim role-test.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: logs-reader
namespace: test
rules:
- apiGroups: [""]
resources: ["pods","pods/log"]
verbs: ["get","list","watch"]
[root@k8s-master01 serviceaccount]# kubectl apply -f role-test.yaml
role.rbac.authorization.k8s.io/logs-reader created
[root@k8s-master01 serviceaccount]# kubectl get role -n test
NAME CREATED AT
logs-reader 2022-12-29T10:33:18Z
# 创建服务账户
[root@k8s-master01 serviceaccount]# kubectl create sa sa-test -n test
serviceaccount/sa-test created
# 创建角色绑定
[root@k8s-master01 serviceaccount]# kubectl create rolebinding sa-test-1 -n test --role=logs-reader --serviceaccount=test:sa-test
# 创建 pod
[root@k8s-master01 serviceaccount]# vim pod-test.yaml
apiVersion: v1
kind: Pod
metadata:
name: sa-test-pod
namespace: test
labels:
app: sa
spec:
serviceAccountName: sa-test
containers:
- name: sa-tomcat
ports:
- containerPort: 80
image: nginx
imagePullPolicy: IfNotPresent
[root@k8s-master01 serviceaccount]# kubectl apply -f pod-test.yaml
pod/sa-test-pod created
# 进入 pod 访问资源
[root@k8s-master01 serviceaccount]# kubectl exec -it -n test sa-test-pod -- bash
root@sa-test-pod:/# cd /var/run/secrets/kubernetes.io/serviceaccount/
root@sa-test-pod:/var/run/secrets/kubernetes.io/serviceaccount# ls
ca.crt namespace token
# 可以访问 test 空间的 pods 资源
root@sa-test-pod:/var/run/secrets/kubernetes.io/serviceaccount# curl --cacert ./ca.crt -H "Authorization: Bearer $(cat ./token)" https://kubernetes.default/api/v1/namespaces/test/pods/
# 可以访问 test 空间的 pods/log 资源
root@sa-test-pod:/var/run/secrets/kubernetes.io/serviceaccount# curl --cacert ./ca.crt -H "Authorization: Bearer $(cat ./token)" https://kubernetes.dfault/api/v1/namespaces/test/pods/sa-test-pod/log
# 不能访问默认空间下的资源
root@sa-test-pod:/var/run/secrets/kubernetes.io/serviceaccount# curl --cacert ./ca.crt -H "Authorization: Bearer $(cat ./token)" https://kubernetes.dfault/api/v1/namespaces/default/pods/log
资源还可以通过名称(ResourceName)进行引用,在指定 ResourceName 后,使用 get、delete、update、patch 请求,就会被限制在这个资源实例范围内。
例如,下面的声明让一个主体只能对名为 my-configmap 的 Configmap 进行 get 和 update 操作:
apiVersion: rabc.authorization.k8s.io/v1
kind: Role
metadata:
namaspace: default
name: configmap-update
rules:
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["my-configmap"]
verbs: ["get","update"]
对资源的引用:使用 RBAC 鉴权 | Kubernetes
上一篇文章:【云原生 | Kubernetes 实战】17、K8s 配置管理中心 Secret 实现加密数据配置管理_Stars.Sky的博客-CSDN博客