Introduction to VPN at Scale for Financial Services

Remote access to AWS accounts with security, traceability, and audit requirements.

For financial services organizations, there are many unique security, regulatory, and compliance obligations institutions face on a daily basis. This is especially relevant when it comes to their VPN strategy. Historically, providing employees with VPN access into secure environments within AWS has required the use of third party solutions that need to be built and managed. With AWS’s VPN solution, AWS provides these capabilities as a service. With AWS VPN, companies can implement a solution that provides remote access to their AWS accounts while still being able to enforce security, traceability, and auditability requirements.

Some of the business benefits that can be gained from using a combination of AWS Client VPN and Transit Gateway:

  • Allows a business and its employees to work from anywhere and be able to access their required AWS resources with multiple OpenVPN compatible software appliances
  • Reduces the overhead and cost of managing VPN access for many AWS accounts by utilizing a centralized AWS account that acts as the VPN network hub for an organization. AWS Client VPN is an AWS managed service that scales accordingly to meet VPN access load.
  • Provides a secure VPN connection with compatibility of employee authentication through existing Active Directory or other authentication protocols.
  • Provides flexibility to integrate this type of solution with connection to on-premise resources.

In this blog post, we’ll go over using a combination of AWS Client VPN and AWS Transit Gateway to create a centralized network hub account VPC that acts as a router for VPN communication to many VPCs across an AWS Organization. By the end of this post, you will learn how you can create a centralized hub-and-spoke VPN network strategy, that can scale with your organization, providing your employees with remote VPN access from anywhere in a secure fashion.

Solution

With the given scenario above, let’s look at how we can solve this with a combination of AWS services and AWS Client VPN to provide an automated solution for configuring a centralized remote VPN network within AWS.

ProblemSolutionAWS Services
Remote VPN access into AWSCreating an AWS Client VPN Endpoint to serve as the endpoint for remote clients to connect to and gain VPN access into AWSAWS Client VPN
Authentication for remote VPN clientsIntegrating AWS Simple Active Directory as the mechanism for authenticating against the Client VPN Endpoint.AWS Simple Active Directory
Overhead of maintaining VPN accessEstablishing a dedicated network hub account and VPC to act as the centralized point of remote client VPN access. This centralized network hub account is where remote clients can connect to and communicate with other VPCs using AWS Transit Gateway as the routing mechanism.AWS VPC, AWS Transit Gateway
Segregation of user access into specific networksWith combining AWS Simple Active Directory and AWS Client VPN, you are able to authorize different users/groups to specific VPC networks that are attached to the centralized network hub VPC Transit Gateway.AWS Client VPN Authorization Rules, AWS Simple Active Directory
Manual configuration of VPN access to other VPCsImplementing a solution of using a combination of AWS Service Catalog and AWS CloudFormation to provide automation for configuring remote VPN access to other VPCsAWS Service Catalog, AWS CloudFormation

NOTE: The exercises throughout this blog post can only be completed in either the “us-east-1” or “us-west-2” AWS Regions due to limitations with AWS SimpleAD.

Setup

Here we will create an AWS Cloud9 Environment for you to execute the activities described within this blog post:

  1. Go to the AWS Cloud9 console and select Create Environment
  2. Enter a Name and Description
  3. Select Next Step
  4. Select Create a new instance for the environment (EC2)
  5. Select t2.micro
  6. Leave the Cost-saving setting at the After 30-minute (default) option enabled
  7. Select Next Step
  8. Review best practices and select Create Environment
  9. Once your Cloud environment has been launched, open a new terminal in Cloud9

Clone the Repository

From your Cloud9 terminal, run the following commands:

  1. git clone https://github.com/VerticalRelevance/aws-client-vpn-factory.git
  2. cd aws-client-vpn-factory

Creating a Server Certificate for AWS ACM

We will need to create a server certificate and upload it to the AWS Certificate Manager. This certificate will be used for the Client VPN Endpoint and is a requirement when creating a Client VPN Endpoint.

From  your Cloud9 terminal, run the following commands:

  1. export REGION=<your_aws_region>
  2. ./create_and_import_acm_cert.sh
  3. At the terminal prompt where it states “Common Name (eg: your user, host, or server name) [Easy-RSA CA]:” just hit “Enter” to continue
#!/bin/bash

if [ -z $REGION ];
  then echo "ERROR: Please set 'REGION' to your current AWS Region!" && exit 1;
fi
ROOT_DIR=`pwd`

git clone https://github.com/OpenVPN/easy-rsa.git
cd easy-rsa/easyrsa3
./easyrsa init-pki
./easyrsa build-ca nopass
./easyrsa build-server-full clientvpn-ad-test nopass
mkdir custom_folder/
cp pki/ca.crt custom_folder/
cp pki/issued/clientvpn-ad-test.crt custom_folder/
cp pki/private/clientvpn-ad-test.key custom_folder/
cd custom_folder/

