6.1 K8s认证&授权&准入控制
无论是k8s集群管理员(使用工具)还是Pod中的应用程序(service account)访问apiserver,都需要经过身份认证、授权检查、准入控制检查等步骤。k8s高度模块化,认证和授权都是通过插件的形式实现,支持多种不同的认证、授权方式。我们可以同时启用多种不同的身份认证插件或授权插件,如果一种插件检查不通过,会继续进行其他插件的检查;如果一种插件通过检查,则不再进行后续插件的检查。即“通过短路”机制。

认证这个概念比较好理解,就是做身份校验,解决“你是谁?”的问题。
在认证阶段最重要的就是身份(Identity),我们需要从中获取到用户的相关信息。通常,Identity 信息可以通过 User 或者 Group 来定义,但是 Kubernetes 中其实并没有相关定义,所以你无法通过 Kubernetes API 接口进行创建、查询和管理。
Kubernetes 认为 User 这类信息由外部系统来管理,自己并不负责管理和设计,这样可以避免重复定义用户模型,方便第三方用户权限平台(比如keycloak、LDAP)进行对接。所以 Kuberentes 是如何做身份认证的呢?这里我们简单介绍一下 Kubernetes 中几种常见的用户认证方式(认证插件)。
apiserver通过--basic-auth-file=/path/to/basic-auth.csv指定认证文件,在认证文件basic-auth.csv中拥有以下列作为单位的认证信息,格式为password,username,uid,示例:
passwd,kinderao,1
password2,test,2
然后在 kube-apiserver启动的时候加上--basic-auth-file=/path/to/basic-auth.csv这个参数,启动起来过后再使用k8s的api就需要加上认证信息,否则就会unauthorized,加认证信息的方法是在http请求的header中添加一个Authorization,value是Basic base64编码后的用户名密码信息。
注意,该方式在 1.16 版本开始已经被废弃掉了。
用户自己提供的静态 Token(JWT token)。像MySQL那样先为帐号创建一个密码,然后客户端携带这个密码去进行身份验证。在restful风格的http访问方式下,该密码会封装在http报文认证首部中,我们称之为的Token。可以将这些 Token 写到一个 CSV 文件中,其中至少包含 3 列:分别为 Token、用户名和用户 ID。你可以增加一个可选列包含 group 的信息,注意,如果您有多个组,则列必须是双引号 。比如:
token1,user1,uid1,"group1,group2,group3"
APIServer 在启动时会通过参数--token-auth-file=/path/to/token-auth.csv来指定读取的 CSV 文件。不过这种方式,实际环境中基本上不会使用到。毕竟这种静态文件不方便修改,每次修改后还需要重新启动 APIServer。
同样也是在apiserver的启动参数里面加入--token-auth-file=/path/to/token-auth.csv这个参数,然后在请求的时候同样在header中添加Authorization,value是Bearer token
示例:配置kubectl客户端通过token方式访问kube-apiserver : kubectl --token
1、本文档用到的变量定义如下:
$ export MASTER_IP=XX.XX.XX.XX # 替换为 kubernetes master VIP
$ export KUBE_APISERVER="https://${MASTER_IP}:6443"
2、创建 kubectl config 文件
$ # 设置集群参数
$ kubectl config set-cluster kubernetes \
--insecure-skip-tls-verify=true \
--server=${KUBE_APISERVER}
$ # 设置客户端认证参数
$ kubectl config set-credentials crd-admin \
--token=7176d48e4e66ddb3557a82f2dd316a93
$ # 设置上下文参数
$ kubectl config set-context kubernetes \
--cluster=kubernetes \
--user=crd-admin \
--namespace=crd
$ # 设置默认上下文
$ kubectl config use-context kubernetes
使用命令head -c 16 /dev/urandom | od -An -t x | tr -d ' ' 生成token
3、kube-apiserver设置
添加kube-apiserver端token证书
$ cat > /etc/kubernetes/pki/token_auth_file<<EOF
7176d48e4e66ddb3557a82f2dd316a93,crd-admin,1
EOF
第一列为刚刚生成的token,要与config里的token一致
第二列为user, 要与config里的use一致
第三列为编号或是序列号
添加kube-spiserver启动参数 --token-auth-file=/etc/kubernetes/pki/token_auth_file
需要重启kube-apiserver。注意:证书验证和token和同时启用的,但是token和用户名密码,不可同时启用
4、配置客户端RBAC相关
限制 crd-admin 用户的行为,需要使用 RBAC 将该用户的行为限制在crd namespace 空间范围内
kubectl create -f crd-rbac.yaml #这样 crd-admin 用户对 crd namespace 具有完全访问权限。
#crd-rbac.yaml具体内容:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: crdadmin-admin-binding
namespace: crd
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: admin
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: crd-admin
ServiceAccount认证本质上也是一种Token认证。ServiceAccount Token是 Kubernetes 中使用最为广泛的认证方式之一,主要用来给 Pod 提供访问 APIServer 的权限,通过使用 Volume 的方式挂载到 Pod 中。我们之前讲 Pod、Volume 的时候提到过,你也可以参考这份文档重温下如何为 Pod 配置 ServiceAccount。
当你创建一个 ServiceAccount 的时候,kube-controller-manager 会自动帮你创建出一个Secret来保存 Token,比如:
$ kubectl create sa demo
serviceaccount/demo created
➜ ~ kubectl get sa demo
NAME SECRETS AGE
demo 1 6s
➜ ~ kubectl describe sa demo
Name: demo
Namespace: default
Labels: <none>
Annotations: <none>
Image pull secrets: <none>
Mountable secrets: demo-token-fvsjg
Tokens: demo-token-fvsjg #Token存储在Secret中
Events: <none>
可以看到这里自动创建了一个名为demo-token-fvsjg的 Secret,我们来查看一下其中的内容:
$ kubectl get secret demo-token-fvsjg
NAME TYPE DATA AGE
demo-token-fvsjg kubernetes.io/service-account-token 3 27s
$ kubectl describe secret demo-token-fvsjg
Name: demo-token-fvsjg
Namespace: default
Labels: <none>
Annotations: kubernetes.io/service-account.name: demo
kubernetes.io/service-account.uid: f8fe4799-9add-4a36-8c29-a6b2744ba9a2
Type: kubernetes.io/service-account-token
Data
====
token: eyJhbGciOiJSUzI1NiIsImtpZCI6Ildhc3plOFE0UXBlRE8xTFdsRThRVXktRF93T0otcW55VXI3R0RvV1IzVjgifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlbW8tdG9rZW4tZnZzamciLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVtbyIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6ImY4ZmU0Nzk5LTlhZGQtNGEzNi04YzI5LWE2YjI3NDRiYTlhMiIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlbW8ifQ.t0sNk8PiRIgMXaauzM8v8eW_AF7J5Su_TpyURaeBQX0BuFDpU-rrTC6h9sjetAQOLa6iLFsrG2tqH04pNo2N6TB73-K-M54veVpP6FGhceLO0Vsz_iTJuOsnkS_6agqfW0UrtaY8_oCk1OZipdbTJYZlmwlb6opcfP1j7bFHCdDbwfbWx8ilHqTaJ5_r7zrdhxsmC5ogtBRDaJfEYmsIBZSgnt_0qMHNj9L47n2mj_wo-aQQ25o0lbO_7RSeVA17kkgbAJ2wzwvv8-i3f7K23DSxab3k8trbuIRt1R3Gl33fv5WIwiz9okYIFfus4pe0MCtjBxxTMa526cdezKS4LQ
ca.crt: 1025 bytes
namespace: 7 bytes
Data 里面的 token 是base64加密的,我们可以到https://jwt.io/中 Decode 出来:

