AWS上结合Router53构建思科远程访问VPN高可用架构

AWS上结合Router53构建思科远程访问VPN高可用架构
B站视频

一、背景

因为疫情的推动,居家办公已经成为常态化的趋势,员工居家办公的场景下,需要安全的访问企业内网。思科的AnyConnect RA-VPN(Remote Access VPN)是比较常见的解决方案,这篇文档介绍在亚马逊云科技上构建高可用的思科远程访问VPN架构。

在AWS Marketplace上,提供思科的ASAv和FTDv这两款防火墙产品,都可以用来部署AnyConnect RA-VPN,这里介绍更加常见的,通过ASAv防火墙来部署。

RA-VPN会作为员工访问内网的入口,一旦出现故障,将会影响所有员工的远程访问,所以为RA-VPN部署高可用架构是非常重要的。

在AWS上,可以通过两种方式来构建思科RA-VPN的高可用架构:

  • Router 53的DNS负载均衡。将VPN的域名配置两个权重相同的A记录,分别指向两台ASAv的公网地址,当用户通过VPN域名拨号时,平均来说,Router 53会负载分担的返回两台ASAv的公网IP地址,用户从而拨入到不同的ASAv上。
  • Network Load Balancer进行负载均衡。创建一个面向互联网的NLB,关联两台ASAv实例,为VPN的域名设置CNAME记录,指向NLB的DNS名称。这样用户通过VPN域名拨号时,会解析到NLB的DNS名称,NLB会将流量负载分担到两台ASAv上。

更加推荐使用第一种高可用架构,基于Router 53的DNS负载均衡来构建RA-VPN的高可用。这可以灵活的控制两台防火墙的流量比例,当其中一台防火墙需要维护升级时,也更容易无感知下线。

二、实验介绍

本次实验会创建两台ASAv防火墙,分别在两个可用区。Router 53 为VPN的域名vpn.demonwcd.com配置两条权重相同的A记录,当用户通过域名拨号时,会将流量负载分担到两台ASAv防火墙的公网IP地址上,从而实现远程访问VPN的高可用架构。

用户通过AnyConnect客户端拨号后,获取的IP地址段并不属于VPC CIDR段,思科将这种地址池称为“ghost pool”。通过这种方式,可以让客户端保留源IP地址去访问其他系统,而不用配置源NAT转换。这种配置方式可以与Transit Gateway结合,客户端可以保留源IP地址访问其他VPC。

当使用“ghost pool”之后,路由表需要配置回包路由,指向防火墙的接口,否则客户端无法访问该子网内的系统。

ASAv使用BYOL授权模式的AMI镜像,运行9.18.1的软件版本,不额外导入授权的情况下,默认对流量限速100kbps、100个最大会话。

三、配置部署

3.1 通过CloudFormation创建实验环境

CloudFormation代码如下,代码默认在东京区(ap-northeast-1)运行,如果想要在其他区域运行,需要修改AsavBYOL9181对应的AMI ID信息。

ASAv已经在User Data中完成了SSLVPN的配置,堆栈创建完成后,可以直接使用ASAv的公网IP地址拨号测试。

AWSTemplateFormatVersion: '2010-09-09'

Mappings:
  RegionMap:
    ap-northeast-1:
      AsavBYOL9181: ami-022666395753eceb7

Parameters:
  MyKeyPair:
    Description: Amazon EC2 Key Pair
    Type: AWS::EC2::KeyPair::KeyName
    Default: Global_Tokyo_KeyPair
  EC2InstanceAmiId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2'

Resources:

