Tag: Amazon VPC


如何在AWS上构建基于 OpenSwan 的软件 VPN 解决方案

概述

随着云的普及以及即用即付的模式,正在被大家逐渐接受,那么在初期从原始数据中心到云迁移的过程中,为了保证数据的平稳迁移,并不推荐将应用以及数据库一次性的迁移到云中。所有项目都应该分阶段来进行,阶段迁移的情况下就必须要将云资源与本地数据中心的资源互连互通。

要做到互连互通,有三种备选方案,互联网,专线直连(DX)和 VPN。从三个方面比较下这三种解决方案,安全,稳定性以及费用。DX 服务无疑是最优的一种解决方案,提供安全稳定的网络性能,高吞吐量。由于国内专线铺设所带来的高昂费用,所以在初期阶段,DX 并不是一个最优的。这里面互联网是最便宜的,因为本身数据中心就已经支付了这部分费用,只要保证云中的资源可以上互联网就可以了,但互联网面临的问题是网络依赖互联网,互联网的网络性能并不是可控的,另外一方面是互联网的安全性。VPN 呢是基于互联网的服务,虽然不能保证网络性通的可控,但可以做到数据的安全。

就以上比较而言,在初期阶段,VPN 无疑是一种高性比的安全以及节约成本的方案。考虑到目前北京区域并不支持硬件VPN的服务,即Global区域的VPN Connection。那么有没有可以替代的方案呢?答案是肯定的,一切问题都难不倒我们伟大的开源组织,开源方案如 OpenSwan (今天的主角),StrongSwan,Raccoon等等了。除了开源的解决方案外,还有一些商业解决方案,比如Sanfor 深信服,Hillstone 山石,Checkpoint , Cisco CSR1000v等也可以部署,有兴趣的可以与相应的软件提供商联系。

前面说了那么多关于VPN的各种软件,那么该如何选择呢?这里我们从使用上来划分下吧,将VPN主要划分为两类,一类是工作于客户端到服务端的模式,像OpenVPN,SSL VPN,L2TP,PPTP这些都是需要客户端主动发起连接,拨到Server端在两者之间建立一个逻辑上的隧道 (tunnel)进行通信。这种方式一般适用于个人到总部场景。服务器是无法主动发起连接到客户端。

另外一种就是站点到站点(site-to-site)的模式,像OpenSwan,StrongSwan, Raccoon 等软件,这种情况下两端会各有一个设备负责来建立两个站点之间安全通信的隧道,任何需要到对端的通信都会触发设备来建立安全隧道通信。

那么公司原有数据中心与云通信都是双向通信,所以站点到站点更合理。

实际上这里说的 VPN 即是指 IPsec VPN,IPsec 是一种工业标准,只要支持这种标准的设备都可以互相协商建立一个安全的隧道出来,比如支持的硬件设备有路由器,防火墙以及专业的 VPN 设备。

说了这么多,下面我们就以 AWS 端为 OpenSwan 与 Cisco 的路由器之间的配置为例。

场景及拓扑

拓扑如上图,AWS端建立一个VPC(CIDR:192.168.0.0/16),包含两个子网,一个可以上互联网的Public子网192.168.1.0/24以及私有子网Private 192.168.2.0/24。在公有子网上会配置一台OpenSwan实例与公司的Cisco设备做VPN连接。

OpenSwan的EIP地址为54.223.152.218 子网:192.168.1.0/24

Cisco设备的公网地址为54.223.170.5 子网:10.1.2.0/24

目标:实现AWS上私有子网192.168.2.0/24和数据中心10.1.2.0/24双向互通

详细配置步骤

1.配置 VPC 基础环境

1.1 创建 VPC

在AWS console界面点击VPC,在左侧点击“您的VPC”, 创建VPC

名称标签: MyVPC

CIDR块: 192.168.0.0/16

租赁:默认

1.2 创建子网

将鼠标点到子网,选择创建子网

标签名称:Public

VPC:选择第一步创建好的VPC

可用区:保持默认

CIDR块:192.168.1.0/24

以同样的方法创建一个192.168.2.0/24的子网

1.3 创建路由表

点击路由表,创建路由表

名称标签:PrivateRoute

VPC:选择1.1创建好的VPC

创建完成以后,点到刚刚创建好的路由表,在页面下列点击子网关联,点击编辑

在192.168.2.0/24的路由前打勾,点击保存。

1.4 创建 IGW

点击Internet网关,创建Internet网关

名称标签:MyIGW

创建完成,点击附加到VPC

选择新创建好的VPC

2. 启动并配置 VPN 实例

2.1 启动实例

到EC2页面,点击启动实例。选择Amazon Linux

在详细信息页中,网络请选择新创建的VPC

子网选择192.168.1.0/24

点击下一步,存储标签等按需配置

配置安全组,选择创建一个新的安全组

添加规则

自定义的UDP规则,端口500 来源任何位置

自定义的UDP规则,端口4500 来源任何位置

自定义协议, 协议 50 来源任何位置

所有流量 来源为 192.168.2.0/24

注意:一定要放行来自于192.168.2.0/24的流量,否则无法通信。

最后审核启动,启动时指定一个用于登陆实例的key文件,如果没有创建一个新的。

2.2 配置弹性 IP (EIP)

在EC2页面,左侧导航栏找到弹性IP,申请分配新地址,并将其关联到新创建的OpenSwan实例。这里申请到的为 54.223.152.218。

2.3 关闭源/目的检查

在EC2页面点击OpenSwan实例,右键选择联网,更改源/目标检查,点“是,请禁用”

3. 安装配置 OpenSwan

3.1 登录到实例安装 OpenSwan

如何登陆实例请参考如下文档:

https://docs.amazonaws.cn/AWSEC2/latest/UserGuide/putty.html

登陆完成以后,运行如下命令安装OpenSwan

$ sudo yum -y install openswan

3.2 根据Cisco 参数来配置 OpenSwan

修改/etc/ipsec.conf去掉最后一行include /etc/ipsec.d/*.conf 的注释

$ sudo vim /etc/ipsec.conf

include /etc/ipsec.d/*.conf

 

Cisco路由器的配置如下:

crypto isakmp policy 10

encr aes

authentication pre-share

group 2

crypto isakmp key aws123 address 54.223.152.218

!

!

crypto ipsec transform-set OpenSwan esp-aes esp-sha-hmac

mode tunnel

!

!

!

crypto map OpenSwan 10 ipsec-isakmp

set peer 54.223.152.218

set transform-set OpenSwan

match address 100

!

!

interface GigabitEthernet1

ip address 54.223.170.5 255.255.255.252

ip mtu 1400

ip tcp adjust-mss 1360

crypto map OpenSwan

!

access-list 100 permit ip 10.1.2.0 0.0.0.255 192.168.2.0 0.0.0.255

ip route 0.0.0.0 0.0.0.0 54.223.170.6

根据cisco的配置创建OpenSwan的配置如下:

$ sudo vim /etc/ipsec.d/cisco.conf

conn cisco

authby=secret

auto=start

leftid=54.223.152.218

left=%defaultroute

leftsubnet=192.168.2.0/24

leftnexthop=%defaultroute

right=54.223.170.5

rightsubnet=10.1.2.0/24

keyingtries=%forever

ike=aes128-sha1;modp1024

ikelifetime=86400s

phase2alg=aes128-sha1

salifetime=3600s

pfs=no

配置解释:

leftid 标明本地对应公网IP

letftsubnet为本地子网

right为对端公网地址

rightsubnet为对端子网

ike为第一阶段参数

ikelifetime为第一阶段的生存时间

phase2alg为第二阶段参数

salifetime为第二阶段生存时间

结果如下图:

配置预共享密钥:

$ sudo vim /etc/ipsec.d/cisco.secrets

54.223.152.218  54.223.170.5: PSK “aws123”

启动openswan服务并做配置检查

$ sudo service ipsec start

$ sudo ipsec verify

3.3 修改系统参数

更改系统参数做IP转发并关闭重定向功能

编辑/etc/sysctl.conf内核配置文件,做如下修改

$ vim /etc/sysctl.conf

$ vim /etc/sysctl.conf

net.ipv4.ip_forward = 1

net.ipv4.conf.all.accept_redirects = 0

net.ipv4.conf.all.send_redirects = 0

net.ipv4.conf.default.send_redirects = 0

net.ipv4.conf.eth0.send_redirects = 0

net.ipv4.conf.default.accept_redirects = 0

net.ipv4.conf.eth0.accept_redirects = 0

配置完成以后,启用新的配置

$ sudo sysctl –p

3.4 更改 OpenSwan 的网卡 MSS 值

$ sudo iptables -t mangle -A FORWARD -o eth0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1387

3.5 修改 VPC 私有子网路由表

找到VPC Console页面,在左侧点路由表,找到192.168.2.0/24关联的路由表(1.3中创建), 在页面下方点击路由,编辑

添加其他路由,

目标: 10.1.2.0/24

目标:OpenSwan实例ID (i-xxxxx)

4. 测试连通性

以上步骤都完成以后,就可以在192.168.2.0网段的实例进行测试了。使用 ping 测试到 10.1.2.x private 私有地址段的一个地址。

ping 10.1.2.3

检查 IPsec tunnel 状态:

$ sudo service ipsec status

IPsec running - pluto pid: 25674

pluto pid 25674

1 tunnels up

some eroutes exist

常见问题及排错

1.  IPsec Tunnel 没有正常建立

首先检查到对端 IP 是否可以通信,检查安全组是否放行 IKE 需要的端口 UDP 500。如果网络层没问题,检查各项参数配置是否有错误。

2. 隧道建立成功,但是无法进行通信

首先检查 IP forward 是否设置为 1?VPN 实例安全组是否放行了相应的策略?路由是否正确?NAT 是否影响?

3. NAT 问题

如果您的 CGW 配置了 NAT 与 VPN 工作,根据厂家不同对 VPN 包的处理顺序不一致,如果源 NAT 在 VPN 之前被处理了,因为源地址发生了改变,所以也就不会再被 VPN 处理,造成通信失败。像 Cisco 设备就需要做 NAT 的例外策略。主流的 Cisco ASA 就需要做相应修改,参考如下:

8.2 及以前的版本:

nat (inside) 0 access-list nat_exemption
access-list nat_exemption extended permit ip 192.168.1.0 255.255.255.0 10.0.0.0 255.255.255.0

8.3 及更新的版本:

object network obj-192.168.1.0

subnet 192.168.1.0 255.255.255.0

object network obj-10.0.0.0

subnet 10.0.0.0 255.255.255.0

nat (inside,any) source static obj-192.168.1.0 obj-192.168.1.0 destination static obj-10.0.0.0 obj-10.0.0.0 no-proxy-arp

如果您经过以上步骤还是不能成功搭建 VPN,AWS Support 提供专业化的技术支持,可根据您当前或计划的使用案例为您提供一套独特的工具和专业知识。建议您开案例联系Support 技术支持。

作者介绍:

侯晓鹏

亚马逊AWS云支持工程师,多年从事网络相关支持以及架构咨询工作,在加入 AWS 以前曾担任 Juniper 高级咨询专家,负责为企业网及运营商提供专业架构咨询及网络优化工作。现任亚马逊云支持工程师,AWS 认证 Direct Connect 服务专家。多次帮助大规模企业客户解决混合云复杂技术及架构问题。

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(网络地址转换)的功能。

相比于没有NAT的场景,大部分的应用、软件不需要任何改变就能在基本NAT的场景下继续工作,例如基于HTTP协议的Web应用等;但是对于某些应用,例如FTP、VoIP等,网络地址转换会给应用带来一些意想不到的挑战。今天我们以历史悠久的FTP协议为例,来和大家探讨一下NAT给FTP这样的应用带来什么样的挑战,以及FTP应用和协议又是如何演进去适应它。

被动模式下的FTP

我们重温一下FTP的工作过程。客户端连接服务端TCP 21端口建立命令通道后,输入用户名密码完成登录;随后的每一次数据传输都需要另外建立数据通道进行; 如果数据通道由客户端发起,服务端接受,我们称之为被动模式;反之,如果数据通道由服务端发起,客户端接受,则称之为主动模式。

为简化讨论,我们以被动模式为例。

同一个私网内

我们在EC2实例10.0.0.10上搭建了一台FTP服务器,它监听在21端口。现在我们从同一个VPC里的另外一台EC2上运行FTP客户端;那么整个过程会很顺利。

从Internet访问

现在我们从位于Internet某处的一台PC上运行FTP客户端。

这里连接超时的原因显而易见,FTP服务端发送给客户端的IP地址是服务端的私有地址。位于Internet上的客户端无法建立与位于VPC内部的私有地址10.0.0.10直接通讯。

解决这个问题有多种方法,我们由简单到复杂分别叙述。

增强协议适配NAT

FTP协议针对NAT和其他因素,对协议进行了增强,提供了增强版的被动模式EPSV命令[1]。

下面的例子里,服务端不再显式指定IP地址,只提供数据通道的端口号。客户端默认与控制通道相同的IP地址建立数据通道。

可以看到,解决方案很优雅。实际上如果你在阅读本文的时候,绝大部分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
作者介绍:

丁成银

AWS 解决方案架构师,获得AWS解决方案架构师专业级认证和DevOps工程师专业级认证。负责基于AWS的云计算方案架构的咨询和设计,同时致力于AWS云 服务在国内的应用和推广,在数字媒体、电信、互联网和游戏、企业混合IT等方面有着丰富的实践和设计经验。在加入AWS之前,历任数字媒体娱乐系统工程 师、宽带业务架构师、云解决方案架构师,负责数字媒体娱乐系统、云计算解决方案等服务的咨询和架构设计工作。