Austin Shafer

home contact

Running local servers behind a droplet using IPSec

I have a bit of an unorthodox setup with my FreeBSD 12.0 digitalocean droplet and a server hosted at home. The home server does not have a public IP, which is a problem since I use it to host this website. Instead I use the public-facing droplet to redirect traffic to the server over a VPN connection.

Below are the relevant portions of my configurations. Obviously I am not responsible for any damage caused if you use them. Let me know if you have any fixes or improvements.

How does it work?

Here's a quick rundown on all of the major components and what they do. I'm going to assume you have at least a base knowledge of FreeBSD and VPNs.

  • ipsec.ko - This is the kernel module provided by the base system which implements the ipsec protocols. Ipsec is a set of protocols which provide security and authentication services.
  • Strongswan - a VPN based on ipsec and Internet Key Exchange (IKE) protocols. It agrees on keys and sets up the VPN connection.
  • rinetd - A redirection server which will forward connections from the public droplet to the VPN client (the home server).

Disclaimer

This isn't a "copy directly into your config" tutorial. My configuration was edited for simplicity for this post and isn't guaranteed to work.

This post is designed to show the concepts I use to create a unique setup. Having one droplet and being able to expose ports on a physical machine you own is really useful, and this post tries to show the general organization.

Installation

Here's a quick one liner for installing everything. This needs to be done on both the client and the server:

$ pkg install strongswan rinetd

Server

These are the configs on the public facing droplet which is the VPN server:

/usr/local/etc/ipsec.conf

# basic configuration

config setup
         uniqueids = no

# Connection defaults
conn %default
        keyexchange=ikev2
        ikelifetime=60m
        keylife=20m
        rekeymargin=3m
        keyingtries=1
        keyexchange=ikev2
        authby=secret

# This is a designated connection for the client
conn punk
        left=<public_ip>
        leftid="punk"
        leftsourceip=10.3.0.1
        right=%any
        rightsourceip=10.3.0.2
        auto=add

# ... add more connections

/usr/local/etc/ipsec.secrets

The easiest way (which I don't actually use but is the simplest to configure) is to just use a Pre Shared Key for authentication:

# ipsec.secrets - strongSwan IPsec secrets file
: RSA /usr/local/etc/ipsec.d/private/vpn-server-key.pem

<public_ip> : PSK "passphrase"
punk : PSK "passphrase"

/usr/local/etc/rinetd.conf

The following is just a simple entry to map the public server's port 2222 to the client's ssh port. Add additional rules here for other ports you need.

# rinetd.conf
<public_ip> 2222 10.3.0.2 22

Starting the services

sysrc strongswan_enable=YES rinetd_enable=YES
service strongswan start
service rinetd start

Client

/usr/local/etc/ipsec.conf

# ipsec.conf - strongSwan IPsec configuration file

# basic configuration
config setup
conn %default
        ikelifetime=60m
        keylife=20m
        rekeymargin=3m
        keyingtries=1
        keyexchange=ikev2
        authby=secret              # psk or secret
conn home                          # name used in ipsec(1) commands
        leftsourceip=%config
        right=<public_ip>
        rightid="punk"
        auto=start                  # Add routing entries?

/usr/local/etc/ipsec.secrets

# ipsec.secrets - strongSwan IPsec secrets file

<public_ip> : PSK "passphrase"
punk : PSK "passphrase"

Starting the services

sysrc strongswan_enable=YES rinetd_enable=YES
service strongswan start
service rinetd start

Connecting

To manually initiate a VPN connection from the client, use the following. ipsec up starts up a connection of the given name.

$ ipsec up home

Reaching the client

At this point you should be able to ping the client from the server by using its virtual IP. I use 10.3.0.3 for the client, and hence I can ping and forward any packets to it.

The strange thing (which I can't seem to explain) is that strongswan doesn't add the proper routing rules to have a two-way tunnel between the client and server. Normally this would be a problem, but my use case only requires that the server can reach the client. If you get this working in a better way please let me know!

Miscellaneous

One thing to watch out for is that the normal ipsec_enable rc variable is not compatible with strongswan. Ipsec is also shipped in the base system, and this rcvar will do the setup for those tools.

I really, really hate writing:

  1. Firewall rules
  2. vpn configurations

I can never get them right and it's miserable the entire time.