从解析出来的 Payload 中可以看到, ServiceAccount 所属的 namespace、name 等信息。
除此之外,还支持Bootstrap Token、OpeID Connect Token (OIDC Token)、Webhook Token等,在此不一一介绍。
值得一提的是,Bootstrap Token 是 kubeadm 引入的方便创建新集群所使用的 Token 类型,通过如下类似的命令就可以将其加入新的节点中:
kubeadm join --discovery-token abcdef.1234567890abcdef
不管是上面哪种 Token,我们都可以拿来向 APIServer 发送请求,只需要向 HTTP 请求的 Header 中添加一个键为 Authorization ,值为Bearer THETOKEN的字段,类似如下:
Authorization: Bearer this-is-a-very-very-very-long-token
# 例如:
curl -H "Authorization: Bearer $TOKEN" https://kubernetes/api/v1/namespaces/$NS/pods
Kubernetes 使用mTLS进行身份验证,通过解析请求使用的 客户端(client)证书,将其中的 subject 的通用名称(Common Name)字段(例如"/CN=bob")作为用户名。Kubernetes 集群各个组件间相互通信,都是基于 x509 证书进行身份校验的。关于证书的创建、查看等,可以参考官方文档。该种认证方式同时能够确认服务器身份、加密通信。
apiserver启用如下相关参数:
--secure-port=6443
--client-ca-file=/etc/kubernetes/pki/ca.crt
--tls-cert-file=/etc/kubernetes/pki/apiserver.crt
--tls-private-key-file=/etc/kubernetes/pki/apiserver.key
之后使用curl请求时,都需要带有根证书和client key和client的证书
curl https://hostname:6443 --cacert ca.crt --key client.key --cert client.crt
比如使用openssl命令行工具生成一个证书签名请求(CSR):
openssl req -new -key zhangsan.pem -out zhangsan-csr.pem -subj "/CN=zhangsan/O=app1/O=app2"
这条命令会使用用户名zhangsan生成一个 CSR,且它属于 “app1” 和 “app2” 两个用户组,然后我们使用 CA 证书根据这个 CSR 就可以签发出一对证书。当用这对证书去访问 APIServer 时,它拿到的用户就是zhangsan了。

