10 minute read #AWS, #Network, #VPN

This blog post walks through the setup of an EC2-based VPN endpoint - using Ubuntu Linux 18.04 with Strongswan and FRRouting - for a Site-to-Site VPN connection to AWS with BGP routing. It will allow you to experiment with BGP in your AWS account, test out new AWS features such as AWS Transit Gateway or use it for many other things. Especially if you are interested in learning more about the interaction of BGP over a Site-to-Site VPN with AWS Transit Gateway, this is an easy way to do so.

The desired final setup will look like depicted in Figure 1. The AWS Transit Gateway connects on one side to a VPC with the CIDR 172.31.0.0/16 and on the other side to an AWS Site-to-Site VPN. This AWS Site-to-Site VPN connects to an EC2-based router, which uses Strongswan for IPSec and FRRouting for BGP. To make things interesting the EC2-based router has a second network interface on a private subnet of 10.16.16.0/24, which can be announced via BGP.

Figure 1: Setup Overview of EC2-based VPN endpoint for Site-to-Site VPN with AWS
Figure 1: Setup Overview of EC2-based VPN endpoint for Site-to-Site VPN with AWS

While Transit Gateway and EC2 instance can reside in the same AWS account and even AWS region, the EC2 instance should reside in a different VPC than connected to the Transit Gateway.

AWS Transit Gateway

AWS Transit Gateway is a service that enables customers to connect their Amazon Virtual Private Clouds (VPCs) and their on-premises networks to a single gateway. For on-premises connectivity the AWS Transit Gateway allows you to leverage AWS Site-to-Site VPNs (IPSec) or AWS Direct Connect via AWS Direct Connect Gateways (See Figure 2).

Figure 2: AWS Transit Gateway provides dynamic routing between VPCs, Site-to-Site VPNs, and AWS Direct Connect Gateways
Figure 2: AWS Transit Gateway provides dynamic routing between VPCs, Site-to-Site VPNs, and AWS Direct Connect Gateways

A transit gateway acts as a regional virtual router for traffic flowing between your virtual private clouds (VPC) and VPN or DX connections. A transit gateway scales elastically based on the volume of network traffic. Routing through a transit gateway operates at layer 3, where the packets are sent to a specific next-hop attachment, based on their destination IP addresses.

The AWS Transit Gateway’s hub and spoke model simplifies management and reduces operational costs because each network only has to connect to the Transit Gateway and not to every other network. Any new VPC is simply connected to the Transit Gateway and is then automatically available to every other network that is connected to the Transit Gateway. This ease of connectivity makes it easy to scale your network as you grow.

Transit Gateway and Site-to-Site VPN setup

Follow the AWS documentation for setting up the AWS Transit Gateway and attaching it to an AWS Site-to-Site VPN.

It is recommended to configure “VPN ECMP support” with “enable” to enable Equal Cost Multipath (ECMP) routing support between VPN connections. This will allow you to use both tunnels of the AWS Sit-to-Site VPN connection at the same time.

Linux-based Router

This setup uses Ubuntu 16.04-LTS, Xenial Xerus as the Linux distribution for the EC2-based VPN gateway and router. Strongswan provides the IPSec termination for the AWS Site-to-Site VPN connection. And FRRouting provides the dynamic routing capabilities for BGP.

Interface Setup

As mentioned earlier the Ubuntu Linux EC2 instance uses a secondary network interface on a private subnet. This subnet is announced via BGP towards the AWS Transit Gateway. AWS Premium Support provides a detailed instruction on how to make a secondary network interface work in Ubuntu EC2 instances. The presented concepts are used here.

First create a configuration file for the secondary interface at /etc/network/interfaces.d/99-eth1.cfg. his example uses a secondary interface of ‘eth1’. Be sure to change ‘eth1’ to match your secondary interface name.

#
# /etc/network/interfaces.d/99-eth1.cfg
#

auto eth1
iface eth1 inet dhcp

# control-alias eth0
iface eth1 inet6 dhcp

Next create the restrict-default-gw file to prevent the default gateway from being overwritten on the main table via the /etc/dhcp/dhclient-enter-hooks.d/restrict-default-gw file.

#
# /etc/dhcp/dhclient-enter-hooks.d/restrict-default-gw
#

case ${interface} in
  eth0)
    ;;
  *)
    unset new_routers
    ;;
esac

After restarting your network the secondary interface should acquire an IPv4 and IPv6 address.

