strongSwan IPsec VPN IKEv2 with ChromeOS client

strongSwan is a complete IPsec solution. It can be used to secure the communication between your servers and clients by authentication and encryption.


The installation of strongSwan is pretty simple. In Debian just issue:

apt-get install strongswan strongswan-pki strongswan-swanctl charon-systemd

Certificate setup

Certificate Authority

The strongSwan Quickstart guide shows how to create a ED25519 CA. I was unable to import this in ChromeOS. However, ECDSA works fine, hence going for that one instead. Remember that the CA private key should not be on a system directly exposed to the Internet for security reasons.

# Create the key for the CA
pki --gen --type ecdsa --outform pem > ca.key

# Create the CA Cert with a lifetime of 10 years, replace the values
# for C=, O= and CN= for your own setup
pki --self --ca --lifetime 3652 --in ca.key --dn "C=DE,, CN=jeanbruenn Root CA" --outform pem > ca.crt

# Create an empty certificate revoke list (if you like to)
pki --signcrl --cacert ca.crt --cakey ca.key --lifetime 30 > strongswan.crl

The certificate revoke list might not be necessary for you. If you run a webserver you may make the strongswan.crl accessible by that and add that to the certificates (e.g. –crl https://addresss/strongswan.crl). Remember you will need to re-create that file every 30 days.

Copy the ca.crt to /etc/swanctl/x509ca/
Copy the strongswan.crl (if created) to /etc/swanctl/x509crl/

Gateway (End Entity Certificate)

The certificate for the strongSwan Gateway can be of type ED25519 because this one is NOT used in ChromeOS later:

# create the key for your gateway
pki --gen --type ed25519 --outform pem > vpn1.key

# create the certificate request for your gateway, replace values for 
# C=, O= and CN= for your own setup.
pki --req --type priv --in vpn1.key --dn "C=DE,," --san --outform pem > vpn1.req

# Now issue a certificate using the CA and the previously created req
# add --crl if you use this feature
# I use --flag serverAuth here because this is my Gateway.
pki --issue --cacert ca.crt --cakey ca.key --type pkcs10 --in vpn1.req --serial 01 --lifetime 1826 --outform pem --flag serverAuth > vpn1.crt

Copy the ca.crt to /etc/swanctl/x509ca/, copy/move the vpn1.crt to /etc/swanctl/x509/ and copy/move the vpn1.key to /etc/swanctl/private/


root@vpn1:~# swanctl --load-creds
loaded certificate from '/etc/swanctl/x509/vpn1.crt'
loaded certificate from '/etc/swanctl/x509ca/ca.crt'
loaded ED25519 key from '/etc/swanctl/private/vpn1.key'

Chromebook/ChromeOS (End Entity Certificate)

ChromeOS has IKEv2 integrated so the strongSwan Android Client is not necessary; However, the integrated functionality is a little bit limited while the Android Client allows you to use more settings and finer control.

You may notice that the Quickstart Guide in the strongSwan documentation does not work out-of-the-box / you may have trouble to import the certificate. That’s because ED25519 certificates are somehow not working. At least not for me (I’m using the beta channel, Version 121.0.6167.82). Remember that I wrote I use ECDSA instead?

# Create the ECDSA key for your chromebook
pki --gen --type ecdsa --outform pem > chromebook.key

# Create a certificate request, replace C=, O= and CN= to match your setup.
pki --req --type priv --in chromebook.key --dn "C=DE,," --san --outform pem > chromebook.req

# finally get the certificate... (add --crl
# if you use that feature)
pki --issue --cacert ca.crt --cakey ca.key --type pkcs10 --in chromebook.req --serial 01 --lifetime 1826 --outform pem > chromebook.crt

# And your chromeos wants a PKCS12 file, so create it: 
openssl pkcs12 -export -inkey chromebook.key -in chromebook.crt -name "chromebook" -certfile ca.crt -caname "jeanbruenn Root CA" -out chromebook.p12

Open your Browser / Chrome and open chrome://settings/certificates. Add the ca.crt created in Certificate Authority above to the list of certificate authorities. You may need to rename the ca.crt to ca.pem for this. Add the chromebook.p12 to the user certificates there.

Now the created CA as well as the user-certificate should be available (and probably pre-selected) in the VPN dialog (IKEv2, User Certificate).


Just add some sort of example configuration for testing purposes:

root@vpn1:~/ipsec# cat /etc/swanctl/conf.d/chromebook.conf 
  connections {
    rw {
      local {
        auth = pubkey
        certs = vpn1.crt
        id =
      remote {
        auth = pubkey
      children {
        rw {
          local_ts  =

Verify that the certificate based authentication works correctly by checking the logs:

# This looks good so far.
received cert request for "C=DE,, CN=jeanbruenn Root CA"
received end entity cert "C=DE,,"

# I use and as Identifier; 
# hence it should find the above rw-connection and use the vpn1.crt for auth
looking for peer configs matching x.x.x.x[]...x.x.x.x[]
selected peer config 'rw'
  using certificate "C=DE,,"
  using trusted ca certificate "C=DE,, CN=jeanbruenn Root CA"
  reached self-signed root ca with a path length of 0

# this is because of the CRL part which I did not add here.
checking certificate status of "C=DE,,"
certificate status is not available

# if you do the CRL part correct, this will appear:
checking certificate status of "C=DE,,"
  using trusted certificate "C=DE,, CN=jeanbruenn Root CA"
  crl correctly signed by "C=DE,, CN=jeanbruenn Root CA"
  crl is valid: until Feb 20 21:03:13 2024
  using cached crl
certificate status is good

# Success.
authentication of '' with ECDSA_WITH_SHA384_DER successful
peer supports MOBIKE

# Success.
authentication of '' (myself) with ED25519 successful
sending end entity cert "C=DE,,"

Example roadwarrior connection

I use the following configuration for playing around with IPv6 because depending on whether I am in germany or not I do not have IPv6. In germany I do have. The pool stuff is probably not required for just one client for you. local_ts =,::/0 makes that everything is routed through this tunnel. This is pretty much a VPN.

By the way, the Gateway has configured. And the chromebook gets assigned.

If you are used to stroke based configuration like I am then the below might look difficult at first. Just check the migration documentation.

connections {
  rw {
    encap = yes
    pools = pool_v4, pool_v6

    local {
      auth = pubkey
      certs = vpn1.crt
      id =

    remote {
      auth = pubkey

    children {
      rw {
        local_ts  =,::/0
        updown = /usr/lib/ipsec/_updown iptables

pools {
   pool_v6 {
      dns = 2001:4860:4860::8888
      addrs = xxxx:xxxx:x:xx:xxxx::x/64
   pool_v4 {
      dns =
      addrs =

Don’t forget to enable IP forwarding

net.ipv4.ip_forward = 1

Add MASQUERADE rules using iptables. This is very good explained in the strongSwan documentation:

iptables -t nat -A POSTROUTING -o ens7 -m policy --dir out --pol ipsec -j ACCEPT
iptables -t nat -A POSTROUTING -o ens7 -j MASQUERADE
ip6tables -t nat -A POSTROUTING -o ens7 -m policy --dir out --pol ipsec -j ACCEPT
ip6tables -t nat -A POSTROUTING -o ens7 -j MASQUERADE

Just by the way, I also had to use the following as explained here.

iptables -t mangle -A FORWARD -m policy --pol ipsec --dir in -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360 
iptables -t mangle -A FORWARD -m policy --pol ipsec --dir out -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360

As well as:


Else I would have random hanging / stuck connections sometimes. Like, virt-manager would not properly make a connection. dmesg on some systems would just freeze (while I can start a new connection without trouble).

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.