网络上很多prometheus 的指标抓取都是基于自建k8s环境,不适用于阿里云ACK专有集群。这里不做源码剖析,通过对prometheus中获取指标的认证机制做解析,目的在于:
1. 进一步理解k8s集群中的kubelet和etcd的tls认证机制,扩展使用curl直接访问apiserver API
2. 灵活处理客户端访问集群中https监听的组件或者pod,
3. 快速定位获取https的metrics指标报错401/403的问题。
前提条件:
- ACK专有集群
- 安装应用市场的ack-prometheus-operator :https://help.aliyun.com/document_detail/94622.html
本文环境基于阿里云ACK专有集群1.22以及ACK应用市场的ack-prometheus-operator chart 12.0.0。
管理组件apiserver/scheduler/kcm pod的TLS认证
一句话总结, prometheus利用pod-serviceaccount-clusterrole-clusterrolebinding使得serviceaccount绑定的secret token可以具备RBAC权限获取到集群中的资源指标信息,sa 的token 会被挂载到pod中被prometheus server进程使用, 从而解决对targest发起pull https metrics接口TLS认证问题。 跨集群监控也可借鉴这种认证方式。
1. 先看prometheus server statefulset "prometheus-ack-prometheus-operator-prometheus"绑定的sa 以及其具备的rbac权限,从clusterrole的定义可以看出,prometheus pod具备足够的权限获取集群中资源的指标。
prometheus 实例pod :prometheus-ack-prometheus-operator-prometheus-0 serviceAccount: ack-prometheus-operator-prometheus secret:ack-prometheus-operator-prometheus-token-79hj5
#省略无关行 apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: annotations: labels: name: ack-prometheus-operator-prometheus rules: - apiGroups: - "" resources: - nodes - nodes/metrics - services - endpoints - pods verbs: - get - list - watch - apiGroups: - networking.k8s.io resources: - ingresses verbs: - get - list - watch - nonResourceURLs: - /metrics - /metrics/cadvisor verbs: - get
其中,获取pod 的/metrics接口起作用的RBAC是:
- nonResourceURLs: - /metrics - /metrics/cadvisor verbs: - get
#以上若缺失,会报错 server returned HTTP status 403 Forbidden
"message": "forbidden: User \"system:serviceaccount:monitoring:ack-prometheus-operator-prometheus\" cannot get path \"/metrics\"",
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: ack-prometheus-operator-prometheus roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: ack-prometheus-operator-prometheus subjects: - kind: ServiceAccount name: ack-prometheus-operator-prometheus namespace: monitoring
2. 获取prometheus sa对应的token:
借地儿练习一下shell,基于sa name获取token:
sa="ack-prometheus-operator-prometheus" token=$(kubectl describe secret -n monitoring $(kubectl describe sa -n monitoring $sa |grep Token |awk '{print $2}') |grep token:|awk '{print $2}')
基于跟sa绑定的secret name获取token:
secret="ack-prometheus-operator-prometheus-token-79hj5" token=$(kubectl describe secret -n monitoring $secret |grep token:|awk '{print $2}')
3. 使用token手动curl https监听端口
先看下阿里云ACK集群每个组件的监听端口:
以下几个进程的https端口获取指标均成功,代表prometheus pod可以基于上述sa配置servicemonitor抓取数据:
//使用curl -k,或者--insecure 可不对服务器的https证书进行检查。
//apiserver curl https://10.0.0.9:6443/metrics -H "Authorization: Bearer $token" -k //kcm curl https://localhost:10257/metrics -H "Authorization: Bearer $token" -k //scheduler curl https://localhost:10259/metrics -H "Authorization: Bearer $token" -k
总结:
以上解析可以理解sa token可以通过pod 的tls认证,那么将serviceaccount 的token mount到prometheus pod中,然后在抓取任务中指定这个token即可完成prometheus server 对 tls 指标的获取。K8s中pod指定的 Serviceaccount对应的token是默认mount 的,此处不举例。
scrape_configs: - job_name: monitoring/xxxx metrics_path: /metrics scheme: https bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token tls_config: insecure_skip_verify: true #略
ACK etcd TLS 认证
ACK etcd 支持自动 TLS 以及通过客户端证书的身份验证, 包括客户端到服务器以及对等(服务器到服务器/集群)的通信。Prometheus访问时,需要使用etcd指定ca认证的client证书,而不可以使用serviceaccount token做认证(etcd不支持)。
先看一下etcd运行参数 证书相关的部分:
/usr/bin/etcd --client-cert-auth --trusted-ca-file=/var/lib/etcd/cert/ca.pem --cert-file=/var/lib/etcd/cert/etcd-server.pem --key-file=/var/lib/etcd/cert/etcd-server-key.pem --peer-client-cert-auth --peer-trusted-ca-file=/var/lib/etcd/cert/peer-ca.pem --peer-cert-file=/var/lib/etcd/cert/10.0.0.9-name-3.pem --peer-key-file=/var/lib/etcd/cert/10.0.0.9-name-3-key.pem
不看代码,如何知道etcd客户端证书在哪儿?我们参考apiserver中配置的etcd client 证书,看看apiserver如何访问etcd的,借鉴即可:
kube-apiserver --etcd-cafile=/etc/kubernetes/pki/etcd/ca.pem --etcd-certfile=/etc/kubernetes/pki/etcd/etcd-client.pem --etcd-keyfile=/etc/kubernetes/pki/etcd/etcd-client-key.pem
其中/etc/kubernetes/pki/etcd中的三个客户端证书文件(CA证书ca.pem和签名密钥对c)等效果于/var/lib/etcd/cert/中的client 证书。
[root@ack ~]# cd /var/lib/etcd/cert/ [root@ack cert]# ls 10.0.0.7-name-1.csr 10.0.0.8-name-2.csr 10.0.0.9-name-3.csr ca-config.json ca.pem etcd-client.pem etcd-server.pem peer-ca-key.pem 10.0.0.7-name-1-key.pem 10.0.0.8-name-2-key.pem 10.0.0.9-name-3-key.pem ca.csr etcd-client.csr etcd-server.csr peer-ca-config.json peer-ca.pem 10.0.0.7-name-1.pem 10.0.0.8-name-2.pem 10.0.0.9-name-3.pem ca-key.pem etcd-client-key.pem etcd-server-key.pem peer-ca.csr [root@ack cert]# cd /etc/kubernetes/pki/etcd [root@ack etcd]# ls ca.pem etcd-client-key.pem etcd-client.pem
借鉴apiserver访问etcd方式,客户端到服务器端通讯,prometheus获取etcd metrics可用curl模拟如下:
curl --cacert /etc/kubernetes/pki/etcd/ca.pem --cert /etc/kubernetes/pki/etcd/etcd-client.pem --key /etc/kubernetes/pki/etcd/etcd-client-key.pem -X GET https://10.0.0.9:2379/metrics
总结:
以上解析中可以看到客户端证书可以获取指标了,则etcd客户端证书可以被打包到k8s secret中, mount 到 prometheus server pod里。
volumeMounts: - mountPath: /var/run/secrets/kubernetes.io/k8s-certs/etcd/ name: etcd-certs volumes: - name: etcd-certs secret: secretName: etcd-certs
配置抓取任务的时候,指定prometheus server pod 里证书目录即可。
- job_name: 'k8s-etcd-yibei' scheme: https tls_config: ca_file: /var/run/secrets/kubernetes.io/k8s-certs/etcd/ca.pem cert_file: /var/run/secrets/kubernetes.io/k8s-certs/etcd/etcd-client.pem key_file: /var/run/secrets/kubernetes.io/k8s-certs/etcd/etcd-client-key.pem #略
ACK kubelet 的TLS 认证
ACK集群中kubelet默认开启https端口10250和只读端口10255,ack-prometheus采集kubelet指标走的http端口10255,无须tls认证。部分客户对安全性要求高禁止了10255,本文扩展讲解一下10250端口的tls认证。
小知识:--read-only-port=0 会关闭http监听,可以通过这个参数关闭或者修改只读端口,默认10255.
vi /etc/kubernetes/kubelet-customized-args.conf # KUBELET_CUSTOMIZED_ARGS="--read-only-port=0 ..." systemctl daemon-reload systemctl restart kubelet
基于 kubelet认证的官方介绍,kubelet的tls认证分三种:
- 匿名anonymous认证:默认禁止,比较简单,允许后不加证书即可访问https端口,所以非常不安全,不在本文赘述。
- 基于客户端证书认证:类似etcd tls认证,比如api-server访问kubelet使用的就是客户端证书方式。但是kubelet默认开启了证书轮转更新,而且客户端证书拥有全部权限,可以访问kubelet所有的接口。出于安全考虑,prometheus不采用这种方式,客户端认证本文不赘述。
- 基于SA bearer token认证:kubelet 也支持bearer token认证,通过service account的rbac绑定,可以限制权限仅开放/metrics接口,比较安全,prometheus访问kubelet就是采用这种方式,下文详细解析。
基于SA bearer token 访问kubelet tls端口的解析
看下kubelet https认证参数的官方解释:
--authentication-token-webhook=true This flag enables, that a ServiceAccount token can be used to authenticate against the kubelet(s). This can also be enabled by setting the kubelet configuration value authentication.webhook.enabled to true. --authorization-mode=Webhook This flag enables, that the kubelet will perform an RBAC request with the API to determine, whether the requesting entity (Prometheus in this case) is allowed to access a resource, in specific for this project the /metrics endpoint. This can also be enabled by setting the kubelet configuration value authorization.mode to Webhook.
ACK集群kubelet默认启动参数如下,以下两个参数的配置即开启了token 认证方式(webhook)。
--authorization-mode=Webhook --authentication-token-webhook=true
kubelet 允许token 访问后,接下来看下token绑定的clusterrole中是否具备的nodes/metrics资源的相关权限 。
#省略无关行 apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: ack-prometheus-operator-prometheus rules: - apiGroups: - "" resources: - nodes/metrics verbs: - get - list - watch
curl模拟命令成功获取指标:
sa="ack-prometheus-operator-prometheus" token=$(kubectl describe secret -n monitoring $(kubectl describe sa -n monitoring $sa |grep Token |awk '{print $2}') |grep token:|awk '{print $2}') curl https://10.0.0.9:10250/metrics -H "Authorization: Bearer $token" -k
实验1: 测试RBAC授权
若clusterrole中缺少nodes/metrics资源的get权限,会看到报错
Forbidden (user=system:serviceaccount:monitoring:ack-prometheus-operator-prometheus, verb=get, resource=nodes, subresource=metrics)
同理,换一个没RBAC授权的接口/configz 访问也会报错 :
curl
https://10.0.0.9:10250/configz
-H "Authorization: Bearer $token" -k
Forbidden (user=system:serviceaccount:monitoring:ack-prometheus-operator-prometheus, verb=get, resource=nodes, subresource=proxy)
实验2:关闭kubelet webhook认证
关闭这俩 webhook参数:
vi /etc/systemd/system/kubelet.service.d/10-kubeadm.conf Environment="KUBELET_AUTHZ_ARGS=--authorization-mode=Webhook --authentication-token-webhook=true --anonymous-auth=false --client-ca-file=/etc/kubernetes/pki/ca.crt" 修改为 Environment="KUBELET_AUTHZ_ARGS= --authentication-token-webhook=false --anonymous-auth=false --client-ca-file=/etc/kubernetes/pki/ca.crt" 重启 systemctl daemon-reload systemctl restart kubelet
此时就可以模拟出kubelet https端口401/403了:
总结:
以上解析可以理解sa token可以通过kubelet 的tls认证,那么将serviceaccount 的token mount到prometheus server pod中,然后在抓取任务中指定这个token即可完成prometheus server 对 tls 指标的获取。K8s中pod指定的 Serviceaccount对应的token是默认mount 的,此处不举例。
scrape_configs: - job_name: monitoring/kubelet-xxxx metrics_path: /metrics scheme: https bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token tls_config: insecure_skip_verify: true #略
扩展:如何curl连接apiserver
既然可以使用serviceaccount的RBAC认证获取apiserver metrics数据,那么,直接向apiserver请求数据呢?
一样的道理,给SA足够BAC即可。我们借用默认的admin权限的SA: kube-system/admin.
SERVICE_ACCOUNT=admin SECRET=$(kubectl get serviceaccount ${SERVICE_ACCOUNT} -n kube-system -o json | jq -Mr '.secrets[].name | select(contains("token"))') TOKEN=$(kubectl get secret ${SECRET} -n kube-system -o json | jq -Mr '.data.token' | base64 -d) kubectl get secret ${SECRET} -n kube-system -o json | jq -Mr '.data["ca.crt"]' | base64 -d > /tmp/ca.crt APISERVER=https://10.0.0.9:6443 APISERVER=$(kubectl config view | grep server | cut -f 2- -d ":" | tr -d " ") # 带证书访问--cacert /tmp/ca.crt curl -s $APISERVER/apis --header "Authorization: Bearer $TOKEN" --cacert /tmp/ca.crt #insecure访问 -k也可以 curl -s -X GET $APISERVER/apis/metrics.k8s.io/v1beta1/nodes --header "Authorization: Bearer $TOKEN" -k