人家都在抓神奇寶貝,而我在抓 Kubernetes 封包
Wireshark: Packet Don't Lie.
文章 永續性軟體工程: 遠端抓封包實錄 提到如何遠端抓單體 VM 封包,那同樣的招數可不可以去抓 Azure Kubernetes Service 特定 Pod 裡面的封包? 答案是可以,搭配 ksniff 來做就好
來抓 Kubernetes 封包
架構圖
- 因為我不能直接從家裡連線到 Azure Kuberentes Service,所有操作都需要透過 Bastion VM 進行操作連線,所以需要先用 SSH 連線到 Bastion VM 才能做事
- kubectl 一定要能對 Private AKS 操作,不然 ksniff 或程式你都無法部署下去
- 整張圖我沒提的部分就是 Private AKS 和 VM 的 UDR 設定,但這個太 Azure Networking 先跳過
- 一如既往,我是強烈建議人人都要有一個 debug container 好去做一些觀落陰的事情,詳見 當遇到 Distroless Container 除錯要什麼沒什麼該怎麼辦? 你的好朋友 kubectl debug,這邊不贅述
0. 安裝 ksniff
請直接參考 eldadru/ksniff 文件,另外它的前置作業是要裝 krew,務必要把 krew PATH 設定好
1. 部署 httpbin-re 服務
# 確定 kubectl 運作正常且可以連線到 Private AKS
$ kubectl cluster-info
Kubernetes control plane is running at https://dns-aoai.private.eastus2.azmk8s.io:443
CoreDNS is running at https://dns-aoai.private.eastus2.azmk8s.io:443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
Metrics-server is running at https://dns-aoai.private.eastus2.azmk8s.io:443/api/v1/namespaces/kube-system/services/https:metrics-server:/proxy
# 跑一個 http server 服務起來
$ kubectl apply -f https://raw.githubusercontent.com/pichuang/httpbin-re/master/k8s/httpbin-re.yaml
deployment.apps/httpbin-re created
service/httpbin-re created
# 確認服務有揭露出來
$ kubectl get svc httpbin-rek
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
httpbin-re ClusterIP 10.0.133.240 <none> 8080/TCP 7h26m
主要是要部署一個 HTTP Server,待會要透過 Ksniff 去觀察這個服務的狀況,如果你有其他服務也可以自行替換,這邊就是 Demo 方便
2. 部署陪測用 Pod
# 部署陪測用 Pod,用順手的就好,或者是你可以考慮用 nicolaka/netshoot
$ kubectl apply -f https://raw.githubusercontent.com/pichuang/debug-container/master/debug-container.yaml
pod/debug-container created
# 測試可以跟 httpbin-re 連線,同 namespace,不同 Pod
# kubectl exec --it <debug pod> -- curl http://<svc name>/path
$ kubectl exec --it debug-container -- curl http://httpbin-re:8080 | head -n 5
$ kubectl exec --it debug-container -- curl http://httpbin-re.default:8080 | head -n 5
$ kubectl exec --it debug-container -- curl http://httpbin-re.default.svc:8080 | head -n 5
<!DOCTYPE html>
<html lang="en">
...omit...
# 測試可以跟 metrics-server 連線,不同 namespace,不同 Pod
# kubectl exec --it <debug pod> -- curl --insecure https://<svc name>.<namespace>:<svc port>/path
$ kubectl exec --it debug-container -- curl --insecure https://metrics-server.kube-system:443/metrics | head -n 5
$ kubectl exec --it debug-container -- curl --insecure https://metrics-server.kube-system.svc:443/metrics | head -n 5
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {},
"status": "Failure",
"message": "forbidden: User \"system:anonymous\" cannot get path \"/metrics\"",
"reason": "Forbidden",
"details": {},
"code": 403
}%
關於這個以 Pod 為出發點,服務之間的連線議題,以前有寫過為什麼我佈署的 Kubernetes 服務不會動!? 個人除錯思路分享 一文,裡面有比較仔細的說明,這邊就不贅述了
3. 遠端抓 httpbin-re Pod 的封包
# 列舉出 httpbin-re Pod 的名稱
$ kubectl get pod -n default -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
debug-container 1/1 Running 0 15m `10.0.129.18` aks-agentpool-14864487-vmss000000 <none> <none>
httpbin-re-866499b564-blc95 1/1 Running 0 7h38m 10.0.129.6 aks-agentpool-14864487-vmss000000 <none> <none>
httpbin-re-866499b564-xl726 1/1 Running 0 7h38m `10.0.129.9` aks-agentpool-14864487-vmss000000 <none> <none>
ksniff-grlzn 1/1 Running 0 82s 10.0.129.33 aks-agentpool-14864487-vmss000000 <none> <none>
ksniff-n9ps6 1/1 Running 0 36m 10.0.129.29 aks-agentpool-14864487-vmss000000 <none> <none>
# 透過 ksniff 來抓封包
# ssh <Target username>@<Target IP> -p <Target Port> -i <SSH Private Key if have> "/bin/bash --login -c 'kubectl sniff -n <namespace> <pod name> -p -o -'" | /mnt/c/Program\ Files/Wireshark/Wireshark.exe -k -i -
ssh [email protected] -p 5566 -i ~/.ssh/wolala-rsa "/bin/bash --login -c 'kubectl sniff -n default httpbin-re-866499b564-xl726 -p -o -'" | /mnt/c/Program\ Files/Wireshark/Wireshark.exe -k -i -
這邊要注意的是 /bin/bash --login -c
,因為 SSH 登入的時候吃的 $PATH 預設是不會去吃 ~/.bashrc,可以用 env
驗證看看,所以需要先用 --login
後才能正確咬到 $PATH 裡面的 krew 路徑,其餘就是 pipeline 和 I/O 的串流處理了
此外,原先應該要填 Ethernet Header (12 bytes) 的欄位,會變成 Linux cooked capture v2 (SLL_v2)
,按照 Linux cooked-mode capture (SLL) 的說明,這是一個偽造協議,對分析 Kubernetes 上的服務比較沒什麼影響,因為主要都是看 L3 / L4 層級居多,包含 Pod IP / Pod Port / Service IP / Service Port / Node Port 等,同場加映一下,
HWCHIU 學習筆記 - Kubernetes 之封包去哪兒
4. 清除 ksniff Pod
$ kubectl get pod -l app=ksniff -A
NAME READY STATUS RESTARTS AGE
ksniff-g4bjz 1/1 Running 0 11m
ksniff-j7gnc 1/1 Running 0 16m
ksniff-kxfl5 1/1 Running 0 9m54s
ksniff-lb4xg 1/1 Running 0 6m29s
ksniff-n9ps6 1/1 Running 0 68m
ksniff-sph6l 1/1 Running 0 9m17s
ksniff-v2vd5 1/1 Running 0 14m
ksniff-vl627 1/1 Running 0 16m
$ kubectl delete pod -l app=ksniff -A
pod "ksniff-g4bjz" deleted
pod "ksniff-j7gnc" deleted
pod "ksniff-kxfl5" deleted
pod "ksniff-lb4xg" deleted
pod "ksniff-n9ps6" deleted
pod "ksniff-sph6l" deleted
pod "ksniff-v2vd5" deleted
pod "ksniff-vl627" deleted
退出的時候看起來是不會順便把 ksniff 的 Pod 移除掉,記得要手動移除,不然會有資源浪費的問題
後話
ksniff 沒有參數可以讓我去掐封包大小,沒意外的話,還需要再跳板機多一段 tcpdump pipeline 處理,但這樣就會收到一堆有的沒的封包,還沒想到怎麼用
Q&A
Q1: 如果我是 Red Hat OpenShift Container Platform 或 Azure Red Hat OpenShift 的使用者,但權限鎖超嚴的怎麼辦?
這個只能說 Red Hat 一如既往地已經先幫妳整好到 oc 去了,你不用特別裝 ksniff,權限就是照你拿到的 kubeconfig 為主
$ oc version
Client Version: 4.12.36
Kustomize Version: v4.5.7
Kubernetes Version: v1.27.3
$ oc sniff
Usage:
sniff pod [-n namespace] [-c container] [-f filter] [-o output-file] [-l local-tcpdump-path] [-r remote-tcpdump-path] [flags]
Examples:
kubectl sniff hello-minikube-7c77b68cff-qbvsd -c hello-minikube
Flags:
-c, --container string container (optional)
-x, --context string kubectl context to work on (optional)
-f, --filter string tcpdump filter (optional)
-h, --help help for sniff
--image string the privileged container image (optional)
-i, --interface string pod interface to packet capture (optional) (default "any")
-l, --local-tcpdump-path string local static tcpdump binary path (optional)
-n, --namespace string namespace (optional)
-o, --output-file string output file path, tcpdump output will be redirect to this file instead of wireshark (optional) ('-' stdout)
--pod-creation-timeout duration the length of time to wait for privileged pod to be created (e.g. 20s, 2m, 1h). A value of zero means the creation never times out. (default 1m0s)
-p, --privileged if specified, ksniff will deploy another pod that have privileges to attach target pod network namespace
-r, --remote-tcpdump-path string remote static tcpdump binary path (optional) (default "/tmp/static-tcpdump")
--socket string the container runtime socket path (optional)
--tcpdump-image string the tcpdump container image (optional)
-v, --verbose if specified, ksniff output will include debug information (optional)