Article / 文章中心

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

发布时间:2021-11-22 点击数:619

当我们通过kubectl来查看、修改Kubernetes资源时,有没有想过后面的接口到底是怎样的?有没有办法探查这些交互数据呢?

Kuberenetes客户端和服务端交互的接口,是基于http协议的。所以只需要能够捕捉并解析https流量,我们就能看到kubernetes的API流量。

但是由于kubenetes使用了客户端私钥来实现对客户端的认证,所以抓包配置要复杂一点。具体是如下的结构:

 

如果想了解更多Kubernetes证书的知识,可以看下这篇Kubernetes证书解析的文章

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

配置Charles代理软件

从第一张图可以看出,代理软件的作用有两个:一是接收https流量并转发,二是转发到kubernetes apiserver的时候,使用指定的客户端私钥。

首先配置Charles,让他拦截所有的https流量:

 

然后配置客户端私钥,即对于发送到apiserver的请求,统一使用指定的客户端私钥进行认证:

 

配置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 pod

NAME                    READY   STATUS    RESTARTS   AGE

sc-b-7f5dfb694b-xtfrz   2/2     Running   0          2d20h

我们就可以看到get pod的网络请求了:

 

可以看到,get pod的endpoint是GET /api/v1/namespaces/<namespace>/pods

让我们再尝试下创建pod的请求:

$ cat <<EOF >pod.yaml

apiVersion: v1

kind: Pod

metadata:

  name: nginx-robberphex

spec:

  containers:

  - name: nginx

    image: nginx:1.14.2

EOF

$ kubectl --insecure-skip-tls-verify apply -f pod.yaml

pod/nginx-robberphex created

也同样可以抓到包:

 

创建pod的endpoint是POST /api/v1/namespaces/<namespace>/pods

配置kubenetes client

我们先从写一个用kubernetes go client来获取pod的例子(注意,代码中已经信任所有的证书,所以可以抓到包):

package main

/*

require (

    k8s.io/api v0.18.19

    k8s.io/apimachinery v0.18.19

    k8s.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 *string

    if 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 = nil

    config.TLSClientConfig.Insecure = true

    clientset, 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: %s\n", pod.Name)

    }

 

    fmt.Println("done!")

}

然后编译执行:

$ go build -o kube-client

$ export https_proxy=http://127.0.0.1:8888/

$ ./kube-client

podName: nginx-robberphex

podName: sc-b-7f5dfb694b-xtfrzdone!

这时也可以抓到同样的结果:

 

基于此,我们就可以分析一个Kubernetes到底干了什么,也是我们分析Kubernetes实现的入口。

 

 

版权声明:本文内容转发自阿里云社区,由阿里云实名注册用户自发贡献版权归原作者所有本站不拥有其著作权,亦不承担相应法律责任。如果您发现本中有涉嫌抄袭的内容,请联系站内客服,本将立刻删除涉嫌侵权内容。