Strongswan: Dynamic (BGP) IPsec VPN tunnel with strongSwan

This guide, coming from Google, explains how to use a dynamic IPsec VPN tunnel with strongSwan on Linux

Step 1: Configure BIRD

/etc/bird/bird.conf

 
# Config example for bird 1.6 
#debug protocols all;

router id
169.254.2.2;

# Watch interface up/down events
protocol device
{
       scan time
10;
}

# Import interface routes (Connected)
# (Not required in this example as kernel import all is used here to workaround the /32 on eth0 GCE VM setup)
#protocol direct {
#       interface "*";
#}

# Sync routes to kernel
protocol kernel
{
       learn
;
       merge paths on
; # For ECMP
       
export filter {
              krt_prefsrc
= 10.164.0.6; # Internal IP Address of the strongSwan VM.
              accept
; # Sync all routes to kernel
       
};
       
import all; # Required due to /32 on GCE VMs for the static route below
}

# Configure a static route to make sure route exists
protocol
static {
       
# Network connected to eth0
       route
10.164.0.0/20 recursive 10.164.0.1; # Network connected to eth0
       
# Or blackhole the aggregate
       
# route 10.164.0.0/20 blackhole;
}

# Prefix lists for routing security
# (Accept /24 as the most specific route)
define GCP_VPC_A_PREFIXES
= [ 192.168.0.0/16{16,24} ]; # VPC A address space
define LOCAL_PREFIXES    
= [ 10.164.0.0/16{16,24} ];  # Local address space

# Filter received prefixes
filter gcp_vpc_a_in
{
     
if (net ~ GCP_VPC_A_PREFIXES) then accept;
     
else reject;
}

# Filter advertised prefixes
filter gcp_vpc_a_out
{
     
if (net ~ LOCAL_PREFIXES) then accept;
     
else reject;
}

template bgp gcp_vpc_a {
       keepalive time
20;
       hold time
60;
       graceful restart aware
; # Cloud Router uses GR during maintenance
       
#multihop 3; # Required for Dedicated/Partner Interconnect

       
import filter gcp_vpc_a_in;
       
import limit 10 action warn; # restart | block | disable

       
export filter gcp_vpc_a_out;
       
export limit 10 action warn; # restart | block | disable
}

protocol bgp gcp_vpc_a_tun1
from gcp_vpc_a
{
       
local 169.254.2.2 as 65002;
       neighbor
169.254.2.1 as 65000;
}

Step 2: Disable automatic routes in strongSwan

Routes are handled by BIRD, so you must disable automatic route creation in strongSwan.

/etc/strongswan.d/vti.conf

 
charon {
   
# We will handle routes by ourselves
    install_routes
= no
}

Step 3: Create a script that will configure the VTI interface

This script is called every time a new tunnel is established, and it takes care of proper interface configuration, including MTU, etc.

/var/lib/strongswan/ipsec-vti.sh

 
#!/bin/bash
set -o nounset
set -o errexit

IP
=$(which ip)

