ljzsdut
GitHubToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

6.1 K8s认证&授权&准入控制

认证&授权&准入控制

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

img

认证:

认证这个概念比较好理解,就是做身份校验,解决“你是谁?”的问题。

在认证阶段最重要的就是身份(Identity),我们需要从中获取到用户的相关信息。通常,Identity 信息可以通过 User 或者 Group 来定义,但是 Kubernetes 中其实并没有相关定义,所以你无法通过 Kubernetes API 接口进行创建、查询和管理。

Kubernetes 认为 User 这类信息由外部系统来管理,自己并不负责管理和设计,这样可以避免重复定义用户模型,方便第三方用户权限平台(比如keycloak、LDAP)进行对接。所以 Kuberentes 是如何做身份认证的呢?这里我们简单介绍一下 Kubernetes 中几种常见的用户认证方式(认证插件)。

基本认证(basic-auth):静态用户密码文件

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-auth):静态Token

用户自己提供的静态 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认证。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 出来:

img

从解析出来的 Payload 中可以看到, ServiceAccount 所属的 namespace、name 等信息。

除此之外,还支持Bootstrap TokenOpeID 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

参考:K8S自定义webhook实现认证管理

x509 证书认证

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了。

image-20211228152351460

授权:

授权负责做权限管控,解决“你能干什么?”的问题,给予你能访问 Kubernetes 集群中的哪些资源以及做哪些操作的权利,比如你是否能创建一个 Pod,是否能删除一个 Pod。

Kubernetes 内部有多种授权模块,比如 NodeABACRBACWebhook。授权阶段会根据从 AuthN 拿到的用户信息,依次按配置的授权次序逐一进行权限验证。任一授权模块验证通过,即允许该请求继续访问。

  • RBAC:只有许可授权,没有拒绝授权,默认拒绝
  • webhook
  • node认证

ABAC

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

相对而言,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
    • 集群级别:例如
  • 对api资源的操作action

    Create: create Update: replace patch Read: get list 显示一组资源 watch Delete: delete

参考资料:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.10/

ServiceAccount

简称: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为他们进行授权。

1、创建sa

**命令行定义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

2、在Pod中使用sa:

[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等组件。

KUBECONFIG

kubectl是怎么使用配置文件的?

kubeconfig

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

1、准备证书:使用CA进行签署

[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

2、在Config中创建用户User:

#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

​ 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。

访问控制就是指权限,基于角色的访问控制,见名知意:就是将权限授予角色,然后将角色与用户进行绑定从而完成对用户的授权,而不是直接将权限授予用户(基于用户的访问控制).

kubernetes-rbac

从上图中我们可以看到:

  • 角色即许可:名为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中定义。

工作逻辑:

1535682463211

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是定义在某个名称空间中的

1、创建role、clusterrole:

实现了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

2、创建rolebinding、clusterRolebinding:

实现了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

总结:

1535699865608

RBAC实战

创建一个名称为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是有查看权限的。