# Import server cert to ACM
aws acm import-certificate \
  --certificate fileb://clientvpn-ad-test.crt \
  --private-key fileb://clientvpn-ad-test.key \
  --certificate-chain fileb://ca.crt \
  --tags Key=Name,Value=clientvpn-ad-test \
  --region $REGION
cd $ROOT_DIR
rm -rf easy-rsa

This will import a server certificate with a domain name of “clientvpn-ad-test up to AWS ACM.

Install OpenVPN Tool

For the exercises in this blog post, I will be using Tunnelblick OpenVPN client to test VPN connectivity later on. You can, however, use your choice of OpenVPN compatible tool to test out the VPN connectivity. You can find the downloads page for Tunnelblick here
Note: The installation of the Tunnelblick client is meant to be done on your own local workstation as you will use this later on to test out the Client VPN connectivity we’re building.

Remote VPN Scaling – Multi-Account Strategy

For this blog post, we will use a single AWS account to reduce the complexity, but we recommend investing in building a multi-account remote VPN solution that can scale with your business. Below is a sample architecture that depicts the types of possibilities that can be achieved through an automated multi-account remote VPN strategy.

Figure 1 – Potential Multi-Account Client VPN Strategy

The networking would be configured through the use of hub and spoke VPCs. This is done through:

  1. An AWS Client VPN is provisioned in a Central Network Account and is associated with a centralized hub VPC. The AWS Client VPN is integrated with Active Directory which enables the organization to use an existing Active Directory as the authentication mechanism for employees to gain remote access to the AWS Client VPN. The AWS Client VPN can be configured to only allow access to specific networks based on Active Directory users and/or groups. This allows an organization to maintain secure and segregated network access based on existing identity structure.
  2. A centralized hub VPC is deployed in the Central Network Account that acts as a router for remote client VPN traffic to other VPCs across the AWS Organization.
  3. An AWS Transit Gateway is provisioned in the Central Network Account that is attached to the centralized hub VPC. This Transit Gateway is where remote client VPN traffic will flow to and from the centralized hub VPC and other VPCs across the organization. The Transit Gateway resource can also be shared with other member accounts within the AWS Organization so that VPCs within those accounts have access to it.
  4. An AWS Service Catalog Product for Transit Gateway Attachment and a Lambda Function are shared/deployed to other accounts via CloudFormation StackSets
  5. An AWS Service Catalog Products for Client VPN Authorization Ingress and Route Configuration are provisioned automatically with the use of Lambda Functions. This provides an automated mechanism for configuring the centralized AWS Client VPN hub VPC to connect to new/existing VPCs within the AWS Organization.
  6. An AWS Service Catalog Product for Transit Gateway Attachments are provisioned automatically for specific VPCs in other accounts based on Tag look-ups or other identifying metadata.

With a multi-account strategy, you can also take advantage of the AWS Transit Gateway network manager to provide global visibility of your network resources.

Centralized Hub VPN VPC

In this section, we will deploy the CodePipeline that creates:

  1. The main centralized hub VPC with SimpleAD named “Main Network Hub Client VPN VPC”
  2. Client VPN Endpoint associated with the hub VPC named “AWS Client VPN authentication with AWS Simple AD”
  3. Transit Gateway attached to the hub VPC named “Central network hub TGW”
  4. Two private subnets and the Route Table associated with them (also associated with the Client VPN) named “Main Network Hub VPC Client VPN Subnet Route Table”

With the use of predefined Python scripts utilizing the AWS Python SDK, this pipeline will launch the following CloudFormation templates located within the repo:

cfn_templates/network_account_main_vpc_with_simple_ad.yml

  
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  Organization:
    Type: String
    Default: Vertical Relevance
  DomainName:
    Description: FQDN of the domain for this directory
    Type: String
    Default: clientvpnad.verticalrelevance.com
  SimpleADShortName:
    Description: Netbios name of the domain for this directory
    Type: String
    Default: clientvpnad
  SimpleADPW:
    Description: Domain admin Password
    Type: String
    NoEcho: true
    Default: Password123!
  Size:
    Description: Size of the Simple AD
    Type: String
    AllowedValues:
      - Small
      - Large
    Default: Small
  VpnVpcCidr:
    Description: CIDR Block of central hub VPN VPC
    Type: String
    Default: '12.0.0.0/16'
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
    AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$"
  PrivateVpnRouterSubnet1:
    Description: Private Subnet to be used as target network Subnet for Client VPN
    Type: String
    Default: '12.0.1.0/24'
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
    AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$"
  PrivateVpnRouterSubnet2:
    Description: Private Subnet to be used as target network Subnet for Client VPN
    Type: String
    Default: '12.0.2.0/24'
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
    AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$"
  NatGatewayVpnRouterSubnet:
    Description: Private Subnet to be used as target network Subnet for Client VPN
    Type: String
    Default: '12.0.3.0/24'
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
    AllowedPattern: "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/(1[6-9]|2[0-8]))$"
  BusinessUnit:
    Description: AWS Organizational Unit
    Type: String
    Default: Developer