PLUTO_MARK_OUT_ARR
=(${PLUTO_MARK_OUT//// })
PLUTO_MARK_IN_ARR
=(${PLUTO_MARK_IN//// })

VTI_TUNNEL_ID
=${1}
VTI_REMOTE
=${2}
VTI_LOCAL
=${3}

LOCAL_IF
="${PLUTO_INTERFACE}"
VTI_IF
="vti${VTI_TUNNEL_ID}"
# GCP's MTU is 1460, so it's hardcoded
GCP_MTU
="1460"
# ipsec overhead is 73 bytes, we need to compute new mtu.
VTI_MTU
=$((GCP_MTU-73))

case "${PLUTO_VERB}" in
    up
-client)
        $
{IP} link add ${VTI_IF} type vti local ${PLUTO_ME} remote ${PLUTO_PEER} okey ${PLUTO_MARK_OUT_ARR[0]} ikey ${PLUTO_MARK_IN_ARR[0]}
        $
{IP} addr add ${VTI_LOCAL} remote ${VTI_REMOTE} dev "${VTI_IF}"
        $
{IP} link set ${VTI_IF} up mtu ${VTI_MTU}

       
# Disable IPSEC Policy
        sysctl
-w net.ipv4.conf.${VTI_IF}.disable_policy=1

       
# Enable loosy source validation, if possible. Otherwise disable validation.
        sysctl
-w net.ipv4.conf.${VTI_IF}.rp_filter=2 || sysctl -w net.ipv4.conf.${VTI_IF}.rp_filter=0

       
# If you would like to use VTI for policy-based you shoud take care of routing by yourselv, e.x.
       
#if [[ "${PLUTO_PEER_CLIENT}" != "0.0.0.0/0" ]]; then
       
#    ${IP} r add "${PLUTO_PEER_CLIENT}" dev "${VTI_IF}"
       
#fi
       
;;
    down
-client)
        $
{IP} tunnel del "${VTI_IF}"
       
;;
esac

# Enable IPv4 forwarding
sysctl
-w net.ipv4.ip_forward=1

# Disable IPSEC Encryption on local net
sysctl
-w net.ipv4.conf.${LOCAL_IF}.disable_xfrm=1
sysctl
-w net.ipv4.conf.${LOCAL_IF}.disable_policy=1

You should also make /var/lib/strongswan/ipsec-vti.sh executable by using following command:

 
chmod +x /var/lib/strongswan/ipsec-vti.sh

Step 4: Configure IPsec credentials

Ensure that the following line is in the file:

/var/lib/strongswan/ipsec.secrets.inc

 
35.204.151.163 : PSK "secret"

Step 5: Configure IPsec connection

/var/lib/strongswan/ipsec.conf.inc

 
include /etc/ipsec.d/gcp.conf

/etc/ipsec.d/gcp.conf

 
conn %default
    ikelifetime
=600m # 36,000 s
    keylife
=180m # 10,800 s
    rekeymargin
=3m
    keyingtries
=3
    keyexchange
=ikev2
    mobike
=no
    ike
=aes256gcm16-sha512-modp4096
    esp
=aes256gcm16-sha512-modp8192
    authby
=psk

conn net
-net
    leftupdown
="/var/lib/strongswan/ipsec-vti.sh 0 169.254.2.1/30 169.254.2.2/30"
    left
=35.204.200.153 # In case of NAT set to internal IP, e.x. 10.164.0.6
    leftid
=35.204.200.153
    leftsubnet
=0.0.0.0/0
    leftauth
=psk
    right
=35.204.151.163
    rightid
=35.204.151.163
    rightsubnet
=0.0.0.0/0
    rightauth
=psk
    type
=tunnel
   
# auto=add - means strongSwan won't try to initiate it
   
# auto=start - means strongSwan will try to establish connection as well
   
# Note that Google Cloud will also try to initiate the connection
   
auto=start
   
# dpdaction=restart - means strongSwan will try to reconnect if Dead Peer Detection spots
   
#                  a problem. Change to 'clear' if needed
    dpdaction
=restart
   
# mark=%unique - We use this to mark VPN-related packets with iptables
   
#                %unique ensures that all tunnels will have a unique mark here
    mark
=%unique

leftupdown contains a path to a script and its command-line parameters: * The first parameter is the tunnel ID because you cannot rely on strongSwan’s PLUTO_UNIQUEID variable if you need the tunnel ID to be persistent. * The second parameter specifies the Cloud Router IP and configured subnet. * The third parameter specifies the IP address of the vti0 interface and where BIRD is configured.

Step 3: Start strongSwan and BIRD

 
systemctl start bird
systemctl start strongswan

After you make sure it’s working as expected, you can add BIRD and strongSwan to autostart:

 
systemctl enable bird
systemctl enable strongswan
Share your love