IPsec server in OpenWrt

NOTE: Please, check this updated post about this topic.

I have configured a IPsec server in my OpenWrt router to use it from my Android device when I am connected to an untrusted network. Previously I’ve used OpenVPN, but it drains too much battery, so I want to test if this solution, which is integrated in Android, works better.

I have taken the configuration from the OpenWrt Wiki.

In OpenWrt, I have installed this packages:

strongswan - 5.0.0-1
strongswan-charon - 5.0.0-1
strongswan-default - 5.0.0-1
strongswan-mod-aes - 5.0.0-1
strongswan-mod-af-alg - 5.0.0-1
strongswan-mod-attr - 5.0.0-1
strongswan-mod-blowfish - 5.0.0-1
strongswan-mod-constraints - 5.0.0-1
strongswan-mod-des - 5.0.0-1
strongswan-mod-dhcp - 5.0.0-1
strongswan-mod-dnskey - 5.0.0-1
strongswan-mod-farp - 5.0.0-1
strongswan-mod-fips-prf - 5.0.0-1
strongswan-mod-gcrypt - 5.0.0-1
strongswan-mod-gmp - 5.0.0-1
strongswan-mod-hmac - 5.0.0-1
strongswan-mod-kernel-netlink - 5.0.0-1
strongswan-mod-md4 - 5.0.0-1
strongswan-mod-md5 - 5.0.0-1
strongswan-mod-nonce - 5.0.0-1
strongswan-mod-openssl - 5.0.0-1
strongswan-mod-pem - 5.0.0-1
strongswan-mod-pgp - 5.0.0-1
strongswan-mod-pkcs1 - 5.0.0-1
strongswan-mod-pkcs11 - 5.0.0-1
strongswan-mod-pkcs8 - 5.0.0-1
strongswan-mod-pubkey - 5.0.0-1
strongswan-mod-random - 5.0.0-1
strongswan-mod-resolve - 5.0.0-1
strongswan-mod-revocation - 5.0.0-1
strongswan-mod-sha1 - 5.0.0-1
strongswan-mod-sha2 - 5.0.0-1
strongswan-mod-socket-default - 5.0.0-1
strongswan-mod-stroke - 5.0.0-1
strongswan-mod-test-vectors - 5.0.0-1
strongswan-mod-updown - 5.0.0-1
strongswan-mod-x509 - 5.0.0-1
strongswan-mod-xauth-generic - 5.0.0-1
strongswan-mod-xcbc - 5.0.0-1
strongswan-utils - 5.0.0-1

I have done the authentication only with certificates and configured the tunnel dual stack. Android doesn’t seem to support getting two IPs, IPv4 and IPv6, but probably with other clients you can get a dual stack IPsec tunnel, I haven’t tested this.

In /etc/ipsec.conf:

# ipsec.conf - strongSwan IPsec configuration file

config setup

conn %default

conn roadwarrior

In /etc/strongswan.conf:

# strongswan.conf - strongSwan configuration file

