Tutorial: how to create a personal VPN server based on Linux Debian, strongSwan, certificates authentification and ready to use .mobileconfig profiles to use on iPhone, iPad and Mac
This is a part of a big article I posted on Medium. Here is only a tutorial.
We're going to create a personal VPN server, using the following technologies:
- IKEv2 as a VPN protocol
- Linux Debian as a server OS
- strongSwan as a VPN server
- Certificates as an authentication method
You can use this tutorial on any hosting you prefer.
The tutorial has been checked for errors and has been applied many times so it will work for sure on a clean system with no errors. Configuring will take from 15 min, depending on your work speed.
We're going to make all manipulations under a root user. Let’s go:
# sudo su
First of all, update packets indexes in repositories, probably there might be updates:
# apt-get update
And then install these updates:
# apt-get upgrade
Let’s install stongSwan:
# apt-get install strongswan
We’ll get back to the detailed configure of strongSwan a little bit later, but now let’s create certificates for our devices so they will be able to connect by VPN.
Generating access certificates
We'll use self-signed certificates since the VPN server will be used only by us. To create certificates we need a
strongswan-pki packet. Let’s install it:
# apt-get install strongswan-pki
Moving to certificates creation.
First of all we have to create a root certificate CA (Certificate Authority), which will issue other certificates. Create it in a
# cd /etc/ipsec.d # ipsec pki --gen --type rsa --size 4096 --outform pem > private/ca.pem # ipsec pki --self --ca --lifetime 3650 --in private/ca.pem \ > --type rsa --digest sha256 \ > --dn "CN=YOUR_EXTERNAL_VPS_IP" \ > --outform pem > cacerts/ca.pem
Then create server’s private key certificate in the
# ipsec pki --gen --type rsa --size 4096 --outform pem > private/debian.pem # ipsec pki --pub --in private/debian.pem --type rsa | > ipsec pki --issue --lifetime 3650 --digest sha256 \ > --cacert cacerts/ca.pem --cakey private/ca.pem \ > --dn "CN=YOUR_EXTERNAL_VPS_IP" \ > --san YOUR_EXTERNAL_VPS_IP \ > --flag serverAuth --outform pem > certs/debian.pem
And now create the certificate for our devices in the
# ipsec pki --gen --type rsa --size 4096 --outform pem > private/me.pem # ipsec pki --pub --in private/me.pem --type rsa | > ipsec pki --issue --lifetime 3650 --digest sha256 \ > --cacert cacerts/ca.pem --cakey private/ca.pem \ > --dn "CN=me" --san me \ > --flag clientAuth \ > --outform pem > certs/me.pem
Let’s remove the
ca.pem file for safety as we don’t need it anymore:
# rm /etc/ipsec.d/private/ca.pem
Certificates creation is completed.
If certificates are being created too long
If your certificates are being created too long, more than five seconds, for example, it could evidence about the low level of entropy. I've encountered the same situation in my time with Hetzner’s cloud servers. The entropy was too low and certificates creation time was stretching to 40-50 minutes, so I decided to refuse their services.
You can check the level of the entropy, running one more session in a new tab:
$ cat /proc/sys/kernel/random/entropy_avail
This command will show you the level of the entropy for the moment of a request. To monitor the entropy in real time, execute a command:
$ watch -n 0.25 cat /proc/sys/kernel/random/entropy_avail
If the entropy is less than 200, I would recommend you to change a hosting company. Or you can install
haveged packet, which allegedly generates the entropy, but you do that at your own risk.
To exit a request press Ctrl+Z.
Configuring strongSwan itself
Clear a default strongSwan config with a command:
# > /etc/ipsec.conf
And create a new one using
nano text editor:
# nano /etc/ipsec.conf
Paste the following text into it, replacing YOUR_EXTERNAL_VPS_IP to an external static IP of your VPS machine:
include /var/lib/strongswan/ipsec.conf.inc config setup uniqueids=never charondebug="ike 2, knl 2, cfg 2, net 2, esp 2, dmn 2, mgr 2" conn %default keyexchange=ikev2 ike=aes128gcm16-sha2_256-prfsha256-ecp256! esp=aes128gcm16-sha2_256-ecp256! fragmentation=yes rekey=no compress=yes dpdaction=clear left=%any leftauth=pubkey leftsourceip=YOUR_EXTERNAL_VPS_IP leftid=YOUR_EXTERNAL_VPS_IP leftcert=debian.pem leftsendcert=always leftsubnet=0.0.0.0/0 right=%any rightauth=pubkey rightsourceip=10.10.10.0/24 rightdns=188.8.131.52,184.108.40.206 conn ikev2-pubkey auto=add
Caution! strongSwan is meticulous about spacing on its config file, so check that each parameter of config parts is space-separated with Tab as it shown in the example, or at least with one space, otherwise strongSwan won't run.
Save the file with Ctrl+X and let's move forward.
Add a point to the server certificate to
ipsec.secrets file, which is a repository of certificates links and auth keys:
# nano /etc/ipsec.secrets include /var/lib/strongswan/ipsec.secrets.inc : RSA debian.pem
With that, we have finished strongSwan setup, let’s restart the service:
# ipsec restart
If everything is ok, the server will restart:
Starting strongSwan 5.5.1 IPsec [starter]...
If it fails, we can look at what has really happened, by reading the system log. The command will show you the last 50 log lines:
# tail -n 50 > /var/log/syslog
Let’s configure a core network parameters
Now we need to add some changes to the
# nano /etc/sysctl.conf
Using Ctrl+W find the following variables and make changes to them:
#Uncomment this variable to enable packets forwarding net.ipv4.ip_forward = 1 #Uncomment this variable to prevent MITM-attacks net.ipv4.conf.all.accept_redirects = 0 #Uncomment this variable to disable the sending of ICMP-redirects net.ipv4.conf.all.send_redirects = 0 ... #In any place of the file on a new line add this variable to permit PMTU searching net.ipv4.ip_no_pmtu_disc = 1
Load new values:
# sysctl -p
Configuration of the core network parameters is completed.
iptables is a tool which manages Linux internal firewall called netfilter. To save and autoload iptables rules after each system restart install
# apt-get install iptables-persistent
We will be asked whether to save the current IPv4 and IPv6 rules or not. Answer “No”, because we have a clean system and, in fact, there is nothing to save.
Let’s move to the formation of
Just in case, let’s clear all chains:
# iptables -P INPUT ACCEPT # iptables -P FORWARD ACCEPT # iptables -F # iptables -Z
Allow connection for a SSH at port 22 in order not to lose access to the machine:
# iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # iptables -A INPUT -p tcp --dport 22 -j ACCEPT
Allow connections on loopback-interface:
# iptables -A INPUT -i lo -j ACCEPT
Now allow incoming IPSec connections at UDP ports 500 and 4500:
# iptables -A INPUT -p udp --dport 500 -j ACCEPT # iptables -A INPUT -p udp --dport 4500 -j ACCEPT
Allow ESP-traffic forwarding:
# iptables -A FORWARD --match policy --pol ipsec --dir in --proto esp -s 10.10.10.0/24 -j ACCEPT # iptables -A FORWARD --match policy --pol ipsec --dir out --proto esp -d 10.10.10.0/24 -j ACCEPT
Configure traffic masquerading since our VPN server acts as a gateway between VPN clients and the Internet:
# iptables -t nat -A POSTROUTING -s 10.10.10.0/24 -o eth0 -m policy --pol ipsec --dir out -j ACCEPT # iptables -t nat -A POSTROUTING -s 10.10.10.0/24 -o eth0 -j MASQUERADE
Adjust the packets maximum segment size:
# iptables -t mangle -A FORWARD --match policy --pol ipsec --dir in -s 10.10.10.0/24 -o eth0 -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360
Deny any other connections to the server:
# iptables -A INPUT -j DROP # iptables -A FORWARD -j DROP
Save the rules to autoload them after each system restart:
# netfilter-persistent save # netfilter-persistent reload
iptables configuration is completed.
Let’s reboot the server:
And find out if
iptables is working or not:
$ sudo su # iptables -S root@XX.XX.XX.XX:/home/admin# iptables -S -P INPUT ACCEPT -P FORWARD ACCEPT -P OUTPUT ACCEPT -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -p udp -m udp --dport 500 -j ACCEPT -A INPUT -p udp -m udp --dport 4500 -j ACCEPT -A INPUT -j DROP -A FORWARD -s 10.10.10.0/24 -m policy --dir in --pol ipsec --proto esp -j ACCEPT -A FORWARD -d 10.10.10.0/24 -m policy --dir out --pol ipsec --proto esp -j ACCEPT -A FORWARD -j DROP
Yes, it’s working.
Also, check if strongSwan is working or not:
# ipsec statusall root@XX.XX.XX.XX:/home/admin# ipsec statusall Status of IKE charon daemon (strongSwan 5.5.1, Linux 4.9.0-8-amd64, x86_64): uptime: 71 seconds, since Jan 23 23:22:16 2019 ...
Yes, it’s working.
Creating .mobileconfig for iPhone, iPad and Mac
We’re going to use one VPN profile
.mobileconfig for all our devices: iPhone, iPad and Mac. We’ll create such type of config called “On Demand”. It automatically initiates a VPN connection when any service or app needs the Internet. Thus, we’ll avoid the situation when we forget to initiate a VPN connection manually after device restarts, for example, and the traffic goes via main Internet channel of your provider which we really don’t want to happen.
Download the script which generates a
.mobileconfig for us:
# wget https://gist.githubusercontent.com/borisovonline/955b7c583c049464c878bbe43329a521/raw/966e8a1b0a413f794280aba147b7cea0661f77a8/mobileconfig.sh
zsh packet for the script to work properly, install it:
# apt-get install zsh
Edit the name of the server on your taste and fill an external static IP of your VPS machine, which we used when created certificates:
# nano mobileconfig.sh
... SERVER="My Personal VPN" FQDN="YOUR_EXTERNAL_VPS_IP" ...
Run the script and get the ready
$ ./mobileconfig.sh > iphone.mobileconfig
Take this file from the server with the help of any SFTP clients, Transmit or Cyberduck, for example, and send it to all of your devices via Airdrop. Confirm the configuration installation on your devices.
All done! VPN connection will be established automatically.
Clean up afterwards:
# rm mobileconfig.sh # rm iphone.mobileconfig
Pumping a SSH security — optional step
Our VPN server is already working and is protected well, however I suggest pumping a SSH security a little bit more.
To prevent botnets knocking in our SSH via default port guessing passwords and leaving lots of garbage in logs, let’s change the default SSH port for anything different. Also, let’s make some other changes to SSH config.
You can choose any port to your taste starting 1024, however, I recommend finding such port which has no history of being used by viruses, trojans and also isn’t used by any popular services, applications or equipment manufacturers. Find such “clean” port on SpeedGuide or adminsubnet.
In our tutorial we’re going to use the port 45323.
Caution! Don’t restart
iptables services and don’t restart the machine, until you have finished this part to the end. Otherwise, you will lose access to the machine!
Now let’s configure a SSH itself:
# nano /etc/ssh/sshd_config #Uncomment and place the new port Port 45323 #Uncomment and deny connection attempts with an empty password PermitEmptyPasswords no #Uncomment and configure automatic disconnect after 360 seconds of inactivity. This will be useful if you forget that you have an active SSH session on your computer and leave your place. Server will automatically close the SSH connection after 6 minutes. ClientAliveInterval 360 ClientAliveCountMax 0
Now let’s update
iptables rules and change the old SSH port with a new one:
# nano /etc/iptables/rules.v4
Replace “22” in this line:
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 45323 -j ACCEPT
Save the file via Ctrl+X and restart the server:
After server restart check the SSH connection using the new port with adding a “-p” variable:
$ ssh -i YOUR_DOWNLOADED_KEY.pem admin@YOUR_LIGHTSAIL_IP -p 45323
It all should work.