Resources:
    VPC:
        Type: AWS::EC2::VPC
        Properties:
            EnableDnsSupport: true
            EnableDnsHostnames: true
            CidrBlock: !Ref VpnVpcCidr
            Tags:
              - Key: Name
                Value: !Sub 'Main Network Hub Client VPN VPC'

    SubnetOne:
        Type: AWS::EC2::Subnet
        Properties:
            AvailabilityZone:
                Fn::Select:
                - 0
                - Fn::GetAZs: {Ref: 'AWS::Region'}
            VpcId: !Ref 'VPC'
            CidrBlock: !Ref PrivateVpnRouterSubnet1
            Tags:
              - Key: Name
                Value: !Sub 'Main Network Hub Client VPN VPC Private Subnet 1'

    SubnetTwo:
        Type: AWS::EC2::Subnet
        Properties:
            AvailabilityZone:
                Fn::Select:
                - 1
                - Fn::GetAZs: {Ref: 'AWS::Region'}
            VpcId: !Ref 'VPC'
            CidrBlock: !Ref PrivateVpnRouterSubnet2
            Tags:
              - Key: Name
                Value: !Sub 'Main Network Hub Client VPN VPC Private Subnet 2'

    InternetGateway:
        Type: AWS::EC2::InternetGateway

    GatewayAttachement:
        Type: AWS::EC2::VPCGatewayAttachment
        Properties:
            VpcId: !Ref 'VPC'
            InternetGatewayId: !Ref 'InternetGateway'

    RouteTable:
        Type: AWS::EC2::RouteTable
        Properties:
            VpcId: !Ref 'VPC'
            Tags:
              - Key: Name
                Value: Main Network Hub VPC Client VPN Subnet Route Table

    SubnetOneRouteTableAssociation:
        Type: AWS::EC2::SubnetRouteTableAssociation
        Properties:
            SubnetId: !Ref SubnetOne
            RouteTableId: !Ref RouteTable

    SubnetTwoRouteTableAssociation:
        Type: AWS::EC2::SubnetRouteTableAssociation
        Properties:
            SubnetId: !Ref SubnetTwo
            RouteTableId: !Ref RouteTable

    NatSubnetRouteTableAssociation:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
        SubnetId: !Ref NatGatewaySubnet
        RouteTableId: !Ref NatGatewayRouteTable

    # Nat Gateway Config
    NatGatewaySubnet:
      Type: AWS::EC2::Subnet
      Properties:
        AvailabilityZone:
          Fn::Select:
            - 1
            - Fn::GetAZs: {Ref: 'AWS::Region'}
        VpcId: !Ref 'VPC'
        CidrBlock: !Ref NatGatewayVpnRouterSubnet
        Tags:
          - Key: Name
            Value: !Sub 'Main Network Hub Client VPN VPC NAT Gateway Subnet'

    NatGateway:
      Type: AWS::EC2::NatGateway
      Properties:
        AllocationId: !GetAtt NatGatewayEIP.AllocationId
        SubnetId: !Ref NatGatewaySubnet
        Tags:
          - Key: Organization
            Value: !Ref Organization

    NatGatewayEIP:
      Type: AWS::EC2::EIP
      Properties:
        Domain: vpc

    NatGatewayRouteTable:
      Type: AWS::EC2::RouteTable
      Properties:
        VpcId: !Ref 'VPC'

    NatGatewayRoute:
      Type: AWS::EC2::Route
      DependsOn: GatewayAttachement
      Properties:
        RouteTableId: !Ref 'NatGatewayRouteTable'
        DestinationCidrBlock: '0.0.0.0/0'
        GatewayId: !Ref 'InternetGateway'

    PrivateSubnetNatGatewayRoute:
      Type: AWS::EC2::Route
      DependsOn: GatewayAttachement
      Properties:
        RouteTableId: !Ref 'RouteTable'
        DestinationCidrBlock: '0.0.0.0/0'
        NatGatewayId: !Ref 'NatGateway'

