Skip to content

Instantly share code, notes, and snippets.

@SamuelDudley
Last active July 25, 2019 17:24
Show Gist options
  • Save SamuelDudley/40ea891c7f1fd0221bc0b25e185fe8c6 to your computer and use it in GitHub Desktop.
Save SamuelDudley/40ea891c7f1fd0221bc0b25e185fe8c6 to your computer and use it in GitHub Desktop.
StrongSwan vici python usage snippet

StrongSwan VPN install and usage

Reliable VPN connection between a companion computer on an air vehicle and a ground control station

These settings are for a hub and spoke model. Rover connections are rw-1 and rw-2 while base is a server with a static ip.

Usage model

  • rw-1 is a ground control station running with a dynamic and possibly changing ip (3G, LTE, SATCOM, etc...)
  • rw-2 is a companion computer on an air vehicle with a dynamic and possibly changing ip (3G, LTE, SATCOM, etc...)
  • base is a server with a known static ip

After installing StrongSwan and setting up the connections, rw-1 and rw-2 can connect to the base. Once connected, rw-1 can communicate directly with rw-2 using the IP addresses which are assigned to them via the base.

Setting it up

  1. Download and install strongswan as per StrongSwan_build_notes.txt

  2. edit /etc/strongswan.conf

  3. edit /etc/ipsec.conf NOTE: Change the IP and select the correct config file (rover or base)

  4. edit /etc/ipsec.secrets

  5. BASE ONLY: Setup ipv4 port forwarding on server with static ip. Edit /etc/sysctl.conf to include the following:

    #VPN
    net.ipv4.ip_forward = 1
    net.ipv4.conf.all.accept_redirects = 0
    net.ipv4.conf.all.send_redirects = 0
    
  6. OPTIONAL: Use the included python scripts to monitor and auto re-connect the tunnel(s) in case of failure. The python script is not required to setup and run a tunnel, rather it shows how an external python script can bring a tunnel up / down and monitor its status.

Some ipsec commands

sudo ipsec start sudo ipsec statusall sudo ipsec up <connection name> sudo ipsec down <connection name> sudo ipsec stop Have fun! :)

