Please enable Javascript to view the contents

世事洞明皆學問 - tcpdump 抓取 Istio 流量真心话

 ·  ☕ 4 分钟

image-20221218113505437

图片来源: Rowan Atkinson Goes to Battle in ‘Man vs. Bee’

本文摘自我的开源书《Istio & Envoy 内幕》 中的 Istio 端口 与 组件。如图片不清,可回到原文。

最近,需要在 mTLS 的 Istio 1.12 环境下定位一些 “神奇” 的网络问题。经历过一些 tcpdump 抓取 Istio 流量的折腾,被迫又复习了一次 Istio 的网络魔术,固记录于本文。

前言

无论你承认与否,我们习惯是缘用旧方法或工具去解决新问题。就算已经到达 Cloud Natvie, Service Mesh 时代,人们也会默认继续用 tcpdump 去定位网络问题。这有问题吗?或者有,或者没有,我没答案。

tcpdump 的历史

https://en.wikipedia.org/wiki/Tcpdump

tcpdump was originally written in 1988 by Van Jacobson, Sally Floyd, Vern Paxson and Steven McCanne who were, at the time, working in the Lawrence Berkeley Laboratory Network Research Group. By the late 1990s there were numerous versions of tcpdump distributed as part of various operating systems, and numerous patches that were not well coordinated. Michael Richardson (mcr) and Bill Fenner created www.tcpdump.org in 1999.

对读者的假设

假设读者已经了解过 Istio。想看看其 POD 内的流量魔术,以在排坑时有一定套路和理论依据。

互动图片

📢 本文的正常打开方法是,点击 “用 Draw.io 打开” 后,进入互动图片状态。本文的大部分内容是放在图中了。看图比文字更重要。

Istio 的网络魔术预览

原始的我是个喜欢说话直接了当的人(虽然社会已经迫我学习和爱上绕圈子)。这里先直接贴上我的研究结果图,后面会分析。

图:Istio端口与组件

图:Istio端口与组件

用 Draw.io 打开

tcpdump 抓包方法

这里先给个结论。后面再讲原理和绕圈子。

Sidecar 抓包

图:Istio端口与组件 中包含了一些在 Istio 中时行 tcpdump 的说明。在此不再赘述。要补充一下是的 tcpdump 的抓包点。因为,这个点影响了,tcpdump 的过滤条件与输出。主要是这个点与 iptables 的 redirect 规则生效的前后问题。

tcpdump capture pinpoint:

tcpdump will see inbound traffic before iptables, but will see
outbound traffic only after the firewall has processed it.

As a matter of fact, tcpdump is the first software found after the wire (and the NIC, if you will) on the way IN, and the last one on the way OUT.

  • Wire -> NIC -> tcpdump -> netfilter/iptables
  • iptables -> tcpdump -> NIC -> Wire
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
######## sidecar ########

sudo nsenter -t $PID_OF_SIDECAR_ENVOY -n -u

export ETH0_IP=$(ip addr show eth0 | grep "inet\b" | awk '{print $2}' | cut -d/ -f1)
export LOCAL_IP=$(ip addr show lo | grep "inet\b" | awk '{print $2}' | cut -d/ -f1)

# inbound mTLS
sudo tcpdump -i eth0 -n -vvvv  "(dst port 8080 and dst $ETH0_IP) or (src port 8080 and src $ETH0_IP)"
# inbound 明文
sudo tcpdump -i lo -n -vvvv -A "(dst port 8080 and dst $ETH0_IP) or (src port 8080 and src $ETH0_IP)"

# outbound 明文
sudo tcpdump -i lo -n -vvvv -A  "((dst port 15001 and dst 127.0.0.1) or (dst portrange 20000-65535 and dst $ETH0_IP))"
# outbound mTLS
sudo tcpdump -i eth0 -n -vvvv -A  "((src portrange 20000-65535 and src $ETH0_IP) or (dst portrange 20000-65535 and dst $ETH0_IP))"

