Address
304 North Cardinal St.
Dorchester Center, MA 02124

Work Hours
Monday to Friday: 7AM - 7PM
Weekend: 10AM - 5PM

Setting up a DigitalOcean VPN with strongSwan

In keeping with the VPN theme, here’s a quick guide on setting up a DigitalOcean VPN with strongSwan.

DigitalOcean VPN – Introduction

strongSwan is, “an open-source IPsec-based VPN Solution.” While I don’t necessary need another VPN solution, this will prove useful in another upcoming post.

As you may know, I’m already doing most of my hosting on DigitalOcean, so setting it up there just made it a little easier to manage. That said, you could follow these steps on any Ubuntu system, or modify them where appropriate.

Most of this post was based on the following two tutorials, so I highly recommend checking them out as well!

Preparation

First, I created a new, basic droplet.

DigitalOcean VPN - Droplet

When I first logged in, I ran any necessary updates and upgrades, to make sure that the system was good to go.

root@ubuntu-vpn:~# apt-get update && apt-get upgrade
Get:1 http://mirrors.digitalocean.com/ubuntu disco InRelease [257 kB]
Get:2 http://mirrors.digitalocean.com/ubuntu disco-updates InRelease [88.4 kB]    
Get:3 http://mirrors.digitalocean.com/ubuntu disco-backports InRelease [66.2 kB]

strongSwan Installation

First, I installed strongSwan and the strongswan-pki package.

root@ubuntu-vpn:~# apt-get install strongswan strongswan-pki
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following additional packages will be installed:
  libcharon-standard-plugins libstrongswan libstrongswan-standard-plugins strongswan-charon strongswan-libcharon strongswan-starter
Suggested packages:
  libstrongswan-extra-plugins libcharon-extra-plugins
The following NEW packages will be installed:
  libcharon-standard-plugins libstrongswan libstrongswan-standard-plugins strongswan strongswan-charon strongswan-libcharon strongswan-pki strongswan-starter
0 upgraded, 8 newly installed, 0 to remove and 0 not upgraded.
Need to get 929 kB of archives.
After this operation, 4379 kB of additional disk space will be used.
Do you want to continue? [Y/n] y

This was all I needed to do for the installation, so it was time to move on to configuration!

strongSwan Configuration

First, I created some directories to store the certificates that I would be creating.

root@ubuntu-vpn:~# mkdir -p ~/pki/{cacerts,certs,private}
root@ubuntu-vpn:~# chmod 700 pki/

Certificate Authority

Next, I generated my root key.

root@ubuntu-vpn:~# ipsec pki --gen --type rsa --size 4096 --outform pem > ~/pki/private/ca-key.pem

Using the root key for signing, I created a root certificate authority. Note that I used something generic, but feel free to changed the distinguished name (DN) value.

root@ubuntu-vpn:~# ipsec pki --self --ca --lifetime 3650 --in ~/pki/private/ca-key.pem --type rsa --dn "CN=VPN root CA" --outform pem > ~/pki/cacerts/ca-cert.pem

VPN Server Certificate

With my Certificate Authority created, it was time to create a certificate for the VPN server.

First, I created a private key for the server.

root@ubuntu-vpn:~# ipsec pki --gen --type rsa --size 4096 --outform pem > ~/pki/private/server-key.pem

Next, I created and signed the server certificate using the previously created private key. Note that I configured the distinguished name (DN) and subject alternate name (SAN) to match the IP of my VPN server. You can also configure these to match the hostname that you will be using, if you prefer that instead.

root@ubuntu-vpn:~# ipsec pki --pub --in ~/pki/private/server-key.pem --type rsa | ipsec pki --issue --lifetime 1825 --cacert ~/pki/cacerts/ca-cert.pem --cakey ~/pki/private/ca-key.pem --dn "CN=138.x.x.x" --san "138.x.x.x" --flag serverAuth --flag ikeIntermediate --outform pem > ~/pki/certs/server-cert.pem

With all of my files created, I copied over everything to the ipsec.d directory.

root@ubuntu-vpn:~# cp -r ~/pki/* /etc/ipsec.d/

Configuration

With all the required certificates created and moved, I could finally move on to the actual configuration.

First, I backed up the original configuration file before making any changes.

root@ubuntu-vpn:~# mv /etc/ipsec.conf{,.original}

Next, I opened a new, blank configuration file.

root@ubuntu-vpn:~# vi /etc/ipsec.conf

I’ll try to cover the configuration file as best as possible, but I highly recommend you read over the previously linked tutorials on the DigitalOcean community if you have further questions.

The following configuration file will create an IKEv2 VPN on my server that allows duplicate connections using my previously generated certificates. Additionally, it will use EAS-MSCHAPv2 for authentication, and give clients an IP in the 10.10.10.0/24 range.