# /etc/ipsec.conf - strongSwan IPsec configuration file
config setup
conn %default
ike=aes128-sha256-ecp256,aes256-sha384-ecp384,aes128-sha256-modp2048,aes128-sha1-modp2048,aes256-sha384-modp4096,aes256-sha256-modp4096,aes256-sha1-modp4096,aes128-sha256-modp1536,aes128-sha1-modp1536,aes256-sha384-modp2048,aes256-sha256-modp2048,aes256-sha1-modp2048,aes128-sha256-modp1024,aes128-sha1-modp1024,aes256-sha384-modp1536,aes256-sha256-modp1536,aes256-sha1-modp1536,aes256-sha384-modp1024,aes256-sha256-modp1024,aes256-sha1-modp1024!
esp=aes128gcm16-ecp256,aes256gcm16-ecp384,aes128-sha256-ecp256,aes256-sha384-ecp384,aes128-sha256-modp2048,aes128-sha1-modp2048,aes256-sha384-modp4096,aes256-sha256-modp4096,aes256-sha1-modp4096,aes128-sha256-modp1536,aes128-sha1-modp1536,aes256-sha384-modp2048,aes256-sha256-modp2048,aes256-sha1-modp2048,aes128-sha256-modp1024,aes128-sha1-modp1024,aes256-sha384-modp1536,aes256-sha256-modp1536,aes256-sha1-modp1536,aes256-sha384-modp1024,aes256-sha256-modp1024,aes256-sha1-modp1024,aes128gcm16,aes256gcm16,aes128-sha256,aes128-sha1,aes256-sha384,aes256-sha256,aes256-sha1!
ikelifetime=3h
keylife=20m
reauth=no
rekey=no
rekeymargin=3m
keyingtries=%forever
keyexchange=ikev2
authby=secret
left=X.X.X.X #<-- Replace with internet facing static IP of base
leftfirewall=yes
lefthostaccess=yes
leftid=@server.strongswan.org
leftsubnet=10.7.7.0/24
conn rw-1
right=%any
rightid=@client1.strongswan.org
rightsourceip=10.7.7.1
auto=route
conn rw-2
right=%any
rightid=@client2.strongswan.org
rightsourceip=10.7.7.2
auto=route
# ipsec.conf - strongSwan IPsec configuration file
# basic configuration
config setup
# strictcrlpolicy=yes
# uniqueids = no
# Add connections here.
conn %default
ikelifetime=30m
keylife=20m
rekeymargin=3m
keyingtries=%forever
keyexchange=ikev2
authby=secret
reauth=no
conn rw-1
left=%defaultroute
leftsourceip=%config
leftfirewall=yes
leftid=@client1.strongswan.org
right=X.X.X.X #<-- Replace with internet facing static IP of base
rightsubnet=10.7.7.0/24
rightid=@server.strongswan.org
auto=start
# ipsec.conf - strongSwan IPsec configuration file
# basic configuration
config setup
# strictcrlpolicy=yes
# uniqueids = no
# Add connections here.
conn %default
ikelifetime=30m
keylife=20m
rekeymargin=3m
keyingtries=%forever
keyexchange=ikev2
authby=secret
reauth=no
conn rw-2
left=%defaultroute
leftsourceip=%config
leftfirewall=yes
leftid=@client2.strongswan.org
right=X.X.X.X #<-- Replace with internet facing static IP of base
rightsubnet=10.7.7.0/24
rightid=@server.strongswan.org
auto=start
: PSK 1sFpZAZqEN6Ti9sqt4ZP5EW
# strongswan.conf - strongSwan configuration file
#
# Refer to the strongswan.conf(5) manpage for details
#
# Configuration changes should be made in the included files
charon {
load_modular = no
make_before_break = yes
plugins {
include strongswan.d/charon/*.conf
}
}
include strongswan.d/*.conf
# install notes for ubuntu 14.04 / odroid xu4
sudo apt-get install libgmp3-dev
# dont apt-get StrongSwan! (its really old)
wget https://download.strongswan.org/strongswan-5.5.1.tar.bz2
tar xvfj strongswan-5.5.1.tar.bz2
cd strongswan-5.5.1
sudo ./configure --prefix=/usr --sysconfdir=/etc --enable-python-eggs --enable-python-eggs-install --enable-vici
sudo make
sudo make install
# This python script is *not* required to setup and run a tunnel,
# rather it shows how an external python script can bring a tunnel up / down and monitor its status.
import vici
import multiprocessing
import collections
import time
# NOTE: unless you are root you will need to do the following: sudo chmod 777 /var/run/charon.vici
# Edit target_connections in the VState to include the VPN connections you would like to keep alive
# if this connection is dropped for some reason it will be re-started automatically by the python script
class VState(object):
"""holds the VPN state"""
def __init__(self):
self.alive = True
self.session = vici.Session()
self.possible_connections = []
self.target_connections = ['rw-2']
self.active_connections = []
class StrongSwan(object):
def __init__(self, queue = None):
self.state = VState()
self.get_possible_connections()
def process_control_connection_in(self):
'''handle incoming mavlink packets'''
pass
def check_interfaces(self):
state = self.state
for vpn_conn in state.session.list_sas():
for key in state.active_connections:
try:
print 'key', key
print vpn_conn[key]
print vpn_conn[key]['established']
print vpn_conn[key]['state']
print vpn_conn[key]['local-host']
print vpn_conn[key]['remote-host']
except:
pass
try:
child = vpn_conn[key]['child-sas']
if child == {}:
child = None
except:
print 'tunnel not connected at child level!'
child = None
if child is not None:
for child_key in child:
print 'time: ', time.time(), 'child key', child_key, child[child_key]['bytes-in'], child[child_key]['bytes-out']
#print 'packets'
#print 'in: ', child[child_key]['packets-in']
#print 'out: ', child[child_key]['packets-out']
#print 'bytes'
#print 'in: ', child[child_key]['bytes-in']
#print 'out: ', child[child_key]['bytes-out']
#print child[child_key]['mode']
#print 'ip: ', child[child_key]['local-ts']
#print child[child_key]['remote-ts']
#print 'key: ', child[child_key]['rekey-time']
#print 'life: ', child[child_key]['life-time']
if key in state.target_connections and child is None:
self.connection_down(key)
self.connection_up(key)
for key in state.target_connections:
if key not in state.active_connections:
#the connection is inactive
self.connection_up(key)
def connection_up(self, key):
state = self.state
print 'up: ', key
sa = collections.OrderedDict()
sa['child'] = key
sa['timeout'] = '2000'
sa['loglevel'] = '0'
rep =state.session.initiate(sa)
rep.next()
rep.close()
#TODO: handle errors, log?
def connection_down(self, key):
state = self.state
print 'down: ', key
sa = collections.OrderedDict()
sa['ike'] = key
sa['timeout'] = '2000'
sa['loglevel'] = '0'
rep =state.session.terminate(sa)
rep.next()
rep.close()
#TODO: handle errors, log?
def get_possible_connections(self):
'''reset and repopulate possible connections based on /etc/ipsec.conf'''
state = self.state
state.possible_connections = []
for conn in state.session.list_conns():
for key in conn:
state.possible_connections.append(key)
print 'p',state.possible_connections
def get_active_connections(self):
state = self.state
state.active_connections = []
for conn in state.session.list_sas():
for key in conn:
state.active_connections.append(key)
print 'a', state.active_connections
def is_alive(self):
return self.state.alive
def main_loop():
'''main processing loop'''
#make a strongSwan control object
VPN = StrongSwan()
while VPN.is_alive():
VPN.process_control_connection_in()
VPN.get_possible_connections()
VPN.get_active_connections()
VPN.check_interfaces()
time.sleep(1.0)
if __name__ == '__main__':
#run main loop as a process
main = multiprocessing.Process(target=main_loop)
main.start()
main.join()
@zentavr
Copy link

zentavr commented Aug 27, 2018

@SamuelDudley, seems like your python script gets terminated with the exception when:

  • The remote server gets rebooted/stays unavailable
  • The VPN cannot be established >2000msec

I took your script as the basis and trying to adopt it in order to be able to restart the VPN connections on our remote peers,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment