S2S VPN between Linux Box and Unifi USG

Want to setup a Site-to-Site VPN beween your USG and a Linux Box? Read below.

Posted by Henning on 2nd Jan 2021

In my flat I have only a standard DSL connection. And this connection has two big downsides (beside the speed, but I cannot change that...)

  1. I have only 1 public IP Address
  2. This public IP Address changes every 24 hours

So what can I do to "hack" this situation?

I have a Linux box running in the "free tier" of the Oracle Cloud and want to use the public IP Address attached to this box a fixed IP Address for my home. Therefore I needed to setup a VPN between my Router (Unifi Dreammachine or Unifi USG) and the Linux Server. The Linux Server is running Ubuntu.

Overview

Unifi provides two types of VPN: OpenVPN and IPSec. I decided to use IPSec for my purpose. On the Linux side, I use stronSwan as IPSec driver.

VPN Setup

Let's setup the VPN on both sides.

USG Configuration

Go to your Unifi Controller, open the Settings and chose "VPN Connections". From there, create a new connection:

Fillout the Remote Subnet as well as your public IP Address (I know it will change - don't worry) and the Peers public IP Address.

UDM config

Make sure to adjust the advanced Settings

UDM Config 2

Linux Setup

Connect to your Linux Server via ssh and install strongSwan

$ sudo apt install strongswan

Create your config file

Since my USG is behind a Router the submitted "ID" is an internal IP - in my case 192.168.179.24. You need to adjust this.

/etc/ipsec.conf

# ipsec.conf - strongSwan IPsec configuration file

# basic configuration

config setup
    # strictcrlpolicy=yes
    # uniqueids = no

# Add connections here.

conn %default
  ikelifetime=60m
  keylife=20m
  rekeymargin=3m
  keyingtries=1
  keyexchange=ikev1
  authby=secret
  ike=aes128-sha1-modp2048
  esp=aes128-sha1-modp2048

conn myvpn
  keyexchange=ikev1
  left=%defaultroute
  leftid=123.123.123.123
  auto=add
  authby=secret
  type=tunnel
  right=your-dyndns-domain.com
  rightid=192.168.179.24 
  rightsubnet=192.168.1.1/24
  rightfirewall=yes

Also setup your secrets-file

/etc/ipsec.secrets

: PSK "<insert pre-shared key here>"

You need to set the permissions for the secrects-file

$ chmod 600 /etc/ipsec.secrets

Once you are done, you can restart the strongSwan Application by entering

$ ipsec stop

$ ipsec start
Starting strongSwan 5.8.2 IPsec [starter]...
charon is already running (/var/run/charon.pid exists) -- skipping daemon start
starter is already running (/var/run/starter.charon.pid exists) -- no fork done

Now start your VPN!


$ ipsec up myvpn
initiating Main Mode IKE_SA myvpn[1] to 123.123.123.120
generating ID_PROT request 0 [ SA V V V V V ]
sending packet: from 10.0.0.4[500] to 123.123.123.120[500] (240 bytes)
received packet: from 123.123.123.120[500] to 10.0.0.4[500] (160 bytes)
parsed ID_PROT response 0 [ SA V V V V ]
received XAuth vendor ID
received DPD vendor ID
received FRAGMENTATION vendor ID
received NAT-T (RFC 3947) vendor ID
selected proposal: IKE:AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_2048
generating ID_PROT request 0 [ KE No NAT-D NAT-D ]
sending packet: from 10.0.0.4[500] to 123.123.123.120[500] (372 bytes)
received packet: from 123.123.123.120[500] to 10.0.0.4[500] (372 bytes)
parsed ID_PROT response 0 [ KE No NAT-D NAT-D ]
local host is behind NAT, sending keep alives
remote host is behind NAT
generating ID_PROT request 0 [ ID HASH N(INITIAL_CONTACT) ]
sending packet: from 10.0.0.4[4500] to 123.123.123.120[4500] (108 bytes)
received packet: from 123.123.123.120[4500] to 10.0.0.4[4500] (76 bytes)
parsed ID_PROT response 0 [ ID HASH ]
IKE_SA myvpn[1] established between 10.0.0.4[123.123.123.123]...123.123.123.120[192.168.179.24]
scheduling reauthentication in 3324s
maximum IKE_SA lifetime 3504s
generating QUICK_MODE request 3353461985 [ HASH SA No KE ID ID ]
sending packet: from 10.0.0.4[4500] to 123.123.123.120[4500] (444 bytes)
received packet: from 123.123.123.120[4500] to 10.0.0.4[4500] (444 bytes)
parsed QUICK_MODE response 3353461985 [ HASH SA No KE ID ID ]
selected proposal: ESP:AES_CBC_128/HMAC_SHA1_96/MODP_2048/NO_EXT_SEQ
CHILD_SA myvpn{1} established with SPIs cb2bb1fb_i cfe93623_o and TS 10.0.0.4/32 === 192.168.1.0/24
connection 'myvpn' established successfully

Check it!

Once you are done, you can confirm your VPN is running by pinging a Host in your network or logging in to the USG using SSH and run a

  ___ ___      .__________.__
 |   |   |____ |__\_  ____/__|
 |   |   /    \|  ||  __) |  |   (c) 2010-2019
 |   |  |   |  \  ||  \   |  |   Ubiquiti Networks, Inc.
 |______|___|  /__||__/   |__|
            |_/                  http://www.ui.com

      Welcome to UniFi Dream Machine!
$  swanctl --list-sas
290f_b6f7_cc20_93ba: #1, ESTABLISHED, IKEv1, f7416b242b61d974_i* 6b4c65a24e1b3191_r
  local  '192.168.179.24' @ 192.168.179.24[4500]
  remote '1123.123.123.123' @ 123.123.123.123[4500]
  AES_CBC-128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_2048
  established 21s ago, reauth in 27798s
  93cb_c0fb_5b69_8b13: #2, reqid 1, INSTALLED, TUNNEL-in-UDP, ESP:AES_CBC-128/HMAC_SHA1_96/MODP_2048
    installed 21s ago, rekeying in 2782s, expires in 3579s
    in  c7cc7961,      0 bytes,     0 packets
    out ca70180c,      0 bytes,     0 packets
    local  192.168.1.0/24
    remote 10.0.0.4/32

IP Forwarding

So, we have built a VPN between a Linux Server in the cloud and your home Router. They can communicate between each other and send packet. But how do we use the public IP of the Server as our Public IP??

The solution is quite simple: IPTABLES.

Setup IPTABLES

Basically all we need to do is

  1. Setup a Portforwarding
  2. Setup a NAT or Masquerading so that our internal Server-IP is used for communication to our VPN Endpoint

Hint: you may need to add this port to your Firwall / Security Group of the server in your Admin Portal!

$ iptables -t nat -A PREROUTING -p tcp --dport 1234 -j DNAT --to-destination 192.168.1.100:3000
$ iptables -t nat -A POSTROUTING -j MASQUERADE

Thats it!

To confirm that the rules are applied run

$ iptables -t nat -v -L -n --line-number
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination
1        0     0 DNAT       tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:1234 to:192.168.1.100:3000

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination

Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination
1        0     0 SNAT       all  --  *      *       10.8.0.0/24         !10.8.0.0/24          to:10.0.0.4
2        0     0 MASQUERADE  all  --  *      *       0.0.0.0/0            0.0.0.0/0

Recap

We have managed to setup a VPN Connection beween our Linux Server and the USG Router. The communication is working in both directions and we can forward ports over our VPN!

Credits

Two articles, that helped me set this whole thing up. Thanks!