跳到主要内容

3、RBAC

RBAC

image-20230117220146448

目录

[toc]

本节实战

实战名称
💘 实战:查看k8s里api对象-2023.1.18(测试成功)
💘 实战1:只能访问某个 namespace 的普通用户-2023.2.3(测试成功)(openssl)
💘 实战2:对用户授权访问K8s(TLS证书-cfgssl)-2023.2.6(测试成功)(cfgssl)
💘 实战:<=1.20 k8s版本sa测试-2023.1.18(测试成功)
💘 实战:对应用程序授权访问K8s(ServiceAccount)-2023.5.24(测试成功)
💘 实战:>=1.21版本 && <=1.23版本sa测试-2023.1.18(测试成功)
💘 实战:>=1.24版本sa测试-2023.2.5(测试成功)
💘 实战:为 ServiceAccount 分配权限-2023.2.5(测试成功)
💘 实战:命令行配置sa权限-2023.5.24(测试成功)
💘 实战:cka-rbac考试题-创建sa并赋予权限-2023.2.6(测试成功)
💘 实战:可以全局访问的 ServiceAccount-2023.2.5(测试成功)

前言

前面我们已经学习一些常用的资源对象的使用,我们知道对于资源对象的操作都是通过 APIServer 进行的,那么集群是怎样知道我们的请求就是合法的请求呢?这个就需要了解 Kubernetes 中另外一个非常重要的知识点了:RBAC(基于角色的权限控制)

管理员可以通过 Kubernetes API 动态配置策略来启用 RBAC ,需要在 kube-apiserver 中添加参数--authorization-mode=RBAC如果使用的 kubeadm 安装的集群那么是默认开启了 RBAC 的,可以通过查看 Master节点上 apiserver 的静态 Pod 定义文件:

☸ ➜ cat /etc/kubernetes/manifests/kube-apiserver.yaml
……
- --authorization-mode=Node,RBAC
……

如果是二进制的方式搭建的集群,添加这个参数过后,记得要重启 kube-apiserver 服务。

1、API对象

在学习 RBAC 之前,我们还需要再去理解下 Kubernetes 集群中的对象。我们知道,在 Kubernetes 集群中,Kubernetes 对象是我们持久化的实体,就是最终存入 etcd 中的数据,集群中通过这些实体来表示整个集群的状态。前面我们都直接编写的 YAML 文件,通过 kubectl 来提交的资源清单文件,然后创建的对应的资源对象,那么它究竟是如何将我们的 YAML 文件转换成集群中的一个 API 对象的呢?

这个就需要去了解下声明式 API的设计,Kubernetes API 是一个以 JSON 为主要序列化方式的 HTTP 服务(可以理解为一个webserver),除此之外也支持 Protocol Buffers 序列化方式,主要用于集群内部组件间的通信。为了可扩展性,Kubernetes 在不同的 API 路径(比如/api/v1 或者 /apis/batch)下面支持了多个 API 版本,不同的 API 版本意味着不同级别的稳定性和支持:

  • Alpha 级别:例如 v1alpha1 默认情况下是被禁用的,可以随时删除对功能的支持,所以要慎用;

  • Beta 级别:例如 v2beta1 默认情况下是启用的,表示代码已经经过了很好的测试,但是对象的语义可能会在随后的版本中以不兼容的方式更改;

  • 稳定级别:比如 v1 表示已经是稳定版本了,也会出现在后续的很多版本中;

在 Kubernetes 集群中,一个 API 对象在 Etcd 里的完整资源路径,是由:Group(API 组)Version(API 版本)Resource(API 资源类型)三个部分组成的。通过这样的结构,整个 Kubernetes 里的所有 API 对象,实际上就可以用如下的树形结构表示出来:

image-20230118071807336

从上图中我们也可以看出 Kubernetes 的 API 对象的组织方式,在顶层,我们可以看到有一个核心组(由于历史原因,是 /api/v1 下的所有内容而不是在 /apis/core/v1 下面)和命名组(路径 /apis/$NAME/$VERSION)和系统范围内的实体,比如 /metrics

比如我们来查看批处理这个操作,在我们当前这个版本中存在两个版本的操作:/apis/batch/v1/apis/batch/v1beta1,分别暴露了可以查询和操作的不同实体集合。

通常,Kubernetes API 支持通过标准 HTTP POSTPUTDELETEGET 在指定 PATH 路径上创建、更新、删除和检索操作,并使用 JSON 作为默认的数据交互格式。比如现在我们要创建一个 Deployment 对象,那么我们的 YAML 文件的声明就需要怎么写:

apiVersion: apps/v1
kind: Deployment

其中 Deployment 就是这个 API 对象的资源类型(Resource),apps 就是它的组(Group),v1 就是它的版本(Version)。API Group、Version 和 资源就唯一定义了一个 HTTP 路径,然后在 kube-apiserver 端对这个 url 进行了监听,然后把对应的请求传递给了对应的控制器进行处理而已,当然在 Kuberentes 中的实现过程是非常复杂的。

==💘 实战:查看k8s里api对象-2023.1.18(测试成功)==

image-20230118071807336

  • 实验环境
实验环境:
1、win10,vmwrokstation虚机;
2、k8s集群:3台centos7.6 1810虚机,1个master节点,2个node节点
k8s version:v1.25.4
containerd://1.6.10
  • 实验软件(无)

  • 我们也可以用下面的命令来查看集群中的 API 组织形式:

[root@master1 ~]#kubectl get --raw /
{
"paths": [
"/.well-known/openid-configuration",
"/api",
"/api/v1",
"/apis",
"/apis/",
"/apis/admissionregistration.k8s.io",
"/apis/admissionregistration.k8s.io/v1",
"/apis/apiextensions.k8s.io",
"/apis/apiextensions.k8s.io/v1",
"/apis/apiregistration.k8s.io",
"/apis/apiregistration.k8s.io/v1",
"/apis/apps",
"/apis/apps/v1",
"/apis/authentication.k8s.io",
"/apis/authentication.k8s.io/v1",
"/apis/authorization.k8s.io",
"/apis/authorization.k8s.io/v1",
"/apis/autoscaling",
"/apis/autoscaling/v1",
"/apis/autoscaling/v2beta1",
"/apis/autoscaling/v2beta2",
"/apis/batch",
"/apis/batch/v1",
"/apis/batch/v1beta1",
"/apis/certificates.k8s.io",
"/apis/certificates.k8s.io/v1",
"/apis/coordination.k8s.io",
"/apis/coordination.k8s.io/v1",
"/apis/discovery.k8s.io",
"/apis/discovery.k8s.io/v1",
"/apis/discovery.k8s.io/v1beta1",
"/apis/events.k8s.io",
"/apis/events.k8s.io/v1",
"/apis/events.k8s.io/v1beta1",
"/apis/flowcontrol.apiserver.k8s.io",
"/apis/flowcontrol.apiserver.k8s.io/v1beta1",
"/apis/metrics.k8s.io",
"/apis/metrics.k8s.io/v1beta1",
"/apis/networking.k8s.io",
"/apis/networking.k8s.io/v1",
"/apis/node.k8s.io",
"/apis/node.k8s.io/v1",
"/apis/node.k8s.io/v1beta1",
"/apis/policy",
"/apis/policy/v1",
"/apis/policy/v1beta1",
"/apis/rbac.authorization.k8s.io",
"/apis/rbac.authorization.k8s.io/v1",
"/apis/scheduling.k8s.io",
"/apis/scheduling.k8s.io/v1",
"/apis/storage.k8s.io",
"/apis/storage.k8s.io/v1",
"/apis/storage.k8s.io/v1beta1",
"/healthz",
"/healthz/autoregister-completion",
"/healthz/etcd",
"/healthz/log",
"/healthz/ping",
"/healthz/poststarthook/aggregator-reload-proxy-client-cert",
"/healthz/poststarthook/apiservice-openapi-controller",
"/healthz/poststarthook/apiservice-registration-controller",
"/healthz/poststarthook/apiservice-status-available-controller",
"/healthz/poststarthook/bootstrap-controller",
"/healthz/poststarthook/crd-informer-synced",
"/healthz/poststarthook/generic-apiserver-start-informers",
"/healthz/poststarthook/kube-apiserver-autoregistration",
"/healthz/poststarthook/priority-and-fairness-config-consumer",
"/healthz/poststarthook/priority-and-fairness-config-producer",
"/healthz/poststarthook/priority-and-fairness-filter",
"/healthz/poststarthook/rbac/bootstrap-roles",
"/healthz/poststarthook/scheduling/bootstrap-system-priority-classes",
"/healthz/poststarthook/start-apiextensions-controllers",
"/healthz/poststarthook/start-apiextensions-informers",
"/healthz/poststarthook/start-cluster-authentication-info-controller",
"/healthz/poststarthook/start-kube-aggregator-informers",
"/healthz/poststarthook/start-kube-apiserver-admission-initializer",
"/livez",
"/livez/autoregister-completion",
"/livez/etcd",
"/livez/log",
"/livez/ping",
"/livez/poststarthook/aggregator-reload-proxy-client-cert",
"/livez/poststarthook/apiservice-openapi-controller",
"/livez/poststarthook/apiservice-registration-controller",
"/livez/poststarthook/apiservice-status-available-controller",
"/livez/poststarthook/bootstrap-controller",
"/livez/poststarthook/crd-informer-synced",
"/livez/poststarthook/generic-apiserver-start-informers",
"/livez/poststarthook/kube-apiserver-autoregistration",
"/livez/poststarthook/priority-and-fairness-config-consumer",
"/livez/poststarthook/priority-and-fairness-config-producer",
"/livez/poststarthook/priority-and-fairness-filter",
"/livez/poststarthook/rbac/bootstrap-roles",
"/livez/poststarthook/scheduling/bootstrap-system-priority-classes",
"/livez/poststarthook/start-apiextensions-controllers",
"/livez/poststarthook/start-apiextensions-informers",
"/livez/poststarthook/start-cluster-authentication-info-controller",
"/livez/poststarthook/start-kube-aggregator-informers",
"/livez/poststarthook/start-kube-apiserver-admission-initializer",
"/logs",
"/metrics",
"/openapi/v2",
"/openid/v1/jwks",
"/readyz",
"/readyz/autoregister-completion",
"/readyz/etcd",
"/readyz/informer-sync",
"/readyz/log",
"/readyz/ping",
"/readyz/poststarthook/aggregator-reload-proxy-client-cert",
"/readyz/poststarthook/apiservice-openapi-controller",
"/readyz/poststarthook/apiservice-registration-controller",
"/readyz/poststarthook/apiservice-status-available-controller",
"/readyz/poststarthook/bootstrap-controller",
"/readyz/poststarthook/crd-informer-synced",
"/readyz/poststarthook/generic-apiserver-start-informers",
"/readyz/poststarthook/kube-apiserver-autoregistration",
"/readyz/poststarthook/priority-and-fairness-config-consumer",
"/readyz/poststarthook/priority-and-fairness-config-producer",
"/readyz/poststarthook/priority-and-fairness-filter",
"/readyz/poststarthook/rbac/bootstrap-roles",
"/readyz/poststarthook/scheduling/bootstrap-system-priority-classes",
"/readyz/poststarthook/start-apiextensions-controllers",
"/readyz/poststarthook/start-apiextensions-informers",
"/readyz/poststarthook/start-cluster-authentication-info-controller",
"/readyz/poststarthook/start-kube-aggregator-informers",
"/readyz/poststarthook/start-kube-apiserver-admission-initializer",
"/readyz/shutdown",
"/version"
]
}
[root@master1 ~]#
  • 同样我们还是可以通过 kubectl 来查询对应对象下面的数据
[root@master1 ~]#kubectl get --raw /apis/batch/v1 | python -m json.tool
{
"apiVersion": "v1",
"groupVersion": "batch/v1",
"kind": "APIResourceList",
"resources": [
{
"categories": [
"all"
],
"kind": "CronJob",
"name": "cronjobs",
"namespaced": true,
"shortNames": [
"cj"
],
"singularName": "",
"storageVersionHash": "sd5LIXh4Fjs=",
"verbs": [
"create",
"delete",
"deletecollection",
"get",
"list",
"patch",
"update",
"watch"
]
},
{
"kind": "CronJob",
"name": "cronjobs/status",
"namespaced": true,
"singularName": "",
"verbs": [
"get",
"patch",
"update"
]
},
{
"categories": [
"all"
],
"kind": "Job",
"name": "jobs",
"namespaced": true,
"singularName": "",
"storageVersionHash": "mudhfqk/qZY=",
"verbs": [
"create",
"delete",
"deletecollection",
"get",
"list",
"patch",
"update",
"watch"
]
},
{
"kind": "Job",
"name": "jobs/status",
"namespaced": true,
"singularName": "",
"verbs": [
"get",
"patch",
"update"
]
}
]
}


[root@master1 ~]#kubectl get --raw /apis/batch/v1beta1 | python -m json.tool
{
"apiVersion": "v1",
"groupVersion": "batch/v1beta1",
"kind": "APIResourceList",
"resources": [
{
"categories": [
"all"
],
"kind": "CronJob",
"name": "cronjobs",
"namespaced": true,
"shortNames": [
"cj"
],
"singularName": "",
"storageVersionHash": "sd5LIXh4Fjs=",
"verbs": [
"create",
"delete",
"deletecollection",
"get",
"list",
"patch",
"update",
"watch"
]
},
{
"kind": "CronJob",
"name": "cronjobs/status",
"namespaced": true,
"singularName": "",
"verbs": [
"get",
"patch",
"update"
]
}
]
}
[root@master1 ~]#
  • 但是这个操作和我们平时操作 HTTP 服务的方式不太一样,这里我们可以通过 kubectl proxy 命令来开启对 apiserver 的访问:

现在我们知道了 API Server 可以通过 URI 的形式访问到资源,我们可以通过 kubectl proxy 来代理一个本地的API Server 端口,这样就可以绕过认证了,直接可以以 http 的形式进行:

[root@master1 ~]#kubectl proxy
Starting to serve on 127.0.0.1:8001
  • 然后重新开启一个新的终端,我们可以通过如下方式来访问批处理的 API 服务:
[root@master1 ~]#curl http://127.0.0.1:8001/apis/batch/v1
{
"kind": "APIResourceList",
"apiVersion": "v1",
"groupVersion": "batch/v1",
"resources": [
{
"name": "cronjobs",
"singularName": "",
"namespaced": true,
"kind": "CronJob",
"verbs": [
"create",
"delete",
"deletecollection",
"get",
"list",
"patch",
"update",
"watch"
],
"shortNames": [
"cj"
],
"categories": [
"all"
],
"storageVersionHash": "sd5LIXh4Fjs="
},
{
"name": "cronjobs/status",
"singularName": "",
"namespaced": true,
"kind": "CronJob",
"verbs": [
"get",
"patch",
"update"
]
},
{
"name": "jobs",
"singularName": "",
"namespaced": true,
"kind": "Job",
"verbs": [
"create",
"delete",
"deletecollection",
"get",
"list",
"patch",
"update",
"watch"
],
"categories": [
"all"
],
"storageVersionHash": "mudhfqk/qZY="
},
{
"name": "jobs/status",
"singularName": "",
"namespaced": true,
"kind": "Job",
"verbs": [
"get",
"patch",
"update"
]
}
]
}[root@master1 ~]#
  • 同样也可以去访问另外一个版本的对象数据:
[root@master1 ~]#curl http://127.0.0.1:8001/apis/batch/v1beta1
{
"kind": "APIResourceList",
"apiVersion": "v1",
"groupVersion": "batch/v1beta1",
"resources": [
{
"name": "cronjobs",
"singularName": "",
"namespaced": true,
"kind": "CronJob",
"verbs": [
"create",
"delete",
"deletecollection",
"get",
"list",
"patch",
"update",
"watch"
],
"shortNames": [
"cj"
],
"categories": [
"all"
],
"storageVersionHash": "sd5LIXh4Fjs="
},
{
"name": "cronjobs/status",
"singularName": "",
"namespaced": true,
"kind": "CronJob",
"verbs": [
"get",
"patch",
"update"
]
}
]
}[root@master1 ~]#
  • 我们就可以直接访问 API Server 了:

image-20230201212742134

  • 如果想看系统支持哪些 GVK ,那么可以通过 kubectl 的命令查看:
[root@master1 ~]#kubectl api-resources
NAME SHORTNAMES APIVERSION NAMESPACED KIND
bindings v1 true Binding
componentstatuses cs v1 false ComponentStatus
configmaps cm v1 true ConfigMap
endpoints ep v1 true Endpoints
events ev v1 true Event
limitranges limits v1 true LimitRange
namespaces ns v1 false Namespace
nodes no v1 false Node
persistentvolumeclaims pvc v1 true PersistentVolumeClaim
persistentvolumes pv v1 false PersistentVolume
pods po v1 true Pod
podtemplates v1 true PodTemplate
replicationcontrollers rc v1 true ReplicationController
resourcequotas quota v1 true ResourceQuota
secrets v1 true Secret
serviceaccounts sa v1 true ServiceAccount
services svc v1 true Service
mutatingwebhookconfigurations admissionregistration.k8s.io/v1 false MutatingWebhookConfiguration
validatingwebhookconfigurations admissionregistration.k8s.io/v1 false ValidatingWebhookConfiguration
customresourcedefinitions crd,crds apiextensions.k8s.io/v1 false CustomResourceDefinition
apiservices apiregistration.k8s.io/v1 false APIService
controllerrevisions apps/v1 true ControllerRevision
daemonsets ds apps/v1 true DaemonSet
deployments deploy apps/v1 true Deployment
replicasets rs apps/v1 true ReplicaSet
statefulsets sts apps/v1 true StatefulSet
tokenreviews authentication.k8s.io/v1 false TokenReview
localsubjectaccessreviews authorization.k8s.io/v1 true LocalSubjectAccessReview
selfsubjectaccessreviews authorization.k8s.io/v1 false SelfSubjectAccessReview
selfsubjectrulesreviews authorization.k8s.io/v1 false SelfSubjectRulesReview
subjectaccessreviews authorization.k8s.io/v1 false SubjectAccessReview
horizontalpodautoscalers hpa autoscaling/v2 true HorizontalPodAutoscaler
verticalpodautoscalercheckpoints vpacheckpoint autoscaling.k8s.io/v1 true VerticalPodAutoscalerCheckpoint
verticalpodautoscalers vpa autoscaling.k8s.io/v1 true VerticalPodAutoscaler
cronjobs cj batch/v1 true CronJob
jobs batch/v1 true Job
certificatesigningrequests csr certificates.k8s.io/v1 false CertificateSigningRequest
leases coordination.k8s.io/v1 true Lease
endpointslices discovery.k8s.io/v1 true EndpointSlice
events ev events.k8s.io/v1 true Event
flowschemas flowcontrol.apiserver.k8s.io/v1beta2 false FlowSchema
prioritylevelconfigurations flowcontrol.apiserver.k8s.io/v1beta2 false PriorityLevelConfiguration
nodes metrics.k8s.io/v1beta1 false NodeMetrics
pods metrics.k8s.io/v1beta1 true PodMetrics
ingressclasses networking.k8s.io/v1 false IngressClass
ingresses ing networking.k8s.io/v1 true Ingress
networkpolicies netpol networking.k8s.io/v1 true NetworkPolicy
runtimeclasses node.k8s.io/v1 false RuntimeClass
poddisruptionbudgets pdb policy/v1 true PodDisruptionBudget
clusterrolebindings rbac.authorization.k8s.io/v1 false ClusterRoleBinding
clusterroles rbac.authorization.k8s.io/v1 false ClusterRole
rolebindings rbac.authorization.k8s.io/v1 true RoleBinding
roles rbac.authorization.k8s.io/v1 true Role
priorityclasses pc scheduling.k8s.io/v1 false PriorityClass
csidrivers storage.k8s.io/v1 false CSIDriver
csinodes storage.k8s.io/v1 false CSINode
csistoragecapacities storage.k8s.io/v1 true CSIStorageCapacity
storageclasses sc storage.k8s.io/v1 false StorageClass
volumeattachments storage.k8s.io/v1 false VolumeAttachment
[root@master1 ~]#

这里我们可以发现一些特别的地方,对于 Pod 和 Service,它的 API GROUP 居然是空的,这其实就是 Kubernetes核心(core)资源的含义,也就是所谓的 Kubernetes 中的基础资源,他们不需要 Group,只有 Version 和Kind ,其实主要是历史原因导致的,在 Kubernetes 的开始之初还不支持自定义类型的时候就没考虑过 Group。

测试结束。😘

🍀 备注

api-server就是一个webserver。

image-20230118065658892

image-20230118070243256

image-20230118070323754

image-20230118070732948

1.GVK

我们知道要表达一个资源对象需要指定 group/kind 和 version ,这个在 Kubernetes 的 API Server 中简称为GVK,GVK 是定位一种类型的方式,例如 daemonsets 就是 Kubernetes 中的一种资源,当我们跟 Kubernetes 说我想要创建一个 daemonsets 的时候,kubectl 是如何知道该怎么向 API Server 发送请求呢?是所有的不同资源都发向同一个 Endpoint,还是每种资源都是不同的?

我们回顾下定义的 daemonsets 的资源清单文件:

apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-exporter
namespace: kube-system
spec:
# ......

这里声明的 apiVersion 是 apps/v1 ,其实就是隐含了 Group 是 apps, Version 是 v1 ,Kind 就是定义的DaemonSet,而 kubectl 接收到这个清单之后,就可以根据这个声明去调用 API Server 对应的 URL 去获取信息,例如这里就是 /api/apps/v1/daemonset 。所以我们说 Kubernetes 组织资源的方式是以 REST 的 URI 的形式来的,而组织的路径就是:

image-20230201210651724

我们这里不是说是 G(roup)V(ersion)K(ind) 吗,怎么现在又变成了 G(roup)V(ersion)R(esource) ?这就是API Server 中的第二个概念: GVR。

2.GVR

理解了 GVK 之后再理解 GVR 就很容易了,这就是面向对象编程里面的类和对象的概念是一样的:

image-20230201212108767

Kind 其实就是一个类,用于描述对象的;而 Resource 就是具体的 Kind,可以理解成类已经实例化成对象了。Resource 只是 API 中的一个 Kind 的使用方式,通常情况下, Kind 和 Resource 之间有一个一对一的映射。例如,pods 资源对应于 Pod 种类,但是有时,同一类型可能由多个资源返回。例如 Scale Kind 是由所有 scale 子资源返回的,如 deployments/scale 或 replicasets/scale,这就是允许 KubernetesHorizontalPodAutoscaler(HPA) 与不同资源交互的原因。

当我们要定义一个 GVR 的时候,那么怎么知道这个 GVR 是属于哪个 GVK 的呢?也就是前面说的,kubectl 是如何从YAML 资源清单文件中知道该请求的是哪个 GVR 路径的?这就是 REST Mapping 的功能, REST Mapping 可以指定一个 GVR (例如 daemonset 的这个例子),然后它返回对应的 GVK 以及支持的操作等。

在代码中,其实就对应于这个接口:

image-20230201212351570

这样,就把 GVK 和 GVR 联系起来了。

noResources

image-20230202204824171

image-20230202204835457

如何确定某个资源对象属于哪个apiGroup呢

法1:kubectl explains命令

[root@master1 rbac]#kubectl explain pod
KIND: Pod
VERSION: v1
……

方法2:kubectl api-resource命令

[root@master1 rbac]#kubectl api-resources |head -1;kubectl api-resources |grep pods
NAME SHORTNAMES APIVERSION NAMESPACED KIND
pods po v1 true Pod
pods metrics.k8s.io/v1beta1 true PodMetrics
podsecuritypolicies psp policy/v1beta1 false PodSecurityPolicy
[root@master1 rbac]#

image-20230202205116622

2、Kubernetes 安全框架

• K8S安全控制框架主要由下面3个阶段进行控制,每一个阶段都支持插件方式,通过API Server配置来启用插件。

  1. Authentication(鉴权)
  2. Authorization(授权)
  3. Admission Control(准入控制)

客户端要想访问K8s集群API Server,一般需要证书、Token或者用户名+密码;如果Pod访问,需要ServiceAccount

#形象记忆
门卫:鉴权
门禁卡:授权
准入控制:安全扫描设备

1.鉴权(Authentication)

K8s Apiserver提供三种客户端身份认证: 1.HTTPS 证书认证:基于CA证书签名的数字证书认证(kubeconfig/root/.kube/config文件 2.HTTP Token认证:通过一个Token来识别用户(serviceaccount#serviceAccount:k8s为应用程序准备的一个账号。一般我们很少去用,主要可能是开发用会用; 之前实验有用到" HTTP Token认证:通过一个Token来识别用户(serviceaccount)":登录dashboard时用到了这个功能。 3.HTTP Base认证:用户名+密码的方式认证(1.19版本已经弃用了

/root/.kube/config文件内容:

image-20230522064701488

2.授权(Authorization)

RBAC(Role-Based Access Control,基于角色的访问控制):负责完成授权(Authorization)工作。

RBAC根据API请求属性,决定允许还是拒绝。

比较常见的授权维度:

• user:用户名

• group:用户分组

• 资源,例如pod、deployment

• 资源操作方法:get,list,create,update,patch,watch,delete

• 命名空间

• API组

3.准入控制(Admission Control)

Adminssion Control实际上是一个准入控制器插件列表发送到API Server的请求都需要经过这个列表中的每个准入控制器插件的检查,检查不通过,则拒绝请求。

启用一个准入控制器:
kube-apiserver --enable-admission-plugins=NamespaceLifecycle,LimitRanger ...

关闭一个准入控制器:
kube-apiserver --disable-admission-plugins=PodNodeSelector,AlwaysDeny ...

查看默认启用:
kubectl exec kube-apiserver-k8s-master -n kube-system -- kube-apiserver -h | grep enable-admission-plugins
[root@k8s-master1 ~]#kubectl exec kube-apiserver-k8s-master1 -n kube-system -- kube-apiserver -h | grep enable-admission-plugins
--admission-control strings Admission is divided into two phases. In the first phase, only mutating admission plugins run. In the second phase, only validating admission plugins run. The names in the below list may represent a validating plugin, a mutating plugin, or both. The order of plugins in which they are passed to this flag does not matter. Comma-delimited list of: AlwaysAdmit, AlwaysDeny, AlwaysPullImages, CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, DefaultStorageClass, DefaultTolerationSeconds, DenyEscalatingExec, DenyExecOnPrivileged, EventRateLimit, ExtendedResourceToleration, ImagePolicyWebhook, LimitPodHardAntiAffinityTopology, LimitRanger, MutatingAdmissionWebhook, NamespaceAutoProvision, NamespaceExists, NamespaceLifecycle, NodeRestriction, OwnerReferencesPermissionEnforcement, PersistentVolumeClaimResize, PersistentVolumeLabel, PodNodeSelector, PodSecurityPolicy, PodTolerationRestriction, Priority, ResourceQuota, RuntimeClass, SecurityContextDeny, ServiceAccount, StorageObjectInUseProtection, TaintNodesByCondition, ValidatingAdmissionWebhook. (DEPRECATED: Use --enable-admission-plugins or --disable-admission-plugins instead. Will be removed in a future version.)
--enable-admission-plugins strings admission plugins that should be enabled in addition to default enabled ones

(NamespaceLifecycle, LimitRanger, ServiceAccount, TaintNodesByCondition, Priority, DefaultTolerationSeconds, DefaultStorageClass, StorageObjectInUseProtection, PersistentVolumeClaimResize, RuntimeClass, CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, MutatingAdmissionWebhook, ValidatingAdmissionWebhook, ResourceQuota).

Comma-delimited list of admission plugins: AlwaysAdmit, AlwaysDeny, AlwaysPullImages, CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, DefaultStorageClass, DefaultTolerationSeconds, DenyEscalatingExec, DenyExecOnPrivileged, EventRateLimit, ExtendedResourceToleration, ImagePolicyWebhook, LimitPodHardAntiAffinityTopology, LimitRanger, MutatingAdmissionWebhook, NamespaceAutoProvision, NamespaceExists, NamespaceLifecycle, NodeRestriction, OwnerReferencesPermissionEnforcement, PersistentVolumeClaimResize, PersistentVolumeLabel, PodNodeSelector, PodSecurityPolicy, PodTolerationRestriction, Priority, ResourceQuota, RuntimeClass, SecurityContextDeny, ServiceAccount, StorageObjectInUseProtection, TaintNodesByCondition, ValidatingAdmissionWebhook. The order of plugins in this flag does not matter.

k8s使用准入控制的目的:

1、方便用户扩展apiserver功能(对所有请求处理的功能)

2、k8s提供很多功能,把这些功能插件化设计

3、RBAC

基于角色的权限访问控制:RBAC

上面我们介绍了 Kubernetes 所有资源对象都是模型化的 API 对象,允许执行 CRUD(Create、Read、Update、Delete) 等操作(也就是我们常说的增、删、改、查操作),比如下面的这些资源:

  • Pods
  • ConfigMaps
  • Deployments
  • Nodes
  • Secrets
  • Namespaces
  • ......

对于上面这些资源对象的可能存在的操作有:

  • create
  • get
  • delete
  • list
  • update
  • edit
  • watch
  • exec
  • patch

在更上层,这些资源和 API Group 进行关联,比如 Pods 属于 Core API Group,而 Deployements 属于 appsAPI Group,现在我们要在 Kubernetes 中通过 RBAC 来对资源进行权限管理,除了上面的这些资源和操作以外,我们还需要了解另外几个概念:

Rule :规则,规则是一组属于不同 API Group 资源上的一组操作的集合

Role 和 ClusterRole :角色和集群角色,这两个对象都包含上面的 Rules 元素,二者的区别在于,在 Role中,定义的规则只适用于单个命名空间,也就是和 namespace 关联的,而 ClusterRole 是集群范围内的,因此定义的规则不受命名空间的约束。另外 Role 和 ClusterRole 在 Kubernetes 中都被定义为集群内部的 API 资源,和我们前面学习过的 Pod、Deployment 这些对象类似,都是我们集群的资源对象,所以同样的可以使用 YAML文件来描述,用 kubectl 工具来管理。

Subject:主题,对应集群中尝试操作的对象,集群中定义了 3 种类型的主题资源: User Account:用户,这是有外部独立服务进行管理的,管理员进行私钥的分配,用户可以使用 KeyStone 或者 Goolge 帐号,甚至一个用户名和密码的文件列表也可以。对于用户的管理集群内部没有一个关联的资源对象,所以用户不能通过集群内部的 API 来进行管理 Group :组,这是用来关联多个账户的,集群中有一些默认创建的组,比如 cluster-admin Service Account:服务帐号,通过 Kubernetes API 来管理的一些用户帐号,和 namespace 进行关联的,适用于集群内部运行的应用程序,需要通过 API 来完成权限认证,所以在集群内部进行权限操作,我们都需要使用到 ServiceAccount,这也是我们这节课的重点

RoleBinding 和 ClusterRoleBinding :角色绑定和集群角色绑定,简单来说就是把声明的 Subject 和我们的Role 进行绑定的过程(给某个用户绑定上操作的权限),二者的区别也是作用范围的区别:RoleBinding 只会影响到当前 namespace 下面的资源操作权限,而 ClusterRoleBinding 会影响到所有的 namespace。

⚠️ 注意

rolebinding是绑定某个命名空间的; clusterrolebinding是针对all命名空间的; 正常情况下:我们创建一个role ,就对应一个rolebinding。我们创建一个clusterrole ,就对应一个clusterrolebinding;

image-20230202211426451

接下来我们来通过几个简单的示例,来学习下在 Kubernetes 集群中如何使用 RBAC。

一个sa可以绑定多个role的吧,如果多个role,会取交集。(具体现象可以再测试下) 例如,A允许,B没做权限(也就是拒绝),那么最后权限就是允许了。

RBAC(Role-Based Access Control,基于角色的访问控制),

是K8s默认授权策略,并且是动态配置策略(修改即时生效)。

主体(subject)

• User:用户

• Group:用户组

• ServiceAccount:服务账号

角色 (角色=权限组)

• Role:授权特定命名空间的访问权限

• ClusterRole:授权所有命名空间的访问权限

角色绑定

• RoleBinding:将角色绑定到主体(即subject)

• ClusterRoleBinding:将集群角色绑定到主体

注:RoleBinding在指定命名空间中执行授权,ClusterRoleBinding在集群范围执行授权。

image-20230522072055305

k8s预定好了四个集群角色供用户使用,使用kubectl get clusterrole查看,其中systemd:开头的为系统内部使用。

image-20230522072357959

TLS证书认证流程

image-20230523211108092

1.只能访问某个 namespace 的普通用户

实战1:只能访问某个 namespace 的普通用户(openssl)

实战1:只能访问某个 namespace 的普通用户-2023.2.3(测试成功)(openssl)

image-20230203075216847

  • 实验环境
实验环境:
1、win10,vmwrokstation虚机;
2、k8s集群:3台centos7.6 1810虚机,1个master节点,2个node节点
k8s version:v1.25.4
containerd://1.6.10
  • 实验软件(无)
  • 实验步骤
1.创建用户凭证
2.创建角色
3.创建角色权限绑定
4.测试

==1、创建用户凭证==

我们想要创建一个 User Account,只能访问 kube-system 这个命名空间,对应的用户信息如下所示:

username: cnych
group: youdianzhishi

我们前面已经提到过,Kubernetes 没有 User Account 的 API 对象,不过要创建一个用户帐号的话也是挺简单的,利用管理员分配给你的一个私钥就可以创建了,这个我们可以参考官方文档中的方法,这里我们来使用 OpenSSL 证书来创建一个 User,当然我们也可以使用更简单的 cfssl工具来创建:

  • 给用户 cnych 创建一个私钥,命名成 cnych.key
[root@master1 ~]#mkdir rbac
[root@master1 ~]#cd rbac/
[root@master1 rbac]#openssl genrsa -out cnych.key 2048
Generating RSA private key, 2048 bit long modulus
..+++
..........................................................................+++
e is 65537 (0x10001)
[root@master1 rbac]#ls
cnych.key
  • 使用我们刚刚创建的私钥创建一个证书签名请求文件cnych.csr,要注意需要确保在-subj参数中指定用户名和组(CN表示用户名,O表示组):
[root@master1 rbac]#openssl req -new -key cnych.key -out cnych.csr -subj "/CN=cnych/O=youdianzhishi"
[root@master1 rbac]#ls
cnych.csr cnych.key
  • 然后找到我们的 Kubernetes 集群的 CA 证书,我们使用的是 kubeadm 安装的集群,CA 相关证书位于 /etc/kubernetes/pki/ 目录下面,如果你是二进制方式搭建的,你应该在最开始搭建集群的时候就已经指定好了 CA 的目录,我们会利用该目录下面的 ca.crt ca.key两个文件来批准上面的证书请求。生成最终的证书文件,我们这里设置证书的有效期为 500 天:
[root@master1 rbac]#openssl x509 -req -in cnych.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out cnych.crt -days 500
Signature ok
subject=/CN=cnych/O=youdianzhishi
Getting CA Private Key

#现在查看我们当前文件夹下面是否生成了一个证书文件:
[root@master1 rbac]#ls
cnych.crt cnych.csr cnych.key
  • 现在我们可以使用刚刚创建的证书文件和私钥文件在集群中创建新的凭证和上下文(Context),我们先来创建新的凭证:
[root@master1 rbac]#kubectl config --help #这里我们可以先看下config的帮助信息
……
Available Commands:
current-context Display the current-context #care
delete-cluster Delete the specified cluster from the kubeconfig
delete-context Delete the specified context from the kubeconfig
delete-user Delete the specified user from the kubeconfig
get-clusters Display clusters defined in the kubeconfig #care
get-contexts Describe one or many contexts
get-users Display users defined in the kubeconfig
rename-context Rename a context from the kubeconfig file
set Set an individual value in a kubeconfig file
set-cluster Set a cluster entry in kubeconfig
set-context Set a context entry in kubeconfig
set-credentials Set a user entry in kubeconfig #care
unset Unset an individual value in a kubeconfig file
use-context Set the current-context in a kubeconfig file
view Display merged kubeconfig settings or a specified kubeconfig file
……
  • 我们先来创建新的凭证:
[root@master1 rbac]#kubectl config set-credentials cnych --client-certificate=cnych.crt --client-key=cnych.key
User "cnych" set.
  • 我们可以看到一个用户 cnych 创建了,然后为这个用户设置新的 Context,我们这里指定特定的一个 namespace:
#因为我们当前只有一个k8s集群,我么可以用命令先看一下
[root@master1 rbac]#kubectl config get-clusters
NAME
kubernetes

#为这个用户设置新的 Context
[root@master1 rbac]#kubectl config set-context cnych-context --cluster=kubernetes --namespace=kube-system --user=cnych
Context "cnych-context" created.
  • 到这里,我们的用户 cnych 就已经创建成功了,现在我们使用当前的这个配置文件来操作 kubectl 命令的时候,应该会出现错误,因为我们还没有为该用户定义任何操作的权限呢:
[root@master1 rbac]#kubectl get pod --context=cnych-context
Error from server (Forbidden): pods is forbidden: User "cnych" cannot list resource "pods" in API group "" in the namespace "kube-system"

因此,需要按如下思路去操作:

image-20230203074335320

==2、创建角色==

  • 用户创建完成后,接下来就需要给该用户添加操作权限,我们来定义一个 YAML 文件,创建一个允许用户操作 Deployment、Pod、ReplicaSets 的角色,如下定义:
#cnych-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: cnych-role
namespace: kube-system
rules:
- apiGroups: ["", "apps"]
resources: ["deployments", "replicasets", "pods"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] # 也可以使用['*']

其中 Pod 属于 core 这个 API Group,在 YAML 中用空字符就可以,而 Deployment 和 ReplicaSet 现在都属于 apps 这个 API Group(如果不知道则可以用 kubectl explain 命令查看)。所以 rules 下面的 apiGroups 就综合了这几个资源的 API Group:["", "apps"],其中 verbs 就是我们上面提到的可以对这些资源对象执行的操作,我们这里需要所有的操作方法,所以我们也可以使用['*']来代替。

  • 然后直接创建这个 Role:
[root@master1 rbac]#kubectl apply -f cnych-role.yaml
role.rbac.authorization.k8s.io/cnych-role created

注意这里我们没有使用上面的 cnych-context 这个上下文,因为暂时还没有权限。

==3、创建角色权限绑定==

  • Role 创建完成了,但是很明显现在我们这个 Role 和我们的用户 cnych 还没有任何关系,对吧?这里就需要创建一个 RoleBinding 对象,在 kube-system 这个命名空间下面将上面的 cnych-role 角色和用户 cnych 进行绑定:
#cnych-rolebinding.yaml 
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: cnych-rolebinding
namespace: kube-system
subjects:
- kind: User #外部的用户
name: cnych
apiGroup: "" #没有就填个空字符串!!!
roleRef: #管理员权限声明的角色
kind: Role
name: cnych-role
apiGroup: rbac.authorization.k8s.io # 留空字符串也可以,则使用当前的apiGroup

#把cnych用户和cnych-role角色绑定在一起,而Role角色具有相应权限,就相当于cnych用户有了这个权限。
  • 上面的 YAML 文件中我们看到了 subjects 字段,这里就是我们上面提到的用来尝试操作集群的对象,这里对应上面的 User 帐号 cnych,使用kubectl 创建上面的资源对象:
[root@master1 rbac]#kubectl apply -f cnych-rolebinding.yaml
rolebinding.rbac.authorization.k8s.io/cnych-rolebinding created

==4、测试==

现在我们应该可以上面的 cnych-context 上下文来操作集群了:

[root@master1 rbac]#kubectl get pod --context=cnych-context
NAME READY STATUS RESTARTS AGE
coredns-7568f67dbd-9dls5 1/1 Running 0 16d
coredns-7568f67dbd-kqhjq 1/1 Running 0 27d
etcd-master1 1/1 Running 1 28d
kube-apiserver-master1 1/1 Running 2 (28d ago) 28d
kube-controller-manager-master1 1/1 Running 2 28d
kube-flannel-ds-5lm5t 1/1 Running 0 25d
kube-flannel-ds-rmtfk 1/1 Running 0 25d
kube-flannel-ds-xbp6g 1/1 Running 0 25d
kube-proxy-4sw76 1/1 Running 0 28d
kube-proxy-mkghd 1/1 Running 0 28d
kube-proxy-s2748 1/1 Running 0 28d
kube-scheduler-master1 1/1 Running 2 28d
metrics-server-76b8bfbb47-8v2lr 1/1 Running 0 6d15h

[root@master1 rbac]#kubectl get pod,deploy,rs --context=cnych-context
NAME READY STATUS RESTARTS AGE
pod/coredns-7568f67dbd-9dls5 1/1 Running 0 16d
pod/coredns-7568f67dbd-kqhjq 1/1 Running 0 27d
pod/etcd-master1 1/1 Running 1 28d
pod/kube-apiserver-master1 1/1 Running 2 (28d ago) 28d
pod/kube-controller-manager-master1 1/1 Running 2 28d
pod/kube-flannel-ds-5lm5t 1/1 Running 0 25d
pod/kube-flannel-ds-rmtfk 1/1 Running 0 25d
pod/kube-flannel-ds-xbp6g 1/1 Running 0 25d
pod/kube-proxy-4sw76 1/1 Running 0 28d
pod/kube-proxy-mkghd 1/1 Running 0 28d
pod/kube-proxy-s2748 1/1 Running 0 28d
pod/kube-scheduler-master1 1/1 Running 2 28d
pod/metrics-server-76b8bfbb47-8v2lr 1/1 Running 0 6d15h

NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/coredns 2/2 2 2 28d
deployment.apps/metrics-server 1/1 1 1 6d15h

NAME DESIRED CURRENT READY AGE
replicaset.apps/coredns-7568f67dbd 2 2 2 28d
replicaset.apps/metrics-server-76b8bfbb47 1 1 1 6d15h
replicaset.apps/metrics-server-859bf7c488 0 0 0 6d15h
[root@master1 rbac]#
  • 我们可以看到我们使用 kubectl 的使用并没有指定 namespace,这是因为我们我们上面创建这个 Context 的时候就绑定在了 kube-system 这个命名空间下面,如果我们在后面加上一个-n default试看看呢?
[root@master1 rbac]#kubectl get po --context=cnych-context --namespace=deafult
Error from server (Forbidden): pods is forbidden: User "cnych" cannot list resource "pods" in API group "" in the namespace "deafult"
[root@master1 rbac]#kubectl get po --context=cnych-context -n default
Error from server (Forbidden): pods is forbidden: User "cnych" cannot list resource "pods" in API group "" in the namespace "default"
[root@master1 rbac]#
  • 如果去获取其他的资源对象呢:
[root@master1 rbac]#kubectl get svc --context=cnych-context
Error from server (Forbidden): services is forbidden: User "cnych" cannot list resource "services" in API group "" in the namespace "kube-system"
[root@master1 ~]#kubectl get daemonsets --context=cnych-context
Error from server (Forbidden): daemonsets.apps is forbidden: User "cnych" cannot list resource "daemonsets" in API group "apps" in the namespace "kube-system"

我们可以看到没有权限获取,因为我们并没有为当前操作用户指定其他对象资源的访问权限,是符合我们的预期的。

  • 此时,我们给role加上允许访问daemonsets的权限
[root@master1 rbac]#vim cnych-role.yaml
#cnych-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: cnych-role
namespace: kube-system
rules:
- apiGroups: ["", "apps"]
resources: ["deployments", "replicasets", "pods", "daemonsets"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] # 也可以使用['*']

#部署
[root@master1 rbac]#kubectl apply -f cnych-role.yaml
role.rbac.authorization.k8s.io/cnych-role configured

#验证
[root@master1 rbac]#kubectl get daemonsets --context=cnych-context
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
kube-proxy 3 3 3 3 3 kubernetes.io/os=linux 61d

这样我们就创建了一个只有单个命名空间访问权限的普通 User 。

实验结束。😘

实战2:只能访问某个 namespace 的普通用户(TLS证书-cfgssl)

==💘 实战2:只能访问某个 namespace 的普通用户(TLS证书-cfgssl)-2023.2.6(测试成功)(cfgssl)==

image-20230206212010809

  • 实验环境
实验环境:
1、win10,vmwrokstation虚机;
2、k8s集群:3台centos7.6 1810虚机,1个master节点,2个node节点
k8s version:v1.20.0
docker://20.10.7
  • 实验软件
链接:https://pan.baidu.com/s/1-mUGhzvXQu6xZnPNyvlt5A?pwd=0820 
提取码:0820
2023.5.23-实战2:只能访问某个 namespace 的普通用户(cfgssl)-实验软件

image-20230523212321549

  • 实验步骤
graph LR
A[实战步骤] -->B(1.用K8S CA签发客户端证书)
A[实战步骤] -->C(2.生成kubeconfig授权文件)
A[实战步骤] -->D(3.创建RBAC权限策略)
A[实战步骤] -->E(4.指定kubeconfig文件测试权限)
A[实战步骤] -->F(5.对用户组授权k8s访问)
  • 背景

案例:为指定用户授权访问不同命名空间权限,例如新入职一个小弟,希望让他先熟悉K8s集群,为了安全性,先不能给他太大权限,因此先给他授权访问default命名空间Pod读取权限。

实施大致步骤:

  1. 用K8S CA签发客户端证书
  2. 生成kubeconfig授权文件
  3. 创建RBAC权限策略
  4. 指定kubeconfig文件测试权限 kubectl get pods --kubeconfig=./aliang.kubeconfig

1、用K8S CA(根证书)签发客户端证书

注意,前期需要先安装cfgssl命令才行。

  • 安装cfgssl工具
#将cfssl工具安装包和脚本上传到服务器:
[root@k8s-master1 ~]#ls -lh cfssl.tar.gz
-rw-r--r-- 1 root root 5.6M Nov 25 2019 cfssl.tar.gz
-rw-r--r-- 1 root root 1005 Mar 26 2021 certs.sh
[root@k8s-master1 ~]#tar tvf cfssl.tar.gz
-rwxr-xr-x root/root 10376657 2019-11-25 06:36 cfssl
-rwxr-xr-x root/root 6595195 2019-11-25 06:36 cfssl-certinfo
-rwxr-xr-x root/root 2277873 2019-11-25 06:36 cfssljson
[root@k8s-master1 ~]#tar xf cfssl.tar.gz -C /usr/bin/

#验证
[root@k8s-master1 ~]#cfssl --help
Usage:
Available commands:
bundle
certinfo
ocspsign
selfsign
scan
print-defaults
sign
gencert
ocspdump
version
genkey
gencrl
ocsprefresh
info
serve
ocspserve
revoke
Top-level flags:
-allow_verification_with_non_compliant_keys
Allow a SignatureVerifier to use keys which are technically non-compliant with RFC6962.
-loglevel int
Log level (0 = DEBUG, 5 = FATAL) (default 1)
  • 用K8S CA(根证书)签发客户端证书
[root@k8s-master ~]#ll -h
total 4.0K
-rw-r--r-- 1 root root 1.4K Jul 6 14:02 rbac.zip
drwxr-xr-x 6 root root 78 Jul 6 13:59 yaml #上传rbac.zip文件到家目录下并解压
[root@k8s-master ~]#unzip rbac.zip
[root@k8s-master ~]#cd rbac/
[root@k8s-master rbac]#ls
cert.sh kubeconfig.sh rbac.yaml


[root@k8s-master rbac]#sh cert.sh #直接执行
2021/07/05 05:50:00 [INFO] generate received request
2021/07/05 05:50:00 [INFO] received CSR
2021/07/05 05:50:00 [INFO] generating key: rsa-2048
2021/07/05 05:50:00 [INFO] encoded CSR
2021/07/05 05:50:00 [INFO] signed certificate with serial number 159473389712332926121043715574924065961218702913
2021/07/05 05:50:00 [WARNING] This certificate lacks a "hosts" field. This makes it unsuitable for
websites. For more information see the Baseline Requirements for the Issuance and Management
of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org);
specifically, section 10.2.3 ("Information Requirements").
[root@k8s-master rbac]#
[root@k8s-master1 rbac]#ls
aliang.csr aliang-csr.json aliang-key.pem aliang.pem ca-config.json cert.sh kubeconfig.sh rbac.yaml

注意:用K8S CA签发客户端证书

image-20230206211046745

image-20230523065927799

cert.sh文件内容:

cat > ca-config.json <<EOF
{
"signing": {
"default": {
"expiry": "87600h"
},
"profiles": {
"kubernetes": {
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
],
"expiry": "87600h"
}
}
}
}
EOF

cat > aliang-csr.json <<EOF
{
"CN": "aliang",
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "k8s",
"OU": "System"
}
]
}
EOF

cfssl gencert -ca=/etc/kubernetes/pki/ca.crt -ca-key=/etc/kubernetes/pki/ca.key -config=ca-config.json -profile=kubernetes aliang-csr.json | cfssljson -bare aliang

2. 生成kubeconfig授权文件

[root@k8s-master rbac]#pwd
/root/rbac
[root@k8s-master rbac]#ls
aliang.csr aliang-csr.json aliang-key.pem aliang.pem ca-config.json cert.sh kubeconfig.sh rbac rbac.yaml rbac.zip

kubeconfig.sh文件默认内容如下:

kubectl config set-cluster kubernetes \
--certificate-authority=/etc/kubernetes/pki/ca.crt \
--embed-certs=true \
--server=https://192.168.31.61:6443 \
--kubeconfig=aliang.kubeconfig

# 设置客户端认证
kubectl config set-credentials aliang \
--client-key=aliang-key.pem \
--client-certificate=aliang.pem \
--embed-certs=true \
--kubeconfig=aliang.kubeconfig

# 设置默认上下文
kubectl config set-context kubernetes \
--cluster=kubernetes \
--user=aliang \
--kubeconfig=aliang.kubeconfig

# 设置当前使用配置
kubectl config use-context kubernetes --kubeconfig=aliang.kubeconfig

需修改以上文件内容为如下:

kubectl config set-cluster kubernetes \
--certificate-authority=/etc/kubernetes/pki/ca.crt \
--embed-certs=true \
--server=https://172.29.9.31:6443 \ #修改位置一:改成你自己集群的api-server地址
--kubeconfig=aliang.kubeconfig

# 设置客户端认证
kubectl config set-credentials aliang \
--client-key=aliang-key.pem \
--client-certificate=aliang.pem \
--embed-certs=true \
--kubeconfig=aliang.kubeconfig

# 设置默认上下文
kubectl config set-context kubernetes \
--cluster=kubernetes \
--user=aliang \
--kubeconfig=aliang.kubeconfig

# 设置当前使用配置
kubectl config use-context kubernetes --kubeconfig=aliang.kubeconfig
[root@k8s-master1 ~]#kubectl get svc,ep
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 212d

NAME ENDPOINTS AGE
endpoints/kubernetes 172.29.9.31:6443 212d

image-20230523071028347

直接执行脚本:

[root@k8s-master1 rbac]#sh kubeconfig.sh 
Cluster "kubernetes" set.
User "aliang" set.
Context "kubernetes" created.
Switched to context "kubernetes".

[root@k8s-master1 rbac]#ls
aliang.csr aliang-csr.json aliang-key.pem aliang.kubeconfig aliang.pem ca-config.json cert.sh kubeconfig.sh rbac.yaml

image-20230523071140406

此时,我们可以测试下:

[root@k8s-master rbac]#kubectl get pod --kubeconfig=aliang.kubeconfig
Error from server (Forbidden): pods is forbidden: User "aliang" cannot list resource "pods" in API group "" in the namespace "default"
#此时可以看到,访问被禁止了。到这里我们相当于完成了第一步"鉴权":即k8s是认你这个客户端证书的。现在就将要开始第二步授权操作了。

3. 创建RBAC权限策略

  • 查看rbac.yaml内容:
[root@k8s-master rbac]#cat rbac.yaml
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list"]

---

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: read-pods
namespace: default
subjects:
- kind: User #主体
name: aliang #主体名称
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
[root@k8s-master rbac]#

image-20230523071952422

这2个地方的名称必须是一致的才行。

  • apply下rbac.yaml并查看:
#apply下rbac.yaml
[root@k8s-master rbac]#kubectl apply -f rbac.yaml
role.rbac.authorization.k8s.io/pod-reader created
rolebinding.rbac.authorization.k8s.io/read-pods created

#查看
[root@k8s-master rbac]#kubectl get role|grep pod-reader
pod-reader 2021-07-04T22:20:54Z

[root@k8s-master rbac]#kubectl describe role pod-reader
Name: pod-reader
Labels: <none>
Annotations: <none>
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
pods [] [] [get watch list]
[root@k8s-master rbac]#

4.指定kubeconfig文件测试权限

  • 以上all策略均已经配置完成了,现在进行测试:
[root@k8s-master rbac]#kubectl get pod --kubeconfig=aliang.kubeconfig #现在终于可以查看了
NAME READY STATUS RESTARTS AGE
my-pod 1/1 Running 0 38h
my-pod1 1/2 CrashLoopBackOff 23 38h

#以下无分配权限,次用户将不能进行其他操作
[root@k8s-master rbac]#kubectl get deployments --kubeconfig=aliang.kubeconfig #以下下均查看失败
Error from server (Forbidden): deployments.apps is forbidden: User "aliang" cannot list resource "deployments" in API group "apps" in the namespace "default"

[root@k8s-master rbac]#kubectl get pod --kubeconfig=aliang.kubeconfig -n kube-system
Error from server (Forbidden): pods is forbidden: User "aliang" cannot list resource "pods" in API group "" in the namespace "kube-system"

[root@k8s-master rbac]#kubectl get service --kubeconfig=aliang.kubeconfig
Error from server (Forbidden): services is forbidden: User "aliang" cannot list resource "services" in API group "" in the namespace "default"

#同样,kubectl describe也是可以使用的
[root@k8s-master rbac]#kubectl describe pod my-pod1 --kubeconfig=aliang.kubeconfig
Name: my-pod1
Namespace: default
Priority: 0
Node: k8s-node2/172.29.9.33
Start Time: Sat, 03 Jul 2021 15:43:45 +0800
Labels: <none>
Annotations: cni.projectcalico.org/podIP: 10.244.169.149/32

5.现在这个小伙子技术差不多了,该如何给他放大权限呢?

  • 继续编辑rbac.yaml,添加相应权限
[root@k8s-master rbac]#vim rbac.yaml #注意,以下有没有空格都是ok的
……
- apiGroups: ["", "apps"] #添加
resources: ["pods", "deployments", "services"] #添加,这里都是复数
verbs: ["get", "watch", "list"]
……

#注意:rbac都是配置即生效的。

image-20230206211426190

  • 再次apply下并测试:
#apply
[root@k8s-master rbac]#kubectl apply -f rbac.yaml
role.rbac.authorization.k8s.io/pod-reader configured
rolebinding.rbac.authorization.k8s.io/read-pods unchanged

#查看=>符合预期,可以正常查看deployment/svc资源
[root@k8s-master rbac]#kubectl get deployments --kubeconfig=aliang.kubeconfig
NAME READY UP-TO-DATE AVAILABLE AGE
web 1/1 1 1 5s
[root@k8s-master rbac]#kubectl get service --kubeconfig=aliang.kubeconfig
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 33d

技巧:如果我们实在不知道该如何配置策略,我们可以根据权限不足提示进行配置策略。

注意:
deployments/statefulsets/repilcasets在apps组里面;
namespace、pod、service、pv、pvc都在核心组里面;
[root@k8s-master1 rbac]#kubectl api-resources |grep pod #查看某个对象归属哪个组命令
pods po v1 true Pod
podtemplates v1 true PodTemplate
horizontalpodautoscalers hpa autoscaling/v1 true HorizontalPodAutoscaler
poddisruptionbudgets pdb policy/v1beta1 true PodDisruptionBudget
podsecuritypolicies psp policy/v1beta1 false PodSecurityPolicy
[root@k8s-master1 rbac]#kubectl api-resources |grep deployment
deployments deploy apps/v1 true Deployment
[root@k8s-master1 rbac]#

[root@k8s-master rbac]#kubectl get deployments --kubeconfig=aliang.kubeconfig #一下均查看失败
Error from server (Forbidden): deployments.apps is forbidden: User "aliang" cannot list resource "deployments" in API group "apps" in the namespace "default"

[root@k8s-master rbac]#kubectl get pod --kubeconfig=aliang.kubeconfig -n kube-system
Error from server (Forbidden): pods is forbidden: User "aliang" cannot list resource "pods" in API group "" in the namespace "kube-system"

[root@k8s-master rbac]#kubectl get service --kubeconfig=aliang.kubeconfig
Error from server (Forbidden): services is forbidden: User "aliang" cannot list resource "services" in API group "" in the namespace "default"
  • 再测试:我们给这个小弟添加delete权限
1、默认删除失败:
[root@k8s-master rbac]#kubectl get deployments.apps --kubeconfig=aliang.kubeconfig
NAME READY UP-TO-DATE AVAILABLE AGE
web 1/1 1 1 18m

[root@k8s-master rbac]#kubectl delete deployments.apps web --kubeconfig=aliang.kubeconfig
Error from server (Forbidden): deployments.apps "web" is forbidden: User "aliang" cannot delete resource "deployments" in API group "apps" in the namespace "default"
[root@k8s-master rbac]#

2、添加delete权限
[root@k8s-master rbac]#vim rbac.yaml
- apiGroups: ["","apps"]
resources: ["pods","deployments","services"]
verbs: ["get","watch","list","delete"] #添加dlete权限,注意,这里添加了删除操作后,代表对上面所有资源都具有删除权限。

3、apply并查看
[root@k8s-master rbac]#kubectl apply -f rbac.yaml
role.rbac.authorization.k8s.io/pod-reader configured
rolebinding.rbac.authorization.k8s.io/read-pods unchanged

4、测试
[root@k8s-master rbac]#kubectl delete deployments.apps web --kubeconfig=aliang.kubeconfig #添加delete后,删除成功
deployment.apps "web" deleted
[root@k8s-master rbac]#kubectl get deployments.apps --kubeconfig=aliang.kubeconfig
No resources found in default namespace.
[root@k8s-master rbac]#

6.此时,这个小伙子就可以欢乐去使用k8s集群了

我们 可以把这个aliang.kubeconfig配置文件发给小弟,他可以通过如下命令进行访问k8s集群:

[root@k8s-master rbac]#pwd
/root/rbac
[root@k8s-master rbac]#ls
aliang.csr aliang-csr.json aliang-key.pem aliang.kubeconfig aliang.pem ca-config.json cert.sh kubeconfig.sh rbac rbac.yaml rbac.zip

[root@k8s-master rbac]#
kubectl get pod --kubeconfig=aliang.kubeconfig
kubectl get deployment --kubeconfig=aliang.kubeconfig
kubectl get service --kubeconfig=aliang.kubeconfig

注意:

他可以在任何机器上去访问这个k8s集群,但要能通是前提

使用之前可以先安装好kubectl命令。

aliang.kubeconfig访问k8s里面所有的资源都在这里面了。

它如果不想每次后面都加上--kubeconfig=aliang.kubeconfig的话,我们可以去修改默认kubeconfig文件的-->即:用aliang.kubeconfig文件替换掉/root/.kube/config就ok。

如果登录到了master,默认使用的就是/root/.kube/config配置文件了。

7.对用户组授权访问k8s

image-20230523204707065

**用户组:**用户组的好处是无需单独为某个用户创建权限,统一为这个组名进行授权,所有的用户都以组的身份访问资源。

例如:为dev用户组统一授权

1、将certs.sh文件中的aliang-csr.json下的O字段改为dev,并重新生成证书和kubeconfig文件

2、将dev用户组绑定Role(pod-reader)

3、测试,只要O字段都是dev,这些用户持有的kubeconfig文件都拥有相同的权限

测试过程如下:

  • 准备环境
[root@k8s-master1 ~]#ls
cfssl.tar.gz rbac rbac.zip
[root@k8s-master1 ~]#cp -R rbac rbac2
[root@k8s-master1 ~]#cd rbac2/
[root@k8s-master1 rbac2]#ls
aliang.csr aliang-csr.json aliang-key.pem aliang.kubeconfig aliang.pem ca-config.json cert.sh kubeconfig.sh rbac.yaml
[root@k8s-master1 rbac2]#rm -rf aliang* ca-config.json
[root@k8s-master1 rbac2]#ls
cert.sh kubeconfig.sh rbac.yaml
[root@k8s-master1 rbac2]#
  • 修改cert.sh文件,用K8S CA签发客户端证书
[root@k8s-master1 rbac2]#vim cert.sh
cat > ca-config.json <<EOF
{
"signing": {
"default": {
"expiry": "87600h"
},
"profiles": {
"kubernetes": {
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
],
"expiry": "87600h"
}
}
}
}
EOF

cat > dev-csr.json <<EOF
{
"CN": "xxx",
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "dev",
"OU": "System"
}
]
}
EOF

cfssl gencert -ca=/etc/kubernetes/pki/ca.crt -ca-key=/etc/kubernetes/pki/ca.key -config=ca-config.json -profile=kubernetes dev-csr.json | cfssljson -bare dev

image-20230523205652622

刚才那个用户是基于这个CN字段,现在这个组是基于这个O字段

  • 执行cer.sh脚本
[root@k8s-master1 rbac2]#sh cert.sh 
2023/05/23 20:58:03 [INFO] generate received request
2023/05/23 20:58:03 [INFO] received CSR
2023/05/23 20:58:03 [INFO] generating key: rsa-2048
2023/05/23 20:58:03 [INFO] encoded CSR
2023/05/23 20:58:03 [INFO] signed certificate with serial number 14301059926732727887815038576691743284710927492
2023/05/23 20:58:03 [WARNING] This certificate lacks a "hosts" field. This makes it unsuitable for
websites. For more information see the Baseline Requirements for the Issuance and Management
of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org);
specifically, section 10.2.3 ("Information Requirements").
[root@k8s-master1 rbac2]#ls
ca-config.json cert.sh dev.csr dev-csr.json dev-key.pem dev.pem kubeconfig.sh rbac.yaml
  • 生成kubeconfig授权文件
[root@k8s-master1 rbac2]#vim kubeconfig.sh
kubectl config set-cluster kubernetes \
--certificate-authority=/etc/kubernetes/pki/ca.crt \
--embed-certs=true \
--server=https://172.29.9.31:6443 \
--kubeconfig=dev.kubeconfig

# 设置客户端认证
kubectl config set-credentials dev \
--client-key=dev-key.pem \
--client-certificate=dev.pem \
--embed-certs=true \
--kubeconfig=dev.kubeconfig

# 设置默认上下文
kubectl config set-context kubernetes \
--cluster=kubernetes \
--user=dev \
--kubeconfig=dev.kubeconfig

# 设置当前使用配置
kubectl config use-context kubernetes --kubeconfig=dev.kubeconfig

image-20230523205950588

将aliang全部替换为dev

执行脚本:

[root@k8s-master1 rbac2]#sh kubeconfig.sh 
Cluster "kubernetes" set.
User "dev" set.
Context "kubernetes" created.
Switched to context "kubernetes".
[root@k8s-master1 rbac2]#ls
ca-config.json cert.sh dev.csr dev-csr.json dev-key.pem dev.kubeconfig dev.pem kubeconfig.sh rbac.yaml

测试

[root@k8s-master1 rbac2]#kubectl get pod --kubeconfig=dev.kubeconfig
Error from server (Forbidden): pods is forbidden: User "xxx" cannot list resource "pods" in API group "" in the namespace "default"
  • 编辑rbac.yaml
