IKEv2 VPN with Ubuntu & Apple
An IKEv2 Tunnel is a safe way to protect your internet traffic and only requires a configuration file. No additional software. We’re assuming you are running as root. If you’re currently a user you can enter sudo su
and become root.
Before we begin, you must know your external network interface name, you need to come up with a private IPv4 address, and an IPv6 Unique Local Address
Finding your External Network Interface
Typically you only have a single interface. But if you’re building a VPN server you may have an internal network. Here’s something you can run to figure out what the name of your external network interface is.
┌[ star@vpn ] ~
└➤ ip route | awk '/default/ {print $5}'
eth0
Generating a Private IPv4 Network
All you want to avoid is the super common ones. 192.168.1.0/24, 10.0.0.0/24. Private IPv4 addresses are the addresses that don’t route on the internet. RFC 1918 has it all listed out in dry detail. But let’s keep this simple.
It’s four parts and in this case we’ll start with a 10. Every number ranges from 0 to 255. The last number will be a zero and you will add /24
at the end. These all work:
- 10.1.2.0/24
- 10.255.0.0/24
- 10.84.67.0/24
Generating a ULA IPv6 Network
IPv6 uses hexadecimal and is longer. That’s the hard part. a ULA starts with fd
and has three parts we’ll create with a ::/64
at the end.
- fd:1:2:3::/64
- fd:ffff:ffff:ffff::/64
- fd:abcd:ef:1::/64
Generating a Universally Unique Identifier
This is a simple command.
uuidgen
This will output a long randomly generated identifier like af732f13-4368-4450-a671-0e70c18035cc
. You will need that later.
Install Packages
Install the required packages.
apt install -y certbot strongswan libstrongswan-standard-plugins strongswan-libcharon libcharon-extra-plugins libcharon-extauth-plugins
Certificate
We’re going to use a free Let’s Encrypt certificate with modern encryption.
nano /etc/letsencrypt/cli.ini
Enter these contents:
standalone = true
agree-tos = true
non-interactive = true
preferred-challenges = http
key-type = ecdsa
elliptic-curve = secp384r1
email = [YOUR EMAIL]
pre-hook = /sbin/ufw allow from any to any port 80 proto tcp
post-hook = /sbin/ufw delete allow from any to any port 80 proto tcp
renew-hook = /usr/sbin/ipsec reload && /usr/sbin/ipsec secrets
The purpose of the pre-hook and post-hook is so you don’t keep 80/TCP open all the time. Just open it for the request & renewal then shut it afterwards.
Now we generate the certificate.
certbot certonly --key-type ecdsa -d [YOUR DOMAIN]
Link the certificate where IPSec can find it.
ln -f -s "/etc/letsencrypt/live/[YOUR DOMAIN]/cert.pem" /etc/ipsec.d/certs/cert.pem
ln -f -s "/etc/letsencrypt/live/[YOUR DOMAIN]/privkey.pem" /etc/ipsec.d/private/privkey.pem
ln -f -s "/etc/letsencrypt/live/[YOUR DOMAIN]/chain.pem" /etc/ipsec.d/cacerts/chain.pem
AppArmor Configuration
Create the configuration file
nano /etc/apparmor.d/local/usr.lib.ipsec.charon
Enter this one line in the configuration file.
/etc/letsencrypt/archive/[YOUR DOMAIN]/* r,
Now enable app armor with the configuration.
aa-status --enabled && invoke-rc.d apparmor reload
Sysctl Configuration
First step is adding onto the sysctl.conf
file.
nano /etc/sysctl.conf
Enter this at the end of the configuration profile.
net.ipv4.ip_forward = 1
net.ipv4.ip_no_pmtu_disc = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv6.conf.all.forwarding = 1
Now implement the changes.
sysctl -p
UFW Forwarding
You will need to edit the forwarding policy of UFW.
nano /etc/default/ufw
Look for the line that says DEFAULT_FORWARD_POLICY="DROP"
and change it to DEFAULT_FORWARD_POLICY="ACCEPT"
.
UFW IPv4 NAT
We’re going to add onto an existing configuration file.
nano /etc/ufw/before.rules
Enter this at the end.
# NAT for IPSec
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s [IPv4 VPN NETWORK] -o [INTERFACE] -j MASQUERADE
COMMIT
UFW IPv6 NAT
Again, we’re adding onto an existing configuration file.
nano /etc/ufw/before6.rules
Enter this at the end.
# NAT for IPSec
*nat
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s [IPv6 VPN NETWORK] -o [INTERFACE] -j MASQUERADE
COMMIT
UFW Firewall Configuration
Enter these commands in terminal.
ufw allow from any to any port 500 proto udp
ufw allow from any to any port 4500 proto udp
ufw allow from [IPv4 VPN NETWORK] to any
ufw allow from [IPv6 VPN NETWORK] to any
ufw limit from [YOUR HOME IPV4 ADDRESS] to any port 22 proto tcp
ufw limit from [YOUR HOME IPV6 ADDRESS] to any port 22 proto tcp
Now implement the changes.
ufw enable
IPSec Configuration
You will need to edit the configuration file. It’s ok to remove it and start fresh.
rm /etc/ipsec.conf
nano /etc/ipsec.conf
Enter this into the configuration file.
config setup
strictcrlpolicy=yes
uniqueids=never
conn roadwarrior
auto=add
compress=no
type=tunnel
keyexchange=ikev2
fragmentation=yes
forceencaps=yes
ike=aes256gcm16-prfsha384-ecp384,aes256gcm16-prfsha256-ecp256!
esp=aes256gcm16-ecp384!
dpdaction=clear
dpddelay=900s
rekey=no
left=%any
leftid=@[YOUR DOMAIN]
leftcert=cert.pem
leftsendcert=always
leftsubnet=::/0,0.0.0.0/0
right=%any
rightid=%any
rightauth=eap-mschapv2
eap_identity=%any
rightdns=2a01:4f8:c17:2c61::213,2a01:4f8:c013:5ec0::154,49.12.222.213,88.198.122.154
rightsourceip=[IPv6 VPN NETWORK],[IPv4 VPN NETWORK]
rightsendcert=never
Configure IPSec Secrets
These are similar to your login password. You will be authenticating using a username and password.
nano /etc/ipsec.secrets
Obviously modify this as needed. No square brackets.
[YOUR DOMAIN] : ECDSA "privkey.pem"
[USER1] : EAP "[PASSWORD]"
[USER2] : EAP "[PASSWORD]"
Now implement the changes.
ipsec enable
ipsec restart
ipsec status
Create the Configuration Profile for macOS, iPadOS, and iOS
Read through this. You will need to enter your domain, provide a different universally unique identifier for each time it says [UNIQUE ID]
. When you save it, save it with the file extension .mobileconfig
.
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE plist PUBLIC '-//Apple//DTD PLIST 1.0//EN' 'http://www.apple.com/DTDs/PropertyList-1.0.dtd'>
<plist version='1.0'>
<dict>
<key>PayloadContent</key>
<array>
<dict>
<key>IKEv2</key>
<dict>
<key>AuthenticationMethod</key>
<string>None</string>
<key>ChildSecurityAssociationParameters</key>
<dict>
<key>EncryptionAlgorithm</key>
<string>AES-256-GCM</string>
<key>IntegrityAlgorithm</key>
<string>SHA2-384</string>
<key>DiffieHellmanGroup</key>
<integer>20</integer>
<key>LifeTimeInMinutes</key>
<integer>1440</integer>
</dict>
<key>DeadPeerDetectionRate</key>
<string>Medium</string>
<key>DisableMOBIKE</key>
<integer>0</integer>
<key>DisableRedirect</key>
<integer>0</integer>
<key>EnableCertificateRevocationCheck</key>
<integer>0</integer>
<key>EnablePFS</key>
<true/>
<key>ExtendedAuthEnabled</key>
<true/>
<key>IKESecurityAssociationParameters</key>
<dict>
<key>EncryptionAlgorithm</key>
<string>AES-256-GCM</string>
<key>IntegrityAlgorithm</key>
<string>SHA2-384</string>
<key>DiffieHellmanGroup</key>
<integer>20</integer>
<key>LifeTimeInMinutes</key>
<integer>1440</integer>
</dict>
<key>RemoteAddress</key>
<key>OnDemandEnabled</key>
<integer>1</integer>
<key>OnDemandRules</key>
<array>
<dict>
<key>Action</key>
<string>Connect</string>
</dict>
</array>
<string>[YOUR DOMAIN]</string>
<key>RemoteIdentifier</key>
<string>[YOUR DOMAIN]</string>
<key>UseConfigurationAttributeInternalIPSubnet</key>
<integer>0</integer>
</dict>
<key>IPv4</key>
<dict>
<key>OverridePrimary</key>
<integer>1</integer>
</dict>
<key>PayloadDescription</key>
<string>Configures VPN settings</string>
<key>PayloadDisplayName</key>
<string>[YOUR DOMAIN]</string>
<key>PayloadIdentifier</key>
<string>com.apple.vpn.managed.[UNIQUE ID]</string>
<key>PayloadType</key>
<string>com.apple.vpn.managed</string>
<key>PayloadUUID</key>
<string>[UNIQUE ID]</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>Proxies</key>
<dict>
<key>HTTPEnable</key>
<integer>0</integer>
<key>HTTPSEnable</key>
<integer>0</integer>
</dict>
<key>UserDefinedName</key>
<string>[YOUR DOMAIN]</string>
<key>VPNType</key>
<string>IKEv2</string>
</dict>
</array>
<key>PayloadDisplayName</key>
<string>IKEv2 VPN configuration ([YOUR DOMAIN])</string>
<key>PayloadIdentifier</key>
<string>[UNIQUE ID]</string>
<key>PayloadRemovalDisallowed</key>
<false/>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadUUID</key>
<string>[UNIQUE ID]</string>
<key>PayloadVersion</key>
<integer>1</integer>
</dict>
</plist>
Now that you have the .mobileconfig
file created, you can get it setup on any apple device.
For macOS, I just double click on it. Then I go to Settings and click on “Profile Downloaded” to install it. Double click on the configuration profile and click “Install“.
After installing it on macOS, you will have to define the login. Go to Settings > VPN > and click on the little “i” for your connection. Under “Authentication” click on “Certificate” and change it to “Username“. Enter your username and password you created in the ipsec.secrets
configuration file.
Now you can use it. In macOS you can also enable it in the menu bar (control center) by going to Settings > Control Center and going to “Menu Bar Only” section and changing VPN to “Show in Menu Bar.”
For iOS or iPadOS, you may AirDrop it or email it to yourself. You can also now open it from your iCloud Drive directly in the Files app. You’ll be prompted to go to Settings. You’ll see where it says “Profile Downloaded” near the top of settings. Choose to install it and follow the prompts. You will be asked for your device passcode, your username, and your password for the VPN.
It will enable and you will be able to use it.
If you want to make it not turn on automatically, you can go to Settings > General> VPN & Device Management > VPN and set “Connect on demand” to off.