授权负责做权限管控,解决“你能干什么?”的问题,给予你能访问 Kubernetes 集群中的哪些资源以及做哪些操作的权利,比如你是否能创建一个 Pod,是否能删除一个 Pod。
Kubernetes 内部有多种授权模块,比如 Node、ABAC、RBAC、Webhook。授权阶段会根据从 AuthN 拿到的用户信息,依次按配置的授权次序逐一进行权限验证。任一授权模块验证通过,即允许该请求继续访问。
- RBAC:只有许可授权,没有拒绝授权,默认拒绝
- webhook
- node认证
ABAC (Attribute Based Access Control) 是一种基于属性的访问控制,可以给 APIServer 指定一个 JSON 文件--authorization-policy-file=SOME_FILENAME,该文件描述了一组属性组合策略,比如:
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "zhangsan", "namespace": "*", "resource": "pods", "readonly": true}}
这条策略表示,用户 zhangsan 可以以只读的方式读取任何 namespace 中的 Pod。这里有一份示例文件,你可以参考一下。
不过在实际使用中,ABAC 使用得比较少,跟我们上面 AuthN 中提到的静态 Token 一样,不方便修改、更新,每次变更后都需要重启 APIServer。所以在实际使用中,RBAC 最常见,使用更广泛。
相对而言,RBAC 使用起来就非常方便了,通过 Kubernetes 的对象就可以直接进行管理,也便于动态调整权限。
RBAC 中引入了角色,所有的权限都是围着这个角色进行展开的,每个角色里面定义了可以操作的资源以及操作方式。在 Kubernetes 中有两种角色,一种是 namespace 级别的 Role ,一种是集群级别的 ClusterRole 。
在 Kubernetes 中有些资源是 namespace 级别的,比如 Pod、Service 等,而有些则属于集群级别,比如 Node、PV 等。所以有了 Role 和 ClusterRole 两种角色。
我们来看个例子:
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""] # 空字符串""表明使用core API group
resources: ["pods"]
verbs: ["get", "watch", "list"]
这样一个角色表示可以访问 default 命名空间下面的 Pods,并可以对其进行 get、watch 以及 list 操作。
ClusterRole 除了可以定义集群方位的资源外,比如 Node,还可以定义跨 namespace 的资源访问,比如你想访问所有命名空间下面的 Pod,就可以这么定义:
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
# 鉴于ClusterRole是集群范围对象,所以这里不需要定义"namespace"字段
name: pods-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list"]
RBAC 中定义这些角色包含了一组权限规则,这些规则纯粹以累加形式组合,没有互斥的概念。 定义好角色,我们就可以做权限绑定了。这里分别有两个 API 对象,RoleBinding 和 ClusterRoleBinding。角色绑定就是把一个权限授予一个或者一组用户,例如:
apiVersion: rbac.authorization.k8s.io/v1
# 此角色绑定使得用户 "jane" 或者 "manager"组(Group)能够读取 "default" 命名空间中的 Pods
kind: RoleBinding
metadata:
name: read-pods
namespace: default
subjects:
- kind: User
name: jane # 名称区分大小写
apiGroup: rbac.authorization.k8s.io
- kind: Group
name: manager # 名称区分大小写
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role # 这里可以是 Role,也可以是 ClusterRole
name: pods-reader # 这里的名称必须与你想要绑定的 Role 或 ClusterRole 名称一致
apiGroup: rbac.authorization.k8s.io
这里定义的 RoleBinding 对象在 default 命名空间中将 pods-reader 角色授予用户 jane和组 manager。 这一授权将允许用户 jane 和 manager 组中的各个用户从default 命名空间中读取到 Pod。
在授权检查完成以后,准入控制会做一些后续的其他安全检查操作,进一步补充了授权机制。准入控制可以帮助我们在APIServer真正**处理对象前(写入etcd之前)**做一些校验以及修改的工作。从字面上你可能不太好理解。其实就是由一组控制逻辑级联而成,对对象进行拦截校验、更改等操作。比如你打算在一个名为test的namespace中创建一个 Pod,如果这个namespace不存在,集群要不要自动创建出来?或者直接拒绝掉该请求?这些逻辑都可以通过准入控制(NamespaceExists)进行配置。
准入控制(AdmissionControl )本质上为一段准入代码,在对kubernetes api的请求过程中,顺序为:先经过认证 & 授权,然后执行准入操作,最后对目标对象进行操作。这个准入代码在APIServer中,而且必须被编译到二进制文件中才能被执行。 在对集群进行请求时,每个准入控制代码都按照一定顺序执行。如果有一个准入控制拒绝了此次请求,那么整个请求的结果将会立即返回,并提示用户相应的error信息。
在APIServer中通过--enable-admission-plugins=xx,yy,zz指定启用的准入控制。
这里官方列出了很多准入控制组件,官方已经将使用方法写得很清楚了,你可以根据自己的需要,选择合适的准入控制插件,我就不再赘述了。常用组件(控制代码)如下:
- AlwaysAdmit:允许所有请求
- AlwaysDeny:禁止所有请求,多用于测试环境
- ServiceAccount:它将serviceAccounts实现了自动化,它会辅助serviceAccount做一些事情,比如如果pod没有serviceAccount属性,它会自动添加一个default,并确保pod的serviceAccount始终存在
- LimitRanger:他会观察所有的请求,确保没有违反已经定义好的约束条件,这些条件定义在namespace中LimitRange对象中。如果在kubernetes中使用LimitRange对象,则必须使用这个插件。
- NamespaceExists:它会观察所有的请求,如果请求尝试创建一个不存在的namespace,则这个请求被拒绝。
示例:ingress-nginx-controller使用AdmissionControl作为ingress配置文件的校验:https://qingwave.github.io/ingress-nginx-controller-admission-webhook/
【总结】apiserver在认证与鉴权过程中需要获取的信息:
客户端对apiserver发起请求时,apiserver需要哪些信息才能完成对客户端的认证与鉴权(识别用户有没有请求权限):
user:username、uid
group:
extra:提供额外信息
api资源:在http协议中使用Request path标识,有两类:名称空间级别、集群级别。
- 名称空间级别:例如https://${IP}:6443/apis/apps/v1/namespaces/default/deployments/myapp-deployments。我们使用kubectl proxy创建代理后,我们就可以使用curl命令来访问,此时,我们使用curl访问的是proxy代理,kubectl将代理接受的请求转发给apiserver,curl无需完成认证授权,认证和授权操作由kubectl请求apiserver时完成。例如
curl http://localhost:8888/apis/apps/v1/namespaces/kube-system/deployments/tiller-deploy - 集群级别:例如
- 名称空间级别:例如https://${IP}:6443/apis/apps/v1/namespaces/default/deployments/myapp-deployments。我们使用kubectl proxy创建代理后,我们就可以使用curl命令来访问,此时,我们使用curl访问的是proxy代理,kubectl将代理接受的请求转发给apiserver,curl无需完成认证授权,认证和授权操作由kubectl请求apiserver时完成。例如
对api资源的操作action
Create: create Update: replace patch Read: get list 显示一组资源 watch Delete: delete
参考资料:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.10/
简称:sa,是k8s标准资源对象。
k8s集群中,与apiserver交流时,需要认证的有两类帐号:
- 用户帐号UserAccount(User),它是现实中的人(如k8s管理员)使用客户端(例如kubectl)来操作k8s的帐号,此类账户在Config中定义;
- 服务帐号ServiceAccount,它是Pod用来让pod中的应用程序访问API server时用到的认证帐号。认证是有pod提供而不是pod中的应用提供。该类用户使用名称为kubernetes的svc与apiserver进行交流。
k8s集群中,每个namespace下有一个默认的ServiceAccount对象,名称为default,这个ServiceAccount绑定了一个默认的token(secret对象)。当Pod启动时这个Secret会被自动Mount到Pod的特定目录下,用来协助完成Pod中的进程访问API Server时的身份认证过程,只不过这个默认的sa分配的权限很低,只能获取pod本身最基本的数据。如果一个Pod在定义时没有指定spec.serviceAccountName属性,则系统会自动为其赋值为“Default”,即使用同一namespace下默认的ServiceAccount,如果某个Pod需要使用非default的ServiceAccount,需要在定义时指定。
说明:两类帐号(ServiceAccount和UserAccount)的创建与授权是分开的,创建了一个ServiceAccount或UserAccount后,是没有任何权限的,它只用于身份认证。 我们可以使用rbac为他们进行授权。
**命令行定义ServiceAccount: **
[root@k8smaster manifests]# kubectl create serviceaccount admin
serviceaccount/admin created
[root@k8smaster manifests]# kubectl get sa admin -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
creationTimestamp: 2018-08-30T10:11:58Z
name: admin
namespace: default
resourceVersion: "1332570"
selfLink: /api/v1/namespaces/default/serviceaccounts/admin
uid: 213d5550-ac3d-11e8-8232-5254001d21da
secrets: #系统自动为sa生成secret
- name: admin-token-2q4vr #sa拥有的一个加密的token(secret对象),用于身份验证,此secret自动生成
[root@k8smaster manifests]# kubectl get sa
NAME SECRETS AGE
admin 1 30s
default 1 34d
[root@k8smaster manifests]# kubectl get secret
NAME TYPE DATA AGE
admin-token-2q4vr kubernetes.io/service-account-token 3 1m
default-token-cm4hr kubernetes.io/service-account-token 3 34d
资源清单定义:
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
namespace: kube-system
#imagePullSecrets: dockerhub-secret #可以将登陆私有仓库的secret在sa中定义,而不在pod中定义,更安全
#secrets: #可以自定义secret,如果缺省则使用默认的secret
#- xxx
从上面可以看到,其实定义一个ServiceAccount,其实就是为这个sa创建并绑定了一个token(secret对象),用于身份验证。注意这里只是进行身份认证,没有权限验证,授权需要使用rbac。
[root@k8smaster manifests]# cat pod-sa-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-sa-demo
namespace: default
labels:
app: myapp
tier: frontend
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
serviceAccountName: admin #指定Pod使用的sa,每个sa会有一个token(secret对象),pod会以存储卷的形式将这个secret对象挂载到pod中的特定目录例如/var/run/secrets/kubernetes.io/serviceaccount下。之后pod便可以使用该token去访问apiserver等组件。
kubectl是怎么使用配置文件的?