[root@k8s-master1 rbac2]#vim rbac.yaml 
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default
name: dev-pod-reader
rules:
- apiGroups: ["", "apps"]
resources: ["pods", "deployments"]
verbs: ["get", "watch", "list"]

---

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: dev-read-pods
namespace: default
subjects:
- kind: Group
name: dev
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: dev-pod-reader
apiGroup: rbac.authorization.k8s.io

image-20230523210704926

部署:

[root@k8s-master1 rbac2]#kubectl apply -f rbac.yaml 
role.rbac.authorization.k8s.io/dev-pod-reader created
rolebinding.rbac.authorization.k8s.io/dev-read-pods created
  • 测试
[root@k8s-master1 rbac2]#kubectl get pod --kubeconfig=dev.kubeconfig
NAME READY STATUS RESTARTS AGE
busybox 1/1 Running 4 2d
busybox2 1/1 Running 4 2d
[root@k8s-master1 rbac2]#kubectl get deploy --kubeconfig=dev.kubeconfig
No resources found in default namespace.
[root@k8s-master1 rbac2]#kubectl get svc --kubeconfig=dev.kubeconfig
Error from server (Forbidden): services is forbidden: User "xxx" cannot list resource "services" in API group "" in the namespace "default"

注意:

用户、用户组都是提取证书中的一个字段,不是在集群中创建的 !

CN:用户名

0:用户组

用户组是一个逻辑的概念

K8s没有将用户加入到用户组这一个操作,只是他把用户组用类似于用户这种方式体现了,在这里只是称之为组而已,对组进行统一授权。

c

因此,这里用户组和用户所实现的效果是一样的。例如公司有10个新入职同事,我可以给分配10个可以访问k8s资源的用户,也可以分配一个用户组给这10个人(但是这里只能是查看权限的吧,如果是删除权限的话,就不能唯一确认是谁做的操作了……)。

符合预期,测试结束。😘

2.只能访问某个 namespace 的 ServiceAccount

(1)ServiceAccount

image-20230204175205074

ServiceAccount 为 Pod 中运行的进程提供了一个身份,Pod 内的进程可以使用其关联服务账号的身份,向集群的APIServer 进行身份认证。

  • sa是命名空间级别的,当创建naamespace时,会自动创建一个名为default的SA,这个SA没有绑定任何权限

  • 当default SA创建时,会自动创建一个default-token-xxx的secret,并自动关联到SA。

  • 当创建 Pod 的时候规范下面有一个 spec.serviceAccount 的属性用来指定该 Pod 使用哪个 ServiceAccount,如果没有指定的话则默认使用 default 这个 sa。然后通过投射卷,在 Pod 的目录/run/secrets/kubernetes.io/serviceaccount/ 下有一个 token 令牌文件。我们通过 RBAC 对该 sa 授予了什么权限,那么容器里的应用拿着这个 token 后,就具备了对应的权限。

验证默认SA权限:kubectl --as=system:serviceaccount:default:default get pods

ServiceAccount主要是用于解决 Pod 在集群中的身份认证问题的。认证使用的授权信息其实就是利用前面我们讲到的一个类型为kubernetes.io/service-account-token进行管理的。

⚠️ 注意:2种方式访问k8s集群,serviceaccount只能在pod里使用

1.集群外访问:kubectl

2.集群内访问:sa

image-20230206212823567

image-20230206212828165

但是需要注意的是不同的 K8s 版本对该 token 文件的使用是不一样的,所以我们可以分几个版本来分别讨论下。

1)<=1.20版本
实战1:kind集群测试sa

==💘 实战:<=1.20 k8s版本sa测试-2023.1.18(测试成功)==

image-20230204190627533

  • 实验环境
实验环境:
kind环境,k8s v1.20.15
  • 实验软件(无)

  • 使用 kind 快速创建一个小于等于 v1.20 版本的集群:

[root@docker ~]#kind create cluster --name kind120 --image kindest/node:v1.20.15
[root@docker ~]#kubectl get node
NAME STATUS ROLES AGE VERSION
kind120-control-plane Ready control-plane,master 8m2s v1.20.15
  • 我们先创建一个字为 sa-demo 的 ServiceAccount 对象:
[root@docker ~]#kubectl create sa sa-demo
serviceaccount/sa-demo created
[root@docker ~]#kubectl get sa
NAME SECRETS AGE
default 1 8m57s
sa-demo 1 9s
[root@docker ~]#kubectl get secret
NAME TYPE DATA AGE
default-token-wc7tx kubernetes.io/service-account-token 3 9m17s
sa-demo-token-4kwph kubernetes.io/service-account-token 3 29s

我们可以看到创建 sa 后自动生成了一个 secret,格式为 <saname>-token-xxxx ,比如我们创建了一个名字为 sa-demo 的 sa 之后,系统自动创建了一个名字为 sa-demo-token-4kwph 的 secret,这个 secret 里就包含了一个token。

[root@docker ~]#kubectl describe secret sa-demo-token-4kwph
Name: sa-demo-token-4kwph
Namespace: default
Labels: <none>
Annotations: kubernetes.io/service-account.name: sa-demo
kubernetes.io/service-account.uid: 0318b17e-6bf6-4831-ba34-792ad305018e

Type: kubernetes.io/service-account-token

Data
====
ca.crt: 1066 bytes
namespace: 7 bytes
token: eyJhbGciOiJSUzI1NiIsImtpZCI6InpxQ3dCV1JGREVtaVo2TlBqTmN4dVF3VVo4eVdXeXA5RGhSaWZKdHMzb0EifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6InNhLWRlbW8tdG9rZW4tNGt3cGgiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoic2EtZGVtbyIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjAzMThiMTdlLTZiZjYtNDgzMS1iYTM0LTc5MmFkMzA1MDE4ZSIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OnNhLWRlbW8ifQ.ThtpMWagNdebRt0GPPvOoJLpnwSpkZM_RtmcSyiexGbzXdp93JkFDBinAmHSVKno3id2qWwfEvkLCKDq4pTDlcG_GsM3vliTFwbWprtC_HdMbtWSm5bvA1TFapJ_JIKXeHD9ovkluuYquccQBvOxKmE-s4jkuldK4WAyJ1oAY9-Dcwb92E4rTCw7J2mWsGoCKXKhl55cbFovQD1Ug6f966MgAjci8EUw9qkOTYQEL7eK74GCQVfBUAqEcBFutotOi8BiZopPfqGy0nnYGw3KgrkFFCAaNQUJ_FXLeP2YH4zHqMLZ7ggLpS_bvx79gQ1Lf-JxQ2keBwosUbcH79ckqw

可以看到自动生成的这个 secret 对象里面包含一个 token,我们也可以通过下面的命令来获取:

[root@docker ~]#kubectl get  secret sa-demo-token-4kwph  -o jsonpath='{.data.token}' | base64 -d
eyJhbGciOiJSUzI1NiIsImtpZCI6InpxQ3dCV1JGREVtaVo2TlBqTmN4dVF3VVo4eVdXeXA5RGhSaWZKdHMzb0EifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6InNhLWRlbW8tdG9rZW4tNGt3cGgiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoic2EtZGVtbyIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjAzMThiMTdlLTZiZjYtNDgzMS1iYTM0LTc5MmFkMzA1MDE4ZSIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OnNhLWRlbW8ifQ.ThtpMWagNdebRt0GPPvOoJLpnwSpkZM_RtmcSyiexGbzXdp93JkFDBinAmHSVKno3id2qWwfEvkLCKDq4pTDlcG_GsM3vliTFwbWprtC_HdMbtWSm5bvA1TFapJ_JIKXeHD9ovkluuYquccQBvOxKmE-s4jkuldK4WAyJ1oAY9-Dcwb92E4rTCw7J2mWsGoCKXKhl55cbFovQD1Ug6f966MgAjci8EUw9qkOTYQEL7eK74GCQVfBUAqEcBFutotOi8BiZopPfqGy0nnYGw3KgrkFFCAaNQUJ_FXLeP2YH4zHqMLZ7ggLpS_bvx79gQ1Lf-JxQ2keBwosUbcH79ckqw[root@docker ~]#

这个 token 是 JWT 结构的,我们可以把这个 token 复制到 jwt.io 网站进行解码。

https://jwt.io/

image-20230204185401990

右侧部分显示了 token 被解码之后的内容,其中 PAYLOAD 部分是 token 里包含的 sa-demo 的信息,可以看到里面没有过期时间,也说明了该 token 是永不过期的

  • 现在我们使用上面我们创建的 sa 来运行一个 Pod:
# demo-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: demo
spec:
serviceAccount: sa-demo
containers:
- name: demo
image: nginx:1.7.9
ports:
- containerPort: 80

直接创建该 Pod 即可:

[root@docker ~]#kubectl apply -f demo-pod.yaml
pod/demo created
[root@docker ~]#kubectl get po
NAME READY STATUS RESTARTS AGE
demo 1/1 Running 0 75s

[root@docker ~]#kubectl get po demo -oyaml
apiVersion: v1
kind: Pod
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"name":"demo","namespace":"default"},"spec":{"containers":[{"image":"nginx:1.7.9","name":"demo","ports":[{"containerPort":80}]}],"serviceAccount":"sa-demo"}}
creationTimestamp: "2023-02-04T10:56:35Z"
name: demo
namespace: default
resourceVersion: "1859"
uid: 994996db-d7cd-4a63-8558-b0697ac33585
spec:
containers:
- image: nginx:1.7.9
imagePullPolicy: IfNotPresent
name: demo
ports:
- containerPort: 80
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: sa-demo-token-4kwph
readOnly: true
dnsPolicy: ClusterFirst
enableServiceLinks: true
nodeName: kind120-control-plane
preemptionPolicy: PreemptLowerPriority
priority: 0
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccount: sa-demo
serviceAccountName: sa-demo
terminationGracePeriodSeconds: 30
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 300
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 300
volumes:
- name: sa-demo-token-4kwph
secret:
defaultMode: 420
secretName: sa-demo-token-4kwph
status:
conditions:
- lastProbeTime: null
lastTransitionTime: "2023-02-04T10:56:35Z"
status: "True"
type: Initialized
- lastProbeTime: null
lastTransitionTime: "2023-02-04T10:57:10Z"
status: "True"
type: Ready
- lastProbeTime: null
lastTransitionTime: "2023-02-04T10:57:10Z"
status: "True"
type: ContainersReady
- lastProbeTime: null
lastTransitionTime: "2023-02-04T10:56:35Z"
status: "True"
type: PodScheduled
containerStatuses:
- containerID: containerd://d05bdfc60b7b0edd569a7a3aa495e2b1c2510e9238a3bdec22730384f41df9e6
image: docker.io/library/nginx:1.7.9
imageID: sha256:35d28df486f6150fa3174367499d1eb01f22f5a410afe4b9581ac0e0e58b3eaf
lastState: {}
name: demo
ready: true
restartCount: 0
started: true
state:
running:
startedAt: "2023-02-04T10:57:09Z"
hostIP: 172.18.0.3
phase: Running
podIP: 10.244.0.5
podIPs:
- ip: 10.244.0.5
qosClass: BestEffort
startTime: "2023-02-04T10:56:35Z"
[root@docker ~]#
  • Pod 创建后我们可以看到会自动将指定 sa 对应的 secret 挂载到容器的/var/run/secrets/kubernetes.io/serviceaccount 目录下去,所以现在该目录下面肯定包含对应的 token 文件,我们可以查看该值来验证下:
[root@docker ~]#kubectl exec -it demo -- cat /run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCI6InpxQ3dCV1JGREVtaVo2TlBqTmN4dVF3VVo4eVdXeXA5RGhSaWZKdHMzb0EifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6InNhLWRlbW8tdG9rZW4tNGt3cGgiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoic2EtZGVtbyIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjAzMThiMTdlLTZiZjYtNDgzMS1iYTM0LTc5MmFkMzA1MDE4ZSIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OnNhLWRlbW8ifQ.ThtpMWagNdebRt0GPPvOoJLpnwSpkZM_RtmcSyiexGbzXdp93JkFDBinAmHSVKno3id2qWwfEvkLCKDq4pTDlcG_GsM3vliTFwbWprtC_HdMbtWSm5bvA1TFapJ_JIKXeHD9ovkluuYquccQBvOxKmE-s4jkuldK4WAyJ1oAY9-Dcwb92E4rTCw7J2mWsGoCKXKhl55cbFovQD1Ug6f966MgAjci8EUw9qkOTYQEL7eK74GCQVfBUAqEcBFutotOi8BiZopPfqGy0nnYGw3KgrkFFCAaNQUJ_FXLeP2YH4zHqMLZ7ggLpS_bvx79gQ1Lf-JxQ2keBwosUbcH79ckqw[root@docker ~]#

可以看到 Pod 里通过投射卷所挂载的 token 跟 sa-demo 对应的 secret 包含的 token 是一模一样的,这个 token是永不过期的,所以即使删除了 Pod 之后重新创建,Pod 里的 token 仍然是不变的,因为 secret 对象里面的 token数据并没有变化。

如果需要在 Pod 中去访问 K8s 集群的资源对象,现在就可以为使用的 sa 绑定上相应的权限,然后在 Pod 应用中使用该对应的 token 去和 APIServer 进行通信就可以了,这个时候的 token 就能识别到对应的权限了。

⚠️ 注意

  • 这些系统pod里的镜像是没有的,我们可以创建一个nginx pod来测试
[root@master1 ~]#kubectl get po -A
NAMESPACE NAME READY STATUS RESTARTS AGE
default nginx-7498977d8c-r6sqx 1/1 Running 0 19d
default pod1 1/1 Running 0 19d
default secret1-pod 0/1 CrashLoopBackOff 51 (4m7s ago) 18d
default secret2-pod 1/1 Running 40 (4m7s ago) 19d
default secret3-pod 1/1 Running 39 (3m36s ago) 19d
default testcm1-pod 0/1 CrashLoopBackOff 263 (3m36s ago) 20d
default testcm2-pod 0/1 CrashLoopBackOff 210 (4m6s ago) 20d
default testcm3-pod 0/1 CrashLoopBackOff 207 (3m5s ago) 20d
default testcm4-pod 0/1 CrashLoopBackOff 207 (5m23s ago) 20d
default testcm5-pod 1/1 Running 0 20d
kube-flannel kube-flannel-ds-955fg 1/1 Running 1 (59d ago) 61d
kube-flannel kube-flannel-ds-ddfqs 1/1 Running 0 61d
kube-flannel kube-flannel-ds-x2gd6 1/1 Running 1 (51d ago) 61d
kube-system coredns-7b884d5cb7-6jzn6 1/1 Running 1 (51d ago) 61d
kube-system coredns-7b884d5cb7-gcszd 1/1 Running 1 (51d ago) 61d
kube-system etcd-master1 1/1 Running 1 (51d ago) 61d
kube-system kube-apiserver-master1 1/1 Running 1 (51d ago) 61d
kube-system kube-controller-manager-master1 1/1 Running 2 (47d ago) 61d
kube-system kube-proxy-l6np4 1/1 Running 0 61d
kube-system kube-proxy-vb49s 1/1 Running 1 (51d ago) 61d
kube-system kube-proxy-xmg7m 1/1 Running 1 (59d ago) 61d
kube-system kube-scheduler-master1 1/1 Running 2 (47d ago) 61d
kube-system metrics-server-7c5487d558-nwj59 1/1 Running 0 41d
kube-system vpa-admission-controller-8599bb878d-q25wx 1/1 Running 0 40d
kube-system vpa-recommender-6bf6dd6795-wchjc 1/1 Running 0 40d
kube-system vpa-updater-697666f758-6vl75 1/1 Running 0 40d
kubernetes-dashboard dashboard-metrics-scraper-64bcc67c9c-27pqg 1/1 Running 0 61d
kubernetes-dashboard kubernetes-dashboard-5c8bd6b59-qlgjk 1/1 Running 9 (60d ago) 61d

