kubelet 认证鉴权

大番茄 2020年12月16日 3,725次浏览

https://kubernetes.io/zh/docs/reference/command-line-tools-reference/kubelet-authentication-authorization/

kubelet也有身份认证和鉴权部分, 比apiserver只是少了准入控制。
它的鉴权部分是通过webhook请求apiserver实现的。

一、身份认证

https://kubernetes.io/zh/docs/reference/command-line-tools-reference/kubelet-authentication-authorization/#kubelet-%E8%BA%AB%E4%BB%BD%E8%AE%A4%E8%AF%81

可以同时开启多个。


我觉得证书认证与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 logskubectl 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文件的行不行,没有测试过。


二、鉴权

https://kubernetes.io/zh/docs/reference/command-line-tools-reference/kubelet-authentication-authorization/#kubelet-%E9%89%B4%E6%9D%83

--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的过程是这个样子
image.png

第一步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做认证是另一个阶段。