当咱们通过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 podNAME                    READY   STATUS    RESTARTS   AGEsc-b-7f5dfb694b-xtfrz   2/2     Running   0          2d20h

咱们就能够看到get pod的网络申请了:

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

让咱们再尝试下创立pod的申请:

$ cat <<EOF >pod.yamlapiVersion: v1kind: Podmetadata:  name: nginx-robberphexspec:  containers:  - name: nginx    image: nginx:1.14.2EOF$ kubectl --insecure-skip-tls-verify apply -f pod.yamlpod/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-clientpodName: nginx-robberphexpodName: sc-b-7f5dfb694b-xtfrzdone!

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

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