#coredns等的镜像很精简,就是一个二进制文件,这里我们看下自己创建的pod就好。
[root@master1 ~]#kubectl exec -it coredns-7b884d5cb7-6jzn6 -nkube-system -- ls /run/secrets/kubernetes.io/serviceaccount/
error: Internal error occurred: error executing command in container: failed to exec in container: failed to start exec "0aa9b2c89f8e5ac588074955ad37e3d54d64116a9edee4ee977fe77f00a4b512": OCI runtime exec failed: exec failed: unable to start container process: exec: "ls": executable file not found in $PATH: unknown
[root@master1 ~]#kubectl exec -it kubernetes-dashboard-5c8bd6b59-qlgjk -nkubernetes-dashboard -- ls /run/secrets/kubernetes.io/serviceaccount/
error: Internal error occurred: error executing command in container: failed to exec in container: failed to start exec "2883ecb081fd96339dc1123b05331e044b4dc7ed762685b670f8ea37d7e87272": OCI runtime exec failed: exec failed: unable to start container process: exec: "ls": executable file not found in $PATH: unknown

[root@master1 ~]#kubectl exec -it nginx-7498977d8c-r6sqx -- ls /run/secrets/kubernetes.io/serviceaccount/
ca.crt namespace token

测试结束。😘

实战2:py程序访问k8s api

==💘 实战:对应用程序授权访问K8s(ServiceAccount)-2023.5.24(测试成功)==

image-20230524064413411

image-20230524064458418

  • 实验环境
实验环境:
1、win10,vmwrokstation虚机;
2、k8s集群:3台centos7.6 1810虚机,1个master节点,2个node节点
k8s version:v1.20.0
docker://20.10.7
  • 实验软件
链接:https://pan.baidu.com/s/1wTQY4zkQDj_2-7CGv4oGmg?pwd=0820 
提取码:0820

2023.5.24-py-k8s-serviceaccount-code

image-20230524071840957

  • 实验步骤
graph LR
A[实战步骤] -->B(1.创建Role ServiceAccount RoleBinding)
A[实战步骤] -->C(2.为Pod指定自定义的SA)
A[实战步骤] -->D(3.进入容器里执行Python程序测试操作K8s API权限)
  • 查看默认命名空间下sa情况

calico/coredns/dashboard(serviceaccount)->apiserver

#sa是命名空间级别的,当创建naamespace时,会自动创建一个名为default的SA,这个SA没有绑定任何权限。 
[root@k8s-master1 ~]#kubectl get sa
NAME SECRETS AGE
default 1 213d
[root@k8s-master1 ~]#kubectl get secret
NAME TYPE DATA AGE
default-token-xkms9 kubernetes.io/service-account-token 3 213d
foo-tls kubernetes.io/tls 2 175d
web-aliangedu-cn kubernetes.io/tls 2 141d
[root@k8s-master1 tmp]#kubectl --as=system:serviceaccount:default:default get pods
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:default:default" cannot list resource "pods" in API group "" in the namespace "default"

#当创建 Pod 的时候规范下面有一个 spec.serviceAccount 的属性用来指定该 Pod 使用哪个 ServiceAccount,如果没有指定的话则默认使用 **default** 这个 sa。然后通过投射卷,在 Pod 的目录`/run/secrets/kubernetes.io/serviceaccount/` 下有一个 token 令牌文件。我们通过 RBAC 对该 sa 授予了什么权限,那么容器里的应用拿着这个 token 后,就具备了对应的权限。
[root@k8s-master1 ~]#kubectl get po
NAME READY STATUS RESTARTS AGE
busybox 1/1 Running 4 2d10h
busybox2 1/1 Running 4 2d10h
[root@k8s-master1 ~]#kubectl describe po busybox
Name: busybox
Namespace: default
Priority: 0
……
Ready: True
Restart Count: 4
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-xkms9 (ro)

[root@k8s-master1 ~]#kubectl exec -it busybox -- sh
/ #
/ # cd /var/run/secrets/kubernetes.io/serviceaccount/
/var/run/secrets/kubernetes.io/serviceaccount # ls
ca.crt namespace token
/var/run/secrets/kubernetes.io/serviceaccount # cat token
eyJhbGciOiJSUzI1NiIsImtpZCI6IlpvT3NGQl8yam1FTXI1eDcyS0oteFR4OU0xTTUzaV9rdFFabHpvX2FxaGsifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlZmF1bHQtdG9rZW4teGttczkiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVmYXVsdCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjczNjI3OGFiLTMzN2YtNDZkYS04NmMzLTFlMzgxMmUxMzAxYSIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlZmF1bHQifQ.mny-q5xmajpGnrodiy-UPzyrToJeRdj3Fcd9fo5wgLcFxXIJRq4f4nxVdFR1rZhm7bLEgxVzIloj37wtC90U-eRAZ7NRcr2Wetf9uDabA0i3tlbVKTMbPnuaGG5XsbEjoJQ9M0xZAKKp6M_pJn-QktbR8Ts7FJEn8UWdyyRsLemiKwWTumPfL032GPflizSAxLsa22lv_-A-dsolI5byh0PHpy7VNFzPLplUoY5QCh9vOD-ym1DtqwjWQgxs-NM8BNWtsRYkzFWvrZfNGhBFTj4IG4ofAHpktLWGSlCD7NC-ZP0E-WZ4CPpdeOpjJB-ndRNv6jpnTRwxMnUMFkFD1g/var/run/secrets/kubernetes.io/serviceaccount #

#serviceaccount只能在pod里使用
[root@k8s-master1 ~]#kubectl get sa -nkube-system|grep calico
calico-kube-controllers 1 213d
calico-node 1 213d
[root@k8s-master1 ~]#kubectl get sa -nkube-system|grep coredns
coredns 1 213d
[root@k8s-master1 ~]#kubectl get sa -nkube-system|grep dashboard
dashboard-admin 1 213d

1、创建Role、ServiceAccount、RoleBinding

[root@k8s-master1 ~]#mkdir sa
[root@k8s-master1 ~]#cd sa/
[root@k8s-master1 sa]#vim sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: py-k8s
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: py-role
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: py-role
namespace: default
subjects:
- kind: ServiceAccount
name: py-k8s
roleRef:
kind: Role
name: py-role
apiGroup: rbac.authorization.k8s.io

部署:

[root@k8s-master1 sa]#kubectl apply -f sa.yaml 
serviceaccount/py-k8s created
role.rbac.authorization.k8s.io/py-role created
rolebinding.rbac.authorization.k8s.io/py-role created

测试:

[root@k8s-master1 sa]#kubectl get sa
NAME SECRETS AGE
default 1 213d
py-k8s 1 45s
[root@k8s-master1 sa]#kubectl get role
NAME CREATED AT
dev-pod-reader 2023-05-23T13:07:33Z
pod-reader 2023-05-22T23:20:21Z
py-role 2023-05-23T22:58:25Z

#这里使用刚才创建的py-k8s sa就可以正常访问k8s pod资源了
[root@k8s-master1 sa]#kubectl --as=system:serviceaccount:default:py-k8s get pods
NAME READY STATUS RESTARTS AGE
busybox 1/1 Running 4 2d10h
busybox2 1/1 Running 4 2d10h

2、为Pod指定自定义的SA

  • 看下k8s-api-test.py文件内容
from kubernetes import client, config

with open('/var/run/secrets/kubernetes.io/serviceaccount/token') as f:
token = f.read()

configuration = client.Configuration()
configuration.host = "https://kubernetes" # APISERVER地址
configuration.ssl_ca_cert="/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" # CA证书
configuration.verify_ssl = True # 启用证书验证
configuration.api_key = {"authorization": "Bearer " + token} # 指定Token字符串
client.Configuration.set_default(configuration)
apps_api = client.AppsV1Api()
core_api = client.CoreV1Api()
try:
print("###### Deployment列表 ######")
#列出default命名空间所有deployment名称
for dp in apps_api.list_namespaced_deployment("default").items:
print(dp.metadata.name)
except:
print("没有权限访问Deployment资源!")

try:
#列出default命名空间所有pod名称
print("###### Pod列表 ######")
for po in core_api.list_namespaced_pod("default").items:
print(po.metadata.name)
except:
print("没有权限访问Pod资源!")
  • 创建pod
[root@k8s-master1 sa]#vim pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: py-k8s
spec:
serviceAccountName: py-k8s
containers:
- image: python:3
name: python
command:
- sleep
- 24h

部署:

[root@k8s-master1 sa]#kubectl apply -f pod.yaml 
pod/py-k8s created

3、进入容器里执行Python程序测试操作K8s API权限

[root@k8s-master1 sa]#kubectl get po
NAME READY STATUS RESTARTS AGE
busybox 1/1 Running 4 2d10h
busybox2 1/1 Running 4 2d10h
py-k8s 1/1 Running 0 59s

#将k8s-api-test.py测试文件拷贝到pod里
[root@k8s-master1 sa]#kubectl cp k8s-api-test.py py-k8s:/root

#进入py-k8s pod里
[root@k8s-master1 sa]#kubectl exec -it py-k8s -- bash
root@py-k8s:/# cd /root/
root@py-k8s:~# ls
k8s-api-test.py
root@py-k8s:~# python
python python-config python3 python3-config python3.11 python3.11-config python3.9
root@py-k8s:~# python3 k8s-api-test.py
Traceback (most recent call last):
File "/root/k8s-api-test.py", line 1, in <module>
from kubernetes import client, config
ModuleNotFoundError: No module named 'kubernetes'
#报错了,提示没有kubermetes模块,我们这里手动安装下
root@py-k8s:~# pip install kubernetes -i https://pypi.tuna.tsinghua.edu.cn/simple

测试:

root@py-k8s:~# python3 k8s-api-test.py
###### Deployment列表 ######
没有权限访问Deployment资源!
###### Pod列表 ######
busybox
busybox2
py-k8s
root@py-k8s:~#

可以看到提示没有权限访问Deployment资源!,我们接着给py-k8s sa配置下deployments的权限,并在此验证:

[root@k8s-master1 sa]#vim sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: py-k8s
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: py-role
rules:
- apiGroups: ["", "apps"]
resources: ["pods", "deployments"]
verbs: ["get", "watch", "list"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: py-role
namespace: default
subjects:
- kind: ServiceAccount
name: py-k8s
roleRef:
kind: Role
name: py-role
apiGroup: rbac.authorization.k8s.io

image-20230524071253803

更新:

[root@k8s-master1 sa]#kubectl apply -f sa.yaml 
serviceaccount/py-k8s unchanged
role.rbac.authorization.k8s.io/py-role configured
rolebinding.rbac.authorization.k8s.io/py-role unchanged
[root@k8s-master1 sa]#kubectl --as=system:serviceaccount:default:py-k8s get deployment
No resources found in default namespace.

root@py-k8s:~# python3 k8s-api-test.py
###### Deployment列表 ###### #可以看到,给予py-k8s sa权限后,这里就可以正常访问deployment资源了。
###### Pod列表 ######
busybox
busybox2
py-k8s
root@py-k8s:~#

测试结束。😘

2)>=1.21版本 && <=1.23版本

==💘 实战:>=1.21版本 && <=1.23版本sa测试-2023.1.18(测试成功)==

image-20230205141912034

  • 实验环境
实验环境:
kind环境,k8s v1.22.15
  • 实验软件(无)

接下来我们基于>=1.21版本 && <=1.23版本的 K8s 集群进行测试。

  • 这里我们使用 kind 快速创建一个 v1.22.15 版本的集群:
[root@docker ~]#kind create cluster --name kind122 --image kindest/node:v1.22.15
[root@docker ~]#kubectl get node
NAME STATUS ROLES AGE VERSION
kind122-control-plane Ready control-plane,master 11m v1.22.15
  • 同样首先创建一个名为 sa-demo 的 ServiceAccount 对象:
[root@docker ~]#kubectl create sa sa-demo
serviceaccount/sa-demo created
[root@docker ~]#kubectl get sa
NAME SECRETS AGE
default 1 9m17s
sa-demo 0 10s
[root@docker ~]#kubectl get secret
NAME TYPE DATA AGE
default-token-bzm5c kubernetes.io/service-account-token 3 13m
sa-demo-token-5tc2g kubernetes.io/service-account-token 3 12s

同样可以看到创建 sa 后系统也自动创建了一个对应的 secret 对象,和以前版本没什么区别,我们也可以通过下面的命令来获得该 secret 对象里面包含的 token 值:

[root@docker ~]#kubectl get  secret sa-demo-token-n9z89  -o jsonpath='{.data.token}' | base64 -d
eyJhbGciOiJSUzI1NiIsImtpZCI6IlBwOGtYcVlpRVdOLUc0NHByNE5obTlyc0tQTVlLa1R5a3F2TEQ2WnhkSWsifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6InNhLWRlbW8tdG9rZW4tbjl6ODkiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoic2EtZGVtbyIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6ImY5ZTYyNzE1LTMwOWItNDE0YS04YTkzLTc5MTJhNzY1Nzk1MyIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OnNhLWRlbW8ifQ.WSpB6KkX55sC_K8MdCTH0uV71O5Cry7MYKE3dfvCMzZYseAgLpk_FXFr-IdlMzfN23Saur4Y-8v1jlyJ_h8ipbxXb2mgVCg46ORb65u8FZxWky6c7bDbARMcdiNxvtQystWos5vOSSYu9Gd61RU15zXE7SJByPAMeGDJIn94PXszmnKa037cKhKVIhfgXTRh4Rw_Xu66a9ALYU0b8dTAGfM8sBLwI-wEUolosTRNkC5M87i3cSHRsdv3WqsqW-2QytVW3COWL7erirb5XR5yTtLx0BWSXPFswR2UVpWpoW5AnQYh9M2xAd0O_FF6f_9r7I8RP0fUO6_kzxp2kCvJLw[root@docker ~]#

同样将该 token 值拷贝到 jwt.io 网站进行解码。

image-20230204195305391

从解码后的值可以看到该 token 值里面同样不包含任何过期时间,也说明了我们创建 sa 之后,所对应的 token 是永不过期的。

  • 同样我们再次使用上面的 sa 来创建一个 Pod,如下所示:
# demo-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: demo
spec:
serviceAccount: sa-demo
containers:
- name: demo
image: nginx:1.7.9
ports:
- containerPort: 80

直接创建该 Pod:

[root@docker ~]#kubectl apply -f demo-pod.yaml
pod/demo created
[root@docker ~]#kubectl get po
NAME READY STATUS RESTARTS AGE
demo 1/1 Running 0 50s

[root@docker ~]#kubectl get po demo -oyaml
apiVersion: v1
kind: Pod
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"name":"demo","namespace":"default"},"spec":{"containers":[{"image":"nginx:1.7.9","name":"demo","ports":[{"containerPort":80}]}],"serviceAccount":"sa-demo"}}
creationTimestamp: "2023-02-05T06:05:33Z"
name: demo
namespace: default
resourceVersion: "811"
uid: ecb8a127-1e56-4b0a-8760-ad952b83800f
spec:
containers:
- image: nginx:1.7.9
imagePullPolicy: IfNotPresent
name: demo
ports:
- containerPort: 80
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:#这里
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: kube-api-access-cds9k
readOnly: true
dnsPolicy: ClusterFirst
enableServiceLinks: true
nodeName: kind122-control-plane
preemptionPolicy: PreemptLowerPriority
priority: 0
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccount: sa-demo
serviceAccountName: sa-demo
terminationGracePeriodSeconds: 30
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 300
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 300
volumes:#这里
- name: kube-api-access-cds9k
projected:
defaultMode: 420
sources:
- serviceAccountToken:
expirationSeconds: 3607
path: token
- configMap:
items:
- key: ca.crt
path: ca.crt
name: kube-root-ca.crt
- downwardAPI:
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
path: namespace
status:
conditions:
- lastProbeTime: null
lastTransitionTime: "2023-02-05T06:05:33Z"
status: "True"
type: Initialized
- lastProbeTime: null
lastTransitionTime: "2023-02-05T06:06:13Z"
status: "True"
type: Ready
- lastProbeTime: null
lastTransitionTime: "2023-02-05T06:06:13Z"
status: "True"
type: ContainersReady
- lastProbeTime: null
lastTransitionTime: "2023-02-05T06:05:33Z"
status: "True"
type: PodScheduled
containerStatuses:
- containerID: containerd://b684a2c6ea8a02c86659045064cfead08019bfc30e7e27d1d998df6fa7481c5a
image: docker.io/library/nginx:1.7.9
imageID: sha256:35d28df486f6150fa3174367499d1eb01f22f5a410afe4b9581ac0e0e58b3eaf
lastState: {}
name: demo
ready: true
restartCount: 0
started: true
state:
running:
startedAt: "2023-02-05T06:06:12Z"
hostIP: 172.18.0.3
phase: Running
podIP: 10.244.0.5
podIPs:
- ip: 10.244.0.5
qosClass: BestEffort
startTime: "2023-02-05T06:05:33Z"
[root@docker ~]#

当 Pod 创建后查看对应的资源对象,可以看到和之前的版本已经有一个很大的区别了,并不是将上面自动创建的 secret挂载到容器的 /var/run/secrets/kubernetes.io/serviceaccount 目录。我们可以查看下 Pod 中的 token 值来和 secret 包含的 token 值进行对比:

[root@docker ~]# kubectl exec -it demo -- cat /run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCI6ImdsZHBQY2ZjRkZQUFNtbTkzLXhQSjFnN19JdUZBYWRrV2NLYUowanFMamcifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNzA3MTEzMTMzLCJpYXQiOjE2NzU1NzcxMzMsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0IiwicG9kIjp7Im5hbWUiOiJkZW1vIiwidWlkIjoiZWNiOGExMjctMWU1Ni00YjBhLTg3NjAtYWQ5NTJiODM4MDBmIn0sInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJzYS1kZW1vIiwidWlkIjoiZDViNWE2MTktMjkyMC00Y2NiLWFlMTItZTM5YTY1YTYyNmI3In0sIndhcm5hZnRlciI6MTY3NTU4MDc0MH0sIm5iZiI6MTY3NTU3NzEzMywic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlZmF1bHQ6c2EtZGVtbyJ9.WZQ6NLMmmdAx-VdTabT2ENno-C-FfQzzrOLsLEmCL3fL2wc-OrOvgoucgpRUF1gJD1ju95pt6wlpvJzD0I_6b0e3b8__3lbTSaXJigMXkvR_opuaw7EkE-vjtnBZMMn8RHTSfqDsRDfeth3BCBZq5873cTjl5X-s4fsYbBaWQaVfkzDxlt3dF_kdmIChHGRID3PrunwgtfgsrB2bg3UB16RiUXF4HSROugBxpoFymfvIPEuuNOaCv2IdRbWFr9qp0iMQxWvnOemlbFzoQM6NWfqZ6hP7gX_sADHPtOa73PyoPtnFZihhC3bPgS54TIeVOvtGA0_FI2cTfJAvDOPQ5w[root@docker ~]#

可以很明显看到现在 Pod 中的 token 值和自动创建 secret 的 token 值不一样了,同样在 jwt.io 解码该 token值。

image-20230205141202648

可以看到该 token 值解码后的 PAYLOAD 数据中包含了很多不同的数据,其中的 exp 字段表示该 token 的过期时间,可以看到过期时间是 1 年

这里我们可以总结下在 v1.21 到 v1.23 版本的 K8s 集群,当创建 ServiceAccount 对象后,系统仍然会自动创建一个 secret 对象,该 secret 对象里面包含的 token 仍然是永不过期的,但是 Pod 里面并不会使用该 secret 的token 值了。

从上面查看创建后的 Pod 资源清单可以看出,现在创建 Pod 后,Kubernetes 控制平面会自动添加一个投射卷到 Pod,此卷包括了访问 Kubernetes API 的 token,该清单片段定义了由三个数据源组成的投射卷,这三个数据源是:

  • serviceAccountToken 数据源:包含 kubelet 从 kube-apiserver 获取的令牌,kubelet 使用TokenRequest API 获取有时间限制的令牌。为 TokenRequest 服务的这个令牌会在 Pod 被删除或定义的生命周期(默认为 1 小时)结束之后过期。该令牌绑定到特定的 Pod, 并将其 **audience(受众)**设置为与 kubeapiserver 的 audience 相匹配。 这种机制取代了之前基于 Secret 添加卷的机制,之前 Secret 代表了针对Pod 的 ServiceAccount 但不会过期。

  • configMap 数据源 :ConfigMap 包含一组证书颁发机构数据,Pod 可以使用这些证书来确保自己连接到集群的kube-apiserver(而不是连接到中间件或意外配置错误的对等点上)。

  • downwardAPI 数据源:用于查找包含 Pod 的名字空间的名称,并使该名称信息可用于在 Pod 内运行的应用程序代码。

所以我们应该要指定现在版本的 K8s 集群创建的 Pod 里面包含的 token 不是使用 ServiceAccount 自动关联的secret 对象里面的 token 了,而是 kubelet 会向 TokenRequest API 发送一个请求,申请一个新的 token 放在 Pod 的 /run/secrets/kubernetes.io/serviceaccount/token 里。这个 token 会在 1 个小时后由kubelet 重新去申领一个新的 token,所以 1 小时之后再次查看这个 token 的话会发现 token 的内容是变化的,如果删除此 Pod 重新创建的话,则会重新申领 token,被删除 Pod 里的 token 会立即过期。

而且我们还可以手动使用 kubectl create token <sa> 命令来请求 ServiceAccount 的 token,可以指定有效期等:

[root@docker ~]#kubectl create token -h
Request a service account token.

Examples:
# Request a token to authenticate to the kube-apiserver as the service account "myapp" in the current namespace
kubectl create token myapp

# Request a token for a service account in a custom namespace
kubectl create token myapp --namespace myns

# Request a token with a custom expiration
kubectl create token myapp --duration 10m

# Request a token with a custom audience
kubectl create token myapp --audience https://example.com

# Request a token bound to an instance of a Secret object
kubectl create token myapp --bound-object-kind Secret --bound-object-name mysecret

# Request a token bound to an instance of a Secret object with a specific uid
kubectl create token myapp --bound-object-kind Secret --bound-object-name mysecret --bound-object-uid
0d4691ed-659b-4935-a832-355f77ee47cc

Options:
--allow-missing-template-keys=true:
If true, ignore any errors in templates when a field or map key is missing in the template. Only applies to
golang and jsonpath output formats.

--audience=[]:
Audience of the requested token. If unset, defaults to requesting a token for use with the Kubernetes API
server. May be repeated to request a token valid for multiple audiences.

--bound-object-kind='':
Kind of an object to bind the token to. Supported kinds are Pod, Secret. If set, --bound-object-name must be
provided.

--bound-object-name='':
Name of an object to bind the token to. The token will expire when the object is deleted. Requires
--bound-object-kind.

--bound-object-uid='':
UID of an object to bind the token to. Requires --bound-object-kind and --bound-object-name. If unset, the UID
of the existing object is used.

--duration=0s:
Requested lifetime of the issued token. The server may return a token with a longer or shorter lifetime.

-o, --output='':
Output format. One of: (json, yaml, name, go-template, go-template-file, template, templatefile, jsonpath,
jsonpath-as-json, jsonpath-file).

--show-managed-fields=false:
If true, keep the managedFields when printing objects in JSON or YAML format.

--template='':
Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format
is golang templates [http://golang.org/pkg/text/template/#pkg-overview].

Usage:
kubectl create token SERVICE_ACCOUNT_NAME [options]

Use "kubectl options" for a list of global command-line options (applies to all commands).
[root@docker ~]#

测试结束。😘

3)>=1.24版本

==💘 实战:>=1.24版本sa测试-2023.2.5(测试成功)==

image-20230205144327903

  • 实验环境
实验环境:
kind环境,k8s v1.25.3
  • 实验软件(无)

  • 现在我们再来看下 v1.24 版本以上的 K8s 集群中的 ServiceAccount token 是如何工作的。这里我们使用 kind 快速创建一个 v1.25.3 版本的集群:

[root@docker ~]#kind create cluster --name kind125 --image kindest/node:v1.25.3
[root@docker ~]#kubectl get node
NAME STATUS ROLES AGE VERSION
kind122-control-plane Ready control-plane,master 11m v1.22.15
  • 同样首先创建一个名为 sa-demo 的 ServiceAccount 对象:
[root@docker ~]#kubectl create sa sa-demo
serviceaccount/sa-demo created
[root@docker ~]#kubectl get sa
NAME SECRETS AGE
default 0 68s
sa-demo 0 5s
[root@docker ~]#kubectl get secret
No resources found in default namespace.

我们可以看到该 ServiceAccount 创建后并没有创建对应的 Secret 对象。

  • 同样接下来创建一个如下所示的 Pod:
# demo-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: demo
spec:
serviceAccount: sa-demo
containers:
- name: demo
image: nginx:1.7.9
ports:
- containerPort: 80

创建上面的 Pod 后查看详情:

[root@docker ~]#kubectl apply -f demo-pod.yaml
pod/demo created
[root@docker ~]#kubectl get po
NAME READY STATUS RESTARTS AGE
demo 1/1 Running 0 43s

[root@docker ~]#kubectl get po demo -oyaml
apiVersion: v1
kind: Pod
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"name":"demo","namespace":"default"},"spec":{"containers":[{"image":"nginx:1.7.9","name":"demo","ports":[{"containerPort":80}]}],"serviceAccount":"sa-demo"}}
creationTimestamp: "2023-02-05T06:40:36Z"
name: demo
namespace: default
resourceVersion: "665"
uid: d2db49c4-52bd-426b-9571-c40df6a39d3e
spec:
containers:
- image: nginx:1.7.9
imagePullPolicy: IfNotPresent
name: demo
ports:
- containerPort: 80
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:#这里
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: kube-api-access-5mklv
readOnly: true
dnsPolicy: ClusterFirst
enableServiceLinks: true
nodeName: kind125-control-plane
preemptionPolicy: PreemptLowerPriority
priority: 0
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccount: sa-demo
serviceAccountName: sa-demo
terminationGracePeriodSeconds: 30
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 300
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 300
volumes:#这里
- name: kube-api-access-5mklv
projected:
defaultMode: 420
sources:
- serviceAccountToken:
expirationSeconds: 3607
path: token
- configMap:
items:
- key: ca.crt
path: ca.crt
name: kube-root-ca.crt
- downwardAPI:
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
path: namespace
status:
conditions:
- lastProbeTime: null
lastTransitionTime: "2023-02-05T06:40:36Z"
status: "True"
type: Initialized
- lastProbeTime: null
lastTransitionTime: "2023-02-05T06:41:10Z"
status: "True"
type: Ready
- lastProbeTime: null
lastTransitionTime: "2023-02-05T06:41:10Z"
status: "True"
type: ContainersReady
- lastProbeTime: null
lastTransitionTime: "2023-02-05T06:40:36Z"
status: "True"
type: PodScheduled
containerStatuses:
- containerID: containerd://4156c2683a99e178d6ed749014afbb7036c9f47c9ada5982076795807958b10c
image: docker.io/library/nginx:1.7.9
imageID: sha256:35d28df486f6150fa3174367499d1eb01f22f5a410afe4b9581ac0e0e58b3eaf
lastState: {}
name: demo
ready: true
restartCount: 0
started: true
state:
running:
startedAt: "2023-02-05T06:41:10Z"
hostIP: 172.18.0.2
phase: Running
podIP: 10.244.0.5
podIPs:
- ip: 10.244.0.5
qosClass: BestEffort
startTime: "2023-02-05T06:40:36Z"
[root@docker ~]#

可以看到创建 Pod 后同样会自动添加一个投射卷到 Pod,此卷包括了访问 Kubernetes API 的令牌,和>=1.21版本 && <=1.23版本 表现是一致的。

  • 同样我们可以下查看 Pod 中的 token 值来进行验证:
[root@docker ~]#kubectl exec -it demo -- cat /run/secrets/kubernetes.io/serviceaccount/token
eyJhbGciOiJSUzI1NiIsImtpZCI6Im9makVWMHN5dUZnYVFCckJZM1RnUmJJaTFZc1htOG5LcnR5NWNMYVgxd0EifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNzA3MTE1MjM2LCJpYXQiOjE2NzU1NzkyMzYsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0IiwicG9kIjp7Im5hbWUiOiJkZW1vIiwidWlkIjoiZDJkYjQ5YzQtNTJiZC00MjZiLTk1NzEtYzQwZGY2YTM5ZDNlIn0sInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJzYS1kZW1vIiwidWlkIjoiM2EyOWI2ZTYtZTFmZS00NmJiLWExNmYtM2EzMjg4M2FkODVhIn0sIndhcm5hZnRlciI6MTY3NTU4Mjg0M30sIm5iZiI6MTY3NTU3OTIzNiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlZmF1bHQ6c2EtZGVtbyJ9.9k5IEccHUiewSRU_6uEdBY1cykk-frXm_A1X_R1TDkOIShmttc5-XmrST0a45-HX2I2eFkVyvXnuyRCJJh8IT_30kcsuZF3rMZiYBPsi9ybnDtwWK_yZFXiHO9p-3AJ4kxFURmnzcTCbpI6yng58qRJCL1VdVSQ2a6nNrEAa6Mqr0N58o89msEJqdgVnPccDnGhjT7IzEXkV42devSvxtz2hLb53Pb91AsDJqK26bseq88la26ENfl24Q17WkaOU5MmqaqUnk2KPTRUWL5kFhxltr4LJCvTCJLLA2NylNBzVGXsuTxEvS90P2__HS2y0fpNp4SfgV27YABOszCyOTA[root@docker ~]#
  • 我们可以把上面输出的 token 值拷贝到 jwt.io 里进行解码。

image-20230205144327903

从上面的数据可以看到这里的 token 的有效期也为 1 年,这个 token 在 Pod 里也是每 1 小时会更新一次,如果Pod 被删除重建,那么会重新申领一个新的 token,被删除 Pod 里的 token 立即过期。

需要注意的没有特定的机制使通过 TokenRequest 签发的令牌无效,如果你不再信任为某个 Pod 绑定的ServiceAccount 令牌,你可以删除该 Pod,删除 Pod 将使其绑定的令牌过期。

测试结束。😘

总结

我们可以简单总结下不同版本的 K8s 集群下面的 ServiceAccount Token 是如何工作的。

  • 1.20(含 1.20)之前的版本,在创建 sa 时会自动创建一个 secret,然后这个会把这个 secret 通过投射卷挂载到 pod 里,该 secret 里面包含的 token 是永久有效的

  • 1.21~1.23 版本,在创建 sa 时也会自动创建 secret,但是在 pod 里并不会使用 secret 里的 token,而是由kubelet 到 TokenRequest API 去申请一个 token,该 token 默认有效期为一年,但是 pod 每一个小时会更新一次 token。

  • 1.24 版本及以上,在创建 sa 时不再自动创建 secret 了,只保留由 kubelet 到 TokenRequest API 去申请token。

当然我们仍然可以手动创建 Secret 来保存 ServiceAccount 令牌,例如在你需要一个永不过期的令牌的时候。一旦手动创建一个 Secret 并将其关联到 ServiceAccount,Kubernetes 控制平面就会自动将令牌填充到该 Secret 中。

尽管存在手动创建长久 ServiceAccount 令牌的机制,但还是推荐使用 TokenRequest 获得短期的 API 访问令牌。

  • 画图说明:

image-20230204174056393

(2)为 ServiceAccount 分配权限

实战1

==💘 实战:为 ServiceAccount 分配权限-2023.2.5(测试成功)==

image-20230205180709817

  • 实验环境
实验环境:
1、win10,vmwrokstation虚机;
2、k8s集群:3台centos7.6 1810虚机,1个master节点,2个node节点
k8s version:v1.25.4
containerd://1.6.10
  • 实验软件(无)

