写点什么

VPC 中 NAT 的那点事

  • 2019-11-20
  • 本文字数:3485 字

    阅读完需:约 11 分钟

VPC中NAT的那点事
### NAT就在那里


下图 是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之前,历任数字媒体娱乐系统工程 师、宽带业务架构师、云解决方案架构师,负责数字媒体娱乐系统、云计算解决方案等服务的咨询和架构设计工作。
复制代码


TAGS:


Amazon VPC


,


大咖专栏


,


网络和内容发布


2019-11-20 08:00834

评论

发布
暂无评论
发现更多内容

【薪火计划】02 - 一将功成万骨枯

码字与律动

管理

训练营第4周作业

爱码士

训练营

在历史的天空重逢!大话鲲鹏上海记

脑极体

Java 集合(4)-- iterable和iterator异同分析

秦怀杂货店

Java Iterator和Iterable 集合 iterable

架构师训练营 1 期 - 第八周总结(vaik)

行之

极客大学架构师训练营

每个程序员都应该知道的数字

多颗糖

数据库 计算机基础 存储

第 4 周作业

Steven

极客大学架构师训练营

面试官:Redis 主从复制时网络开小差了怎么整?

redis 学习 面试

限流、熔断与降级

idonkeyliu

后台开发 熔断 限流 降级

Java集合(2)-- Iterator接口超级详细解读

秦怀杂货店

Java 源码 Iterator和Iterable 集合 Iterator

【干货】内存条的基础讲解,够用绝大多数情况

亚兰—硅的传奇official

计算机基础 内存 装机 硬件

第5周 技术选型(一)-作业

SuGeek

极客大学架构师训练营

适合初学者的Web Components教程[2019](译)

西贝

Java Web Component 组件

flink-sql流计算可视化UI平台

无情

sql flink 实时计算 平台

高可用可伸缩微服务架构:基于Dubbo、SpringCloud和ServiceMesh完整蓝光版PDF分享

Java架构追梦

Java 微服务 dubbo Service Mesh SpringCloud

我画着图,FluentAPI 她自己就生成了

newbe36524

C# .net .net core netcore

过滤Spark数据集的四种方法

大数志

大数据 spark 数据分析 数据科学

SpringBoot系列(5)- 自定义配置

引花眠

springboot

8.5网络通信基本原理与性能优化

张荣召

Spring改变版本号命名规则:此举对非英语国家很友好

YourBatman

Spring Boot 语义化 Spring Framework 日历化

架构师训练营第 1 期 第 8 周作业

李循律

极客大学架构师训练营

训练营第四周学习总结

爱码士

训练营

Java集合(1)-- 俯瞰Java集合源码以及分类

秦怀杂货店

Java 源码 Iterator和Iterable JAVA集合

学习总结--week08

张荣召

超赞!吃透这份Github点赞120k的Java知识面试笔记Offer拿到手软

比伯

Java 编程 程序员 架构 面试

va_list 可变长参数原理

liu_liu

c 可变参数 va_list

机器学习的产业化变革中,华为云ModelArts的新成绩单

脑极体

8.4经典算法

张荣召

8.6非阻塞网络I/O

张荣召

高性能-Nginx多进程高并发、低时延、高可靠机制在百万级缓存(redis、memcache)代理中间件中的应用

杨亚洲(专注MongoDB及高性能中间件)

nginx memcached 分布式缓存 redis cluster 分布式数据库mongodb

Java集合(3)-- iterable接口超级详细解读

秦怀杂货店

Java 源码 Iterator和Iterable 集合 iterable

VPC中NAT的那点事_其他_亚马逊云科技 (Amazon Web Services)_InfoQ精选文章