# Simple AD Setup
    SimpleAD:
        Type: 'AWS::DirectoryService::SimpleAD'
        Properties:
            CreateAlias: false
            EnableSso: false
            Name: !Ref DomainName
            Password: !Ref SimpleADPW
            ShortName: !Ref SimpleADShortName
            Size: !Ref Size
            VpcSettings:
                SubnetIds:
                - !Ref SubnetOne
                - !Ref SubnetTwo
                VpcId: !Ref VPC

# Transit Gateway
    TransitGateway:
      Type: AWS::EC2::TransitGateway
      Properties:
        AutoAcceptSharedAttachments: enable
        DefaultRouteTableAssociation: enable
        DefaultRouteTablePropagation: enable
        Description: Central network hub TGW for VPN forwarding
        DnsSupport: enable
        Tags:
          - Key: Name
            Value: Central network hub TGW
        VpnEcmpSupport: enable

    TransitGatewayAttachment:
      Type: AWS::EC2::TransitGatewayAttachment
      Properties:
        SubnetIds:
          - !Ref SubnetOne
          - !Ref SubnetTwo
        Tags:
          - Key: Name
            Value: Central network hub TGW Attachment
        TransitGatewayId: !Ref TransitGateway
        VpcId: !Ref VPC

Outputs:
  VpcCidr:
    Description: ClientVpnTargetNetworkVpcCidr
    Value: !Ref VpnVpcCidr
  RouteTableId:
    Description: RouteTableId
    Value: !Ref RouteTable
  TransitGatewayId:
    Description: TransitGatewayId
    Value: !Ref TransitGateway
  BusinessUnit:
    Description: AWS Organizational Unit
    Value: !Ref BusinessUnit
  VpcID:
    Description: ID of VPC
    Value: !Ref VPC
  SubnetOneID:
    Description: ID of SubnetOne
    Value: !Ref SubnetOne
  SubnetTwoID:
    Description: ID of SubnetTwo
    Value: !Ref SubnetTwo
  DirectoryID:
    Description: ID of the SimpleAD
    Value: !Ref SimpleAD
  PrimaryDNS:
    Description: DNS IPs of the SimpleAD
    Value: !Select
      - '0'
      - !GetAtt
        - SimpleAD
        - DnsIpAddresses
  SecondaryDNS:
    Description: DNS IPs of the SimpleAD
    Value: !Select
      - '1'
      - !GetAtt
        - SimpleAD
        - DnsIpAddresses

cfn_templates/client_vpn_endpoint.yml

AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  VpnClientCidrRange:
    Type: String
    Description: CIDR Range for VPN Clients
    Default: '10.0.0.0/16'
  ServerCertificateArn:
    Type: String
    Description: ACM Server Certificate ARN used for Client VPN
  ClientVpnTargetNetworkVpcCidr:
    Type: String
    Description: Network account central VPC CIDR to be used as initial authorized route for Client VPN connection
  ClientVpnTargetNetworkVpc:
    Type: String
    Description: Network account central VPC ID for Client VPN connection association
  ClientVpnTargetNetworkSubnet1:
    Type: String
    Description: Network account central VPC Subnet ID for Client VPN connection association
  ClientVpnTargetNetworkSubnet2:
    Type: String
    Description: Network account central VPC Subnet ID for Client VPN connection association
  ActiveDirectoryId:
    Description: Active Directory ID for Client VPN Auth
    Type: String
  ActiveDirectoryDnsServer1:
    Description: Active Directory DNS Server 1
    Type: String
  ActiveDirectoryDnsServer2:
    Description: Active Directory DNS Server 2
    Type: String
  BusinessUnit:
    Description: AWS Organizational Unit
    Type: String
    Default: Business-Unit-1
  AuthorizeAllUsers:
    Description: Authorize all users in Active Directory for Client VPN Authorization Rule
    Type: String
    Default: true
  AccessGroupId:
    Description: Access Group ID in Active Directory for Client VPN Authorization Rule
    Type: String
    Default: ''
Conditions:
  AuthorizeAllUsersForVpnAuthRule: !And
    - !Equals [ !Ref AuthorizeAllUsers, true ]
    - !Equals [ !Ref AccessGroupId, '' ]

  AllowOnlyAccessGroupForVpnAuthRule: !And
    - !Equals [ !Ref AuthorizeAllUsers, false ]
    - !Not [!Equals [!Ref AccessGroupId, '']]
