Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save metral/3039944f6a7aed50b5347643825c996f to your computer and use it in GitHub Desktop.
Save metral/3039944f6a7aed50b5347643825c996f 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()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment