Last updated at Wed, 06 Dec 2017 20:49:36 GMT

Synopsis:

We will look at how to configure an L2TP over IPSEC VPN using OpenSWAN and how to connect to it using Mac OSX. This guide is written for running the VPN software on a CentOS 7 x86_64 EC2 instance (ami-6d1c2007) provided by Amazon Web Services. The VPN will be configured to use local authentication and a pre-shared key. This is a great way to allow access into your AWS VPC.

Procedure:

The procedure is broken into 3 parts:

  • AWS – Create an EC2 instance
  • VPN – Configure the server
  • Client – Configure an OSX VPN client

AWS Configuration:

Create a new EC2 instance with an image of ami-6d1c2007 or similar. Assign it a public Elastic IP and create a security group with the following settings to allow the VPN protocols.

Security Group

If you were to deploy the EC2 instance via Terraform, the configuration would like similar to the example below.

 resource "aws_security_group" "nat" { 
 name = "nat" 
 description = "Allow services from the private subnet through NAT"
 
 ingress { 
 from_port = 0 
 to_port = 0 
 protocol = "-1" 
 cidr_blocks = ["10.0.0.0/8"] 
 }
 
 ingress { 
 from_port = 0 
 to_port = 65535 
 protocol = "tcp" 
 cidr_blocks = ["${aws_subnet.us-east-1b-private.cidr_block}"] 
 }
 
 egress { 
 from_port = 0 
 to_port = 0 
 protocol = "-1" 
 cidr_blocks = ["0.0.0.0/0"] 
 }
 
 vpc_id = "${aws_vpc.default.id}" 
 }
 
 resource "aws_security_group" "vpn" {
 
 name = "vpn" 
 description = "Allow SSH, IPSec and L2TP traffic"
 
 ingress { 
 from_port = 0 
 to_port = 0 
 protocol = "-1" 
 cidr_blocks = ["10.0.0.0/8"] 
 }
 ingress { 
 from_port = 1701 
 to_port = 1701 
 protocol = "udp" 
 cidr_blocks = ["0.0.0.0/0"] 
 }
 
 ingress {
 from_port = 500 
 o_port = 500 
 protocol = "udp" 
 cidr_blocks = ["0.0.0.0/0"] 
 }
 
 ingress { 
 from_port = 4500 
 to_port = 4500 
 protocol = "udp" 
 cidr_blocks = ["0.0.0.0/0"] 
 }
 
 egress {
 from_port = 0 
 to_port = 0 
 protocol = "-1" 
 cidr_blocks = ["0.0.0.0/0"] 
 }
 
 vpc_id = "${aws_vpc.default.id}" 
 }
 
 resource "aws_instance" "nat" { 
 ami = "ami-6d1c2007" 
 availability_zone = "us-east-1b" 
 instance_type = "t2.small" 
 key_name = "${var.aws_key_name}" 
 security_groups = ["${aws_security_group.nat.id}", "${aws_security_group.vpn.id}"] 
 subnet_id = "${aws_subnet.us-east-1b-public.id}" 
 associate_public_ip_address = true 
 private_ip = "10.0.0.5" 
 source_dest_check = false 
 }
 
 resource "aws_eip" "nat" {
 instance = "${aws_instance.nat.id}" 
 vpc = true 
 }

VPN Configuration:

We need to configure the following pieces of software to provide a working VPN service.

  • RNGD – Random Number Generator Daemon
  • OpenSWAN – IPSEC implementation for VPNs
  • XL2TPD – L2TP implementation

RNG

First, we will install the random number generator service (RNG) which OpenSWAN can take advantage of to create strong random numbers.

yum install rng-tools

There’s not always a hardware generator available in virtual machines so to avoid potential errors, the contents of /etc/sysconfig/rngd should contain the following:

 EXTRAOPTIONS="--no-tpm=1 --quiet -r /dev/urandom"

Now, start the RNG service

systemctl start rngd

OpenSWAN

Install the OpenSwan package:

yum install openswan

Now that OpenSWAN is installed we can add the IPSEC configuration to /etc/ipsec.conf. Replace the values of leftid and leftsourceip with the private IP address of the EC2 instance.

 version 2.0
 
 config setup 
     dumpdir=/var/run/pluto/ 
     plutostderrlog=/var/log/pluto.log 
     nat_traversal=yes 
     virtual_private=%v4:192.168.0.0/24,%v4:172.16.0.0/12 
     oe=off 
     protostack=netkey 
     nhelpers=0 
     interfaces=%defaultroute
 
 conn L2TP-PSK 
     auto=add 
     left=%defaultroute 
     leftid=10.0.0.5 
     leftsourceip=10.0.0.5 
     leftnexthop=%defaultroute 
     leftprotoport=17/%any 
     rightprotoport=17/%any 
     right=%any 
     rightsubnet=vhost:%no,%priv 
     forceencaps=yes 
     authby=secret 
     pfs=no 
     type=transport 
     auth=esp 
     dpddelay=30 
     dpdtimeout=120 
     dpdaction=clear

