天天看點

如何通過抓包來檢視Kubernetes API流量從kubeconfig中提取出用戶端證書和私鑰配置Charles代理軟體配置kubectl配置kubenetes client

當我們通過kubectl來檢視、修改Kubernetes資源時,有沒有想過後面的接口到底是怎樣的?有沒有辦法探查這些互動資料呢?

Kuberenetes用戶端和服務端互動的接口,是基于http協定的。是以隻需要能夠捕捉并解析https流量,我們就能看到kubernetes的API流量。

但是由于kubenetes使用了用戶端私鑰來實作對用戶端的認證,是以抓包配置要複雜一點。具體是如下的結構:

如何通過抓包來檢視Kubernetes API流量從kubeconfig中提取出用戶端證書和私鑰配置Charles代理軟體配置kubectl配置kubenetes client
如果想了解更多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流量:

如何通過抓包來檢視Kubernetes API流量從kubeconfig中提取出用戶端證書和私鑰配置Charles代理軟體配置kubectl配置kubenetes client

然後配置用戶端私鑰,即對于發送到apiserver的請求,統一使用指定的用戶端私鑰進行認證:

如何通過抓包來檢視Kubernetes API流量從kubeconfig中提取出用戶端證書和私鑰配置Charles代理軟體配置kubectl配置kubenetes client

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

的網絡請求了:

如何通過抓包來檢視Kubernetes API流量從kubeconfig中提取出用戶端證書和私鑰配置Charles代理軟體配置kubectl配置kubenetes client

可以看到,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           

也同樣可以抓到包:

如何通過抓包來檢視Kubernetes API流量從kubeconfig中提取出用戶端證書和私鑰配置Charles代理軟體配置kubectl配置kubenetes client

建立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 API流量從kubeconfig中提取出用戶端證書和私鑰配置Charles代理軟體配置kubectl配置kubenetes client

基于此,我們就可以分析一個Kubernetes到底幹了什麼,也是我們分析Kubernetes​實作的入口。

本文首發于

https://robberphex.com/how-to-inspect-kubernetes-api/