root@ubuntu-vpn:~# cat /etc/ipsec.conf
config setup
    charondebug="ike 1, knl 1, cfg 0"
    uniqueids=no

conn ikev2-vpn
    auto=add
    compress=no
    type=tunnel
    keyexchange=ikev2
    fragmentation=yes
    forceencaps=yes

    dpdaction=clear
    dpdelay=300s
    rekey=no

    left=%any
    leftid=138.x.x.x
    leftcert=server-cert.pem
    leftsendcert=always
    leftsubnet=0.0.0.0/0

    right=%any
    rightid=%any
    rightauth=eap-mschapv2
    rightsourceip=10.10.10.0/24
    rightdns=8.8.8.8,8.8.4.4
    rightsendcert=never

    eap_identity=%identity

VPN Authentication

With my configuration file created, I moved onto the ipsec.secrets file. The server uses this file for the location of the server private key, as well as any user authentication information.

root@ubuntu-vpn:~# cat /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.

: RSA "server-key.pem"

<username> : EAP "<password>"

With my test user created, I restarted the strongSwan service.

root@ubuntu-vpn:~# systemctl restart strongswan

DigitalOcean VPN – Firewall Rules

Once I finshed configuring strongSwan, I moved on to the iptables rules.

As you can see, I started off with a completely empty list of rules.

root@ubuntu-vpn:~# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         

I’m no iptables wizard, so I’m not going to cover each of these commands individually. That said, the strongSwan on Ubuntu 16.04 tutorial does a great job breaking down what each one does.

Make sure that any of your IP information, as well as interface (highlighted) is correct. I initially ran into some problems, but that’s because I was typing eth0 instead of ens3.

root@ubuntu-vpn:~# iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
root@ubuntu-vpn:~# iptables -A INPUT -p tcp --dport 22 -j ACCEPT
root@ubuntu-vpn:~# iptables -A INPUT -i lo -j ACCEPT
root@ubuntu-vpn:~# iptables -A INPUT -p udp --dport  500 -j ACCEPT
root@ubuntu-vpn:~# iptables -A INPUT -p udp --dport 4500 -j ACCEPT
root@ubuntu-vpn:~# iptables -A FORWARD --match policy --pol ipsec --dir in  --proto esp -s 10.10.10.10/24 -j ACCEPT
root@ubuntu-vpn:~# iptables -A FORWARD --match policy --pol ipsec --dir out --proto esp -d 10.10.10.10/24 -j ACCEPT
root@ubuntu-vpn:~# iptables -t nat -A POSTROUTING -s 10.10.10.10/24 -o ens3 -m policy --pol ipsec --dir out -j ACCEPT
root@ubuntu-vpn:~# iptables -t nat -A POSTROUTING -s 10.10.10.10/24 -o ens3 -j MASQUERADE
root@ubuntu-vpn:~# iptables -t mangle -A FORWARD --match policy --pol ipsec --dir in -s 10.10.10.10/24 -o ens3 -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360
root@ubuntu-vpn:~# iptables -A INPUT -j DROP
root@ubuntu-vpn:~# iptables -A FORWARD -j DROP

When all was said and done, my iptables rules looked like the following.

root@ubuntu-vpn:~# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
ACCEPT     all  --  anywhere             anywhere             state RELATED,ESTABLISHED
ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:ssh
ACCEPT     all  --  anywhere             anywhere            
ACCEPT     udp  --  anywhere             anywhere             udp dpt:isakmp
ACCEPT     udp  --  anywhere             anywhere             udp dpt:ipsec-nat-t
DROP       all  --  anywhere             anywhere            

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
ACCEPT     all  --  10.10.10.0/24        anywhere             policy match dir in pol ipsec proto esp
ACCEPT     all  --  anywhere             10.10.10.0/24        policy match dir out pol ipsec proto esp
DROP       all  --  anywhere             anywhere            

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         

To persist my iptables information, I just used the iptables-save and iptables-restore commands.

root@ubuntu-vpn:~# iptables-save > /etc/iptables.conf
root@ubuntu-vpn:~# cat /etc/iptables.conf 
# Generated by iptables-save v1.6.1 on Sun May  5 00:44:00 2019
*mangle
:PREROUTING ACCEPT [587:494402]
:INPUT ACCEPT [561:491790]
:FORWARD ACCEPT [26:2612]
:OUTPUT ACCEPT [500:66005]
:POSTROUTING ACCEPT [526:68617]
-A FORWARD -s 10.10.10.0/24 -o ens3 -p tcp -m policy --dir in --pol ipsec -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360

... <snip> ...

-A FORWARD -j DROP
COMMIT
# Completed on Sun May  5 00:44:00 2019