Now, we need to add an IPSEC secrets file. The file should contain both the public IP and private IP address of the EC2 instance. Replace the IP address values below as well as the pre-shared key with something much stronger. The key will be used by the clients to authenticate to the server.

 10.0.0.5 %any : PSK "blah" 
 1.2.3.4 %any : PSK "blah"

The IPSEC service will not pass validation unless we disable reverse path filtering, the sending of ICMP redirects, and the reception of ICMP redirects in the kernel for all interfaces.

 for interface in all default eth0 lo; do 
     sysctl -w net.ipv4.conf.${interface}.send_redirects=0 
     sysctl -w net.ipv4.conf.${interface}.accept_redirects=0 
     sysctl -w net.ipv4.conf.${interface}.rp_filter=0 
 done

Next, we must allow IP forwarding in the kernel.

sysctl -w net.ipv4.ip_forward=1

After, we must enable Proxy ARP for all interfaces.

 for interface in all default eth0 lo; do 
     sysctl -w net.ipv4.conf.${interface}.proxy_arp=1 
 done

At this time we can start the IPSEC service and verify its configuration.

 systemctl start ipsec 
 ipsec verify

If everything looks good move to the L2TP configuration otherwise troubleshoot by checking the following resources:

 ipsec verify 
 ipsec status --all 
 cat /var/log/secure 
 cat /var/log/pluto.log

XL2TPD

Install the L2TP server implementation.

yum install xl2tpd

Configuring the L2TP daemon is easy. Copy the contents to /etc/xl2tpd/xl2tpd.conf and change the value of local ip to the EC2 instance’s private IP address. In addition, you may want to adjust the value of ip range which is the address range handed out to VPN clients. The range should be available addresses within the VPC network.

 [global] 
 port = 1701
 
 [lns default] 
 ip range = 10.0.0.241-10.0.0.254 
 local ip = 10.0.0.5 
 require chap = no 
 unix authentication = yes 
 refuse pap = no 
 require authentication = yes 
 name = l2tpd 
 ppp debug = yes 
 pppoptfile = /etc/ppp/options.xl2tpd 
 length bit = yes

Now, we need to tell the PPP server to use xl2tpd options. To do this, add the following contents to /etc/ppp/options.xl2tpd. Any DNS server addresses will do for the ms-dns values.

 ipcp-accept-local 
 ipcp-accept-remote 
 ms-dns 8.8.8.8 
 ms-dns 8.8.4.4 
 noccp 
 auth 
 crtscts 
 idle 1800 
 mtu 1280 
 mru 1280 
 lock 
 connect-delay 5000 
 name l2tpd 
 proxyarp 
 login 
 logfile /var/log/xl2tpd.log

Next, we will configure the PPP service to use local authentication for users. This means that their user accounts on the VPN server along with the pre-shared key will be used for authentication. Anytime you want to allow a new user to access the VPN you only need to create a new Linux account for them on the server.

Add the following contents to /etc/ppp/pap-secrets:

 # Secrets for authentication on server using CHAP 
 # client server secret IP addresses 
 * l2tpd "" *

After that we need to configure PAM to allow local authentication by editing the file /etc/pam.d/ppp to only contain:

 #%PAM-1.0 
 auth required pam_nologin.so 
 auth required pam_unix.so 
 account required pam_unix.so 
 session required pam_unix.so

Finally, we can start the L2TP service.

systemctl start xl2tpd

If you encounter any issues check the following log files:

 cat /var/log/xl2tpd.log 
 cat /var/log/messages

Client Configuration:

We will use OSX’s builtin VPN client to connect to the VPN. Begin by selecting System Preferences and then Network. Next, choose the “+” on the bottom left to add a new network interface.
Add a connection

Choose VPN as the interface and select L2TP over IPSEC.
Screen Shot 2016-06-25 at 12.22.17 AM

Add the server’s public IP address, a username on the VPN server, and click on Authentication Settings.

VPN Config

For Password, add the user’s server account password and then add the pre-shared key value for Shared Secret.

VPN Auth

Apply, and then connect.

Complementary Tools

More Reading & Other Resources