有一点比较麻烦的是,outbound 明文抓包,出向 ip packet 抓到的是 redirect 后的 127.0.0.1,入向 ip packet 抓到的是未 redirect 前的 ip 地址。如果你用 Wireshark 等工具分析。是无法 Follow TCP Stream 的。

Istio Gateway 抓包

一般,Istio Gateway 的 upsteam (Cluster 内部),与 downsteam(Cluster 外部)会在不同的 subnet,所以,可以用 CIDR 去区分。

首先,看看 kubernetes cluster 的 pod 的 cidr 范围:

1
2
ps -ef | grep cidr
root      48587  20177  0 Dec08 ?        00:21:25 kube-controller-manager ... --cluster-cidr=192.168.0.0/12 ...--service-cluster-ip-range=10.96.0.0/12 ...

这时,如果尝试直接使用上面的参数会出错:

1
2
$ sudo tcpdump -i br0 -vvvv -A  net 192.168.0.0/12
tcpdump: non-network bits set in "192.168.0.0/12"

发现,tcpdump 对 cidr 的格式要求比较严格,要求用首个可用 ip 段。使用 https://cidr.xyz/ 分析出 192.168.0.0/12 的首个可用 ip 为 192.160.0.1,固:

1
sudo tcpdump -i br0 -vvvv -A  net 192.160.0.0/12

Istio 的网络魔术

监听端口

可以用以下方式查看监听的端口:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ nsenter -n -t $PID_OF_ENVOY
$ ss -ln

u_str    LISTEN     etc/istio/proxy/SDS 34782                        * 0            users:(("pilot-agent",pid=3406,fd=13))                                             
u_str    LISTEN     etc/istio/proxy/XDS 34783                        * 0            users:(("pilot-agent",pid=3406,fd=16))                                             
u_str    ESTAB      etc/istio/proxy/XDS 1379729                      * 1379728      users:(("pilot-agent",pid=3406,fd=8))                                              
u_str    ESTAB                        * 1379728                      * 1379729      users:(("envoy",pid=3555,fd=37))                                                   
u_str    ESTAB      etc/istio/proxy/SDS 45274                        * 46319        users:(("pilot-agent",pid=3406,fd=15))                                             
u_str    ESTAB                        * 46319                        * 45274        users:(("envoy",pid=3555,fd=19))                                                   
tcp      LISTEN                 0.0.0.0:15021                  0.0.0.0:*            users:(("envoy",pid=3555,fd=40),("envoy",pid=3555,fd=34),("envoy",pid=3555,fd=22)) 
tcp      LISTEN                 0.0.0.0:15090                  0.0.0.0:*            users:(("envoy",pid=3555,fd=39),("envoy",pid=3555,fd=33),("envoy",pid=3555,fd=21)) 
tcp      LISTEN               127.0.0.1:15000                  0.0.0.0:*            users:(("envoy",pid=3555,fd=18))                                                   
tcp      LISTEN                 0.0.0.0:15001                  0.0.0.0:*            users:(("envoy",pid=3555,fd=41),("envoy",pid=3555,fd=35),("envoy",pid=3555,fd=31)) 
tcp      LISTEN               127.0.0.1:15004                  0.0.0.0:*            users:(("pilot-agent",pid=3406,fd=17))                                             
tcp      LISTEN                 0.0.0.0:15006                  0.0.0.0:*            users:(("envoy",pid=3555,fd=42),("envoy",pid=3555,fd=36),("envoy",pid=3555,fd=32)) 
tcp      ESTAB           172.21.206.227:40560            10.108.217.90:15012        users:(("pilot-agent",pid=3406,fd=19))                                             
tcp      ESTAB           172.21.206.227:43240            10.108.217.90:15012        users:(("pilot-agent",pid=3406,fd=14))                                             
tcp      LISTEN                       *:15020                        *:*            users:(("pilot-agent",pid=3406,fd=12))                                             
tcp      ESTAB                127.0.0.1:35256                127.0.0.1:15020        users:(("envoy",pid=3555,fd=43))                                                   
tcp      ESTAB                127.0.0.1:35238                127.0.0.1:15020        users:(("envoy",pid=3555,fd=20))                                                   
tcp      ESTAB       [::ffff:127.0.0.1]:15020       [::ffff:127.0.0.1]:35238        users:(("pilot-agent",pid=3406,fd=6))                                              
tcp      ESTAB       [::ffff:127.0.0.1]:15020       [::ffff:127.0.0.1]:35256        users:(("pilot-agent",pid=3406,fd=18))                                             

