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.
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 ca.pem
file:
# 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 debian.pem
file:
# 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 device.pem
file:
# 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 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.
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=8.8.8.8,8.8.4.4 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
Now we need to add some changes to the sysctl.conf
file.
# 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 iptables-persistent packet
:
# 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 iptables
rules.
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:
# reboot
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.
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
We need 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 iphone.mobileconfig
file:
$ ./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
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 SSH
and 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
to “45323”:
-A INPUT -p tcp -m tcp --dport 45323 -j ACCEPT
Save the file via Ctrl+X and restart the server:
# reboot
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.
Когда процедуру сделал по созданию mobileconfig и далее загрузка на телефон, при подключении показывает ошибку
"VPN Conection An unexpected error occurred"
Так же заметил, что в данном профиле имеются два сертификата