下图 是EC2实例通过IGW(Internet网关) 接入到Internet的示意图。熟悉AWS的读者会知道,这里EC2实例和Internet通信的两个方向上,实际上发生了如下的转换:
* 从EC2实例发出的前往Internet的IP包,其源地址10.0.0.10在经过IGW时,会被转换为与实例关联的公网地址 54.232.0.1;
* 从Internet发给54.232.0.1的IP包,经过IGW时其目的地址会转换为ENI对应的内网地址10.0.0.10并被送到 EC2实例;
可以看到,这里Internet网关就是起到实例的内网地址和公网地址一对一的基本 NAT(网络地址转换)的功能。
![](https://s3.cn-north-1.amazonaws.com.cn/images-bjs/1017-01.png)
相比于没有NAT的场景,大部分的应用、软件不需要任何改变就能在基本NAT的场景下继续工作,例如基于HTTP协议的Web应用等;但是对于某些应用,例如FTP、VoIP等,网络地址转换会给应用带来一些意想不到的挑战。今天我们以历史悠久的FTP协议为例,来和大家探讨一下NAT给FTP这样的应用带来什么样的挑战,以及FTP应用和协议又是如何演进去适应它。
### 被动模式下的FTP
我们重温一下FTP的工作过程。客户端连接服务端TCP 21端口建立命令通道后,输入用户名密码完成登录;随后的每一次数据传输都需要另外建立数据通道进行; 如果数据通道由客户端发起,服务端接受,我们称之为被动模式;反之,如果数据通道由服务端发起,客户端接受,则称之为主动模式。
为简化讨论,我们以被动模式为例。
**同一个私网内**
我们在EC2实例10.0.0.10上搭建了一台FTP服务器,它监听在21端口。现在我们从同一个VPC里的另外一台EC2上运行FTP客户端;那么整个过程会很顺利。
![](https://s3.cn-north-1.amazonaws.com.cn/images-bjs/1017-2.PNG)
**从Internet访问**
现在我们从位于Internet某处的一台PC上运行FTP客户端。
![](https://s3.cn-north-1.amazonaws.com.cn/images-bjs/1017-3.PNG)
这里连接超时的原因显而易见,FTP服务端发送给客户端的IP地址是服务端的私有地址。位于Internet上的客户端无法建立与位于VPC内部的私有地址10.0.0.10直接通讯。
解决这个问题有多种方法,我们由简单到复杂分别叙述。
### 增强协议适配NAT
FTP协议针对NAT和其他因素,对协议进行了增强,提供了增强版的被动模式EPSV命令[1]。
下面的例子里,服务端不再显式指定IP地址,只提供数据通道的端口号。客户端默认与控制通道相同的IP地址建立数据通道。
![](https://s3.cn-north-1.amazonaws.com.cn/images-bjs/1017-4.PNG)
可以看到,解决方案很优雅。实际上如果你在阅读本文的时候,绝大部分FTP服务端和客户端都已经实现了EPSV,而且优先使用它,所以FTP应用目前在EC2上是可以称之为开箱即用的。当然这需要客户端和服务端都支持增强的协议才能达成;如果我们不修改协议,能否解决这个问题呢。
### 放开那协议!我来!
有些时候,修改协议和实现需要多方协调和很长的时间才能完成。在RFC2428标准化之前,一些FTP实现就已经通过修改实现来适配基本NAT,而非修改协议。
以vsftpd为例,它允许通过配置文件vsftpd.conf中的配置项 pasv_address=54.232.0.1 告知服务端,在PASV被动模式下,应当指示客户端连接到配置项指定的IP(而不是服务端的私有IP)来适配基本NAT。
其他的一些常见应用例如VoIP类应用,也有类似的机制去适配基本NAT;例如引入STUN/TURN/ICE等方式[2]适配各种更加复杂的NAT穿越场景;但是针对部署在EC2上的基本NAT环境,也有通过实现上的简单调整,例如开源VoIP应用Asterisk就支持通过在配置文件/etc/asterisk/sip.conf里指定本机的公网地址和本地网段的方式来适配基本NAT。
nat=yes
externaddr=54.223.0.1
localnet=10.0.0.0/16
### 协议和实现我都不想动!
作为一枚任性的读者,如果您既不想动协议也不想动实现,这里笔者给读者介绍一种剑走偏锋的方式,读者若有兴趣,可以轻松愉快的在AWS上试一试,看看能否解决你的应用适配基本NAT。下面是一段shell脚本,当然是运行在Linux操作系统上的。
_\#从EC2 实例元数据服务获取本实例的公网IP(如有)、私网IP_
_public_ipv4=`curl -s http://169.254.169.254/latest/meta-data/public-ipv4`_
_local_ipv4=`curl -s http://169.254.169.254/latest/meta-data/local-ipv4`_
_\#配置私网地址段,这里应为EC2实例所在VPC的地址范围_
_local_net=10.0.0.0/16_
_if [ “x${public_ipv4}” == “x” ]_
_then_
_echo “No public IPv4 address available for this instance, abort.”_
_exit 1_
_else_
_\#如果EC2实例的公网IP不为空,则将该公网地址添加到eth0上_
_ip address add ${public_ipv4}/32 dev eth0_
_\#本地接受的连接,如果来源不是本VPC,那么将IP包的目的地址改写为公网IP_
_iptables -t nat -A PREROUTING ! -s ${local_net} -d ${local_ipv4} -i eth0 -j DNAT –to ${public_ipv4}_
_\#本地发起的连接,如果出方向流量的源IP地址是公网地址,那么需要改写为私网IP_
_iptables -t nat -A POSTROUTING -s ${public_ipv4} -o eth0 -j SNAT –to ${local_ipv4}_
_fi_
正常情况下,脚本执行完毕后,可以通过如下方式验证效果。
### 首先检查本实例的公网IP是否已经正确配置到eth0上。
~ # ip addr show dev eth0
eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc pfifo_fast state UP group default qlen 1000
link/ether 02:c7:6b:9b:d2:b6 brd ff:ff:ff:ff:ff:ff
inet 10.0.0.10/24 brd 10.0.0.255 scope global eth0
valid_lft forever preferred_lft forever
inet 54.232.0.1/32 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::c7:6bff:fe9b:d2b6/64 scope link
valid_lft forever preferred_lft forever
可以看到,公有IP地址(54.232.0.1/32)已经成功添加到eth0接口。
### 然后检查iptables的NAT规则是否正确配置
~ # iptables -t nat -nvL
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 DNAT all — eth0 * ! 10.0.0.0/16 10.0.0.10 to:54.223.74.106
Chain INPUT (policy ACCEPT 1 packets, 64 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 3 packets, 222 bytes)
pkts bytes target prot opt in out source destination
Chain POSTROUTING (policy ACCEPT 3 packets, 222 bytes)
pkts bytes target prot opt in out source destination
0 0 SNAT all — * eth0 54.223.74.106 0.0.0.0/0 to:10.0.0.1
从上面可以看到传入、传出数据包的数量以及IP地址在传入前,传出后的地址改写情况。
### 最后分别从VPC内部和Internet连接到服务,验证结果
~ $ ss -nt
State Recv-Q Send-Q Local Address:Port Peer Address:Port
ESTAB 0 72 54.223.74.106:21 119.xx.x.xx:52425
ESTAB 0 1272 54.223.74.106:12081 119.xx.x.xx:23710
ESTAB 0 72 10.0.0.10:21 10.xx.x.xx:48361
ESTAB 0 1272 10.0.0.10:12090 10.xx.x.xx:32115
笔者在没有修改任何vsftpd的配置文件的前提下,通过上述脚本的运行和配置,同一个VPC内部的客户端和Internet客户端都能完成FTP被动模式的文件传输全流程。
值得一提的是,本方法仅供参考,不建议在生产环境中大规模使用,推荐的解决方案请参考前文关于协议适配和实现适配。
[1] FTP Extensions for IPv6 and NATs https://tools.ietf.org/html/rfc2428
[2] NAT Traversal Practices for Client-Server https://tools.ietf.org/html/rfc6314
**作者介绍:**
![](https://s3.cn-north-1.amazonaws.com.cn/images-bjs/Ding+Chandler.jpg)
丁成银
AWS 解决方案架构师,获得AWS解决方案架构师专业级认证和DevOps工程师专业级认证。负责基于AWS的云计算方案架构的咨询和设计,同时致力于AWS云 服务在国内的应用和推广,在数字媒体、电信、互联网和游戏、企业混合IT等方面有着丰富的实践和设计经验。在加入AWS之前,历任数字媒体娱乐系统工程 师、宽带业务架构师、云解决方案架构师,负责数字媒体娱乐系统、云计算解决方案等服务的咨询和架构设计工作。
更多内容推荐
12. 集群:gRPC 中如何实现广播
2023-09-27
vue 组件通信方式有哪些?
父组件通过props的方式向子组件传递数据,而通过$emit 子组件可以向父组件通信。
2023-01-03
Codeforces Round #787 (Div. 3)
🌲校赛前临时补题之codeforces马上要校赛了,太久没敲题,怕校赛被暴打,于是乎写了一套div3,发现很多不会,然后写一下题解温顾一下,顺一顺思路。
2022-07-15
RabbitMQ 订阅发布模型的用法
2022-09-08
AWS 之 EC2 搭建 WordPress 博客
AWS之EC2搭建WordPress博客
2022-11-30
Java 基础:集合框架之 Map
Map与Collection无继承关系。
2022-06-23
AWS S3 bucket 的 ACL 控制
在新的 AWS S3 控制中,启用了一个默认的配置。
2022-11-02
31|网络通信:CNI 是怎么回事?又是怎么工作的?
针对Docker的跨主机通信网络缺陷,Kubernetes提出了自己的网络模型IP-per-pod,让Pod摆脱了主机的硬限制,适应集群系统的网络需求。
2022-09-02
2. 使用 Netty 实现 IM 系统
2023-09-26
Server 详解与面试要点
2023-09-26
消息传递通信的实现方式?
消息传递通信的实现方式?
2022-04-24
读《Software Systems Architecture》(22)—— The Operational Viewpoint
读《Software Systems Architecture》(22)—— The Operational Viewpoint
2022-06-15
06|云净天空——谈云中负载均衡器(上)
2022-09-21
Qt|编辑框的使用总结
QLineEdit、QTextEdit、QPlainTextEdit、QTextBrowser
2022-07-22
【12.9-12.16】写作社区优秀技术博文一览
为了让更多的创作者有展示自己的舞台,为了让更多优质内容有发光发热的机会,InfoQ 官方写作社区会时不时向大家推荐近期发布在社区的优质作品和入驻的新创作者们。
2022-12-16
纯色山鹪莺
在一片微微摇曳的芦苇荡中,突然一只萌物伴着习习晚风就这样映入眼帘,它乖巧而活泼的样子也如微风吹动了我的心。这只素色却令人心动的小鸟叫做纯色山鹪莺,也叫褐头鹪莺,是雀形目扇尾莺科山鹪莺属的鸟类。尾长,眉纹是淡淡的米白色,体色呈比较淡的黄褐色,
2022-08-06
MySQL 性能指标 TPS\QPS\IOPS 如何压测?
QPS(Queries Per Second)就是每秒的查询数,对数据库而言就是数据库每秒执行的 SQL 数(含 insert、select、update、delete 等)。
2023-02-28
计算机网络的组成
计算机网络的组成
2022-09-04
Pisanix v0.2.0 发布|新增动态读写分离支持
Pisanix 于今天发布了 v0.2.0 版本,在此前版本基础上,本次更新新增了动态读写分离支持,本文将进行详细介绍。
2022-08-04
Matlab 常用图像处理命令 108 例(四)
37.fwind1 38.fwind2 39.getimage 40.gray2ind 41.grayslice 42.histeq 43.hsv2rgb 44.idct2 45.ifft2 46.ifftn 47.sim2bw 48.im2col 49.im2double 50.im2uint8 51.im2uint16 52.imadjust 53.imapprox
2023-03-14
推荐阅读
1、Nginx 概述及 web server 技术选型
2023-09-28
Tongsuo 8.4.0-pre3 发布!
2023-10-10
软件测试 / 测试开发丨接口测试学习笔记,TcpDump 与 WireShark
2023-11-08
忙碌的七个层次
2023-05-16
Matlab 常用图像处理命令 108 例(六)
2023-03-16
2.Eureka-Server 工程搭建
2023-09-29
11-FastDFS 部署与使用
2023-09-28
电子书
大厂实战PPT下载
换一换 李欣 | eBay 人工智能平台/首席架构师
李亚曼 | 京东物流 技术与数据智能部/物流产品负责人
董善东(梵登)博士 | 阿里云 技术专家
评论