Resources:
    ClientVpnCloudWatchLogGroup:
      Type: AWS::Logs::LogGroup
      Properties:
        LogGroupName: 'clientvpn-with-ad-log'

    ClientVpnTargetNetworkAssociation:
      Type: AWS::EC2::ClientVpnTargetNetworkAssociation
      Properties:
        ClientVpnEndpointId: !Ref ClientVpnEndpoint
        SubnetId: !Ref ClientVpnTargetNetworkSubnet1
    ClientVpnTargetNetworkAssociation2:
      Type: AWS::EC2::ClientVpnTargetNetworkAssociation
      Properties:
        ClientVpnEndpointId: !Ref ClientVpnEndpoint
        SubnetId: !Ref ClientVpnTargetNetworkSubnet2

    ClientVpnEndpoint:
      DependsOn: ClientVpnCloudWatchLogGroup
      Type: AWS::EC2::ClientVpnEndpoint
      Properties:
        AuthenticationOptions:
          - ActiveDirectory:
              DirectoryId: !Ref ActiveDirectoryId
            Type: directory-service-authentication
        ClientCidrBlock: !Ref VpnClientCidrRange
        ConnectionLogOptions:
          CloudwatchLogGroup: !Ref ClientVpnCloudWatchLogGroup
          Enabled: true
        Description: AWS Client VPN authenticated with AWS Simple AD
        DnsServers:
          - !Ref ActiveDirectoryDnsServer1
          - !Ref ActiveDirectoryDnsServer2
        ServerCertificateArn: !Ref ServerCertificateArn
        TransportProtocol: tcp
        VpcId: !Ref ClientVpnTargetNetworkVpc
        VpnPort: 443
        TagSpecifications:
          - ResourceType: client-vpn-endpoint
            Tags:
              - Key: Name
                Value: AWS Client VPN authenticated with AWS Simple AD

    ClientVpnAuthorizationRule:
      DependsOn:
        - ClientVpnTargetNetworkAssociation
        - ClientVpnTargetNetworkAssociation2
      Type: AWS::EC2::ClientVpnAuthorizationRule
      Properties:
        AccessGroupId:
          !If [AllowOnlyAccessGroupForVpnAuthRule, !Ref AccessGroupId, !Ref "AWS::NoValue"]
        AuthorizeAllGroups:
          !If [AuthorizeAllUsersForVpnAuthRule, !Ref AuthorizeAllUsers, !Ref "AWS::NoValue"]
        ClientVpnEndpointId: !Ref ClientVpnEndpoint
        Description: Client VPN Authorization rule to allow usage of target network VPC as a route-forwarder to Transit Gateway
        TargetNetworkCidr: !Ref ClientVpnTargetNetworkVpcCidr

  # SSM Parameters
    ClientVpnEndpointSSM:
      Type: AWS::SSM::Parameter
      Properties:
        Description: !Sub 'Client VPN Endpoint for ${BusinessUnit}'
        Name: !Sub '/${BusinessUnit}/client-vpn-endpoint'
        Tier: Standard
        Type: String
        Value: !Ref ClientVpnEndpoint

Outputs:
  ClientVpnEndpoint:
    Description: ID of VPC
    Value: !Ref ClientVpnEndpoint

From your Cloud9 terminal, run the following commands:

  1. export GITHUB_TOKEN=<your_github_token>
  2. ./deploy_hub_vpc_pipeline.sh

Below is a diagram that depicts the infrastructure resources that have been created:

Figure 2 – Centralized Hub VPN VPC
  1. User hits the Client VPN Endpoint with OpenVPN compatible client.
  2. User authenticates with AWS Active Directory credentials and is granted an authorized route ingress based on user/group in Active Directory to the CIDR of the Network Hub VPC (12.0.0.0/16)
  3. Traffic from a remote client is forwarded through the Network Hub VPC Subnets that are associated with the Client VPN Endpoint
  4. The Network Hub VPC is attached to the centralized Transit Gateway “tgw-main” with an attachment named “TGW_ATTACH_1”.

Service Catalog Pipeline

In this section, we will deploy a CodePipeline that creates the following resources:

  1. An S3 Bucket named which will serve as the bucket to store the associated Service Catalog templates.
  2. A Service Catalog Portfolio/Product that consists of a Transit Gateway Attachment and other network configuration – named “AWS Transit Gateway Attachment”
  3. A Service Catalog Portfolio/Product that consists of a Client VPN authorization ingress rule and associated route set up to allow VPN communication to a new VPC – named “AWS Client VPN Auth and Route Config”

Service Catalog Transit Gateway Attachment CloudFormation template (cfn_templates/service-catalog/tgw_attachment/tgw_attachment.yml):

AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  CentralNetworkAccountVpnVpcCIDR:
    Description: VPC CIDR block of the central network VPN hub account VPC
    Type: String
  TgwAttachmentSubnet1:
    Type: String
    Description: Subnet ID to be associated with TGW attachment to VPC
  TgwAttachmentSubnet2:
    Type: String
    Description: Subnet ID to be associated with TGW attachment to VPC
  TransitGatewayId:
    Description: Transit Gateway ID of the central network VPN hub account TGW
    Type: String
  VpcId:
    Description: VPC Id that is getting attached to the central network VPN hub account TGW
    Type: String

