kubelet也有身份认证和鉴权部分, 比apiserver只是少了准入控制。
它的鉴权部分是通过webhook请求apiserver实现的。
一、身份认证
可以同时开启多个。
我觉得证书认证与webhook
的认证顺序是, 先 webhook
没有通过 再证书认证
。 因为metrics-server通过webhook认证, 而这个用户的传递除了证书想不到其它方式,如果在证书认证
那里被拒绝了,就无法认证了。
但是不知道怎么验证。
1、匿名访问
--anonymous-auth
:这个参数,默认是打开的。
没有被其它的认证方法拒绝的访问就是匿名访问。
--anonymous-auth=false
禁用匿名访问。
匿名访问的用户名是system:anonymous
, 用户组是 system:unauthenticated
。 用于后面的授权。
2、证书认证
--client-ca-file
: 验证客户端证书,跟apiserver的--client-ca-file
参数一个意思。
这个参数打开,并且匿名访问关闭,apiserver需要添加--kubelet-client-certificate
与--kubelet-client-key
参数
不然apiserver就会无法访问kubelet
, kubect logs
、kubectl exec
之类的命令就会用不了。 如:
[root@k8smaster1 bin]# kubectl exec pod/mynginx-75b67b9f8-qbtvh /bin/bash -it
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
error: unable to upgrade connection: Unauthorized
apiserver添加参数指定证书就可以了:
--kubelet-client-certificate=/opt/kubernetes/cert/kubelet/apiserver_to_kubelet.crt
--kubelet-client-key=/opt/kubernetes/cert/kubelet/apiserver_to_kubelet.key
使用curl的时候遇到一个坑,可能也不是坑,只是我才发现。指定证书的时候需要绝对路径,不然就是这个样子。跟没有指定证书一样。
[root@k8smaster1 kubelet]# curl --cacert /root/kubectl/cacert.pem --cert apiserver_to_kubelet.crt --key apiserver_to_kubelet.key https://k8snode1:10250/metrics -I
HTTP/1.1 401 Unauthorized
Date: Thu, 17 Dec 2020 10:02:34 GMT
Content-Length: 12
Content-Type: text/plain; charset=utf-8
[root@k8smaster1 kubelet]# curl --cacert /root/kubectl/cacert.pem https://192.168.1.231:10250/metrics
Unauthorized[root@k8smaster1 kubelet]#
而这样就没有问题:
[root@k8smaster1 kubelet]# curl --cacert /root/kubectl/cacert.pem --cert /opt/kubernetes/cert/kubelet/apiserver_to_kubelet.crt --key /opt/kubernetes/cert/kubelet/apiserver_to_kubelet.key https://k8snode1:10250/metrics -I
HTTP/1.1 200 OK
Content-Type: text/plain; version=0.0.4; charset=utf-8
Date: Thu, 17 Dec 2020 10:03:18 GMT
3、webhook
向apiserver请求用户是否存在。
因为apiserver里面只有serviceaccount是集群内的用户, 证书之类的都是外部用户,集群查不到。 所以这个只能是客户端使用了, kubectl命令无法走这个。
--authentication-token-webhook
kubelet添加这个参数。如:
--authentication-token-webhook
只要加上这个参数就可以了。
metrics-server
访问 kubelet
的时候好像用的就是这种方式。 所以在匿名关闭的情况下这个认证还是要打开的。
测试:
curl --cacert /root/kubectl/cacert.pem https://192.168.1.231:10250/metrics -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Il............."
这个token是serviceaccount里的。
不知道token文件的行不行,没有测试过。
二、鉴权
--authorization-mode
默认是AlwaysAllow
, 任何通过身份认证的访问,可以执行任意请求。
要细分权限,还是需要apiserver来认证,修改这个参数为Webhook
:
--authorization-mode=Webhook
kubelet会把用户发给apiserver做鉴权。
注意,这里kubelet只是把用户名发给了apiserver,让apiserver看看用户是否有这个全权限。
所以不需要考虑访问kubelet的证书在apiserver那里是否可以通过。只要在kubelet这里可以通过就行了。apiserver那里仅仅只是对用户做个鉴权。
webhook认证方式:
https://kubernetes.io/zh/docs/reference/access-authn-authz/authentication/#webhook-token-authentication
里面的kubeconfig不用看,因为在这里就是kubelet启动时使用的kubeconfig。
注意缓存
kubelet对于使用webhook请求返回的结果, 是有缓存时间的。 在测试的过程中要注意。在刚创建完权限后,如果还不能访问,可能还在失败缓存里。
分别是这俩参数
--authorization-webhook-cache-authorized-ttl # webhook认证成功的缓存时间,默认 5m0s。
--authorization-webhook-cache-unauthorized-ttl # 失败的缓存时间,默认30s。
访问流程
执行kubectl logs之后, apiserver访问kubelet的过程是这个样子
第一步apiserver访问kubelet,在这里的apiserver只是作为客户端,跟使用curl命令访问是一样的。
第二步kubelet向apiserver认证用户权限,
第三步apiserver回应给kubelet,
第四步kubelet回应给apiserver数据。 第四步图上忘了加。
可能会有疑惑。为什么apiserver去访问的,kubelet却还要向apiserver确认权限?
要知道http访问是没有状态的。
apiserver在访问的时候,对于kubelet来说只是一个客户端,不知道你的身份。
只有我访问的我才知道是apiserver。
这个认证方式在k8s的聚合层,添加外部api时还可以看到。
客户端提供访问的证书或者匿名访问, 在通过kubelet的身份认证以后,
kubelet会把证书里的用户或者匿名访问的用户发给apiserver。
匿名访问的用户名:system:unauthenticated
匿名访问的组: system:unauthenticated
跟apiserver上的匿名访问的用户和组的名称是一样的。
内置的角色
集群已经内置了一个角色, 包含了kubelet可以提供给客户端访问的所有权限。
clusterrole/system:kubelet-api-admin
可以根据这个了解所有权限,然后自定义角色,或者直接绑定这个角色。
[root@k8smaster1 k8s]# kubectl get clusterrole/system:kubelet-api-admin -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
......省略......
rules:
- apiGroups:
- ""
resources:
- nodes
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- nodes
verbs:
- proxy
- apiGroups:
- ""
resources:
- nodes/log
- nodes/metrics
- nodes/proxy
- nodes/spec
- nodes/stats
verbs:
- '*'
匿名访问
在kubelet匿名访问开启,证书认证关闭的情况下:
[root@k8smaster1 k8s]# kubectl logs -f pod/mynginx-75b67b9f8-qbtvh
Error from server (Forbidden): Forbidden (user=system:anonymous, verb=get, resource=nodes, subresource=proxy) ( pods/log mynginx-75b67b9f8-qbtvh)
使用curl命令:
[root@k8smaster1 kubelet]# curl --cacert /root/kubectl/cacert.pem https://k8snode1:10250/metrics
Forbidden (user=system:anonymous, verb=get, resource=nodes, subresource=metrics)[root@k8smaster1 kubelet]#
需要给用户system:anonymous
或者组system:unauthenticated
设置相应的权限。
[root@k8smaster1 k8s]# kubectl create clusterrolebinding anonymous_kubelet --clusterrole=system:kubelet-api-admin --user=system:anonymous
clusterrolebinding.rbac.authorization.k8s.io/anonymous_kubelet created
然后就没有问题了:
[root@k8smaster1 k8s]# kubectl logs -f pod/mynginx-75b67b9f8-qbtvh
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
3、证书访问
kubelet:
--client-ca-file
参数需要加上,认证客户端证书。如:
--client-ca-file=/opt/kubernetes/cert/kubelet_ca.pem
apiserver:
--kubelet-client-certificate
与--kubelet-client-key
参数提供访问kubelet的证书。如:
--kubelet-client-certificate=/opt/kubernetes/cert/kubelet/apiserver_to_kubelet.crt \
--kubelet-client-key=/opt/kubernetes/cert/kubelet/apiserver_to_kubelet.key \
访问测试:
[root@k8smaster1 k8s]# kubectl logs -f pod/mynginx-75b67b9f8-qbtvh
Error from server (Forbidden): Forbidden (user=apiserver_to_kubelet, verb=get, resource=nodes, subresource=proxy) ( pods/log mynginx-75b67b9f8-qbtvh)
[root@k8smaster1 k8s]#
curl命令:
[root@k8smaster1 kubelet]# curl --cacert /root/kubectl/cacert.pem --cert /opt/kubernetes/cert/kubelet/apiserver_to_kubelet.crt --key /opt/kubernetes/cert/kubelet/apiserver_to_kubelet.key https://192.168.1.231:10250/metrics
Forbidden (user=apiserver_to_kubelet, verb=get, resource=nodes, subresource=metrics)[root@k8smaster1 kubelet]#
apiserver_to_kubelet
就是证书里的用户,同样的给个权限就可以了。
注意
这里要注意,这个证书是apiserver发给kubelet, 然后kubelet取出证书里的用户信息,发给apiserver做鉴权, 所以这里要给这个用户权限。 而不是给apiserver自己使用的证书的权限,虽然转了一圈回来都是一个东西,但是这个流程不能搞错了。
[root@k8smaster1 k8s]# kubectl create clusterrolebinding apiserver_to_kubelet --clusterrole=system:kubelet-api-admin --user=apiserver_to_kubelet
clusterrolebinding.rbac.authorization.k8s.io/apiserver_to_kubelet created
[root@k8smaster1 k8s]# kubectl logs -f pod/mynginx-75b67b9f8-qbtvh
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
我这里的证书跟kubelet的CA证书是单独的一套CA, 与apiserver里检测客户端证书的CA都不是一套。 但是没问题,客户端到kubelet是一个阶段。 kubelet请求apiserver做认证是另一个阶段。