kubectl config命令其实就是修改~/.kube/config这个配置文件(当然也可以指定修改某个配置文件),相关的命令有:
Available Commands:
current-context Displays the current-context
delete-cluster Delete the specified cluster from the kubeconfig
delete-context Delete the specified context from the kubeconfig
get-clusters Display clusters defined in the kubeconfig
get-contexts Describe one or many contexts
rename-context Renames a context from the kubeconfig file.
set Sets an individual value in a kubeconfig file
set-cluster Sets a cluster entry in kubeconfig
set-context Sets a context entry in kubeconfig
set-credentials Sets a user entry in kubeconfig
unset Unsets an individual value in a kubeconfig file
use-context Sets the current-context in a kubeconfig file
view Display merged kubeconfig settings or a specified kubeconfig file
[root@k8smaster manifests]# kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: REDACTED #apiserver信任的ca证书
server: https://192.168.5.36:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
[root@k8smaster manifests]# kubectl config current-context
kubernetes-admin@kubernetes
# 如果有多个context,可以使用--context xxx临时指定,而无需使用set-context进行切换。例如
[root@k8smaster manifests]# kubectl --context k8s-ali-admin get nodes
[root@k8smaster ~]# cd /etc/kubernetes/pki/
#创建一个私钥
[root@k8smaster pki]# (umask 077;openssl genrsa -out ljzsdut.key 2048)
Generating RSA private key, 2048 bit long modulus
............................+++
............................................+++
e is 65537 (0x10001)
#根据私钥生成一个证书签署请求
[root@k8smaster pki]# openssl req -new -key ljzsdut.key -out ljzsdut.csr -subj "/CN=ljzsdut"
#使用CA证书签证:-req表明输入文件是一个"请求签发证书文件(CSR)",等待进行签发
[root@k8smaster pki]# openssl x509 -req -in ljzsdut.csr -CA ./ca.crt -CAkey ./ca.key -CAcreateserial -out ljzsdut.crt -days 365
Signature ok
subject=/CN=ljzsdut
Getting CA Private Key
[root@k8smaster pki]# ll ljzsdut.*
-rw-r--r--. 1 root root 973 Aug 31 09:18 ljzsdut.crt #经过CA签署后的证书
-rw-r--r--. 1 root root 887 Aug 31 09:13 ljzsdut.csr #证书签署请求
-rw-------. 1 root root 1679 Aug 31 09:10 ljzsdut.key #私钥
[root@k8smaster pki]# openssl x509 -in ljzsdut.crt -text -noout #查看证书内容,主要是Subject: CN=ljzsdut,它对应着user的用户名。
Certificate:
Data:
Version: 1 (0x0)
Serial Number:
99:0b:e6:0b:1d:a1:e2:d9
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=kubernetes
Validity
Not Before: Aug 31 01:18:10 2018 GMT
Not After : Aug 31 01:18:10 2019 GMT
Subject: CN=ljzsdut
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:b9:93:b7:35:02:ab:e7:89:8e:5e:cc:48:f5:5e:
30:3c:3e:7a:27:bf:c6:9b:b3:aa:ce:6e:bf:90:5e:
93:e7:20:24:5e:4e:fa:96:75:fb:83:ad:52:79:ca:
5e:a4:cc:23:54:85:2b:56:fc:cb:2e:c2:63:4e:c8:
6d:1d:f0:ea:c6:64:6a:39:81:30:b0:a2:f0:c5:bb:
9a:0f:0b:9e:f3:76:e4:82:b6:a8:0f:c2:c9:eb:89:
5c:da:a3:89:ac:dd:16:6c:7d:0b:3d:09:08:0b:10:
02:e4:1e:7b:9a:a0:62:aa:aa:03:ae:ca:52:65:de:
e0:74:f5:44:30:e8:ea:e6:84:22:8a:2a:89:be:88:
9a:f8:55:6c:1d:01:33:4c:4d:60:61:07:c1:9a:3a:
a6:88:0b:f1:95:fb:37:89:65:14:9a:f0:85:0d:6d:
f4:68:47:fb:38:4e:eb:89:13:97:c8:a8:49:e1:cc:
d5:67:f1:69:f5:1e:db:52:92:09:7f:62:10:91:9d:
98:19:03:cc:ad:6b:f8:1d:77:21:ac:ab:35:e3:da:
0b:a0:21:9e:26:c3:73:58:7a:32:95:cf:c1:1f:25:
ff:42:b8:4b:08:db:26:9a:1b:eb:4b:46:ec:52:0f:
89:db:f5:9e:fd:71:c9:22:d1:63:4d:d7:87:5d:0d:
a9:e7
Exponent: 65537 (0x10001)
Signature Algorithm: sha256WithRSAEncryption
2a:fb:99:23:b6:1e:34:ff:d2:a5:64:b0:c4:a8:a8:8c:8b:cf:
4a:8b:bb:ef:ed:15:ae:06:6b:f4:e5:a2:79:61:ef:a7:b5:91:
97:c3:00:3d:3b:e2:2c:f9:6c:0e:70:76:ad:22:c8:22:8a:91:
19:de:0a:8d:2e:2d:5f:60:8d:43:34:0d:b1:7e:e0:f1:7b:7d:
36:cf:47:f1:ca:fb:ed:55:96:21:d2:26:86:28:64:ee:9d:49:
e7:e7:00:ae:b5:a7:e2:2f:05:d0:c8:4b:b4:2a:ec:f6:8a:02:
e9:03:fb:63:65:92:9f:fb:bb:c7:83:c6:16:9c:5b:ef:4f:00:
d6:a3:28:1c:3d:21:e0:26:9c:a7:fb:3a:91:3c:0e:32:ad:23:
ae:71:6c:d8:58:5e:ed:31:46:85:b2:a1:07:c7:02:f2:a6:bc:
5f:54:44:ac:5f:71:d3:65:3d:b4:73:25:c1:a3:5d:10:06:04:
7b:e1:5c:eb:a0:7a:05:f3:54:9b:dd:a5:f6:7c:ac:4e:14:d6:
b2:64:95:44:f5:91:4f:ff:a5:99:75:5a:2c:fd:5c:af:52:1c:
92:cc:73:c7:cf:4f:5c:31:ee:47:3b:2d:84:ca:d6:a9:58:49:
77:c1:21:87:21:61:02:30:38:29:f7:91:7d:f8:5e:df:25:b7:
65:d3:a1:84
#kubernetes集群已经存在,所以无需使用kubectl config set-cluster创建集群,此处创建集群mycluster只是为了演示:--certificate-authority指定集群ca的证书,用于验证apiserver发过来的证书以实现验证apiserver身份
[root@k8smaster pki]# kubectl config set-cluster mycluster --kubeconfig=/tmp/k8sconfig --server="http://192.168.5.36:6443" --certificate-authority=/etc/kubernetes/pki/ca.crt --embed-certs=true
Cluster "mycluster" set.
[root@k8smaster pki]# kubectl config view --kubeconfig=/tmp/k8sconfig
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: REDACTED
server: http://192.168.5.36:6443
name: mycluster
contexts: []
current-context: ""
kind: Config
preferences: {}
users: []
[root@k8smaster pki]# ls /tmp/k8sconfig
/tmp/k8sconfig
#或者在默认config中创建cluster:
[root@k8smaster pki]# kubectl config set-cluster mycluster --server="https://192.168.5.36:6443" --certificate-authority=/etc/kubernetes/pki/ca.crt --embed-certs=true
Cluster "mycluster" set.
#创建用户:用户就是一个证书或者token或者basic auth的载体;
#--embed-certs=true表示将crt/key文件嵌入到kubeconfig文件中,否则只是在kubeconfig文件中写入一个crt/key的文件路径来引用证书。
[root@k8smaster pki]# kubectl config set-credentials ljzsdut --client-certificate=/etc/kubernetes/pki/ljzsdut.crt --client-key=/etc/kubernetes/pki/ljzsdut.key --embed-certs=true
User "ljzsdut" set.
#user中除了使用x509证书的认证方式,也可以使用其他认证方式
# 其他认证方式参考:
#1、basic auth
kubectl config set-credentials cluster-admin --username=admin --password=uXFGweU9l35qcif
#2、token
#通过这种方式,我们可以提取事先创建好的sa的token值,然后使用这个token值创建UserAccount,此时整个UserAccount本质上就是一个sa了。这种方式实现了在kubeconfig中使用sa。这种方法实现的根本原因是sa本质上就是一个token,使用token认证。
kubectl config set-credentials cluster-admin --token=7176d48e4e66ddb3557a82f2dd316a93
[root@k8smaster pki]# kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: REDACTED
server: https://192.168.5.36:6443
name: kubernetes
- cluster:
certificate-authority-data: REDACTED
server: https://192.168.5.36:6443
name: mycluster
contexts:
- context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
- name: ljzsdut
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
#创建context:关联集群cluster与用户user,用来指明用哪个用户访问哪个集群
[root@k8smaster pki]# kubectl config set-context ljzsdut@kubernetes --cluster=kubernetes --user=ljzsdut
Context "ljzsdut@kubernetes" created.
[root@k8smaster pki]# kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: REDACTED
server: https://192.168.5.36:6443
name: kubernetes
- cluster:
certificate-authority-data: REDACTED
server: https://192.168.5.36:6443
name: mycluster
contexts:
- context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes
- context:
cluster: kubernetes
user: ljzsdut
name: ljzsdut@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
- name: ljzsdut
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
#切换帐号:修改当前上下文
[root@k8smaster pki]# kubectl config use-context ljzsdut@kubernetes
Switched to context "ljzsdut@kubernetes".
[root@k8smaster pki]# kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: REDACTED
server: https://192.168.5.36:6443
name: kubernetes
- cluster:
certificate-authority-data: REDACTED
server: https://192.168.5.36:6443
name: mycluster
contexts:
- context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes
- context:
cluster: kubernetes
user: ljzsdut
name: ljzsdut@kubernetes
current-context: ljzsdut@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
- name: ljzsdut
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
完成以上步骤,就创建了一个可以访问k8s集群的用户,但这个用户只是能够通过认证,它没有任何权限,需要通过rbac进行对其授权。
RBAC是Role-Based Access Control的简称,中文为基于角色的访问控制;RBAC使用“rbac.authorization.k8s.io”API组来驱动授权决策,允许管理员通过Kubernetes API动态配置策略。从1.8开始,RBAC模式处于稳定版本,并由rbac.authorization.k8s.io/v1 API提供支持。要启用RBAC,请使用--authorization-mode = RBAC启动apiserver。
访问控制就是指权限,基于角色的访问控制,见名知意:就是将权限授予角色,然后将角色与用户进行绑定从而完成对用户的授权,而不是直接将权限授予用户(基于用户的访问控制).

