如何通过抓包来查看Kubernetes API流量?

当我们通过kubectl来查看、修改Kubernetes资源时 , 有没有想过后面的接口到底是怎样的?有没有办法探查这些交互数据呢?
Kuberenetes客户端和服务端交互的接口 , 是基于http协议的 。所以只需要能够捕捉并解析https流量 , 我们就能看到kubernetes的API流量 。
但是由于kubenetes使用了客户端私钥来实现对客户端的认证 , 所以抓包配置要复杂一点 。具体是如下的结构:

如何通过抓包来查看Kubernetes API流量?

文章插图
 
如果想了解更多Kubernetes证书的知识 , 可以看下这篇Kubernetes证书解析的文章 [1]
从kubeconfig中提取出客户端证书和私钥kubeconfig中包含了客户端的证书和私钥 , 我们首先要把它们提取出来:
# 提取出客户端证书grep client-certificate-data ~/.kube/config |awk '{ print $2 }' |base64 --decode > client-cert.pem# 提取出客户端私钥grep client-key-data ~/.kube/config |awk '{ print $2 }' |base64 --decode > client-key.pem# 提取出服务端CA证书grep certificate-authority-data ~/.kube/config |awk '{ print $2 }' |base64 --decode > cluster-ca-cert.pem参考自 Reddit [2]
配置Charles代理软件【如何通过抓包来查看Kubernetes API流量?】从第一张图可以看出 , 代理软件的作用有两个:一是接收https流量并转发 , 二是转发到kubernetes apiserver的时候 , 使用指定的客户端私钥 。
首先配置Charles , 让它拦截所有的https流量:
如何通过抓包来查看Kubernetes API流量?

文章插图
 
然后配置客户端私钥 , 即对于发送到apiserver的请求 , 统一使用指定的客户端私钥进行认证:
如何通过抓包来查看Kubernetes API流量?

文章插图
 
配置kubectl需要抓包kubectl的流量 , 需要两个条件:1. kubectl使用Charles作为代理 , 2. kubectl需要信任Charles的证书 。
# Charles的代理端口是8888 , 设置https_proxy环境变量 , 让kubectl使用Charles代理$ export https_proxy=http://127.0.0.1:8888/# insecure-skip-tls-verify表示不校验服务端证书$ kubectl --insecure-skip-tls-verify get podNAMEREADYSTATUSRESTARTSAGEsc-b-7f5dfb694b-xtfrz2/2Running02d20h我们就可以看到 get pod 的网络请求了:
如何通过抓包来查看Kubernetes API流量?

文章插图
 
可以看到 , get pod的endpoint是 GET /api/v1/namespaces/<namespace>/pods  。
让我们再尝试下创建pod的请求:
$ cat <<EOF >pod.yamlapiVersion: v1kind: Podmetadata:name: Nginx-robberphexspec:containers:- name: nginximage: nginx:1.14.2EOF$ kubectl --insecure-skip-tls-verify Apply -f pod.yamlpod/nginx-robberphex created也同样可以抓到包:
如何通过抓包来查看Kubernetes API流量?

文章插图
 
创建pod的endpoint是 POST /api/v1/namespaces/<namespace>/pods
配置kubenetes client我们先从写一个用kubernetes go client来获取pod的例子(注意 , 代码中已经信任所有的证书 , 所以可以抓到包):
package main/*require (k8s.io/api v0.18.19k8s.io/apimachinery v0.18.19k8s.io/client-go v0.18.19)*/import ("context""flag""fmt""path/filepath"apiv1 "k8s.io/api/core/v1"metav1 "k8s.io/apimachinery/pkg/apis/meta/v1""k8s.io/client-go/kubernetes""k8s.io/client-go/tools/clientcmd""k8s.io/client-go/util/homedir")func main() {ctx := context.Background()var kubeconfig *stringif home := homedir.HomeDir(); home != "" {kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")} else {kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")}flag.Parse()config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)if err != nil {panic(err)}// 让clientset信任所有证书config.TLSClientConfig.CAData = https://www.isolves.com/it/wl/js/2021-11-23/nilconfig.TLSClientConfig.Insecure = trueclientset, err := kubernetes.NewForConfig(config)if err != nil {panic(err)}podClient := clientset.CoreV1().Pods(apiv1.NamespaceDefault)podList, err := podClient.List(ctx, metav1.ListOptions{})if err != nil {panic(err)}for _, pod := range podList.Items {fmt.Printf("podName: %sn", pod.Name)}fmt.Println("done!")}然后编译执行:
$ go build -o kube-client$ export https_proxy=http://127.0.0.1:8888/$ ./kube-clientpodName: nginx-robberphexpodName: sc-b-7f5dfb694b-xtfrzdone!


推荐阅读