我在'云'上的日子 - AWS上流量镜像遇到的坑

​ 自从AWS在6月份新出了Traffic Mirroring功能后, 我也算是第一时间使用了这个功能。与传统的交换机流量镜像不同的是, AWS上是将流量镜像后的数据通过VXLAN协议发送至流量分析引擎(SuricataZeek)。正是由于这一点让我碰到了以下几个问题, 这里写出来希望对大家有所帮助。

  1. 接收流量镜像的目标端, 也就是我们常说的流量分析引擎端是有接收限制的。

​ 如果你是在一个非专用实例上部署的Suricata、Zeek。那么你只能最多同时接收10个源的流量镜像, 也就是你只能接收10个网卡的数据。很不巧的是我们碰上了这个问题, 解决方案也很简单, 使用专用实例(Dedicated instance) 或者 使用AWS的网络负载均衡(Network Load Balancer)。前者可以将Limit提升到100, 后者将不受限。如图:

image-20191016164935291


  1. C5与C4实例的差异

​ 就我目前使用的实例而言, 分别测试过C5 与 C4两种实例。他们的网卡驱动有所区别的, 如图:

image-20191016160923628

​ 我这使用下来最直观的区别, 在C4实例上若不使用PF_RING捕包模式的话, Suricata丢包率感人, 0.5 Gbps就开始丢包。测试的机器配置: 32C 60G的机器, 切换到PF_RING捕包模式无此问题。反之C5实例就不存在这个问题, AF-PACKET直接上到2 Gbps的纯HTTP流量都没有丢包。硬件配置: 16C 32G的机器。


  1. 超过MTU: 9001数据包被截断

​ 这几天在排查安全事件时, 发现监控的同一台Nginx上解析出的流量(HTTP事件)与日志(HTTP事件)数量相差较大。经过两天的排查终于定位了问题, Suricata kernel 并没有丢包, 所以怀疑是不是Suricata HTTP解析出错导致。最终通过抓包发现导致该问题的”罪魁祸首”就是VXLAN, 由于AWS在流量镜像时采用了VXLAN协议进行封装, 导致在原有MTU的基础上增加了50个字节, 造成数据包被截断, 无法还原出HTTP事件。以下截图就是一个无法被正确还原HTTP事件的数据包, 我用Suricata载入数据包后, 只还原出了长度9015数据包之前的HTTP信息, 长度9015数据包之后的所有事件均无法被还原。

image-20191014095249725

image-20191014094741753

官方描述:

​ For example, if an 8996 byte packet is mirrored, and the traffic mirror target MTU value is 9001 bytes, the mirror encapsulation results in the mirrored packet being greater than the MTU value. In this case, the mirror packet is truncated. To prevent mirror packets from being truncated, set the traffic mirror source interface MTU value to 54 bytes less than the traffic mirror target MTU value. For more information about configuring the network MTU value, see Network Maximum Transmission Unit (MTU) for Your EC2 Instance in the Amazon EC2 User Guide for Linux Instances.

​ 例如,如果对8996字节的数据包进行了镜像,并且流量镜像目标MTU值为9001字节,则镜像封装会导致镜像的数据包大于MTU值。在这种情况下,镜像数据包将被截断。为防止镜像数据包被截断,请将流量镜像源接口的MTU值设置为比流量镜像目标MTU值小54个字节。有关配置网络MTU值的更多信息,请参阅Amazon EC2 Linux实例用户指南中的EC2实例的网络最大传输单位(MTU)。


关于VXLAN导致Suricata无法正常解析数据的问题, 特地进行了测试:

准备工作:

  • 新建了test_files文件, 该文件只包含内容’hello, world!‘;
  • 为了使数据包在传输时满足MTU: 9001, 手动生成了一个10MB空文件10mb_exist_files.

共计访问: 14次

访问顺序:

  1. Client -> Web Server -> test_files 3 (次)
  2. Client -> Web Server -> 10mb_exist_files 1 (次)
  3. Client -> Web Server -> test_files 10 (次)

正常情况:

  • Client -> Web Server -> test_files 3 (次) - 正常
  • Client -> Web Server -> 10mb_exist_files 1 (次) - 正常
  • Client -> Web Server -> test_files 10 (次) - 正常

异常情况:

  • Client -> Web Server -> test_files 3 (次) - 正常
  • Client -> Web Server -> 10mb_exist_files 1 (次) - 异常
  • Client -> Web Server -> test_files 10 (次) - 丢失

MTU: 9001

  • 非镜像流量的数据包详情:

可以看到从数据包20到数据包6126之间都是在进行10MB文件(10mb_exist_files)的传输过程。

image-20191016103409831


从http数据包中可以看出, 这里请求包与响应包都可以正常被还原出来。

image-20191016104502919


数据包在Suricata上的解析结果:

1
2
$ cat http-2019-10-16.json | wc -l
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{
"timestamp": "2019-10-15T22:52:10.180505+0800",
"flow_id": 415026399241324,
"pcap_cnt": 6127,
"event_type": "http",
"src_ip": "y.y.y.y",
"src_port": 43418,
"dest_ip": "x.x.x.x",
"dest_port": 8000,
"proto": "TCP",
"tx_id": 3,
"http": {
"hostname": "x.x.x.x",
"http_port": 8000,
"url": "/file/10mb_exist_files",
"http_user_agent": "python-requests/1.2.3 CPython/2.7.16 Linux/4.14.123-86.109.amzn1.x86_64",
"http_content_type": "text/html",
"accept": "*/*",
"accept_encoding": "gzip, deflate, compress",
"content_length": "41943044",
"content_type": "text/html; charset=utf-8",
"date": "Tue, 15 Oct 2019 14:52:10 GMT",
"server": "Werkzeug/0.16.0 Python/2.7.16",
"http_method": "GET",
"protocol": "HTTP/1.1",
"status": 200,
"length": 41943044
}
}

  • 镜像流量的数据包详情:(也就是VXLAN封装后的数据包)

同样可以看到从数据包26到数据包6170之间都是在进行10MB文件(10mb_exist_files)的传输过程。但是在第34个数据包中可以看到, 已经标注了数据超出了最大长度。

image-20191016105636906


超出长度的数据将会被截断, 标注: 50 bytes missing in capture file

image-20191016150322620

从HTTP数据包中可以看出, 这里只有请求包, 由于后续的响应包超出了MTU: 9001, 因此并没有响应包。

image-20191016105948606


数据包在Suricata上的解析结果:

1
2
$ cat http-2019-10-16.json | wc -l
4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"timestamp": "2019-10-15T22:52:48.295823+0800",
"flow_id": 1746715371266426,
"event_type": "http",
"src_ip": "y.y.y.y",
"src_port": 43420,
"dest_ip": "x.x.x.x",
"dest_port": 8000,
"proto": "TCP",
"tx_id": 3,
"http": {
"hostname": "x.x.x.x",
"http_port": 8000,
"url": "/file/10mb_exist_files",
"http_user_agent": "python-requests/1.2.3 CPython/2.7.16 Linux/4.14.123-86.109.amzn1.x86_64",
"accept": "*/*",
"accept_encoding": "gzip, deflate, compress",
"http_method": "GET",
"protocol": "HTTP/1.1",
"status": 200,
"length": 0
}
}

结论:

​ 相比非镜像流量的数据包, Suricata 少了后续10条http请求的数据解析。针对访问10mb_exist_files的请求, 由于超过了MTU, 数据包被阶段了, http的解析数据也是不完整的。

解决方案:

​ 这里直接引用AWS的官方文档描述。如果对8996字节的数据包进行了镜像,并且流量镜像目标MTU值为9001字节,则镜像封装会导致镜像的数据包大于MTU值。在这种情况下,镜像数据包将被截断。为防止镜像数据包被截断,请将流量镜像源接口的MTU值设置为比流量镜像目标MTU值小54个字节。有关配置网络MTU值的更多信息,请参阅Amazon EC2 Linux实例用户指南中的EC2实例的网络最大传输单位(MTU)。

​ 一般来说,降低 MTU 的话,有可能发现网路传输效能有下降,这是因为每个封包 size 变小,所以传送同样的资料量,封包数就会变多,造成 overhead 变多。但是对于传输是不会产生错误的状况的。


MTU:1500

1
2
3
4
5
6
7
8
9
$ ip link show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP mode DEFAULT group default qlen 1000
link/ether 02:8a:2d:87:02:8e brd ff:ff:ff:ff:ff:ff

$ sudo ip link set dev eth0 mtu 1500

$ ip link show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
link/ether 02:8a:2d:87:02:8e brd ff:ff:ff:ff:ff:ff

image-20191016154823748

image-20191016155112376

数据包在Suricata上的解析结果:

1
2
$ cat http-2019-10-16.json | wc -l
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
"timestamp": "2019-10-16T15:14:15.576656+0800",
"flow_id": 1135596924232203,
"event_type": "http",
"src_ip": "y.y.y.y",
"src_port": 43554,
"dest_ip": "x.x.x.x",
"dest_port": 8000,
"proto": "TCP",
"tx_id": 3,
"http": {
"hostname": "x.x.x.x",
"http_port": 8000,
"url": "/file/10mb_exist_files",
"http_user_agent": "python-requests/1.2.3 CPython/2.7.16 Linux/4.14.123-86.109.amzn1.x86_64",
"http_content_type": "text/html",
"accept": "*/*",
"accept_encoding": "gzip, deflate, compress\n",
"content_length": "41943044",
"content_type": "text/html; charset=utf-8",
"date": "Wed, 16 Oct 2019 07:14:15 GMT",
"server": "Werkzeug/0.16.0 Python/2.7.16",
"http_method": "GET",
"protocol": "HTTP/1.1",
"status": 200,
"length": 41943044
}
}

​ 结论: MTU减小到1500时, 无论从WireShark来查看或者Suricata协议还原的角度来说, 都是可以的。


​ 写在最后, 说实话AWS提供了在云上的流量镜像确实很不错, 至少比传统在云上每一台机器通过安装agent把流量外发的形式强, 似乎国内的云厂商现在也没有这个功能!

​ 不过通过VXLAN将数据包封装后导致MTU超过最大值这问题也确实有点坑。你让已经成型的架构去调整MTU值, 虽然理论上是可行, 但实际生产环境中网络的调整都是比较慎重的, 除非企业现在必须得上流量镜像, 否则不太能说服运维的小伙伴去调整, 要是真出了什么问题, 都是大问题。

参考