root@host:~# systemctl restart networking
root@host:~# ifconfig eth1
eth1      Link encap:Ethernet  HWaddr 02:7b:35:90:92:ae
          inet addr:10.16.16.254  Bcast:10.16.16.255  Mask:255.255.255.0
          inet6 addr: fe80::7b:35ff:fe90:92ae/64 Scope:Link
          inet6 addr: 2600:1f16:f2d:1211:2c35:28f7:7d5d:f776/128 Scope:Global
          UP BROADCAST RUNNING MULTICAST  MTU:9001  Metric:1
          RX packets:9081 errors:0 dropped:0 overruns:0 frame:0
          TX packets:2143 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:854331 (854.3 KB)  TX bytes:213798 (213.7 KB)

Source or Destination checking

In AWS the Source or Destination checking attribute on an interface determines whether an instance can handle network traffic that isn’t specifically destined for the instance.

Before installing Strongswan on your EC2 instance disable Source/Destination Checks for this instance. Keep in mind that this needs to be done for both interfaces (ENIs) separately.

Security Group setup

Within the security group for the external interface: Make sure to allow inbound traffic using UDP port 500 (ISAKMP) and 4500 (IPsec NAT-Traversal) from both of the AWS Virtual Private Gateways.

For the security group on the internal interface: Here you want to allow any traffic that you want to be transported inside of the AWS Site-to-Site VPN tunnels.

Strongswan setup

Next use apt-get update && apt-get install -y strongswan to install Strongswan on the Ubuntu Linux 16.04 instance.

Update the configuration file /etc/ipsec.conf with generic settings for an AWS Site-to-Site VPN, as well as the specific settings for the two tunnels that each AWS Site-to-Site VPN provides. Make sure to replace the relevant IPv4 addresses from this example with your IPv4 addresses.

 #
 # /etc/ipsec.conf
 #
 conn %default
         # Authentication Method : Pre-Shared Key
         leftauth=psk
         rightauth=psk
         # Encryption Algorithm : aes-128-cbc
         # Authentication Algorithm : sha1
         # Perfect Forward Secrecy : Diffie-Hellman Group 2
         ike=aes128-sha1-modp1024!
         # Lifetime : 28800 seconds
         ikelifetime=28800s
         # Phase 1 Negotiation Mode : main
         aggressive=no
         # Protocol : esp
         # Encryption Algorithm : aes-128-cbc
         # Authentication Algorithm : hmac-sha1-96
         # Perfect Forward Secrecy : Diffie-Hellman Group 2
         esp=aes128-sha1-modp1024!
         # Lifetime : 3600 seconds
         lifetime=3600s
         # Mode : tunnel
         type=tunnel
         # DPD Interval : 10
         dpddelay=10s
         # DPD Retries : 3
         dpdtimeout=30s
         # Tuning Parameters for AWS Virtual Private Gateway:
         keyexchange=ikev1
         rekey=yes
         reauth=no
         dpdaction=restart
         closeaction=restart
         leftsubnet=0.0.0.0/0,::/0
         rightsubnet=0.0.0.0/0,::/0
         leftupdown=/etc/ipsec-vti.sh
         installpolicy=yes
         compress=no
         mobike=no
conn AWS-VPC-GW1
         # Customer Gateway: :
         left=10.16.1.254
         leftid=18.123.45.67
         # Virtual Private Gateway :
         right=35.98.76.54
         rightid=35.98.76.54
         auto=start
         mark=100
         #reqid=1
conn AWS-VPC-GW2
         # Customer Gateway: :
         left=10.16.1.254
         leftid=18.123.45.67
         # Virtual Private Gateway :
         right=54.98.76.54
         rightid=54.98.76.54
         auto=start
         mark=200

Next update the configuration file /etc/ipsec.secrets with the Pre-Shared Keys of your AWS Site-to-Site VPN. Here also ensure that you update the IPv4 addresses from this example with the IPv4 addresses of your setup.

#
# /etc/ipsec.secrets
#

# This file holds shared secrets or RSA private keys for authentication.

# RSA private key for this host, authenticating it to any other host
# which knows the public part.
18.123.45.67 35.98.76.54 : PSK "7?-Mun7^XC3JUswf$f$mm=8s@v2U=-aG"
18.123.45.67 54.98.76.54 : PSK "!yTF%t$nQTwp++6rB9#qft?Vfa%KAhnU"

As we want to run a dynamic routing protocol with BGP over this AWS Site-to-Site VPN, we need to use a route-based VPN setup instead of a policy-based one.