In addition to the iptables rules, I also made a few changes to /etc/sysctl.conf. I’ve highlighted the changes below, but they perform the following functions.

  • Enable IPv4 packet forwarding
  • Disable sending or accepting ICMP redirects
  • Disable path MTU discovery
root@ubuntu-vpn:~# cat /etc/sysctl.conf 
#
# /etc/sysctl.conf - Configuration file for setting system variables
# See /etc/sysctl.d/ for additional system variables.
# See sysctl.conf (5) for information.
#

...

# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward=1

...

# Do not accept ICMP redirects (prevent MITM attacks)
net.ipv4.conf.all.accept_redirects = 0

...

# Do not send ICMP redirects (we are not a router)
net.ipv4.conf.all.send_redirects = 0

...

net.ipv4.ip_no_pmtu_disc = 1

DigitalOcean VPN – Windows Configuration

With everything configured, I wanted to test the connection on my Windows system.

First, I grabbed the CA certificate from my server.

root@ubuntu-vpn:~# cat /etc/ipsec.d/cacerts/ca-cert.pem 

Next, I installed it by opening mmc.exe and following these steps.

  1. File -> Add/Remove Snap-in
  2. Certificates
  3. “Add”
  4. Computer Account
  5. “Next”
  6. Local computer
  7. “Finish”
  8. Expand the Certificates (Local Computer) menu
  9. Expand the Trusted Root Certification Authorities menu
  10. Select Certificates
  11. Actions -> All Tasks -> Import
  12. Select the previously copied ca-cert.pem file and import it

After setting up a new VPN connection, I was running into some errors.

It turns out that strongSwan needs some additional configuration when it comes to Mac OS or Windows clients.

First, I added the following lines to my ipsec.conf file.

ike=aes256-sha1-modp1024,aes128-sha1-modp1024,3des-sha1-modp1024! 
esp=aes256-sha256,aes256-sha1,3des-sha1!

I was also getting some authentication errors. This was due to a missing EAP plugin that I also had to install.

root@ubuntu-vpn:~# apt-get install libcharon-extra-plugins
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following additional packages will be installed:
  dbus-user-session dconf-gsettings-backend dconf-service glib-networking glib-networking-common
  glib-networking-services gsettings-desktop-schemas libdconf1 libldns2 libmysqlclient20 libpcsclite1
  libproxy1v5 libsoup2.4-1 libstrongswan-extra-plugins libtspi1 libunbound8 mysql-common
  strongswan-tnc-base
Suggested packages:
  pcscd strongswan-tnc-ifmap strongswan-tnc-pdp
The following NEW packages will be installed:
  dbus-user-session dconf-gsettings-backend dconf-service glib-networking glib-networking-common
  glib-networking-services gsettings-desktop-schemas libcharon-extra-plugins libdconf1 libldns2
  libmysqlclient20 libpcsclite1 libproxy1v5 libsoup2.4-1 libstrongswan-extra-plugins libtspi1
  libunbound8 mysql-common strongswan-tnc-base

With my changes made, I restarted strongSwan.

root@ubuntu-vpn:~# service strongswan restart

When I attempted to connect to the VPN this time, it was successful, and I got my IP information.

PPP adapter VPN Connection:

   Connection-specific DNS Suffix  . :
   Description . . . . . . . . . . . : VPN Connection
   Physical Address. . . . . . . . . :
   DHCP Enabled. . . . . . . . . . . : No
   Autoconfiguration Enabled . . . . : Yes
   IPv4 Address. . . . . . . . . . . : 10.10.10.1(Preferred)
   Subnet Mask . . . . . . . . . . . : 255.255.255.255
   Default Gateway . . . . . . . . . :
   DNS Servers . . . . . . . . . . . : 8.8.8.8
                                       8.8.4.4
   NetBIOS over Tcpip. . . . . . . . : Enabled

Note that you may need to make some changes if you wish to send all your traffic over a VPN in Windows.

DigitalOcean VPN – Linux Configuration

With the testing complete on my Windows host, I also wanted to get the VPN working on my Kali instance.

As before, I copied over the CA certificate to my client box.

root@ubuntu-vpn:~# cat /etc/ipsec.d/cacerts/ca-cert.pem 

...

root@kali:~/vpn# vi vpn_root_certificate.pem

For Linux, I decided to install charon-cmd, to connect to the VPN on an as-needed basis.

root@kali:~/vpn# apt-get install charon-cmd libcharon-extra-plugins
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following packages were automatically installed and are no longer required:
  libavahi-gobject0 libfolks-telepathy25 libgail-3-0 libgcab-1.0-0 libgfortran4 libgtk2-perl
  libmission-control-plugins0 libpango-perl libtelepathy-glib0 magictree
  python-backports.ssl-match-hostname python-pam telepathy-mission-control-5 x11proto-dri2-dev
  x11proto-gl-dev