# Conditional Parameters for Adding one or more routes to TGW
  RouteTableId1:
    Description: Subnet Route Table ID for TGW route to central network VPN hub account VPC CIDR - 1
    Type: String
    Default: ''
  RouteTableId2:
    Description: Subnet Route Table ID for TGW route to central network VPN hub account VPC CIDR - 2
    Type: String
    Default: ''
  RouteTableId3:
    Description: Subnet Route Table ID for TGW route to central network VPN hub account VPC CIDR - 3
    Type: String
    Default: ''

Conditions:
  CreateRouteForRouteTable1: !Not [!Equals [!Ref RouteTableId1, '']]
  CreateRouteForRouteTable2: !Not [!Equals [!Ref RouteTableId2, '']]
  CreateRouteForRouteTable3: !Not [!Equals [!Ref RouteTableId3, '']]

Resources:
  TransitGatewayAttachment:
    Type: AWS::EC2::TransitGatewayAttachment
    Properties:
      SubnetIds:
        - !Ref TgwAttachmentSubnet1
        - !Ref TgwAttachmentSubnet2
      Tags:
        - Key: Name
          Value: !Sub 'TGW Attachment for Test VPC to Main Network Hub VPC'
      TransitGatewayId: !Ref TransitGatewayId
      VpcId: !Ref VpcId

  TransitGatewayRoute1:
    DependsOn: TransitGatewayAttachment
    Condition: CreateRouteForRouteTable1
    Type: AWS::EC2::Route
    Properties:
      DestinationCidrBlock: !Ref CentralNetworkAccountVpnVpcCIDR
      RouteTableId: !Ref RouteTableId1
      TransitGatewayId: !Ref TransitGatewayId

  TransitGatewayRoute2:
    DependsOn: TransitGatewayAttachment
    Condition: CreateRouteForRouteTable2
    Type: AWS::EC2::Route
    Properties:
      DestinationCidrBlock: !Ref CentralNetworkAccountVpnVpcCIDR
      RouteTableId: !Ref RouteTableId2
      TransitGatewayId: !Ref TransitGatewayId

  TransitGatewayRoute3:
    DependsOn: TransitGatewayAttachment
    Condition: CreateRouteForRouteTable3
    Type: AWS::EC2::Route
    Properties:
      DestinationCidrBlock: !Ref CentralNetworkAccountVpnVpcCIDR
      RouteTableId: !Ref RouteTableId3
      TransitGatewayId: !Ref TransitGatewayId

Service Catalog Client VPN Route Setup CloudFormation template (cfn_templates/service-catalog/vpn/client_vpn_route_setup.yml):

AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  NewVpcCIDR:
    Type: String
    Description: CIDR Range of the new VPC that is being added to the Client VPN connection via TGW
  ClientVpnEndpoint:
    Type: String
    Description: Client VPN Endpoint
  ClientVpnTargetNetworkSubnet1:
    Type: String
    Description: Client VPN Target Network Subnet 1
  ClientVpnTargetNetworkSubnet2:
    Type: String
    Description: Client VPN Target Network Subnet 2
  TransitGatewayId:
    Description: Transit Gateway ID of the central network VPN hub account TGW
    Type: String
  RouteTableId:
    Description: Subnet Route Table ID of Client VPN Associated Target Network Subnets
    Type: String
  AuthorizeAllUsers:
    Description: Authorize all users in Active Directory for Client VPN Authorization Rule
    Type: String
    Default: true
  AccessGroupId:
    Description: Access Group ID in Active Directory for Client VPN Authorization Rule
    Type: String
    Default: ''

Conditions:
  AuthorizeAllUsersForVpnAuthRule: !And
    - !Equals [ !Ref AuthorizeAllUsers, true ]
    - !Equals [ !Ref AccessGroupId, '' ]

  AllowOnlyAccessGroupForVpnAuthRule: !And
    - !Equals [ !Ref AuthorizeAllUsers, false ]
    - !Not [!Equals [!Ref AccessGroupId, '']]

Resources:
  ClientVpnAuthorizationRule:
    Type: AWS::EC2::ClientVpnAuthorizationRule
    Properties:
      AccessGroupId:
        !If [AllowOnlyAccessGroupForVpnAuthRule, !Ref AccessGroupId, !Ref "AWS::NoValue"]
      AuthorizeAllGroups:
        !If [AuthorizeAllUsersForVpnAuthRule, !Ref AuthorizeAllUsers, !Ref "AWS::NoValue"]
      ClientVpnEndpointId: !Ref ClientVpnEndpoint
      Description: !Sub 'Client VPN Authorization rule to allow clients to access VPC with CIDR ${NewVpcCIDR} through Transit Gateway'
      TargetNetworkCidr: !Ref NewVpcCIDR

  ClientVpnRouteToNewVpc1:
    Type: AWS::EC2::ClientVpnRoute
    Properties:
      ClientVpnEndpointId: !Ref ClientVpnEndpoint
      DestinationCidrBlock: !Ref NewVpcCIDR
      TargetVpcSubnetId: !Ref ClientVpnTargetNetworkSubnet1

  ClientVpnRouteToNewVpc2:
    Type: AWS::EC2::ClientVpnRoute
    Properties:
      ClientVpnEndpointId: !Ref ClientVpnEndpoint
      DestinationCidrBlock: !Ref NewVpcCIDR
      TargetVpcSubnetId: !Ref ClientVpnTargetNetworkSubnet2

  ClientVpnVpcTargetNetworkSubnetsRoute:
    Type: AWS::EC2::Route
    Properties:
      DestinationCidrBlock: !Ref NewVpcCIDR
      RouteTableId: !Ref RouteTableId
      TransitGatewayId: !Ref TransitGatewayId

From your Cloud9 terminal, run the following commands:

  1. If you would like to give your IAM user permission to launch the Service Catalog Products, then run export LINKED_IAM_USER=<your_iam_user_name>
  2. If you would like to give a specific IAM Role permission to launch the Service Catalog Products, then run export LINKED_IAM_ROLE_ARN=<iam_role_arn>
    1. You can set both LINKED_IAM_USER and LINKED_IAM_ROLE_ARN if you would like to provide both with Service Catalog permissions.
  3. export GITHUB_TOKEN=<your_github_token>
  4. export BUCKET_NAME=<unique_bucket_name_for_service_catalog>
  5. ./deploy_service_catalog_pipeline.sh

Below is a diagram that depicts the pipeline and resources that have been created from the Service Catalog Pipeline:

Figure 3 – Service Catalog Pipeline
  1. AWS CodePipeline used for automating the testing and creation of Service Catalog Portfolios and Products
  2. Service Catalog Product for the AWS Transit Gateway Attachment named “AWS Transit Gateway Attachment”
  3. Service Catalog Product for the AWS Client VPN Auth and Route Config named “AWS Client VPN Auth and Route Config”

After the pipeline has validated the Service Catalog templates, it will reach a manual approval gate before continuing to provision the products. Approve the gate and let the pipeline continue.

After successful completion of this pipeline, we can see that there are now two Service Catalog products available:

Deploy the Business-Unit-1 VPC

In this section, we will deploy the “Business-Unit-1” VPC that will act as a new VPC created for a unit within the AWS Organization that will be connected to the centralized hub VPC Client VPN. This will include the following resources:

  1. VPC named “Business-Unit-1 VPC”
  2. Two private subnets and a public subnet for a NAT Gateway
  3. EC2 Instance, with the name “RedHat Bitnami WordPress Site”, located within a private subnet and blocked off from the open internet.
  4. Route Table named “Business-Unit-1” VPC Private Subnet Route Table”

From your Cloud9 terminal, run the following command:

  1. python3 scripts/deploy_test_vpc.py

Launch the Service Catalog Products

In this section, we will launch the two Service Catalog Products that we created earlier. 

From your Cloud9 terminal, run the following Python script:

  1. python3 scripts/launch_test_sc_products.py

The following are screenshots of what has been created and modified:

This will launch the Service Catalog Product named “AWS Transit Gateway Attachment”. This will attach the “Business-Unit-1” VPC to the Transit Gateway (“Central network hub TGW”).
A route is created for the specified Route Table(s) that are associated with the subnets of the new “Business-Unit-1” VPC that need to have VPN connectivity. This route has a destination CIDR of the central network hub VPC and a target of the Transit Gateway.
If we look at the Transit Gateway Route table routes, we can see that a new route has been created with a destination CIDR of the “Business-Unit-1” VPC (15.0.0.0/16). This provides the “Business-Unit-1” VPC with a route to the Transit Gateway.
This will also launch the Service Catalog Product named “AWS Client VPN Auth and Route Config”. This creates a Client VPN Authorization Rule that will allow users who authenticate with SimpleAD access to the destination CIDR of the new “Business-Unit-1” VPC (15.0.0.0/16) that was provisioned.
Two Client VPN Routes – each with a destination CIDR of the new “Business-Unit-1” VPC and target of the Client VPN associated subnets. This will provide the Client VPN Endpoint the ability to route traffic to the new “Business-Unit-1” VPC CIDR range.
A new route added to the central network VPC’s private subnet Route Table with a destination CIDR of the new “Business-Unit-1” VPC that points to the Transit Gateway.