从上图中我们可以看到:
- 角色即许可:名为pod-reader的Role拥有Pod的get和list许可(permissions=object+action,对某个对象允许的操作);
- 名为pod-reader的RoleBinding将pod-reader角色授权给右边的User、Group或ServiceAccount。
说明:Role、RoleBinding、ClusterRole、ClusterRoleBinding都是标准的k8s资源对象。
user和group在tls证书中CN、O中定义。
工作逻辑:

Rolebinding既可以绑定role也可以绑定clusterrole;clusterrolebinding只能绑定clusterrole:
ClusterRoleBinding+ClusterRole=Cluster
RoleBinding+Role=Namespace
RoleBinding+ClusterRole=Namespace 只能处理Rolebinding所在Namespace中的资源
1、User是集群级别的,不在任何的命名空间中;但是ServiceAccount是定义在某个命名空间中的。无论是user还是sa,都与权限的生效范围无关,只与Rolebinding所在Namespace中有关
2、ClusterRole和ClusterRolebindig是集群级别的;Rolebinding和Role是定义在某个名称空间中的
实现了object和action(或者说是verb)的绑定
- kubectl create role NAME –verb=verb –resource=resource.group/subresource [–resource-name=resourcename] [-n NAMESAPCE] (role可以指定名称空间的,缺省为default名称空间)
- kubectl create clusterrole NAME –verb=verb –resource=resource.group [–resource-name=resourcename] [-n NAMESAPCE] (clusterole指定名称空间也会被忽略,仍然定义为集群级别)
- –verb:指定action:[“get”,“list”, “watch”,“post”, “create”, “update”, “patch”, “delete”,“deletecollection”]
- –resource:指定资源对象
[root@k8smaster pki]# kubectl create role pods-reader --verb=get,list,watch --resource=pods
role.rbac.authorization.k8s.io/pods-reader created
[root@k8smaster pki]# kubectl create role pods-reader --verb=get,list,watch --resource=pods --dry-run -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
creationTimestamp: null
name: pods-reader
rules:
- apiGroups:
- "" #表示对哪些api资源组做操作
resources:
- pods
verbs:
- get
- list
- watch
k8s有两个重要的内置clusterrole:admin(适应于rolebinding实现)、cluster-admin
实现了role和user/sa的绑定:
- kubectl create rolebinding NAME –clusterrole=NAME|–role=NAME [–user=username][–group=groupname] [–serviceaccount=namespace:serviceaccountname] [-n NAMESAPCE] (Rolebinding是可以指定名称空间的,缺省为default名称空间,可以绑定clusterrole也可以绑定role)
- kubectl create clusterrolebinding NAME –clusterrole=NAME [–user=username][–group=groupname]
[–serviceaccount=namespace:serviceaccountname] [-n NAMESAPCE] (一般不指定名称空间,clusterrolebinding适用于集群级别所有的名称空间,只能绑定clusterrole,即使指定名称空间也会被忽略,仍然定义为集群级别)
- –serviceaccount:将role赋予一个ServiceAccount,任何使用该ServiceAccount的pod中的应用就拥有了这个role。sa是需要指定命名空间的。
- –user:user无需创建,其并不存在系统上,它只是一个标识;使用kubectl登录时,k8s就能通过config中的user名称来关联并识别它;user是集群级别的,不在任何命名空间中。
[root@k8smaster pki]# kubectl create rolebinding pods-reader-rolebinding --role=pods-reader --user=ljzsdut
rolebinding.rbac.authorization.k8s.io/pods-reader-rolebinding created
[root@k8smaster manifests]# kubectl create rolebinding pods-reader-rolebinding --role=pods-reader --user=ljzsdut --dry-run -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
creationTimestamp: null
name: pods-reader-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: pods-reader
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: ljzsdut
总结:

创建一个名称为ljzsdut的用户,只能查询kube-system命名空间中的pod
[root@k8smaster pki]# (umask 077;openssl genrsa -out ljzsdut.key 2048)
Generating RSA private key, 2048 bit long modulus
.................+++
.................................................................+++
e is 65537 (0x10001)
[root@k8smaster pki]# openssl req -new -key ljzsdut.key -out ljzsdut.csr -subj "/CN=ljzsdut"
[root@k8smaster pki]# openssl x509 -req -in ljzsdut.csr -out ljzsdut.crt -CA ca.crt -CAkey ca.key -CAcreateserial -days 365
Signature ok
subject=/CN=ljzsdut
Getting CA Private Key
[root@k8smaster pki]# ll ljzsdut.*
-rw-r--r--. 1 root root 977 Sep 5 10:24 ljzsdut.crt
-rw-r--r--. 1 root root 887 Sep 5 10:23 ljzsdut.csr
-rw-------. 1 root root 1679 Sep 5 10:18 ljzsdut.key
#以上完成了证书的创建
[root@k8smaster pki]# kubectl config set-cluster kubernetes --server="https://192.168.5.36:6443" --certificate-authority=ca.crt --kubeconfig=/tmp/ljzsdut.config
Cluster "kubernetes" set.
[root@k8smaster pki]# kubectl config set-credentials ljzsdut --client-certificate=ljzsdut.crt --client-key=ljzsdut.key --embed-certs=true --kubeconfig=/tmp/ljzsdut.config
User "ljzsdut" set.
[root@k8smaster pki]# kubectl config set-context ljzsdut@kubernetes --user=ljzsdut --cluster=kubernetes --kubeconfig=/tmp/ljzsdut.config
Context "ljzsdut@kubernetes" created.
[root@k8smaster pki]# kubectl config use-context ljzsdut@kubernetes --kubeconfig=/tmp/ljzsdut.config
Switched to context "ljzsdut@kubernetes".
[root@k8smaster pki]# kubectl config view --kubeconfig=/tmp/ljzsdut.config
apiVersion: v1
clusters:
- cluster:
certificate-authority: /etc/kubernetes/pki/ca.crt
server: https://192.168.5.36:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: ljzsdut
name: ljzsdut@kubernetes
current-context: ljzsdut@kubernetes
kind: Config
preferences: {}
users:
- name: ljzsdut
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
#以上完成了kubeconfig配置文件的创建,运维人员可以使用这个配置文件来认证k8s集群,但是没有任何授权(rbac)
[root@k8smaster pki]# kubectl create role pods-reader-ns-kube-system --verb=get,list,watch --resource=pods -n kube-system
role.rbac.authorization.k8s.io/pods-reader-ns-kube-system created
[root@k8smaster pki]# kubectl create rolebinding pods-reader-ns-kube-system --role=pods-reader-ns-kube-system --user=ljzsdut -n kube-system
rolebinding.rbac.authorization.k8s.io/pods-reader-ns-kube-system created
#注意指定命名空间-n kube-system
#以上完成了对ljzsdut这个用户基于RBAC的授权操作。
#测试
[root@k8smaster pki]# kubectl --kubeconfig=/tmp/ljzsdut.config get pods
No resources found.
Error from server (Forbidden): pods is forbidden: User "ljzsdut" cannot list pods in the namespace "default"
[root@k8smaster pki]# kubectl --kubeconfig=/tmp/ljzsdut.config get pods -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-78fcdf6894-9b8fn 1/1 Running 0 39d
coredns-78fcdf6894-xq69l 1/1 Running 0 39d
etcd-k8smaster 1/1 Running 0 39d
kube-apiserver-k8smaster 1/1 Running 0 22h
kube-controller-manager-k8smaster 1/1 Running 8 39d
kube-flannel-ds-amd64-7l22s 1/1 Running 0 39d
kube-flannel-ds-amd64-h2zzs 1/1 Running 0 39d
kube-flannel-ds-amd64-s6whg 1/1 Running 1 39d
kube-proxy-57znc 1/1 Running 0 39d
kube-proxy-pgrc2 1/1 Running 0 39d
kube-proxy-v6jrs 1/1 Running 2 39d
kube-scheduler-k8smaster 1/1 Running 8 39d
kubernetes-dashboard-767dc7d4d-8f6hk 1/1 Running 0 16h
#以上操作发现用户ljzsdut对default命名空间的pods没有查看权限,但是对kube-system命名空间的pods是有查看权限的。