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