Use 'apt autoremove' to remove them.
The following additional packages will be installed:
  libstrongswan libstrongswan-standard-plugins strongswan-libcharon
Suggested packages:
  libstrongswan-extra-plugins
The following NEW packages will be installed:
  charon-cmd libcharon-extra-plugins libstrongswan libstrongswan-standard-plugins strongswan-libcharon
0 upgraded, 5 newly installed, 0 to remove and 1229 not upgraded.
Need to get 1,299 kB of archives.
After this operation, 4,030 kB of additional disk space will be used.
Do you want to continue? [Y/n] y

When I finished the install, it was just a matter of supplying the CA certificate, VPN host, username, and the ike-proposal method that I wanted to use.

root@kali:~/vpn# charon-cmd --cert vpn_root_certificate.pem --host 138.x.x.x --identity <username> --ike-proposal aes256-sha1-modp1024
00[LIB] dropped capabilities, running as uid 0, gid 0
00[DMN] Starting charon-cmd IKE client (strongSwan 5.7.2, Linux 4.17.0-kali1-686-pae, i686)
00[LIB] loaded plugins: charon-cmd aesni aes rc2 sha2 sha1 md5 mgf1 random nonce x509 revocation constraints pubkey pkcs1 pkcs7 pkcs8 pkcs12 sshkey pem openssl fips-prf gmp agent xcbc hmac gcm kernel-netlink resolve socket-default bypass-lan eap-identity eap-md5 eap-gtc eap-mschapv2 eap-tls eap-ttls xauth-generic
00[JOB] spawning 16 worker threads
06[IKE] installed bypass policy for 192.168.5.0/24
07[IKE] initiating IKE_SA cmd[1] to 138.x.x.x
07[ENC] generating IKE_SA_INIT request 0 [ SA KE No N(NATD_S_IP) N(NATD_D_IP) N(HASH_ALG) N(REDIR_SUP) ]
07[NET] sending packet: from 192.168.5.82[35440] to 138.x.x.x[4500] (328 bytes)
09[NET] received packet: from 138.x.x.x[4500] to 192.168.5.82[35440] (328 bytes)
09[ENC] parsed IKE_SA_INIT response 0 [ SA KE No N(NATD_S_IP) N(NATD_D_IP) N(HASH_ALG) N(MULT_AUTH) ]
09[CFG] selected proposal: IKE:AES_CBC_256/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_1024
09[IKE] local host is behind NAT, sending keep alives
09[IKE] remote host is behind NAT
09[IKE] sending cert request for "CN=VPN root CA"
09[IKE] establishing CHILD_SA cmd{1}
09[ENC] generating IKE_AUTH request 1 [ IDi N(INIT_CONTACT) CERTREQ CPRQ(ADDR DNS) SA TSi TSr N(MOBIKE_SUP) N(NO_ADD_ADDR) N(MULT_AUTH) N(EAP_ONLY) N(MSG_ID_SYN_SUP) ]
09[NET] sending packet: from 192.168.5.82[60898] to 138.x.x.x[4500] (316 bytes)
10[NET] received packet: from 138.x.x.x[4500] to 192.168.5.82[60898] (1916 bytes)
10[ENC] parsed IKE_AUTH response 1 [ IDr CERT AUTH EAP/REQ/ID ]
10[IKE] received end entity cert "CN=138.x.x.x"
10[CFG]   using certificate "CN=138.x.x.x"
10[CFG]   using trusted ca certificate "CN=VPN root CA"
10[CFG] checking certificate status of "CN=138.x.x.x"
10[CFG] certificate status is not available
10[CFG]   reached self-signed root ca with a path length of 0

... <snip> ...

14[ENC] generating INFORMATIONAL request 9 [ N(NATD_S_IP) N(NATD_D_IP) ]
14[NET] sending packet: from 192.168.5.82[34065] to 138.x.x.x[4500] (124 bytes)
15[NET] received packet: from 138.x.x.x[4500] to 192.168.5.82[34065] (124 bytes)

To verify my connection, I grabbed my IP information from ipinfo.io, which confirmed that the VPN was working!

root@kali:~/vpn# curl http://ipinfo.io
{
  "ip": "138.x.x.x",
  "city": "Santa Clara",
  "region": "California",
  "country": "US",
  "loc": "37.3501,-121.9850",
  "postal": "95051",
  "phone": "408",
  "org": "AS14061 DigitalOcean, LLC"
}

DigitalOcean VPN – Conclusion

This was a longer post, but it was surprisingly simple to setup a custom strongSwan VPN on a droplet.

I’m not sure if I will use this much with my other available options, but it is nice to have in my back pocket.

That said, this post will serve as a pre-requisite for an upcoming aggressive post that I’ve wanted to write for some time.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.