关于golang:如何通过抓包来查看Kubernetes-API流量

62次阅读

共计 2768 个字符,预计需要花费 7 分钟才能阅读完成。

当咱们通过 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-xtfrz
done!

这时也能够抓到同样的后果:

基于此,咱们就能够剖析一个 Kubernetes 到底干了什么,也是咱们剖析 Kubernetes​实现的入口。

正文完
 0