Loading

用 tcpdump 对指定 k8s pod 抓包

环境要求

  • 执行命令的主机可以使用 kubectl 命令。

  • 执行命令的主机可以通过 ssh (使用当前用户名)访问容器所在的主机,或者执行命令的主机本身就是容器所在的主机。

  • 容器所在的主机可以使用 tcpdumpdocker/crictl 命令,并且当前用户有权限执行这些命令。

创建脚本 sniff

#!/usr/bin/env bash

set -euxo pipefail

NAMESPACE=${1}; shift
POD=${1}; shift

eval "$(kubectl get pod \
    --namespace "${NAMESPACE}" \
    "${POD}" \
    --output=jsonpath="{.status.containerStatuses[0].containerID}{\"\\000\"}{.status.hostIP}" \
    | xargs -0 bash -c 'printf "${@}"' -- 'CONTAINER_ID=%q\nHOST_IP=%q')"


if [[ ${CONTAINER_ID} == 'docker://'* ]]; then
    CONTAINER_ENGINE=docker
    CONTAINER_ID=${CONTAINER_ID#'docker://'}
elif [[ ${CONTAINER_ID} == 'containerd://'* ]]; then
    CONTAINER_ENGINE=containerd
    CONTAINER_ID=${CONTAINER_ID#'containerd://'}
fi

if [[ -z $(ip address | sed -n "s/inet ${HOST_IP}\//found/p") ]]; then
    SHELL_COMMAND='eval ssh "${HOST_IP}" bash -euxo pipefail -'
else
    SHELL_COMMAND='source /dev/stdin'
fi

${SHELL_COMMAND} <<EOF
PATH=\${PATH}:/usr/local/bin

if [[ ${CONTAINER_ENGINE@Q} == docker ]]; then
    PID=\$(docker inspect --format '{{.State.Pid}}' ${CONTAINER_ID@Q})
elif [[ ${CONTAINER_ENGINE@Q} == containerd ]]; then
    PID=\$(crictl inspect --output go-template --template '{{.info.pid}}' ${CONTAINER_ID@Q})
fi
IF_NO=\$(<"/proc/\${PID}/root/sys/class/net/eth0/iflink")
IF=\$(ip link | sed -n "s/^\${IF_NO}: \([^@]\+\).*$/\1/p")

tcpdump -i "\${IF}" ${@@Q}
EOF

使用

./sniff <NAMESPACE> <POD> [TCPDUMP ARG]...
# 例子:./sniff kubernetes-dashboard kubernetes-dashboard-7c4b498cb4-slkk8 -U -w -

原理

  • 容器网络通常由 veth-pair 实现,和 socketpair 一样有两个网络接口,两个 veth 接口分别设置在主机 network namespace 侧和容器 network namespace 侧,两个 veth 接口对应一个唯一“编号”,只要得到容器内(network namespace)其中一个 veth 的“编号”,就可以根据“编号”在主机侧找到另一个的 veth 接口。然后在主机侧用 tcpdump 抓取这个 veth 接口的包就行了。

  • 如果知道容器在主机上的 pid,容器内 veth 的接口“编号”可以在主机上执行命令 cat /proc/<PID>/root/sys/class/net/eth0/iflink 得到。

  • 如果知道容器的 ID,容器在主机上的 pid 可以在主机上执行命令 docker inspectcrictl inspect 得到。

  • kubectl get pod 可以得到 Pod 容器的 ID,以及容器所在的主机 IP。

注意:一个 pod 可能包含多个容器,这些容器共享共同一个 network namespace,在 veth 上抓包会抓取 pod 内所有容器的流量

posted @ 2021-05-22 19:04  roy2220  阅读(1617)  评论(2编辑  收藏  举报