#===============创建SSM Role===============#
  BastionSsmRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      Path: /

  BastionSsmPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: ApplianceInstanceAccess
      PolicyDocument:
        Statement:
          - Effect: Allow
            Action:
              - ssm:DescribeAssociation
              - ssm:GetDeployablePatchSnapshotForInstance
              - ssm:GetDocument
              - ssm:DescribeDocument
              - ssm:GetManifest
              - ssm:GetParameter
              - ssm:GetParameters
              - ssm:ListAssociations
              - ssm:ListInstanceAssociations
              - ssm:PutInventory
              - ssm:PutComplianceItems
              - ssm:PutConfigurePackageResult
              - ssm:UpdateAssociationStatus
              - ssm:UpdateInstanceAssociationStatus
              - ssm:UpdateInstanceInformation
            Resource: "*"
          - Effect: Allow
            Action:
              - ssmmessages:CreateControlChannel
              - ssmmessages:CreateDataChannel
              - ssmmessages:OpenControlChannel
              - ssmmessages:OpenDataChannel
            Resource: "*"
          - Effect: Allow
            Action:
              - ec2messages:AcknowledgeMessage
              - ec2messages:DeleteMessage
              - ec2messages:FailMessage
              - ec2messages:GetEndpoint
              - ec2messages:GetMessages
              - ec2messages:SendReply
            Resource: "*"
      Roles:
        - !Ref BastionSsmRole

  BastionSsmProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: /
      Roles:
        - !Ref BastionSsmRole

#===============创建SSLVPN VPC===============#
  SSLVPN:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: '10.1.0.0/16'
      EnableDnsSupport: 'true'
      EnableDnsHostnames: 'true'
      Tags:
       - Key: Name
         Value: SSLVPN

# 创建IGW并且关联到VPC
  SSLVPNIgw:
    Type: "AWS::EC2::InternetGateway"
    Properties:
      Tags:
        - Key: Name
          Value: !Sub SSLVPNIGW

  SSLVPNAttachIgw:
    Type: "AWS::EC2::VPCGatewayAttachment"
    Properties:
      VpcId: !Ref SSLVPN
      InternetGatewayId: !Ref SSLVPNIgw

#-----------创建子网-----------#

# SSLVPN 创建公有子网
  SSLVPNPublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref SSLVPN
      CidrBlock: 10.1.1.0/24
      AvailabilityZone:
        Fn::Select:
          - 0
          - Fn::GetAZs: ""
      Tags:
      - Key: Name
        Value: !Sub SSLVPN-public-subnet1

  SSLVPNPublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref SSLVPN
      CidrBlock: 10.1.2.0/24
      AvailabilityZone:
        Fn::Select:
          - 1
          - Fn::GetAZs: ""
      Tags:
      - Key: Name
        Value: !Sub SSLVPN-public-subnet2

# 创建私有子网
  SSLVPNPrivateSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref SSLVPN
      CidrBlock: 10.1.3.0/24
      AvailabilityZone:
        Fn::Select:
          - 0
          - Fn::GetAZs: ""
      Tags:
      - Key: Name
        Value: !Sub SSLVPN-private-subnet1

  SSLVPNPrivateSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref SSLVPN
      CidrBlock: 10.1.4.0/24
      AvailabilityZone:
        Fn::Select:
          - 1
          - Fn::GetAZs: ""
      Tags:
      - Key: Name
        Value: !Sub SSLVPN-private-subnet2

  SSLVPNPrivateSubnet3:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref SSLVPN
      CidrBlock: 10.1.5.0/24
      AvailabilityZone:
        Fn::Select:
          - 0
          - Fn::GetAZs: ""
      Tags:
      - Key: Name
        Value: !Sub SSLVPN-private-subnet3

  SSLVPNPrivateSubnet4:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref SSLVPN
      CidrBlock: 10.1.6.0/24
      AvailabilityZone:
        Fn::Select:
          - 1
          - Fn::GetAZs: ""
      Tags:
      - Key: Name
        Value: !Sub SSLVPN-private-subnet4

#-----------创建路由表及关联子网-----------#

# 公有子网路由表及关联
  SSLVPNPublicRouteTable1:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref SSLVPN
      Tags:
        - Key: Name
          Value: !Sub SSLVPN-public-routetable

  SSLVPNPublicRouteTable1Association1:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      RouteTableId: !Ref SSLVPNPublicRouteTable1
      SubnetId: !Ref SSLVPNPublicSubnet1

  SSLVPNPublicRouteTable1Association2:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      RouteTableId: !Ref SSLVPNPublicRouteTable1
      SubnetId: !Ref SSLVPNPublicSubnet2

# 私有子网路由表及关联
  SSLVPNPrivateRouteTable1:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref SSLVPN
      Tags:
        - Key: Name
          Value: !Sub SSLVPN-private-routetable1

  SSLVPNPrivateRouteTable1Association:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      RouteTableId: !Ref SSLVPNPrivateRouteTable1
      SubnetId: !Ref SSLVPNPrivateSubnet1

  SSLVPNPrivateRouteTable2:
    Type: "AWS::EC2::RouteTable"
    Properties:
      VpcId: !Ref SSLVPN
      Tags:
        - Key: Name
          Value: !Sub SSLVPN-private-routetable2

  SSLVPNPrivateRouteTable2Association:
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties:
      RouteTableId: !Ref SSLVPNPrivateRouteTable2
      SubnetId: !Ref SSLVPNPrivateSubnet2


#-----------NAT Gateway-----------#
  SSLVPNNatGatewayEip1:
     Type: AWS::EC2::EIP
     Properties:
        Tags:
        - Key: Name
          Value: !Sub SSLVPN-natgateway-eip1

  SSLVPNNatGateway1:
     Type: AWS::EC2::NatGateway
     Properties:
        AllocationId: !GetAtt SSLVPNNatGatewayEip1.AllocationId
        SubnetId: !Ref SSLVPNPublicSubnet1
        Tags:
        - Key: Name
          Value: !Sub SSLVPN-natgateway1

  SSLVPNNatGatewayEip2:
     Type: AWS::EC2::EIP
     Properties:
        Tags:
        - Key: Name
          Value: !Sub SSLVPN-natgateway-eip2

  SSLVPNNatGateway2:
     Type: AWS::EC2::NatGateway
     Properties:
        AllocationId: !GetAtt SSLVPNNatGatewayEip2.AllocationId
        SubnetId: !Ref SSLVPNPublicSubnet2
        Tags:
        - Key: Name
          Value: !Sub SSLVPN-natgateway2

#---------------路由表---------------#

# 公有子网添加路由
  SSLVPNPublicSubnetRoute1:
    Type: "AWS::EC2::Route"
    DependsOn: SSLVPNIgw
    Properties:
     RouteTableId: !Ref SSLVPNPublicRouteTable1
     DestinationCidrBlock: 0.0.0.0/0
     GatewayId: !Ref SSLVPNIgw

  SSLVPNPublicSubnetRoute2:
    Type: "AWS::EC2::Route"
    DependsOn: Asav1OutsideEni
    Properties:
     RouteTableId: !Ref SSLVPNPublicRouteTable1
     DestinationCidrBlock: 10.2.1.0/24
     NetworkInterfaceId: !Ref Asav1OutsideEni

  SSLVPNPublicSubnetRoute3:
    Type: "AWS::EC2::Route"
    DependsOn: Asav1OutsideEni
    Properties:
     RouteTableId: !Ref SSLVPNPublicRouteTable1
     DestinationCidrBlock: 10.2.2.0/24
     NetworkInterfaceId: !Ref Asav2OutsideEni


# 私有子网添加路由
  SSLVPNPrivate1SubnetRoute1:
     DependsOn: SSLVPNNatGateway1
     Type: AWS::EC2::Route
     Properties:
        RouteTableId: !Ref SSLVPNPrivateRouteTable1
        DestinationCidrBlock: '0.0.0.0/0'
        NatGatewayId: !Ref SSLVPNNatGateway1

  SSLVPNPrivate1SubnetRoute2:
    Type: "AWS::EC2::Route"
    DependsOn: Asav1OutsideEni
    Properties:
     RouteTableId: !Ref SSLVPNPrivateRouteTable1
     DestinationCidrBlock: 10.2.1.0/24
     NetworkInterfaceId: !Ref Asav1OutsideEni

  SSLVPNPrivate1SubnetRoute3:
    Type: "AWS::EC2::Route"
    DependsOn: Asav1OutsideEni
    Properties:
     RouteTableId: !Ref SSLVPNPrivateRouteTable1
     DestinationCidrBlock: 10.2.2.0/24
     NetworkInterfaceId: !Ref Asav2OutsideEni

  SSLVPNPrivate2SubnetRoute1:
     DependsOn: SSLVPNNatGateway2
     Type: AWS::EC2::Route
     Properties:
        RouteTableId: !Ref SSLVPNPrivateRouteTable2
        DestinationCidrBlock: '0.0.0.0/0'
        NatGatewayId: !Ref SSLVPNNatGateway2

  SSLVPNPrivate2SubnetRoute2:
    Type: "AWS::EC2::Route"
    DependsOn: Asav1OutsideEni
    Properties:
     RouteTableId: !Ref SSLVPNPrivateRouteTable2
     DestinationCidrBlock: 10.2.1.0/24
     NetworkInterfaceId: !Ref Asav1OutsideEni

  SSLVPNPrivate2SubnetRoute3:
    Type: "AWS::EC2::Route"
    DependsOn: Asav1OutsideEni
    Properties:
     RouteTableId: !Ref SSLVPNPrivateRouteTable2
     DestinationCidrBlock: 10.2.2.0/24
     NetworkInterfaceId: !Ref Asav2OutsideEni

#-----------创建安全组----------#
  SSLVPNBastionSg:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: SG to test ping
      VpcId: !Ref SSLVPN
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: 22
        ToPort: 22
        CidrIp: 0.0.0.0/0
      - IpProtocol: icmp
        FromPort: -1
        ToPort: -1
        CidrIp: 0.0.0.0/0
      - IpProtocol: -1
        FromPort: -1
        ToPort: -1
        CidrIp: 10.1.0.0/16
      Tags:
        - Key: Name
          Value: !Sub SSLVPN-bastion-sg

  AsavSg:
    Type: AWS::EC2::SecurityGroup
    DependsOn: SSLVPN
    Properties:
      GroupDescription: Permit SSH HTTP/8443 ICMP
      VpcId:
         Ref: SSLVPN
      SecurityGroupIngress:
      - IpProtocol: -1
        FromPort: -1
        ToPort: -1
        CidrIp: 10.1.0.0/16
      - IpProtocol: tcp
        FromPort: 22
        ToPort: 22
        CidrIp: 0.0.0.0/0
      - IpProtocol: tcp
        FromPort: 8443
        ToPort: 8443
        CidrIp: 0.0.0.0/0
      - IpProtocol: tcp
        FromPort: 443
        ToPort: 443
        CidrIp: 0.0.0.0/0
      - IpProtocol: icmp
        FromPort: -1
        ToPort: -1
        CidrIp: 0.0.0.0/0

# --------------创建ASA1接口--------------

  Asav1OutsideEip:
    Type: "AWS::EC2::EIP"
    Properties:
      Tags:
        - Key: Name
          Value: asav1-outside-eip

  Asav1OutsideEni:
    Type: "AWS::EC2::NetworkInterface"
    Properties:
      SourceDestCheck: 'false'
      GroupSet:
        - Ref: "AsavSg"
      SubnetId:
        Ref: "SSLVPNPublicSubnet1"
      Tags:
        - Key: Name
          Value: asav1-outside-eni

  Asav1InsideEni:
    Type: "AWS::EC2::NetworkInterface"
    Properties:
      SourceDestCheck: 'false'
      GroupSet:
        - Ref: "AsavSg"
      SubnetId:
        Ref: "SSLVPNPrivateSubnet1"
      Tags:
        - Key: Name
          Value: asav1-inside-eni

  Asav1MgmtEni:
    Type: "AWS::EC2::NetworkInterface"
    Properties:
      GroupSet:
        - Ref: "AsavSg"
      SubnetId:
        Ref: "SSLVPNPrivateSubnet3"
      Tags:
        - Key: Name
          Value: asav1-mgmt-eni

  Asav1OutsideEniAssociation:  # 关联公网IP到弹性接口
    DependsOn: Asav1Instance
    Type: AWS::EC2::EIPAssociation
    Properties:
      AllocationId: !GetAtt Asav1OutsideEip.AllocationId
      NetworkInterfaceId: !Ref Asav1OutsideEni

# --------------创建Asav2接口--------------
  Asav2OutsideEip:
    Type: "AWS::EC2::EIP"
    Properties:
      Tags:
        - Key: Name
          Value: asav2-outside-eip

  Asav2OutsideEni:
    Type: "AWS::EC2::NetworkInterface"
    Properties:
      SourceDestCheck: 'false'
      GroupSet:
        - Ref: "AsavSg"
      SubnetId:
        Ref: "SSLVPNPublicSubnet2"
      Tags:
        - Key: Name
          Value: asav2-outside-eni

  Asav2InsideEni:
    Type: "AWS::EC2::NetworkInterface"
    Properties:
      SourceDestCheck: 'false'
      GroupSet:
        - Ref: "AsavSg"
      SubnetId:
        Ref: "SSLVPNPrivateSubnet2"
      Tags:
        - Key: Name
          Value: asav2-inside-eni

  Asav2MgmtEni:
    Type: "AWS::EC2::NetworkInterface"
    Properties:
      GroupSet:
        - Ref: "AsavSg"
      SubnetId:
        Ref: "SSLVPNPrivateSubnet4"
      Tags:
        - Key: Name
          Value: asav2-mgmt-eni

  Asav2OutsideEniAssociation:  # 关联公网IP到弹性接口
    DependsOn: Asav2Instance
    Type: AWS::EC2::EIPAssociation
    Properties:
      AllocationId: !GetAtt Asav2OutsideEip.AllocationId
      NetworkInterfaceId: !Ref Asav2OutsideEni

# --------------实例配置--------------

# 创建ASAv1实例
  Asav1Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !FindInMap [RegionMap, !Ref "AWS::Region", AsavBYOL9181]
      NetworkInterfaces:
        -
          NetworkInterfaceId: !Ref Asav1MgmtEni
          DeviceIndex: 0
        -
          NetworkInterfaceId: !Ref Asav1OutsideEni
          DeviceIndex: 1
        -
          NetworkInterfaceId: !Ref Asav1InsideEni
          DeviceIndex: 2
      InstanceType: c5.xlarge
      Tags:
        - Key: Name
          Value: SSLVPN-cisco-asav1
      KeyName: !Ref MyKeyPair
      UserData:
        Fn::Base64:
          !Sub |
          ! ASA Version 9.18(1)
          hostname asav
          enable password democisco
          username asavuser password PBTjhqPgdrYZWx6EwAn privilege 15
          aaa authentication ssh console LOCAL
          !
          interface Management0/0
           management-only
           no shutdown
           nameif management
           security-level 100
          !
          interface TenGigabitEthernet0/0
          no shutdown
          nameif outside
          ip address dhcp
          !
          interface TenGigabitEthernet0/1
          no shutdown
          nameif inside
          ip address dhcp
          !
          route outside 0 0 10.1.1.1
          !
          crypto key generate rsa modulus 2048
          !
          ssh 0 0 management
          ssh 0 0 outside
          ssh timeout 60
          !
          logging enable
          logging timestamp
          logging buffered informational
          !
          dns domain-lookup outside
          DNS server-group DefaultDNS
           name-server 114.114.114.114
          !
          clock timezone Beijing 8
          name 114.118.7.163 ntp.ntsc.ac.cn
          ntp server ntp.ntsc.ac.cn source outside
          !
          http 0 0 management
          http 0 0 outside
          http server enable
          !
          same-security-traffic permit inter-interface
          same-security-traffic permit intra-interface
          !
          access-list anyconnect_split standard permit 10.1.0.0 255.255.0.0
          access-list anyconnect_split standard permit 10.3.0.0 255.255.0.0
          ip local pool anyconnect_client_pool 10.2.1.1-10.2.1.254 mask 255.255.255.0
          !
          webvpn
           port 8443
           enable outside
           anyconnect enable
          !
          group-policy anyconnect internal
          group-policy anyconnect attributes
           vpn-idle-timeout 7200
           vpn-session-timeout 7200
           vpn-tunnel-protocol ssl-client ssl-clientless
           split-tunnel-policy tunnelspecified
           split-tunnel-network-list value anyconnect_split
           address-pools value anyconnect_client_pool
          !
          username vpnuser password vpnpassword
          username vpnuser attributes
           vpn-group-policy anyconnect

# 创建Asav2实例
  Asav2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !FindInMap [RegionMap, !Ref "AWS::Region", AsavBYOL9181]
      NetworkInterfaces:
        -
          NetworkInterfaceId: !Ref Asav2MgmtEni
          DeviceIndex: 0
        -
          NetworkInterfaceId: !Ref Asav2OutsideEni
          DeviceIndex: 1
        -
          NetworkInterfaceId: !Ref Asav2InsideEni
          DeviceIndex: 2
      InstanceType: c5.xlarge
      Tags:
        - Key: Name
          Value: SSLVPN-cisco-asav2
      KeyName: !Ref MyKeyPair
      UserData:
        Fn::Base64:
          !Sub |
          ! ASA Version 9.18(1)
          hostname asav
          enable password democisco
          username asavuser password PBTjhqPgdrYZWx6EwAn privilege 15
          aaa authentication ssh console LOCAL
          !
          interface Management0/0
           management-only
           no shutdown
           nameif management
           security-level 100
          !
          interface TenGigabitEthernet0/0
           no shutdown
           nameif outside
           ip address dhcp
          !
          interface TenGigabitEthernet0/1
           no shutdown
           nameif inside
           ip address dhcp
          !
          route outside 0 0 10.1.2.1
          !
          crypto key generate rsa modulus 2048
          !
          ssh 0 0 management
          ssh 0 0 outside
          ssh timeout 60
          !
          logging enable
          logging timestamp
          logging buffered informational
          !
          dns domain-lookup outside
          DNS server-group DefaultDNS
           name-server 114.114.114.114
          !
          clock timezone Beijing 8
          name 114.118.7.163 ntp.ntsc.ac.cn
          ntp server ntp.ntsc.ac.cn source outside
          !
          http 0 0 management
          http 0 0 outside
          http server enable
          !
          same-security-traffic permit inter-interface
          same-security-traffic permit intra-interface
          !
          access-list anyconnect_split standard permit 10.1.0.0 255.255.0.0
          access-list anyconnect_split standard permit 10.3.0.0 255.255.0.0
          ip local pool anyconnect_client_pool 10.2.2.1-10.2.2.254 mask 255.255.255.0
          !
          webvpn
           port 8443
           enable outside
           anyconnect enable
          !
          group-policy anyconnect internal
          group-policy anyconnect attributes
           vpn-idle-timeout 7200
           vpn-session-timeout 7200
           vpn-tunnel-protocol ssl-client ssl-clientless
           split-tunnel-policy tunnelspecified
           split-tunnel-network-list value anyconnect_split
           address-pools value anyconnect_client_pool
          !
          username vpnuser password vpnpassword
          username vpnuser attributes
           vpn-group-policy anyconnect

  SSLVPNApp1:
    Type: AWS::EC2::Instance
    Properties:
      IamInstanceProfile: !Ref BastionSsmProfile
      ImageId: !Ref EC2InstanceAmiId
      KeyName: !Ref MyKeyPair
      InstanceType: t2.micro
      SecurityGroupIds:
        - !Ref SSLVPNBastionSg
      SubnetId: !Ref SSLVPNPrivateSubnet1
      Tags:
        - Key: Name
          Value: !Sub SSLVPN-App1

  SSLVPNApp2:
    Type: AWS::EC2::Instance
    Properties:
      IamInstanceProfile: !Ref BastionSsmProfile
      ImageId: !Ref EC2InstanceAmiId
      KeyName: !Ref MyKeyPair
      InstanceType: t2.micro
      SecurityGroupIds:
        - !Ref SSLVPNBastionSg
      SubnetId: !Ref SSLVPNPrivateSubnet2
      Tags:
        - Key: Name
          Value: !Sub SSLVPN-App2

将CloudFormation代码复制到文本文件中,创建堆栈。

设置堆栈名称,选择EC2的密钥。

允许创建IAM资源,提交创建堆栈请求。

需要5分钟左右,堆栈创建完成。

3.2 Router 53 加权路由策略简介

Router53可以配置基于权重(Weight)的路由策略,流量会按照指定的比例路由到不同的资源上。

权重可以在0~255之间任意指定,任何一个资源记录被选中的概率,取决于其占组中所有记录总权重的比例。例如为www.example.com 设置了三个A记录,权重设置为1(25%)、1(25%)、2(50%)(总和=4),平均而言,Route53选择前面两个A记录每一个的时间为四分之一,选择第三个记录的时间为二分之一。如果将权重设置为100、100、200,流量分配的比例不会发生变化。

基于权重的路由策略,常见的应用场景:

  • 区域(region)间负载均衡。NLB无法在区域间进行负载均衡,通过DNS加权路由策略可以在区域间进行负载均衡。
  • A/B测试和试用软件新的版本,例如将测试环境设置1%的权重,将会有1%的流量引导到测试环境。

加权路由策略支持关联健康检查,当资源记录关联的健康检查失败后,Router53不会返回对应的资源记录。[1]

3.3 Router 53 配置加权路由策略

记录防火墙的公网IP地址,这里asav1的地址为35.73.113.152,asav2的地址为54.65.246.122。

创建Route 53 健康检查。

配置健康监测信息,设置协议为HTTPS,IP地址设置为asav1的公网地址,端口设置为8443(SSLVPN的端口)。测试环境下,为了快速切换,设置10秒检测一次,2次探测失败修改状态。

配置健康监测信息,设置协议为HTTPS,IP地址设置为asav2的公网地址,端口设置为8443(SSLVPN的端口)。测试环境下,为了快速切换,设置10秒检测一次,2次探测失败修改状态。

查看健康检测策略。

创建资源记录。

在Router 53上创建A记录,域名为vpn.demonwcd.com,指向asav1的IP地址35.73.113.152。路由策略设置为基于权重负载,权重设置为100。关联asav1的健康监测策略。设置一个唯一的Record ID值。

继续在Router 53上创建A记录,域名为vpn.demonwcd.com,指向asav2的IP地址54.65.246.122。路由策略设置为基于权重负载,权重设置为100。关联asav2的健康监测策略。设置一个唯一的Record ID值。

查看创建的两个A记录,域名vpn.demonwcd.com分别指向两个asav的公网IP地址。因为设置了相同的权重,所以DNS请求会在不同IP之间进行负载均衡。

可以访问https://ipw.cn/dns,用于测试国内不同地区对于`vpn.demonwcd.com`域名的解析情况 [2]。这里可以看出不同地区的DNS请求,会解析到asav1或者asav2的公网IP地址,TTL是默认的300秒。

或者访问https://dnschecker.org,用于测试全球不同地区对于对于vpn.demonwcd.com域名的解析情况 [3]。

也可以通过SSM的方式登录APP1,多次解析vpn.demonwcd.com域名,会解析到asav1或者asav2的公网IP地址。通过上述测试,可以确认DNS请求会进行负载分担。

可以进一步测试故障切换的场景,这里可以通过Stop asav1来模拟实例故障。点击Stop实例后,状态从Stopping变化为Stopped需要两分钟左右,只有状态成为为Stopped,才会停止转发流量,从而健康监测失败。

如果想要更快的模拟故障,也可以停止asav1的webvpn服务,这会更快的模拟出故障。

ciscoasa(config)# webvpn
ciscoasa(config-webvpn)# no enable outside

当Router53 发现82%以上的监测点,都检测到Failure状态,那么资源将会被设置为Unhealthy。[4]

再次查询vpn.demonwcd.com域名,现在只会解析到asav2的公网IP地址。

全球不同地区只只会解析到asav2的公网IP地址,可以确认基于DNS的故障切换是有效的。

四、SSLVPN拨号测试

通过Cisco Secure Client 5.0.00556 进行拨号测试(需要登录思科账号,并且有相应权限才能下载)[5] ,Secure Client客户端是AnyConnect的升级版本。AnyConnect Client和Secure Client安装包已经放在百度网盘 [6]。

这里防火墙没有导入受信任的证书,所以客户端会有证书告警提示。

输入用户名为vpnuser,密码为vpnpass

VPN拨入成功。

拨入后查看获取到的IP地址,这里拨入的是asav1,ping测试到APP1和APP2的连通性。

在客户端上多次拨入测试,当域名解析到asav2上时,也会拨入到asav2上,同样ping测试到APP1和APP2的连通性。

五、文档链接