The bash script in file /etc/ipsec-vti.sh sets up the virtual tunnel interfaces for a route-based IPSec VPN. Also here make sure to replace the IPv4 addresses of this example with the IPv4 addresses of your setup.

#!/bin/bash

#
# /etc/ipsec-vti.sh
#

IP=$(which ip)
IPTABLES=$(which iptables)

PLUTO_MARK_OUT_ARR=(${PLUTO_MARK_OUT//// })
PLUTO_MARK_IN_ARR=(${PLUTO_MARK_IN//// })
case "$PLUTO_CONNECTION" in
AWS-VPC-GW1)
VTI_INTERFACE=vti1
VTI_LOCALADDR=169.254.12.38/30
VTI_REMOTEADDR=169.254.12.37/30
;;
AWS-VPC-GW2)
VTI_INTERFACE=vti2
VTI_LOCALADDR=169.254.14.230/30
VTI_REMOTEADDR=169.254.14.229/30
;;
esac

case "${PLUTO_VERB}" in
up-client)
#$IP tunnel add ${VTI_INTERFACE} mode vti local ${PLUTO_ME} remote ${PLUTO_PEER} okey ${PLUTO_MARK_OUT_ARR[0]} ikey ${PLUTO_MARK_IN_ARR[0]}
$IP link add ${VTI_INTERFACE} type vti local ${PLUTO_ME} remote ${PLUTO_PEER} okey ${PLUTO_MARK_OUT_ARR[0]} ikey ${PLUTO_MARK_IN_ARR[0]}
sysctl -w net.ipv4.conf.${VTI_INTERFACE}.disable_policy=1
sysctl -w net.ipv4.conf.${VTI_INTERFACE}.rp_filter=2 || sysctl -w net.ipv4.conf.${VTI_INTERFACE}.rp_filter=0
$IP addr add ${VTI_LOCALADDR} remote ${VTI_REMOTEADDR} dev ${VTI_INTERFACE}
$IP link set ${VTI_INTERFACE} up mtu 1436
$IPTABLES -t mangle -I FORWARD -o ${VTI_INTERFACE} -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
$IPTABLES -t mangle -I INPUT -p esp -s ${PLUTO_PEER} -d ${PLUTO_ME} -j MARK --set-xmark ${PLUTO_MARK_IN}
$IP route flush table 220
#/etc/init.d/bgpd reload || /etc/init.d/quagga force-reload bgpd
;;
down-client)
#$IP tunnel del ${VTI_INTERFACE}
$IP link del ${VTI_INTERFACE}
$IPTABLES -t mangle -D FORWARD -o ${VTI_INTERFACE} -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
$IPTABLES -t mangle -D INPUT -p esp -s ${PLUTO_PEER} -d ${PLUTO_ME} -j MARK --set-xmark ${PLUTO_MARK_IN}
;;
esac

# Enable IPv4 forwarding
sysctl -w net.ipv4.ip_forward=1
sysctl -w net.ipv4.conf.eth0.disable_xfrm=1
sysctl -w net.ipv4.conf.eth0.disable_policy=1

As this is a bash script, don’t forget to make the file executable:

chmod +x /etc/ipsec-vti.sh

Afterwards restart the Strongwan daemon to load the configuration changes and establish the tunnels:

systemctl restart strongswan

Strongswan validation

You can validate that the two tunnel interfaces vti1 and vti2 are up and running with the commands ip -s tunnel show or ifconfig vti1. You should see the IP address of the tunnels displayed within the 169.254.0.0/16 range.

Looing at the tunnel status you should see bytes being send (TX) and received (RX) for both tunnels.

ubuntu@host:~$ ip -s tunnel show
vti1: ip/ip remote 35.98.76.54 local 18.123.45.67 ttl inherit nopmtudisc key 100
RX: Packets    Bytes        Errors CsumErrs OutOfSeq Mcasts
    12         792          0      0        0        0
TX: Packets    Bytes        Errors DeadLoop NoRoute  NoBufs
    12         714          7      0        7        0
vti2: ip/ip remote 54.98.76.54 local 18.123.45.67 ttl inherit nopmtudisc key 200
RX: Packets    Bytes        Errors CsumErrs OutOfSeq Mcasts
    4          240          0      0        0        0
TX: Packets    Bytes        Errors DeadLoop NoRoute  NoBufs
    4          160          7      0        7        0
ip_vti0: ip/ip remote any local any ttl inherit nopmtudisc key 0
RX: Packets    Bytes        Errors CsumErrs OutOfSeq Mcasts
    0          0            0      0        0        0