Together, these components will allow traffic from the Client VPN Endpoint to be forwarded through the Main Network VPC Client VPN associated subnets >> Transit Gateway >> “Business-Unit-1” VPC. Below is a diagram that shows the changes, from the Service Catalog Products, that have been made to both the central network hub VPC as well as the “Business-Unit-1” VPC:

Figure 4 – Business-Unit-1 VPC now has VPN connectivity to the Central Network Hub VPC
  1. The Service Catalog Product “AWS Transit Gateway Attachment” created a Transit Gateway Attachment (“TGW_ATTACH_2”)  for the “Business-Unit-1” VPC to the “tgw-main” Transit Gateway. It also added a route to the “Business-Unit-1 VPC Subnet Route Table” with a destination CIDR of 12.0.0.0/16 (Network Hub VPC CIDR) with a target of the “tgw-main” Transit Gateway
  2. A new route was added to the “tgw-main” Transit Gateway Route Table with a destination CIDR of the “Business-Unit-1” VPC (15.0.0.0/16) and a target of the Transit Gateway Attachment “TGW_ATTACH_2”. 
  3. The Service Catalog Product “AWS Client VPN Auth and Route Config” added a new Client VPN Authorization Ingress Rule and Route with a destination CIDR of the “Business-Unit-1” VPC (15.0.0.0/16).
  4. The  “AWS Client VPN Auth and Route Config” Service Catalog Product also added a new route to the network hub “Hub VPC Subnet Route Table” with a destination CIDR of the “Business-Unit-1” VPC (15.0.0.0/16) with a target of the “tgw-main” Transit Gateway.

Testing the VPN Connectivity

In this section, we will test out the Client VPN connectivity by trying to load the RedHat Bitnami WordPress web page being hosted on the EC2 Instance named “RedHat Bitnami WordPress Site”. This EC2 Instance is located inside of one of the private subnets within the Test VPC and is not publicly accessible.

To test out your Client VPN connectivity, navigate to the VPC Console > Client VPN Endpoints and click “Download Client Configuration”. This is the OpenVPN configuration file that will be loaded into Tunnelblick (or your choice of OpenVPN client)

Open the Tunnelblick client and drag-and-drop the downloaded configuration file into the “Configurations” menu. Grab the private IP address of the “RedHat Bitnami WordPress Site” EC2 Instance and paste it into your browser. You should see that you are unable to access the webpage and that the site can’t be reached.

Now back in Tunnelblick, click the “Connect” button (for the downloaded Client VPN configuration) and you will be prompted with entering the following:

  1. Username – this is Simple Active Directory initial admin username.
    1. Type “Administrator
  2. Password – this the initial password that was set for the admin user.
    1. Type “Password123!

You should then see some logs generating and when you have successfully connected, you will see something similar to the following:

Note: When connected to this Client VPN, you will not have normal internet access since we did not create a Client VPN Authorization Rule and Route of “0.0.0.0/0”.

Finally, try to load the “RedHat Bitnami WordPress Site” EC2 Instance private IP address into your browser and you should see the WordPress homepage.

At this point, you have successfully established a VPN connection through a centralized hub network VPC to another VPC through the use of AWS Client VPN and Transit Gateway.

Conclusion

In this post, you learned how to create a centralized hub-and-spoke VPN network strategy. By implementing an automated, secure, and scalable multi-account strategy for remote VPN access, financial services organizations can increase the efficiency and capabilities of their remote workforce.

Consider the use of AWS Client VPN and Transit gateway to help provide remote VPN access into AWS at scale. If you have any questions or would like to take this solution further, contact us today.

All of the source code for this solution is located at: https://github.com/VerticalRelevance/aws-client-vpn-factory.git 

Posted July 28, 2020 by The Vertical Relevance Team

Posted in: Insights

Tags: , , , , , , , , ,


About the Author

Trey Mcelhattan is a Cloud Consultant experienced in helping companies adopt trending technologies through operation orchestration, automation, and monitoring. Has extensive experience with Cloud environments and infrastructure automation. Passionate about building self-service automation solutions. Self-driven and works well in teams or individually. Strong focus on delivering quality products on-time. Trey currently holds seven AWS Certifications including the AWS Certified AWS Security – Specialty, AWS Certified AWS DevOps – Professional, and AWS Certified AWS Solutions Architect –  Professional


Previous Post
Vertical Relevance Achieves Advanced Consulting Partner Status in the AWS Partner Network
Next Post
Resiliency on AWS for Financial Services – Introduction to the Testing Framework

About Vertical Relevance

Vertical Relevance was founded to help business leaders drive value through the design and delivery of effective transformation programs across people, processes, and systems. Our mission is to help Financial Services firms at any stage of their journey to develop solutions for success and growth.

Contact Us

Learn More