charon {
  dns1 =
  dns2 = 2001:4860:4860::8888

libstrongswan {
  crypto_test {
    on_add = yes

In /etc/ipsec.secrets:

# /etc/ipsec.secrets - strongSwan IPsec secrets file
: RSA serverKey.pem

To create the CA and the certificates, I’ve used this script:

umask 0077
# CA
ipsec pki --gen --outform pem > caKey.pem
ipsec pki --self --in caKey.pem --dn "C=ES, O=StrongSwan, CN=StrongSwan CA" --ca --digest sha256 --lifetime 3650  --outform pem > caCert.pem
chmod 0644 caCert.pem

# Server
ipsec pki --gen --outform pem > serverKey.pem
ipsec pki --pub --in serverKey.pem | ipsec pki --issue --cacert caCert.pem --cakey caKey.pem --dn "C=ES, O=StrongSwan, CN=openwrt.example.com" --digest sha256 --flag serverAuth --flag ikeIntermediate --lifetime 3650 --san openwrt.example.com --outform pem > serverCert.pem
chmod 0644 bosonCert.pem

# Android
ipsec pki --gen --outform pem > androidKey.pem
ipsec pki --pub --in androidKey.pem | ipsec pki --issue --cacert caCert.pem --cakey caKey.pem --dn "C=ES, O=StrongSwan, CN=android" --digest sha256 --lifetime 3650 --outform pem > androidCert.pem
chmod 0644 androidCert.pem
openssl pkcs12 -export -inkey androidKey.pem -in androidCert.pem -name "android" -certfile caCert.pem -caname "StrongSwan CA" -out androidCert.p12

Now, you have all needed certificates, and you have to copy them to their proper locations:

# mv caCert.pem /etc/ipsec.d/cacerts/
# mv androidCert.pem /etc/ipsec.d/certs
# mv serverCert.pem /etc/ipsec.d/certs
# mv androidKey.pem /etc/ipsec.d/private
# mv serverKey.pem /etc/ipsec.d/private
# mv caKey.pem /etc/ipsec.d/private

The file androidCert.p12 has to be copied to your SD card in your android device, and imported into the certificate store, in Settings -> Security -> Import Certificate.

Enable and start the service:

# /etc/init.d/ipsec enable
# /etc/init.d/ipsec start

Now we have the IKE daemon running.

As IPsec doesn’t create a virtual interface like other VPN solutions, we will see packets from source IPs (as configured in /etc/ipsec.conf) arriving from our wan interface.

To fix this, I have created a new zone called “vpn”,  the fastest way to do this is from the web interface. Create it and allow forwarding to the desired zones.

The zones configuration must be something similar to this:

Luci Firewall zones
Luci Firewall zones

As the zone has no interfaces associated with it, we need to add custom iptables rules to assign the correct packets to our vpn zone. Add this to /etc/firewall.user, so everytime the firewall reloads, it will execute our script. It’s inspired in the script which can be found in the Wiki.

# This file is interpreted as shell script.
# Put your custom iptables rules here, they will
# be executed with each firewall (re-)start.


# NAT tables
iptables -t nat -I PREROUTING 2 -s $subnet -j zone_${zone}_prerouting
iptables -t nat -I zone_${zone}_prerouting 1 -j ACCEPT
iptables -t nat -I POSTROUTING 2 -d $subnet -j zone_${zone}_nat

# FILTER tables
#Populate needed tables
iptables -A zone_${zone}_ACCEPT -d $subnet -j ACCEPT
iptables -A zone_${zone}_ACCEPT -s $subnet -j ACCEPT
ip6tables -A zone_${zone}_ACCEPT -d $subnet6 -j ACCEPT
ip6tables -A zone_${zone}_ACCEPT -s $subnet6 -j ACCEPT

iptables -A zone_${zone}_REJECT -m limit --limit 30/min -j LOG --log-prefix "REJECT(${zone}):"
iptables -A zone_${zone}_REJECT -d $subnet -j reject
iptables -A zone_${zone}_REJECT -s $subnet -j reject
ip6tables -A zone_${zone}_REJECT -m limit --limit 30/min -j LOG --log-prefix "REJECT(${zone}):"
ip6tables -A zone_${zone}_REJECT -d $subnet6 -j reject
ip6tables -A zone_${zone}_REJECT -s $subnet6 -j reject

iptables -I input 1 -s $subnet -j zone_${zone}
ip6tables -I input 1 -s $subnet6 -j zone_${zone}

iptables -I forward 1 -s $subnet -j zone_${zone}_forward
iptables -I forward 1 -d $subnet -j zone_${zone}_forward
ip6tables -I forward 1 -s $subnet6 -j zone_${zone}_forward
ip6tables -I forward 1 -d $subnet6 -j zone_${zone}_forward

Open the wan interface to the IPsec and IKE packets with this rules in /etc/config/firewall:

config rule
        option target 'ACCEPT'
        option src 'wan'
        option name 'IPSEC AH'
        option proto 'all'
        option extra '-m policy --dir in --proto ah'

config rule
        option target 'ACCEPT'
        option src 'wan'
        option name 'IPsec ESP'
        option proto 'esp'

config rule
        option target 'ACCEPT'
        option src 'wan'
        option proto 'udp'
        option dest_port '500'
        option name 'IPsec ISAKMP'

config rule
        option target 'ACCEPT'
        option src 'wan'
        option proto 'udp'
        option dest_port '4500'
        option name 'IPsec NAT-T'

Restart the firewall:

# /etc/init.d/firewall restart

And finally, you can monitor the log with:

# logread && logread -f

For Android, I recommend the StrogSwan app. It’s very easy to set up, and doesn’t need root, it uses the VPN services in Android 4.x.

Just load the certificate in Android settings, like explained before, and create a new profile of type “IKEv2 Certificate”, enter your gateway host, and you are done. All the network traffic will pass through the new VPN.


One thought on “IPsec server in OpenWrt”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s