k8s验证与授权思路综述
相信很多人装k8s都想顺手装个dashboard看看,毕竟GUI有时候还是比较重要的,哈哈。 相信很多人会有同感,那就是一个dashboard搞的那么复杂,不是不能访问,就是不能登录,要么就是没权限。下面通过自己的一点研究,将问题分析过程记录如下,供参考。如有错误,请拍砖留言。
从两个方面来分析,即:
1.dashboard的安装与访问
2.dashboard的身份验证与授权
首先,我们需要有一个共识就是,k8s这套东西非常的解耦和插件化,它为了能够提供更好的二次开发能力,将很多东西搞的从一个用户角度理解起来很费劲。 比如说用户,k8s里面本身是不能创建用户名这样的实体对象的(API basic,bear token认证所设置的相关用户名不算k8s里面的实体资源对象),它只有role,role定义一系列资源权限,然后把一个sa或者group或者user 映射到这个role上来完成权限分配的关系。 这里为啥突然有个user了呢,这里的user实际上是外部的,就像远程AAA一样,user账号是在外部系统上的。我们知道API server是K8S负责验证的服务,不管是怎么访问都是要经过它,dashboard也一样。那么要想API给你做验证做授权有哪些方法(重要常见的,以下未包含全部)?
- 使用http basic 认证,这要求配置APIserver支持这种方式,并传递一个包含用户名密码的list文件给API,API就像是个普通开启了http basic认证的web server一样,会挑战浏览器,要求输入用户名和密码。 API获取到输入的用户名密码后和之前预置的list里比对,匹配了,则验证通过,系统就知道是谁登陆了,然后根据该user与role的binding关系就知道了该用户拥有了哪些权限
- Bearer token认证,这种认证方式也要求API server服务配置一个token list文件,这个文件里包含了token,token对应着user名称,原理与basic类似,不同的是这种要求在访问请求的header中要始终包含 Authorization: Bearer <token>这个header而已,授权的方式和basic一样
- 证书方式的验证和授权,因为证书里可以包含CN和OU这些信息,CN可以表明一个user身份,OU可以表明一个组身份,如果访问者上送的证书被校验通过,那么证书里的CN和OU就可以用来作为条件去rolebinding里查找,进而就可以授权。当然对于dashboard的访问,截止目前v1.11版本,是还不支持使用证书来验证身份的。
- Serviceaccount, SA严格意义上不算是用户身份认证,这个是给运行在k8s里的具体应用服务(你可以理解为pods)提供访问API的一种方法,因为有很多运行在k8s里pod内的应用是需要访问API的,所以SA不代表人,代表服务,给一个具体的服务比如dashboard,创建一个SA,然后将SA关联到pod上,并把SA与一个role进行rolebinding。当然实际上服务出示给API的是一个SA的token(k8s为每一个SA都自动产生一个token并保存在secret资源里),这个token是通过mount的方式将对应的token secret mount到pod上来实现的。所以这又回了API的token认证,token-->知道哪个SA, SA和哪个role对应,这样就完成了验证和授权
1 2 3 4 5 6 7 8 |
BEI-ML-JLIN-:~ jlin$ kubectl get clusterrolebinding -o wide -n kube-system NAME AGE ROLE USERS GROUPS SERVICEACCOUNTS canal-calico 15h ClusterRole/calico kube-system/canal canal-flannel 15h ClusterRole/flannel kube-system/canal cluster-admin 16h ClusterRole/cluster-admin system:masters system:aws-cloud-provider 16h ClusterRole/system:aws-cloud-provider kube-system/aws-cloud-provider system:basic-user 16h ClusterRole/system:basic-user system:authenticated, system:unauthenticated system:controller:attachdetach-controller 16h ClusterRole/system:controller:attachdetach-controller ku |
上面的rolebinding输出里可以看到一个role关联了 users,groups,以及SAs
说了这么多都是为了背景铺垫,免得下面看起来太费劲。回到上面提到的两个问题:
dashboard的安装与访问
安装比较简单,基本上跑一下官方提供的yaml就可以,这里是官方wiki链接,下面分析下这其中的一些小坑。官方的wiki里大约说了两件事:
- 拿到一个证书和key,放到一个目录下,然后跑个命令产生以下secret
1kubectl create secret generic kubernetes-dashboard-certs --from-file=$HOME/certs -n kube-system
这个命令的作用是为了设置dashboard作为web server,配置的服务器侧证书的,把它存到secret里,然后在pod里mount这个secret,然后作为证书配置配置给pod - 跑一下他们的dashboard yaml文件
1kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml
yaml文件很长,解读一下相关的关键:
A. 它也创建了一个名字叫kubernetes-dashboard-certs的secret, 所以实际上跑yaml时候系统会报相关secret已存在
12345678910# ------------------- Dashboard Secret ------------------- #apiVersion: v1kind: Secretmetadata:labels:k8s-app: kubernetes-dashboardname: kubernetes-dashboard-certsnamespace: kube-systemtype: Opaque
B. 创建了一个sa,名字叫kubernetes-dashboard, 这个sa和一个自定义的kubernetes-dashboard-minimal的role进行了绑定
1234567891011121314151617181920212223# ------------------- Dashboard Service Account ------------------- #apiVersion: v1kind: ServiceAccountmetadata:labels:k8s-app: kubernetes-dashboardname: kubernetes-dashboardnamespace: kube-system---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata:name: kubernetes-dashboard-minimalnamespace: kube-systemroleRef:apiGroup: rbac.authorization.k8s.iokind: Rolename: kubernetes-dashboard-minimalsubjects:- kind: ServiceAccountname: kubernetes-dashboardnamespace: kube-system
C.将kubernetes-dashboard-certs这个secret mount到了container上, 挂到了/certs这个目录上
123volumeMounts:- name: kubernetes-dashboard-certsmountPath: /certs
D. 设置了容器运行命令参数 --auto-generate-certificates,这个是干嘛的呢,是自动产生服务器侧证书的,也就是说这个yaml实际上没有用上面mount进去的证书。
123456789spec:containers:- name: kubernetes-dashboardimage: k8s.gcr.io/kubernetes-dashboard-amd64:v1.8.3ports:- containerPort: 8443protocol: TCPargs:- --auto-generate-certificates
所以,这个时候如果你去访问dashboard,浏览器上显示的证书都是临时签发的证书,并不是/certs里的证书,要想实现是自己指定的证书,需要将自动产生这个参数换掉,换成如下
1234Args:--tls-cert-file=/certs/kubernetes-dashboard.pem--tls-key-file=/certs/kubernetes-dashboard-key.pem--default-cert-dir=/certs
安装完毕后,就该是如何访问了。官方的yaml默认是clusterip模式发布的服务,这样要想访问到dashbaor的界面,要么通过kubectl proxy,要么通过将服务暴露给外部,要么通过API的端口来代理访问,否则外面咋访问呢???
A. Kubectl proxy方式,kubectl proxy是通过在kubectl上暴露一个服务端口,通常为127.0.0.1:8001的非https访问, 你不能在master或者node上跑这个命令,否则除非你的master或者node有图形化的浏览器,不然还是无法看到图形dashboard。所以要在你自己的本地跑这个命令(所以,我擦,好麻烦,所以你还有在本地设置你kubectl的kubeconfig确保本地kubectl可以有权限操作所有)。然后就可以通过本地浏览器访问http://127.0.0.1:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/#!/login 看到界面了。 但是!这个dashboard还是会提示你进行身份认证。。。。不理解为啥,kubectl proxy本身不就希望代理权限吗?不管它,关于这里的认证授权后面再分析。
B 通过nodeport或者ingress来暴露出来服务,这种方式比较容易理解,等于正常发布一个服务,所以将官方的的yaml改为nodeport方式即可。
C API server本身的服务端口代理访问,即通过API的端口来代理访问dashboard,URI类似这样 https://<master-ip>:<apiserver-port>/ 首先,这种访问API服务的方式需要浏览器提供客户端证书作为认证,比如你可以把kubectl所用的那张证书导入到浏览器里,这样访问服务的时候,会弹出让你选择证书,等于就是双向SSL过程。然后就可以看到dashboard界面了,但是同样,dashboard依旧会再次出现登录界面提示身份验证,也就是说这种方式和kubectl proxy一样,只是代理了界面入口,验证权限方面并未代理。
浏览器未出示证书的情况下,访问返回403
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
{ "kind": "Status", "apiVersion": "v1", "metadata": { }, "status": "Failure", "message": "services \"https:kubernetes-dashboard:\" is forbidden: User \"system:anonymous\" cannot get services/proxy in the namespace \"kube-system\"", "reason": "Forbidden", "details": { "name": "https:kubernetes-dashboard:", "kind": "services" }, "code": 403 } |
(这是因为API里启用了RBAC,且默认有个--anonymous-auth=false的参数设定,这意味着匿名用户直接被拒绝验证的。如果将值改为true,虽然可以验证了,但是授权还是通不过,会返回如下401,因为匿名用户没权限)
1 2 3 4 5 6 7 8 9 10 11 |
{ "kind": "Status", "apiVersion": "v1", "metadata": { }, "status": "Failure", "message": "Unauthorized", "reason": "Unauthorized", "code": 401 } |
比如,下图显示浏览器访问6443已经可以获得正常的API权限
但是,当真的访问dashboard时候还是提示认证,下图是没有认证通过,提示的无权限界面的(即在界面上点击skip,这个时候系统直接用在安装yaml文件里自定义的sa kubernetes-dashboard来授权了,这个sa只关联到了自定义的一个minimal的一个资源role,所以会报出很多资源没权限的提示)
也就是说,dashboard不管从哪种方式来访问,其本身的身份验证都是这个服务自己独立的,那么这个服务的验证到底是怎样的?
dashboard身份验证与授权
官方文档对验证做如下表述:
As of release 1.7 Dashboard supports user authentication based on:
Authorization: Bearer <token>
header passed in every request to Dashboard. Supported from release 1.6. Has the highest priority. If present, login view will not be shown.- Bearer Token that can be used on Dashboard login view.
- Username/password that can be used on Dashboard login view.
- Kubeconfig file that can be used on Dashboard login view.
简单的来说,就是只支持两种方法,一种是token,一种是user/password。 kubeconfig凡是只是提供了一种便利,并不是一个新的认证方式,如果要用kubeconfig,里面一样,要么使用username/password,要么使用token。跟踪一下看看,通过API proxy方式的方法来访问 https://172.16.150.100:6443/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/api/v1/login/modes 看到如下输出
1 2 3 4 5 |
{ "modes": [ "token" ] } |
很显然,指明了token方式
为什么这样访问,没权限?https://172.16.150.100:6443/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/api/v1/login/status
1 2 3 4 5 |
{ "tokenPresent": false, "headerPresent": false, "httpsMode": true } |
可以看到,login status已经说明,token也没出示,header方式的携带诸如basic认证的方式的token也没带
所以,对于1.7+之上版本的dashboard来说,验证和授权是dashboard完全自己控制的。
根据上述提示,要想登陆进去,要么用用户名密码,要么用token:
- username/password方式
这种方式需要kube-API配合,配置ABAC授权模式,且配置--basic-auth-file,同时dashboard自身的配置里要启用--authentication-mode=basic 配置 - token方式
这种方式就是在login界面输入一下token,token哪里来能,从系统里的SA账户对应的token来,所以这个SA账户要有足够的API权限。下面的步骤是创建SA并通过clusterrolebinding绑定到了clusterrole/cluster-admin上,这样就有了全部的权限
12345678910111213141516BEI-ML-JLIN-:~ jlin$ kubectl create serviceaccount dashboard-gui -n kube-systemserviceaccount "dashboard-gui" createdBEI-ML-JLIN-:~ jlin$ kubectl get sa -n kube-systemNAME SECRETS AGEcanal 1 22hcoredns 1 22hdashboard-gui 1 11sdefault 1 23hkubernetes-dashboard 1 10hBEI-ML-JLIN-:~ jlin$ kubectl create clusterrolebinding dashboard-gui-rolebind --clusterrole=cluster-admin --serviceaccount=kube-system:dashboard-gui -n kube-systemclusterrolebinding.rbac.authorization.k8s.io "dashboard-gui-rolebind" createdBEI-ML-JLIN-:~ jlin$ kubectl get clusterrolebinding dashboard-gui-rolebind -o wide -n kube-systemNAME AGE ROLE USERS GROUPS SERVICEACCOUNTSdashboard-gui-rolebind 6m ClusterRole/cluster-admin kube-system/dashboard-gui
ok,通过在登录界面输入kubectl describe secret dashboard-gui-token-9l25t -n kube-system ,可以正常访问界面了。
所以,整个dashboard是完全可以通过这样的权限控制(sa,自定义role,role绑定)来实现一个用户在GUI界面上能操作哪些namespace里的哪些资源。
附:
如何利用kubeconfig登录dashboard?
如果只是简单的将诸如kubectl所用的kubeconfig拿来登录,系统会提示:
黄色提示很是误导人,实际原因是因为kubeconfig没有包含token也没有username/pwd, 如果将上面的token放置到kubeconfig文件里,一样就可以登录并有权限了
参考:
https://github.com/kubernetes/dashboard/wiki/Accessing-Dashboard---1.7.X-and-above
https://github.com/kubernetes/dashboard/issues/2474
https://github.com/kubernetes/dashboard/wiki/Access-control
文章评论