StrongSwan IKEv2 for macOS, iOS 11, Windows 10 and BlackBerry 10 With Local DNS Cache (Unbound), Dnscrypt-proxy + (Cloudflare DoH) for IPv4/6
This setup is for remote users to connect into an office/home LAN using a VPN (ipsec). This is based on (but not the same as) the strongSwan documentation and this guide: https://raymii.org/s/tutorials/IPSEC_vpn_with_Ubuntu_16.04.html
- StrongSwan 5.6.2
- Dnscrypt-proxy 2.0.12
- Unbound 1.7.1
Set up DNS service Set up a network interface for DNS listening
Edit /etc/network/interfaces with the following content
auto lo:lo4
iface lo:lo4 inet static
address 172.16.0.1
netmask 255.255.255.255
service networking restart
Step 2: check what else is possibly already listening to port 53
If you already have a local DNS cache, it has to be eventually replaced with dnscrypt-proxy. Both can be used simultaneously, but this is outside of the scope of this guide (or, at least, of this Wiki page).
Type the following command:
ss -lp 'sport = :domain'
This may ouptut something similar to:
tcp LISTEN 0 128 127.0.0.1:domain : users:(("unbound",pid=28146,fd=6)) tcp LISTEN 0 128 127.0.0.1:domain : users:(("unbound",pid=28146,fd=4)) Uninstall the corresponding package (in the above example: unbound), with a distribution-specific command such as apt-get remove or pacman -R, then check again with ss -lp 'sport = :domain': there shouldn't be anything listening to the domain port any more.
You may also see the port being served by systemd-resolve. That one cannot be uninstalled, but can be disabled with the following commands:
systemctl stop systemd-resolved
systemctl disable systemd-resolved
Installing dnscrypt-proxy on Linux
cd /opt
wget https://github.com/jedisct1/dnscrypt-proxy/releases/download/2.0.12/dnscrypt-proxy-linux_x86_64-2.0.12.tar.gz
tar zxf dnscrypt-proxy-linux_x86_64-2.0.12.tar.gz
mv linux-x86_64/ dnscrypt-proxy/
cd dnscrypt-proxy/
cp example-dnscrypt-proxy.toml dnscrypt-proxy.toml
./dnscrypt-proxy
Change the system DNS settings
apt-get remove resolvconf
cp /etc/resolv.conf /etc/resolv.conf.backup
rm -f /etc/resolv.conf
echo "nameserver 127.0.0.1" > /etc/resolv.conf
echo "options edns0 single-request-reopen" >> /etc/resolv.conf
# and lock
chattr +i /etc/resolv.conf
# unlock
chattr -i /etc/resolv.conf
Type ./dnscrypt-proxy to start the server, and Control-C to stop it. Test, tweak, stop, test, tweak, stop until you are satisfied.
Install the proxy as a system service
./dnscrypt-proxy -service install
Now that it's installed, it can be started:
./dnscrypt-proxy -service start
Want to stop the service?
./dnscrypt-proxy -service stop
Want to restart the currently running service after a configuration file change?
./dnscrypt-proxy -service restart
Want to uninstall the service?
./dnscrypt-proxy -service uninstall
Want to check that DNS resolution works?
./dnscrypt-proxy -resolve example.com
Want to completely delete that thing?
Add new settings
cat << "EOF" >> /opt/dnscrypt-proxy/dnscrypt-proxy.toml
server_names = ['cloudflare', 'cloudflare-ipv6']
listen_addresses = ['127.0.0.1:5353', '[::1]:5353']
max_clients = 250
ipv4_servers = true
ipv6_servers = true
dnscrypt_servers = true
doh_servers = true
require_dnssec = false
require_nolog = true
require_nofilter = true
force_tcp = false
timeout = 2500
keepalive = 30
cert_refresh_delay = 240
fallback_resolver = '9.9.9.9:53'
ignore_system_dns = false
log_files_max_size = 10
log_files_max_age = 7
log_files_max_backups = 1
block_ipv6 = false
##
cache = false
cache_size = 512
cache_min_ttl = 600
cache_max_ttl = 86400
cache_neg_min_ttl = 60
cache_neg_max_ttl = 600
[sources]
[sources.'public-resolvers']
urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v2/public-resolvers.md', 'https://download.dnscrypt.info/resolvers-list/v2/public-resolvers.md']
cache_file = 'public-resolvers.md'
minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
refresh_delay = 72
prefix = ''
[static]
# [static.'google']
# stamp = 'sdns://AgUAAAAAAAAAAAAOZG5zLmdvb2dsZS5jb20NL2V4cGVyaW1lbnRhbA'
EOF
Set up auto start script
A auto start script with name dnscrypt-proxy.service should be add to folder /etc/systemd/system with the following content.
cat << "EOF" >> /etc/systemd/system/dnscrypt-proxy.service
[Unit]
Description=Encrypted/authenticated DNS proxy
ConditionFileIsExecutable=/opt/dnscrypt-proxy/dnscrypt-proxy
[Service]
StartLimitInterval=5
StartLimitBurst=10
ExecStart=/opt/dnscrypt-proxy/dnscrypt-proxy
WorkingDirectory=/opt/dnscrypt-proxy
Restart=always
RestartSec=120
EnvironmentFile=-/etc/sysconfig/dnscrypt-proxy
[Install]
WantedBy=multi-user.target
EOF
Then start dnscrypt-proxy service.
systemctl daemon-reload
systemctl start dnscrypt-proxy.service
systemctl enable dnscrypt-proxy.service
Update openssl
apt-get install build-essential libssl-dev python -y
cd /usr/local/src
wget https://www.openssl.org/source/latest.tar.gz -O openssl.tar.gz
tar -zxvf openssl.tar.gz
cd openssl-1.1.0h/
make && make test && make install
mv /usr/bin/openssl /usr/bin/openssl.old
ln -s /usr/local/ssl/bin/openssl /usr/bin/openssl
Install ldns
cd /tmp
wget https://www.nlnetlabs.nl/downloads/ldns/ldns-1.7.0.tar.gz
tar -xzvf ldns-1.7.0.tar.gz
cd ldns-1.7.0/
./configure
make && make install
Set up Unbound
apt install unbound
cd /tmp
wget http://www.unbound.net/downloads/unbound-1.7.3.tar.gz
tar -xzvf unbound-1.7.3.tar.gz
cd unbound-1.7.3/
apt install gcc libssl-dev libevent-dev -y
./configure --prefix=/usr --sysconfdir=/etc
make && make install
groupadd unbound
useradd -d /var/unbound -m -g unbound -s /bin/false unbound
wget ftp://FTP.INTERNIC.NET/domain/named.cache -O /etc/unbound/root.hints
Then the following content should be added to /etc/unbound/unbound.conf.
server:
interface: 127.0.0.1
interface: ::1
interface: 172.16.0.1
access-control: 127.0.0.1/8 allow
access-control: 10.0.0.0/8 allow
access-control: 172.16.0.0/12 allow
# unbound optimisation
num-threads: 2
msg-cache-slabs: 16
rrset-cache-slabs: 16
infra-cache-slabs: 16
key-cache-slabs: 16
outgoing-range: 206
so-rcvbuf: 4m
so-sndbuf: 4m
so-reuseport: yes
rrset-cache-size: 100m
msg-cache-size: 50m
# unbound security
do-ip4: yes
do-ip6: yes
do-udp: yes
do-tcp: yes
cache-max-ttl: 86400
cache-min-ttl: 3600
hide-identity: yes
hide-version: yes
minimal-responses: yes
prefetch: yes
use-caps-for-id: yes
verbosity: 1
harden-glue: yes
harden-dnssec-stripped: yes
# download from ftp://ftp.internic.net/domain/named.cache
root-hints: "/etc/unbound/root.hints"
forward-zone:
name: "."
forward-addr: 127.0.0.1@5353
Then restart Unbound service.
service unbound restart
Set up IKEv2 VPN Since strongswan in Ubuntu 16.04 is not the latest version, we compile from source.
apt-get install -y --no-install-recommends build-essential libgmp-dev python-dev libsystemd-dev libssl-dev libpcsclite-dev libsoup2.4-dev
cd /tmp
wget https://download.strongswan.org/strongswan-5.6.3.tar.gz
tar zxf strongswan-5.6.3.tar.gz
cd strongswan-5.6.3/
./configure --prefix=/usr --sysconfdir=/etc \
--enable-hmac \
--enable-kernel-netlink \
--enable-nonce \
--enable-pem \
--enable-pgp \
--enable-pkcs12 \
--enable-pkcs7 \
--enable-pkcs8 \
--enable-pubkey \
--enable-random \
--enable-revocation \
--enable-sha2 \
--enable-socket-default \
--enable-stroke \
--enable-addrblock \
--enable-af-alg \
--enable-agent \
--enable-attr-sql \
--enable-blowfish \
--enable-ccm \
--enable-certexpire \
--enable-conftest \
--enable-coupling \
--enable-ctr \
--enable-dhcp \
--enable-duplicheck \
--enable-eap-aka \
--enable-eap-aka-3gpp2 \
--enable-eap-gtc \
--enable-eap-identity \
--enable-eap-md5 \
--enable-eap-mschapv2 \
--enable-eap-peap \
--enable-eap-radius \
--enable-eap-sim \
--enable-eap-sim-file \
--enable-eap-sim-pcsc \
--enable-eap-simaka-pseudonym \
--enable-eap-simaka-reauth \
--enable-eap-simaka-sql \
--enable-eap-tls \
--enable-eap-tnc \
--enable-eap-ttls \
--enable-farp \
--enable-gcm \
--enable-ha \
--enable-imc-attestation \
--enable-imc-scanner \
--enable-imc-test \
--enable-imv-attestation \
--enable-imv-scanner \
--enable-imv-test \
--enable-integrity-test \
--enable-kernel-libipsec \
--enable-swanctl \
--enable-unity \
--enable-unbound \
--enable-vici \
--enable-xauth-eap \
--enable-xauth-noauth \
--enable-kernel-pfkey \
--enable-leak-detective \
--enable-led \
--enable-load-tester \
--enable-lock-profiler \
--enable-md4 \
--enable-medcli \
--enable-mediation \
--enable-monolithic \
--enable-openssl \
--enable-pkcs11 \
--enable-smp \
--enable-socket-dynamic \
--enable-test-vectors \
--enable-tnccs-11 \
--enable-tnccs-20 \
--enable-tnccs-dynamic \
--enable-tnc-ifmap \
--enable-tnc-imc \
--enable-tnc-imv \
--enable-whitelist \
--enable-x509
make && make install
Replace the variables below with appropriate values for your organisation. Then copy and paste line by line.
COUNTRY_CODE=XX
ORGANISATION=XXXXX
SERVER_NAME=vpn.example.com
IP_ADDRESS=<public ip of the vpn server>
pushd /etc/ipsec.d/
Create self-signed certificate authority (CA):
ipsec pki --gen --type rsa --size 4096 --outform der > private/strongswan.der
chmod 600 private/strongswan.der
ipsec pki --self --ca --lifetime 3650 --in private/strongswan.der --type rsa --dn "C=$COUNTRY_CODE, O=$ORGANISATION, CN=$ORGANISATION Root CA" --outform der > cacerts/strongswan.der
openssl x509 -inform DER -in cacerts/strongswan.der -out cacerts/strongswan.pem -outform PEM
ipsec pki --print --in cacerts/strongswan.der
Create host key:
ipsec pki --gen --type rsa --size 4096 --outform der > private/host-vpn.der
chmod 600 private/host-vpn.der
ipsec pki --pub --in private/host-vpn.der --type rsa | ipsec pki --issue --lifetime 730 --cacert cacerts/strongswan.der --cakey private/strongswan.der --dn "C=$COUNTRY_CODE, O=$ORGANISATION, CN=$SERVER_NAME" --san=$SERVER_NAME --san $IP_ADDRESS --san @$IP_ADDRESS --flag serverAuth --flag ikeIntermediate --outform der > certs/host-vpn.der
ipsec pki --print --in certs/host-vpn.der
mkdir -p p12
popd
Note: if you want to use "Remote ID" to let clients choose between connection profiles, then you need to add them as SANs above, as strongSwan will only let you use leftids
that are named in your host cert.
Edit /etc/ipsec.secrets
to add the host-vpn.der
private key we generated for the host above (see the example file in this Gist).
Copy the example ipsec.conf
attached to this Gist into /etc/ipsec.conf
. Replace <VPN SERVER ID>
with the value of $SERVER_NAME
above. Replace <INTERNAL SUBNET>
with the subnet you want to provide access to in your LAN. If you want to route all internet traffic, use 0.0.0.0/0
, otherwise enter something like 192.168.1.0/24
. Note that you might have trouble if LAN subnet conflicts with subnets the mobile device might otherwise be on. Replace <LAN DNS SERVERS>
with the appropriate DNS servers for your LAN.
Documentation of what the configuration in the conn
section means: https://wiki.strongswan.org/projects/strongswan/wiki/ConnSection
keyexchange=ikev2
We use the modern key exchange protocol. This works on all of the platforms we want to support.ike=...
A list of cipher suites as recommended by the guide linked aboveesp=...
dittodpdaction=clear
When the Dead Peer Detection detects a dead peer it closes the ipsec connectiondpddelay=60s
How often to send the Dead Peer Detection check if the connection is idle. Don't make this too long as some clients close the connection themselves if it is idle for much longer than this.left=%any
We don't need to know what our IP address. Just use one of our IP addresses at runtime, depending upon how the client connected.leftid=...
This is the "Remote ID" string that macOS and iOS clients use. I suggest using your server's fully-qualified domain name, as above.leftsubnet=...
As above.leftcert=host-vpn.der
The certificate representing the server that we created above.leftsendcert=always
See https://wiki.strongswan.org/projects/strongswan/wiki/MacOSX which notes that we should agressively send our certificate so the user doesn't need to install it.right=%any
Our mobile users could have any IP addressrightauth=eap-tls
This activates user authentication on the client. More below.rightsourceip=%dhcp
Use our network's DHCP server to issue an IP address. You can also just give a subnet here, or an IP address range, e.g.10.1.1.50-10.1.1.70
rightdns=...
DNS servers to give to the mobile user. Probably the same DNS servers you use on your LAN.eap_identity=%identity
For Windows 10 to work.auto=add
When strongSwan starts up it should add this connection to its list of connections available to use when a mobile user connects.
If we issue remote clients IPs using our LAN's DHCP server, and the DHCP server is on the same server as the VPN, then we need a little extra configuration. Edit the /etc/strongswan.d/charon/dhcp.conf
file, consulting the example attached to this Gist. Replace <LAN BROADCAST ADDRESS>
with the broadcast address of your LAN.
Documentation: https://wiki.strongswan.org/projects/strongswan/wiki/Dhcpplugin
Install the /etc/sysctl.d/99-strongswan.conf
file listed below, and make sure it's loaded:
sysctl -p
If your VPN server is not public on the internet, you'll need to setup port forwarding on your internet-facing router.
The following ports must be forwarded to your VPN server:
- UDP 500
- UDP 4500 (for nat traversal)
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
iptables -t nat -F
iptables -t mangle -F
iptables -F
iptables -X
ip6tables -P INPUT ACCEPT
ip6tables -P FORWARD ACCEPT
ip6tables -P OUTPUT ACCEPT
ip6tables -t nat -F
ip6tables -t mangle -F
ip6tables -F
ip6tables -X
apt-get install iptables-persistent
iptables -A INPUT -p udp --dport 500 --j ACCEPT
iptables -A INPUT -p udp --dport 4500 --j ACCEPT
iptables -A INPUT -p esp -j ACCEPT
iptables -t nat -A POSTROUTING -j SNAT --to-source YOU_IPV4 -o eth0
ip6tables -A INPUT -p udp --dport 500 --j ACCEPT
ip6tables -A INPUT -p udp --dport 4500 --j ACCEPT
ip6tables -A INPUT -p esp -j ACCEPT
ip6tables -t nat -A POSTROUTING -j SNAT --to-source YOU_IPV6 -o he-ipv6
iptables-save > /etc/iptables/rules.v4
ip6tables-save > /etc/iptables/rules.v6
To startup strongSwan:
ipsec start
To stop it:
ipsec stop
To reload the configuration from /etc/ipsec.conf
when you've made changes, but without interfering with any existing connected users:
ipsec reload
To restart strongSwan when you've made configuration changes, or want to bump connected users:
ipsec restart
To get the status of established strongSwan connections:
ipsec status
To get more details of strongSwan's status:
ipsec statusall
We use certificates to authenticate users. This works on macOS 10.12, iOS 10 and Windows 10. So next you need to create user certificates so that you can connect to the VPN.
Set the variables first. The username should be a string that is okay as a filename, and doesn't contain any spaces. Like a username. The user id is usually an email address.
Note that we store the user files in the same directories as the CA and host ones. So don't use strongswan
as a username, or host-vpn
, or you'll overwrite your previous files!
NAME=John Doe
USERNAME=jdoe
[email protected]
COUNTRY_CODE=XX
ORGANISATION=XXXXX
Then run these commands. You will be prompted for a password on the final openssl command. This password protects the .p12
file that contains the private key and certificate for the user. I suggest making up a random password for each .p12
file.
pushd /etc/ipsec.d
ipsec pki --gen --type rsa --size 2048 --outform der > private/$USERNAME.der
chmod 600 private/$USERNAME.der
ipsec pki --pub --in private/$USERNAME.der --type rsa | ipsec pki --issue --lifetime 730 --cacert cacerts/strongswan.der --cakey private/strongswan.der --dn "C=$COUNTRY_CODE, O=$ORGANISATION, CN=$USERID" --san "$USERID" --outform der > certs/$USERNAME.der
openssl rsa -inform DER -in private/$USERNAME.der -out private/$USERNAME.pem -outform PEM
openssl x509 -inform DER -in certs/$USERNAME.der -out certs/$USERNAME.pem -outform PEM
openssl pkcs12 -export -inkey private/$USERNAME.pem -in certs/$USERNAME.pem -name "$NAME's VPN Certificate" -certfile cacerts/strongswan.pem -caname "$ORGANISATION Root CA" -out p12/$USERNAME.p12
popd
The /etc/ipsec.d/p12/$USERNAME.p12
file contains the user's private key and certificate. It is a binary file. You need to send this file to the user, and send the password (entered above) separately.
Send the .p12
file for the user and the /etc/ipsec.d/cacerts/strongswan.pem
file.
On macOS, double-click the .p12
file, and enter the password, to add the user's certificate and private key to the keychain using Keychain Access.app. In Keychain Access.app you should see the certificate in the My Certificates list of the login keychain, with the user id from above as its name.
Double-click the .pem
file to add our CA certificate to the keychain using Keychain Access.app. Find the CA certificate in the Certificates list of the login keychain, named with your organisation name followed by "Root CA". Double-click to open it. Expand the Trust section, and choose "Always Trust".
- Open the Network system preferences pane.
- Click the + button to add a new connection.
- Interface: VPN
- VPN Type: IKEv2
- Service Name: A name of your choice
- Click "Create", then fill in the details for the connection:
- Server Address: the fully-qualified domain name (or IP) of your VPN server
- Remote ID: the same as the Server Address, unless you've done something different with your
leftid
- Local ID: the user id, and name on your user certificate, probably the email address
- Click "Authentication Settings…"
- Choose "Certificate" from the dropdown for authentication settings
- Click "Select…" and choose the certificate matching the Local ID you entered above.
- Click "OK" to close the Authentication Settings sheet.
- Click "Connect" to connect your VPN.
One way to get the .p12
file and CA cert (/etc/ipsec.d/cacerts/strongswan.pem
) onto an iOS device is to put them on a web server and browse to them. Another is to use the Apple Configurator application.
The web server approach is pretty easy. Browse to each file and follow the prompts to add them to your profiles.
- Open the Settings app
- Open the VPN settings
- Tap "Add VPN Configuration…"
- Type: IKEv2
- Description: Anything here
- Server: the fully-qualified domain name (or IP) of your VPN server
- Remote ID: the same as the Server Address, unless you've done something different with your
leftid
- Local ID: the user id, and name on your user certificate, probably the email address
- User Authentication: Certificate
- Certificate: choose the certificate matching the Local ID you entered above.
- Done
You can now connect to the VPN from your iOS device.
Send the .p12
file for the user and the /etc/ipsec.d/cacerts/strongswan.der
file, but rename strongswan.der
to strongswan.cer
. That will make it easier to use on Windows.
-
On Windows 10, double-click the
.p12
file to open the Certificate Import Wizard. -
Choose "Current User" and click "Next".
-
Check the file path, and click "Next" again.
-
Enter the password for the
.p12
file that you used above. The default import options are fine (just "Include all extended properties" is ticked) -
Choose "Automatically select the certificate store..." and click Next (it will choose the Personal certificate store, which is good).
-
Click Finish.
-
Open the "Manage computer certificates" control panel (use the search from the Windows menu)
-
Right-click on "Trusted Root Certification Authorities" in the tree, and choose All Tasks > Import…
-
The Store Location will be "Local Machine". If not you need to exit out of the app and make sure you are in the computer certificates control panel, not the user certificates. Click Next.
-
Browse for
strongswan.cer
. Click Next. (If the file is namedstrongswan.der
, then you'll need to change the file type dropdown to All Files) -
The certificate store option will be the specific store, "Trusted Root Certification Authoritities". Click Next.
-
Click Finish.
-
Open the "Change virtual private networks (VPN)" control panel.
-
Click the "Add a VPN connection" button
-
VPN provider: Windows (built-in)
-
Connection name: A name of your choice
-
Server name or address: the fully-qualified domain name (or IP) of your VPN server
-
VPN type: IKEv2
-
Type of sign-in info: Certificate
-
User name and Password can stay blank
-
Click "Save"
Then click on the VPN connection you just created and connect.
If you want to route your entire internet connection over the VPN, you need to get to the advanced settings on the IPv4 and enable "Use default gateway on remote network".
- Open the "Network and Sharing Center"
- Click "Change adapter settings" (left)
- Right-click on your VPN and choose Properties
- Click the "Networking" tab.
- Click "Internet Protocol Version 4 (TCP/IPv4)", then click "Properties"
- Tick on "Use default gateway on remote network"
- OK out of all of those screens.
Books:
https://www.thomas-krenn.com/en/wiki/Saving_Iptables_Firewall_Rules_Permanently
https://developers.cloudflare.com/1.1.1.1/dns-over-https/cloudflared-proxy/
http://unbound.nlnetlabs.nl/documentation/howto_setup.html
https://redplus.me/post/set-up-ikev2-vpn-for-ios-and-macos-with-local-dns-cache-and-dnscrypt/