iptables

$ iptables-save

# Generated by iptables-save v1.8.7 on Fri Dec 16 16:18:31 2022
*nat
:PREROUTING ACCEPT [104490:5433496]
:INPUT ACCEPT [105123:5471476]
:OUTPUT ACCEPT [24745:1633905]
:POSTROUTING ACCEPT [42627:2706825]
:ISTIO_INBOUND - [0:0]
:ISTIO_IN_REDIRECT - [0:0]
:ISTIO_OUTPUT - [0:0]
:ISTIO_REDIRECT - [0:0]
-A PREROUTING -p tcp -j ISTIO_INBOUND
-A OUTPUT -p tcp -j ISTIO_OUTPUT
-A ISTIO_INBOUND -p tcp -m tcp --dport 15008 -j RETURN
-A ISTIO_INBOUND -p tcp -m tcp --dport 15090 -j RETURN
-A ISTIO_INBOUND -p tcp -m tcp --dport 15021 -j RETURN
-A ISTIO_INBOUND -p tcp -m tcp --dport 15020 -j RETURN
-A ISTIO_INBOUND -p tcp -j ISTIO_IN_REDIRECT
-A ISTIO_IN_REDIRECT -p tcp -j REDIRECT --to-ports 15006
-A ISTIO_OUTPUT -s 127.0.0.6/32 -o lo -j RETURN
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -m owner --uid-owner 201507 -j ISTIO_IN_REDIRECT
-A ISTIO_OUTPUT -o lo -m owner ! --uid-owner 201507 -j RETURN
-A ISTIO_OUTPUT -m owner --uid-owner 201507 -j RETURN
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -m owner --gid-owner 201507 -j ISTIO_IN_REDIRECT
-A ISTIO_OUTPUT -o lo -m owner ! --gid-owner 201507 -j RETURN
-A ISTIO_OUTPUT -m owner --gid-owner 201507 -j RETURN
-A ISTIO_OUTPUT -d 127.0.0.1/32 -j RETURN
-A ISTIO_OUTPUT -j ISTIO_REDIRECT
-A ISTIO_REDIRECT -p tcp -j REDIRECT --to-ports 15001
COMMIT

这个 iptable 的原理网上说了很多,这里不再说明了。

连接

图:Istio端口与组件 中包含了一些在 Istio 中时行 pod 内部的 TCP 连接的说明(见其中的 ss 命令输出)。在此不再赘述。

神奇的 127.0.0.6

出于非常多的原因,inbound 流量在由 Envoy 转到 app 时,Envoy 建立的 TCP 连接 bind 的 ip 地址是一个ip addr也查不到的,在 lo 接口上的 127.0.0.6。如果你好奇,可以看:

Why the bind magic 127.0.0.6 ?

一点感想

如果用传统 Linux 网络运维的方法,去解决 Istio 复杂实现下的网络问题,非常不直观和容易。在 Service Mesh 应用的同时,可观察性的工具和方法应该作出相应的变化。不然大规模使用后,问题的解决将花费相当大的代价。

其实,在可观察性方面,我们已经有所谓的 distributed tracing(全链路跟踪) 。为何不能 全链路跟踪 TCP 连接的状态变化和事件源?如,是什么组件发出的 TCP RST/FIN/SYN。连接的参与方是什么?影响是什么?

而这些事件源的采集上,可以利用 eBPF 等技术去捕获连接的重要事件。业界已经有一些类似的开源实现。只是大家的重视程度未上去。

公司也好,业界也好,技术的演进大都是由解决问题来推动的,而很少是真正 “设计” 出来的。而关键的,可能是去发现变化和主动适应甚至利用变化。

分享

Mark Zhu
作者
Mark Zhu
Old Developer