TX: Packets    Bytes        Errors DeadLoop NoRoute  NoBufs
    0          0            0      0        0        0

Similarly you should see the interface in an “UP” state.

ubuntu@host:~$ ifconfig vti1
vti1: flags=209<UP,POINTOPOINT,RUNNING,NOARP>  mtu 1436
        inet 169.254.12.38  netmask 255.255.255.252  destination 169.254.12.37
        inet6 fe80::5efe:ac1f:27e0  prefixlen 64  scopeid 0x20<link>
        tunnel   txqueuelen 1000  (IPIP Tunnel)
        RX packets 12  bytes 792 (792.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 12  bytes 714 (714.0 B)
        TX errors 7  dropped 0 overruns 0  carrier 7  collisions 0

FRRouting Setup

Follow the FFRouting documentation for installing FFRouting on Ubuntu 18.04 or have a look at my installation script.

When configuring the /etc/frr/daemons file, ensure to enable the bgpd daemon.

You can now connect via the vtysh Modal CLI to the router process.

ubuntu@host:~$ sudo vtysh

Hello, this is FRRouting (version 4.0+cl3u10).
Copyright 1996-2005 Kunihiro Ishiguro, et al.

First we configure the BGP process with the provided AS number and the two VPN-based peers. We also chose to announce our local private subnet 10.16.16.0/24.

host# conf t
host(config)# router bgp 65016
host(config-router)# no bgp ebgp-requires-policy
host(config-router)# neighbor 169.254.12.229 remote-as 64512
host(config-router)# neighbor 169.254.14.37 remote-as 64512
host(config-router)# address-family ipv4 unicast
host(config-router-af)# neighbor 169.254.12.229 soft-reconfiguration inbound
host(config-router-af)# neighbor 169.254.14.37 soft-reconfiguration inbound
host(config-router-af)# network 10.16.16.0/24
host(config-router-af)# end
host# wr

Next we can check if all interfaces are up and running. Besides eth0 and eth1 with IPv4 and IPv6 addresses, we should also see vti1 and vti2 for the IPSec tunnel interfacces.

host# sh int brief
Interface       Status  VRF             Addresses
---------       ------  ---             ---------
eth0            up      default         10.16.1.254/24
                                        + 2600:1f16:e4d:1a01:fbe6:fa0b:929b:4d72/128
eth1            up      default         10.16.16.254/24
                                        + 2600:1f16:e4d:1a11:2c38:23f7:7a5d:f773/128
ip_vti0         down    default
lo              up      default
vti1            up      default         169.254.12.38/32
vti2            up      default         169.254.14.230/32

Validation

EC2-Based Router

Now it’s time to validate that the AWS Transit Gateway is providing us with routes over BGP. Here we can see that the route for the VPC attached to the Transit Gateway is propagated via BGP to us.

host# sh ip route bgp
Codes: K - kernel route, C - connected, S - static, R - RIP,
       O - OSPF, I - IS-IS, B - BGP, E - EIGRP, N - NHRP,
       T - Table, v - VNC, V - VNC-Direct, A - Babel, D - SHARP,
       F - PBR,
       > - selected route, * - FIB route

B>* 172.31.0.0/16 [20/100] via 169.254.14.229, vti2, 00:23:10
  *                        via 169.254.12.37, vti1, 00:23:10

Notice that in case you configured the AWS Transit Gateway with Equal-cost multi-path routing (ECMP), you will see the remote VPCs CIDR of 172.31.0.0/16 announced over both AWS Site-to-Site VPN tunnels.

AWS Console

Within the AWS Console you should also be able to see the VPN connections for both tunnels in the state “UP” and should see 1 BGP route - which is the 10.16.16.0/24 network to be received over each tunnel (See Figure 3).

Figure 3: AWS Site-to-Site VPN showing as <strong>UP</strong> with routes being received.
Figure 3: AWS Site-to-Site VPN showing as UP with routes being received.

At this point you are all set and your VPN connection along with BGP routing is ready to be used.

Summary

This blog post showed you the setup of an EC2-based VPN endpoint while using Ubuntu Linux 16.04 with Strongswan for a Site-to-Site VPN connection and and FRRouting for BGP. This setup allows you to experiment with BGP in your AWS account. Especially if you are interested in learning more about the interaction of BGP over a Site-to-Site VPN with AWS Transit Gateway, the resulting setup is a great start to do so.

Leave a comment