AWS ECS Iptables 允许源和目标是同一个 ip 地址
AWS ECS Iptables allow source and destination to be the same ip address
目前,AWS ECS 结合内部 NLB 无法进行系统间通信。意思是容器 1(在实例 1 上)-> 内部 NLB -> 容器 2(在实例 1 上)。因为源 IP 地址不会改变并且与目标地址保持相同,所以 ECS 实例会丢弃此流量。
我在 AWS 论坛上找到了一个帖子 https://forums.aws.amazon.com/message.jspa?messageID=806936#806936 解释了我的问题。
我已经联系了 AWS Support,他们表示他们的路线图已得到修复,但他们无法告诉我何时修复此问题,因此我正在寻找自己解决问题的方法,直到 AWS 永久修复它。
它必须可以通过更改 ECS iptables 来修复,但我没有足够的知识来完全阅读他们的 iptables 设置并理解需要更改什么来解决这个问题。
iptabels-保存输出:
:DOCKER - [0:0]
:DOCKER-ISOLATION - [0:0]
:DOCKER-USER - [0:0]
-A FORWARD -j DOCKER-USER
-A FORWARD -j DOCKER-ISOLATION
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT
-A DOCKER -d 172.17.0.3/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 5000 -j ACCEPT
-A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 5000 -j ACCEPT
-A DOCKER -d 172.17.0.5/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 8086 -j ACCEPT
-A DOCKER-ISOLATION -j RETURN
-A DOCKER-USER -j RETURN
COMMIT
# Completed on Wed Jan 31 22:19:47 2018
# Generated by iptables-save v1.4.18 on Wed Jan 31 22:19:47 2018
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [38:2974]
:POSTROUTING ACCEPT [7147:429514]
:DOCKER - [0:0]
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A PREROUTING -d 169.254.170.2/32 -p tcp -m tcp --dport 80 -j DNAT --to-destination 127.0.0.1:51679
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT -d 169.254.170.2/32 -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 51679
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A POSTROUTING -s 172.17.0.3/32 -d 172.17.0.3/32 -p tcp -m tcp --dport 5000 -j MASQUERADE
-A POSTROUTING -s 172.17.0.2/32 -d 172.17.0.2/32 -p tcp -m tcp --dport 5000 -j MASQUERADE
-A POSTROUTING -s 172.17.0.5/32 -d 172.17.0.5/32 -p tcp -m tcp --dport 8086 -j MASQUERADE
-A DOCKER -i docker0 -j RETURN
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 32769 -j DNAT --to-destination 172.17.0.3:5000
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 32777 -j DNAT --to-destination 172.17.0.2:5000
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 32792 -j DNAT --to-destination 172.17.0.5:8086
COMMIT
# Completed on Wed Jan 31 22:19:47 2018
ip一个:
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
link/ether 0a:b4:86:0b:c0:c4 brd ff:ff:ff:ff:ff:ff
inet 10.12.80.181/26 brd 10.12.80.191 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::8b4:86ff:fe0b:c0c4/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ca:cf:36:ae brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:caff:fecf:36ae/64 scope link
valid_lft forever preferred_lft forever
7: vethbd1da82@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 36:6d:d6:bd:d5:d8 brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet6 fe80::346d:d6ff:febd:d5d8/64 scope link
valid_lft forever preferred_lft forever
27: vethc65a98f@if26: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether e6:cf:79:d4:aa:7a brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::e4cf:79ff:fed4:aa7a/64 scope link
valid_lft forever preferred_lft forever
57: veth714e7ab@if56: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 1e:c2:a5:02:f6:ee brd ff:ff:ff:ff:ff:ff link-netnsid 3
inet6 fe80::1cc2:a5ff:fe02:f6ee/64 scope link
valid_lft forever preferred_lft forever
我没有关于即将推出的解决方案的信息,但我怀疑任何解决方法都会涉及阻止实例连接到自身,而是始终连接到不同的实例......或者可能使用平衡器的源地址进行发夹连接而不是原始地址。
根本问题是这样的:平衡器通过与网络基础设施集成,并进行网络地址转换,在出路时更改原始目标地址,在返回时更改源地址,以便目标组中的实例看到客户端的真实源地址,但反过来看不到……但这与非对称路由不兼容。当实例结束与自身对话时,路由非常不对称。
假设平衡器为172.30.1.100,实例为172.30.2.200。
从 172.30.2.200(实例)到 172.30.1.100(平衡器)发起 TCP 连接。端口并不是很重要,但我们假设源端口是 49152(临时),平衡器目标端口是 80,实例目标端口是 8080。
172.30.2.200:49152 > 172.30.1.100:80 SYN
NLB是NAT设备,所以这样翻译:
172.30.2.200:49152 > 172.30.2.200:8080 SYN
这被发送回实例。
这已经没有意义了,因为实例只是从它自己那里收到了一个传入请求,来自外部的某个东西,即使它没有发出那个请求。
假设它响应,而不是丢弃已经是无意义的数据包,现在您有这个:
172.30.2.200:8080 > 172.30.2.200:49152 SYN+ACK
如果 172.30.2.200:49152 实际上向 172.20.2.200:8080 发送了一个数据包,它将以 ACK 响应并建立连接。
但是没有。
接下来发生的事情应该是这样的:
172.30.2.200:49152 > 172.30.2.200:8080 RST
与此同时,172.30.2.200:49152 没有收到来自 172.30.1.100:80 的任何回复,因此它将重试并最终放弃:Connection timed out
.
当源机器和目标机器不同时,NLB 起作用,因为它不像 ELB/ALB 提供的那样是真实的(虚拟的)机器——它是由网络本身完成的。这是唯一可能的解释,因为那些具有转换地址的数据包确实会通过反向 NAT 返回原始机器,并且只有当 VPC 网络保留这些连接的状态表并转换它们时才会发生这种情况。
请注意,在 VPC 中,默认网关不是真实的。事实上,子网不是真实的。以太网络不是真实的。 (none 对此提出了批评。这里有一些非常出色的工程证据。)所有这些都是由 VPC 网络基础设施中的软件模拟的。当同一子网上的两台机器直接相互交谈时……好吧,他们不会。¹他们正在通过 software-defined 网络交谈。因此,即使机器位于同一子网上,网络也可以看到这些数据包并进行 NLB 所需的转换。
但当机器与自身对话时则不然,因为当这种情况发生时,流量永远不会出现在网络上——它保留在单个 VM 内,超出 VPC 网络基础设施的范围。
我不认为 instance-based 解决方法可行。
¹ 他们没有。 一个非常有趣的例子是使用 Wireshark 监控同一子网上两个实例的流量。打开安全组,然后从另一个实例 ping 一个实例。源机器发送一个 ARP 请求,似乎从目标机器得到一个 ARP 响应……但没有证据表明在目标机器上有这种 ARP 交互。那是因为它不会发生。网络处理目标实例的 ARP 响应。这是不可能从一个实例欺骗另一个实例的部分原因——伪造的数据包不会被网络转发,因为它们显然无效,而且网络知道这一点。发生ARP后,ping正常。基于第 2 层 headers,流量似乎直接从一个实例到另一个实例,但实际情况并非如此。
目前,AWS ECS 结合内部 NLB 无法进行系统间通信。意思是容器 1(在实例 1 上)-> 内部 NLB -> 容器 2(在实例 1 上)。因为源 IP 地址不会改变并且与目标地址保持相同,所以 ECS 实例会丢弃此流量。
我在 AWS 论坛上找到了一个帖子 https://forums.aws.amazon.com/message.jspa?messageID=806936#806936 解释了我的问题。
我已经联系了 AWS Support,他们表示他们的路线图已得到修复,但他们无法告诉我何时修复此问题,因此我正在寻找自己解决问题的方法,直到 AWS 永久修复它。
它必须可以通过更改 ECS iptables 来修复,但我没有足够的知识来完全阅读他们的 iptables 设置并理解需要更改什么来解决这个问题。
iptabels-保存输出:
:DOCKER - [0:0]
:DOCKER-ISOLATION - [0:0]
:DOCKER-USER - [0:0]
-A FORWARD -j DOCKER-USER
-A FORWARD -j DOCKER-ISOLATION
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPT
-A DOCKER -d 172.17.0.3/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 5000 -j ACCEPT
-A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 5000 -j ACCEPT
-A DOCKER -d 172.17.0.5/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 8086 -j ACCEPT
-A DOCKER-ISOLATION -j RETURN
-A DOCKER-USER -j RETURN
COMMIT
# Completed on Wed Jan 31 22:19:47 2018
# Generated by iptables-save v1.4.18 on Wed Jan 31 22:19:47 2018
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [38:2974]
:POSTROUTING ACCEPT [7147:429514]
:DOCKER - [0:0]
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A PREROUTING -d 169.254.170.2/32 -p tcp -m tcp --dport 80 -j DNAT --to-destination 127.0.0.1:51679
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT -d 169.254.170.2/32 -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 51679
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A POSTROUTING -s 172.17.0.3/32 -d 172.17.0.3/32 -p tcp -m tcp --dport 5000 -j MASQUERADE
-A POSTROUTING -s 172.17.0.2/32 -d 172.17.0.2/32 -p tcp -m tcp --dport 5000 -j MASQUERADE
-A POSTROUTING -s 172.17.0.5/32 -d 172.17.0.5/32 -p tcp -m tcp --dport 8086 -j MASQUERADE
-A DOCKER -i docker0 -j RETURN
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 32769 -j DNAT --to-destination 172.17.0.3:5000
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 32777 -j DNAT --to-destination 172.17.0.2:5000
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 32792 -j DNAT --to-destination 172.17.0.5:8086
COMMIT
# Completed on Wed Jan 31 22:19:47 2018
ip一个:
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
link/ether 0a:b4:86:0b:c0:c4 brd ff:ff:ff:ff:ff:ff
inet 10.12.80.181/26 brd 10.12.80.191 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::8b4:86ff:fe0b:c0c4/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ca:cf:36:ae brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:caff:fecf:36ae/64 scope link
valid_lft forever preferred_lft forever
7: vethbd1da82@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 36:6d:d6:bd:d5:d8 brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet6 fe80::346d:d6ff:febd:d5d8/64 scope link
valid_lft forever preferred_lft forever
27: vethc65a98f@if26: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether e6:cf:79:d4:aa:7a brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::e4cf:79ff:fed4:aa7a/64 scope link
valid_lft forever preferred_lft forever
57: veth714e7ab@if56: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 1e:c2:a5:02:f6:ee brd ff:ff:ff:ff:ff:ff link-netnsid 3
inet6 fe80::1cc2:a5ff:fe02:f6ee/64 scope link
valid_lft forever preferred_lft forever
我没有关于即将推出的解决方案的信息,但我怀疑任何解决方法都会涉及阻止实例连接到自身,而是始终连接到不同的实例......或者可能使用平衡器的源地址进行发夹连接而不是原始地址。
根本问题是这样的:平衡器通过与网络基础设施集成,并进行网络地址转换,在出路时更改原始目标地址,在返回时更改源地址,以便目标组中的实例看到客户端的真实源地址,但反过来看不到……但这与非对称路由不兼容。当实例结束与自身对话时,路由非常不对称。
假设平衡器为172.30.1.100,实例为172.30.2.200。
从 172.30.2.200(实例)到 172.30.1.100(平衡器)发起 TCP 连接。端口并不是很重要,但我们假设源端口是 49152(临时),平衡器目标端口是 80,实例目标端口是 8080。
172.30.2.200:49152 > 172.30.1.100:80 SYN
NLB是NAT设备,所以这样翻译:
172.30.2.200:49152 > 172.30.2.200:8080 SYN
这被发送回实例。
这已经没有意义了,因为实例只是从它自己那里收到了一个传入请求,来自外部的某个东西,即使它没有发出那个请求。
假设它响应,而不是丢弃已经是无意义的数据包,现在您有这个:
172.30.2.200:8080 > 172.30.2.200:49152 SYN+ACK
如果 172.30.2.200:49152 实际上向 172.20.2.200:8080 发送了一个数据包,它将以 ACK 响应并建立连接。
但是没有。
接下来发生的事情应该是这样的:
172.30.2.200:49152 > 172.30.2.200:8080 RST
与此同时,172.30.2.200:49152 没有收到来自 172.30.1.100:80 的任何回复,因此它将重试并最终放弃:Connection timed out
.
当源机器和目标机器不同时,NLB 起作用,因为它不像 ELB/ALB 提供的那样是真实的(虚拟的)机器——它是由网络本身完成的。这是唯一可能的解释,因为那些具有转换地址的数据包确实会通过反向 NAT 返回原始机器,并且只有当 VPC 网络保留这些连接的状态表并转换它们时才会发生这种情况。
请注意,在 VPC 中,默认网关不是真实的。事实上,子网不是真实的。以太网络不是真实的。 (none 对此提出了批评。这里有一些非常出色的工程证据。)所有这些都是由 VPC 网络基础设施中的软件模拟的。当同一子网上的两台机器直接相互交谈时……好吧,他们不会。¹他们正在通过 software-defined 网络交谈。因此,即使机器位于同一子网上,网络也可以看到这些数据包并进行 NLB 所需的转换。
但当机器与自身对话时则不然,因为当这种情况发生时,流量永远不会出现在网络上——它保留在单个 VM 内,超出 VPC 网络基础设施的范围。
我不认为 instance-based 解决方法可行。
¹ 他们没有。 一个非常有趣的例子是使用 Wireshark 监控同一子网上两个实例的流量。打开安全组,然后从另一个实例 ping 一个实例。源机器发送一个 ARP 请求,似乎从目标机器得到一个 ARP 响应……但没有证据表明在目标机器上有这种 ARP 交互。那是因为它不会发生。网络处理目标实例的 ARP 响应。这是不可能从一个实例欺骗另一个实例的部分原因——伪造的数据包不会被网络转发,因为它们显然无效,而且网络知道这一点。发生ARP后,ping正常。基于第 2 层 headers,流量似乎直接从一个实例到另一个实例,但实际情况并非如此。