上面我们创建了一个只能访问某个命名空间下面的普通用户 普通用户,我们前面也提到过 subjects 下面还有一种类型的主题资源:ServiceAccount,现在我们来创建一个集群内部的用户只能操作 kube-system 这个命名空间下面的 pods 和deployments。

  • 首先来创建一个 ServiceAccount 对象:
[root@master1 ~]#kubectl create sa cnych-sa -n kube-system
serviceaccount/cnych-sa created

当然我们也可以定义成 YAML 文件的形式来创建:

apiVersion: v1
kind: ServiceAccount
metadata:
name: cnych-sa
namespace: kube-system
  • 然后新建一个 Role 对象:
# cnych-sa-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: cnych-sa-role
namespace: kube-system
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

可以看到我们这里定义的角色没有创建、删除、更 创建、删除、更新新 Pod 的权限,待会我们可以重点测试一下

  • 创建该 Role 对象:
[root@master1 ~]#kubectl apply -f cnych-sa-role.yaml
role.rbac.authorization.k8s.io/cnych-sa-role created
  • 然后创建一个 RoleBinding 对象,将上面的 cnych-sa 和角色 cnych-sa-role 进行绑定:
# cnych-sa-rolebinding.yaml
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: cnych-sa-rolebinding
namespace: kube-system
subjects:
- kind: ServiceAccount
name: cnych-sa
namespace: kube-system
roleRef:
kind: Role
name: cnych-sa-role
apiGroup: rbac.authorization.k8s.io
  • 添加这个资源对象:
[root@master1 ~]#kubectl apply -f cnych-sa-rolebinding.yaml
rolebinding.rbac.authorization.k8s.io/cnych-sa-rolebinding created

然后我们怎么去验证这个 ServiceAccount 呢?以前的版本 ServiceAccount 会自动生成一个 Secret 对象和它进行映射,这个 Secret 里面包含一个 token,可以利用这个 token 去登录 Dashboard,然后就可以在 Dashboard 中来验证我们的功能是否符合预期了,但是现在的版本默认已经不支持这种方式了。

但是我们可以手动创建一个长久有效的 ServiceAccount 令牌,要为 ServiceAccount 创建一个不过期、持久化的API 令牌,需要创建一个类型为 kubernetes.io/service-account-token 的 Secret,附带引用ServiceAccount 的注解。控制平面随后生成一个长久的令牌,并使用生成的令牌数据更新该 Secret。

  • 比如我们这里可以创建一个如下所示的 Secret 资源对象:
# cnych-token.yaml
apiVersion: v1
kind: Secret
metadata:
name: cnych-sec
namespace: kube-system
annotations:
kubernetes.io/service-account.name: cnych-sa
type: kubernetes.io/service-account-token

也可以直接 kubectl create token <sa> 命令来创建 token。

部署:

[root@master1 ~]#kubectl apply -f cnych-token.yaml
secret/cnych-sec created
  • 创建后我们就可以使用该 Secret 对象包含的 token 去登录我们的 Dashboard。
[root@master1 ~]#kubectl get secret cnych-sec -o jsonpath={.data.token} -n kube-system |base64 -d
eyJhbGciOiJSUzI1NiIsImtpZCI6ImJXaFVwN29KX0lXODl6SGtFZTN4TklxYnNiQzlmeExpb0dqQ2JYbFNBS3MifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJjbnljaC1zZWMiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiY255Y2gtc2EiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiI3MzMyZWI3ZC1iYTE1LTQ1MjktOGQ0My1iZjYwYjVjMTliMzQiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06Y255Y2gtc2EifQ.UZoduMQCp9rh5IiHqoQKxj_0MXkvmV3lgURZF5XDUmrhYOgIhKdv3HhCap8FFooZ4rjABZM7c818JuH_rye9RrxN6W85wdPYyur5Rps4vCmHVD4mCOrW3m2OwQgC0ADCtQjYobvYFQfuIiI5DpABngG0mtW_CbqOAQyI9junbcvYQjZ0xRaueCKBK449WIKYdY-lHYRhyEMsqKwaO61u2_rQjn5ZiJuYwaJKzKCG9J7UxOKn4kVHR9mwvSLLAhuB03rIYHuqLf5rJrzRvEc_W8GdVeqhFU7uKE7KrD7PX_398eVBPTNQEvsAKwpwVh6T1KHKHE_k9Jl6Q0w9-LDwxA[root@master1 ~]#
  • 使用这里的 token 去 Dashboard 页面进行登录:

image-20230205161839335

我们可以看到上面的提示信息说我们现在使用的这个 ServiceAccount 没有权限获取当前命名空间下面的资源对象,这是因为我们登录进来后默认跳转到 default 命名空间,我们切换到 kube-system 命名空间下面就可以了(需要手动修改浏览器中的 url 地址):

image-20230205180709817

image-20230205180809850

我们可以看到可以访问 pod 列表了,但是也会有一些其他额外的提示: events is forbidden: User“system:serviceaccount:kube-system:cnych-sa” cannot list events in the namespace “kubesystem” ,这是因为当前登录用只被授权了访问 pod 和 deployment 的权限,同样的,访问下 deployment 看看可以了吗?

同样的,你可以根据自己的需求来对访问用户的权限进行限制,可以自己通过 Role 定义更加细粒度的权限,也可以使用系统内置的一些权限……

测试结束。😘

==💘 实战:cka-rbac考试题-创建sa并赋予权限-2023.2.6(测试成功)==

image-20230206212352305

解答:

image-20230206212403907

1、切换环境
kubectl config use-context kubernetes

2、配置
kubectl create clusterrole deployment-clusterrole --verb=create --resource=deployments,daemonsets,statefulsets
kubectl create serviceaccount cicd-token -n app-team1
# 题目中写了“限于 namespace app-team1 中”,则创建 rolebinding。没有写的话,则创建 clusterrolebinding。
kubectl create rolebinding cicd-token-rolebinding --serviceaccount=app-team1:cicd-token --clusterrole=deployment-clusterrole -n app-team1
# rolebinding 后面的名字 cicd-token-rolebinding 随便起的,因为题目中没有要求,如果题目中有要求,就不能随便起了。

3、验证
#稍微验证下
kubectl describe rolebinding cicd-token-rolebinding -napp-team1
实战2:命令行配置sa权限

==💘 实战:命令行配置sa权限-2023.5.24(测试成功)==

image-20230524074510011

  • 实验环境
实验环境:
1、win10,vmwrokstation虚机;
2、k8s集群:3台centos7.6 1810虚机,1个master节点,2个node节点
k8s version:v1.20.0
docker://20.10.7
  • 实验软件(无)

  • 命令行如下

[root@k8s-master1 ~]#kubectl create ns test
namespace/test created

# 创建角色
kubectl create role role-test --verb=get,list --resource=deployments,daemonsets,statefulsets -n test
# 创建服务账号
kubectl create serviceaccount app-demo -n test
# 将服务账号绑定角色
kubectl create rolebinding role-test:app-demo --serviceaccount=test:app-demo --role=role-test -n test
#测试
kubectl --as=system:serviceaccount:test:app-demo get pods -n test

#创建过程
[root@k8s-master1 ~]#kubectl create role role-test --verb=get,list --resource=deployments,daemonsets,statefulsets -n test
app-demo -n test
kubectl create rolebinding role-test:app-demo --serviceaccount=test:app-demo --role=role-test -n test
kubectl --as=system:serviceaccount:test:app-demo get pods -n test role.rbac.authorization.k8s.io/role-test created
[root@k8s-master1 ~]#
[root@k8s-master1 ~]#kubectl create serviceaccount app-demo -n test
serviceaccount/app-demo created
[root@k8s-master1 ~]#
[root@k8s-master1 ~]#kubectl create rolebinding role-test:app-demo --serviceaccount=test:app-demo --role=role-test -n test
rolebinding.rbac.authorization.k8s.io/role-test:app-demo created
[root@k8s-master1 ~]#
[root@k8s-master1 ~]#kubectl --as=system:serviceaccount:test:app-demo get pods -n test
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:test:app-demo" cannot list resource "pods" in API group "" in the namespace "test"
[root@k8s-master1 ~]#kubectl --as=system:serviceaccount:test:app-demo get deployment -n test #测试符合预期
No resources found in test namespace.
[root@k8s-master1 ~]#

测试结束。😘

3.可以全局访问的 ServiceAccount

==💘 实战:可以全局访问的 ServiceAccount-2023.2.5(测试成功)==

image-20230205181704742

刚刚我们创建的 cnych-sa 这个 ServiceAccount 和一个 Role 角色进行绑定的,如果我们现在创建一个新的ServiceAccount,需要他操作的权限作用于所有的 namespace,这个时候我们就需要使用到 ClusterRole 和ClusterRoleBinding 这两种资源对象了。

  • 同样,首先新建一个 ServiceAcount 对象:
# cnych-sa2.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: cnych-sa2
namespace: kube-system

创建:

[root@master1 ~]#kubectl create -f cnych-sa2.yaml
serviceaccount/cnych-sa2 created
  • 然后创建一个 ClusterRoleBinding 对象
# cnych-clusterolebinding.yaml
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: cnych-sa2-clusterrolebinding
subjects:
- kind: ServiceAccount
name: cnych-sa2
namespace: kube-system
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io

从上面我们可以看到我们没有为这个资源对象声明 namespace,因为这是一个 ClusterRoleBinding 资源对象,是作用于整个集群的。我们也没有单独新建一个 ClusterRole 对象,而是使用的 cluster-admin 这个对象,这是Kubernetes 集群内置的 ClusterRole 对象,我们可以使用 kubectl get clusterrole 和 kubectl get clusterrolebinding 查看系统内置的一些集群角色和集群角色绑定,这里我们使用的 cluster-admin 这个集群角色是拥有最高权限的集群角色,所以一般需要谨慎使用该集群角色。

部署:

[root@master1 ~]#kubectl apply -f cnych-clusterolebinding.yaml
clusterrolebinding.rbac.authorization.k8s.io/cnych-sa2-clusterrolebinding created
  • 创建上面集群角色绑定资源对象,然后我们可以使用 kubectl create token 命令来创建一个新的 token 去登录Dashboard:
[root@master1 ~]# kubectl create token cnych-sa2 -n kube-system
eyJhbGciOiJSUzI1NiIsImtpZCI6ImJXaFVwN29KX0lXODl6SGtFZTN4TklxYnNiQzlmeExpb0dqQ2JYbFNBS3MifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNjc1NTk1NzY5LCJpYXQiOjE2NzU1OTIxNjksImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJjbnljaC1zYTIiLCJ1aWQiOiI2ZTA1NWM4MC0wMjdmLTQ1NzAtODhlMi1hZDJkMzdkM2I1NjEifX0sIm5iZiI6MTY3NTU5MjE2OSwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmUtc3lzdGVtOmNueWNoLXNhMiJ9.ndUpljUIvFVIb-3dibZ_j8ZL4e5K4w5gqLlfZ1HK4K2C5bW-Ax3_ACNTz7BhdwRxQoT6mBWgIjuxyvqq7RuyCvleDRPq0x_9xgKIkl4I6jzX-XoexIO6HkskxtsCff8XkkyCfmdyD5bxnRUWvOCqr28d5E9C0gXwEv9LOasSkn4ebdcfV3HpFGtk0wpP04atVLq-deZh2iMSbSj8ttI0I6T3AKMN_upmJv5ivBfGgkJuHgi_R8iNj4uY780cA2j6shdA9maG8TyJmoeU7kPaOlLWL8ZcZqalMg7240gcGAyjKj3sOjNZViI3NO-a3nJ6Ow0a7VZ9jn1jgOaEzlB4rA
[root@master1 ~]#

可以看到使用该 token 登录后没有出现任何权限相关错误了:

image-20230205181704742

我们在最开始接触到 RBAC 认证的时候,可能不太熟悉,特别是不知道应该怎么去编写 rules 规则,大家可以去分析系统自带的 clusterrole、clusterrolebinding 这些资源对象的编写方法,怎么分析?还是利用 kubectl 的 get、describe、 -o yaml 这些操作,所以 kubectl 最基本的操作用户一定要掌握好。

测试结束。😘

FAQ

问题:ClusterRoleBinding可以绑定role吗?

结论:不可以。

image-20230203072622073

⚠️ 注意:角色和绑定之间的对应关系

一般来说,如下是对应绑定的:(但是也可以rolebinding和clusterrole对应) role --> rolebinding 有ns之说 clusterrole --> clusterrolebinding 无ns之说

serviceaccount 有ns之说

cluster-admin 是拥有最高权限的集群角色

这里我们使用的 cluster-admin 这个集群角色是拥有最高权限的集群角色,所以一般需要谨慎使用该集群角色。

[root@master1 ~]# kubectl  get clusterrole
NAME CREATED AT
……
cluster-admin 2021-10-31T00:00:22Z
……

image-20230206205553097

Role资源清单文件里rules字段可以按如下方式编写

1.可以把不同apiGroup放在一起
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: cnych-role
namespace: kube-system
rules:
- apiGroups: ["", "apps"]
resources: ["deployments", "replicasets", "pods"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] # 也可以使用['*']

2.或者把不同apiGroup分开写
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: cnych-role
namespace: kube-system
rules:
- apiGroups: [""] #核心组
resources: ["pods"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] # 也可以使用['*']
- apiGroup: ["apps"] #apps组
resources: ["deployments", "replicasets", "pods"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] # 也可以使用['*']

3.参考cluster-admin 这个cluster-role
[root@master1 ~]#kubectl get clusterrole cluster-admin -nkube-system -oyaml
rules:
- apiGroups: #资源对象
- '*'
resources:
- '*'
verbs:
- '*'
- nonResourceURLs: #非资源对象
- '*'
verbs:
- '*'

Forbidden字段报错更大可能性要想到的可能是权限问题

我们可以先给业务一个小的权限,可根据报错来进一步看他们需要什么权限
[root@master1 rbac]#kubectl get svc --context=cnych-context
Error from server (Forbidden): services is forbidden: User "cnych" cannot list resource "services" in API group "" in the namespace "kube-system"
[root@master1 rbac]#

我们可以去分析系统自带的 clusterrole、clusterrolebinding 这些资源对象的编写方法

我们在最开始接触到 RBAC 认证的时候,可能不太熟悉,特别是不知道应该怎么去编写 rules 规则,大家可以去分析系统自带的 clusterroleclusterrolebinding 这些资源对象的编写方法,怎么分析?还是利用 kubectl 的 get、describe、 -o yaml 这些操作,所以 kubectl 最基本的操作用户一定要掌握好。

kubectl get clusterrole system:basic-user  -nkube-system -oyaml
kubectl get clusterrole cluster-admin -nkube-system -oyaml
kubectl get clusterrole view -nkube-system -oyaml

我们可以通过如下命令来查看当前用户在k8s中拥有哪权限?

[root@k8s-master rbac]#kubectl describe role pod-reader
Name: pod-reader
Labels: <none>
Annotations: <none>
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
deployments [] [] [get watch list delete]
pods [] [] [get watch list delete]
services [] [] [get watch list delete]
deployments.apps [] [] [get watch list delete]
pods.apps [] [] [get watch list delete]
services.apps [] [] [get watch list delete]
[root@k8s-master rbac]#

关于我

我的博客主旨:

  • 排版美观,语言精炼;
  • 文档即手册,步骤明细,拒绝埋坑,提供源码;
  • 本人实战文档都是亲测成功的,各位小伙伴在实际操作过程中如有什么疑问,可随时联系本人帮您解决问题,让我们一起进步!

🍀 微信二维码 x2675263825 (舍得), qq:2675263825。

image-20230107215114763

🍀 微信公众号 《云原生架构师实战》

image-20230107215126971

🍀 博客 www.onlyyou520.com

image-20230107215612315

image-20230107215651525

🍀 csdn https://blog.csdn.net/weixin_39246554?spm=1010.2135.3001.5421

image-20230107215149885

🍀 知乎 https://www.zhihu.com/people/foryouone

image-20230107215203185

最后

好了,关于本次就到这里了,感谢大家阅读,最后祝大家生活快乐,每天都过的有意义哦,我们下期见!

image-20230522071715504

1