A Pleasant Discovery
In the course of technological events arising from the labor of one's job, frequently the occasion occurs that one finds oneself either forestalled by uncertainty and variability in setting up an unfamiliar system/tool or confused by the complexity of such system/tool and its unwholesome documentation or both. (The appreciable frequency of this scenario being one of the reasons why filtering on flat task lists for job candidates often proves so foolhardy even as it is defended as the safe choice. Rather, Aristotle I think would advise not to look upon a person's resume, but at what that person will do next.)
Not as frequently as one would desire, the new system/tool purporting to fix/fulfill one's professional requirement proves pleasantly well-designed. VPN technology is naturally complex as it must interact with multiple functional areas within both networking and cryptography. This is all the more reason to enjoy using strongSwan, which handles the native complexity of the problem space with competence and composure and without arbitrary constructs that obscure intent and inflict intellectual discontent. Much like git with the version control of files, strongSwan seems to anticipate the next level of your thought progress by providing the right features in the right timeplace that behave exactly how you would expect them.
Not being yet enough awesome, strongSwan also provides excellent config documentation and an expansive suite of fully-documented test cases. Even the page on analyzing traffic is loaded with excellent information, which rewards close reading.
So many tutorials amongst the Greater Intarwebz I found consisted mostly of manically crunching together a pile of configs until the author's ends were produced, the resulting glut of info dumped into a blog post marking the achievement of the bonus goal of communal erudition making no distinction or cognition of which configs were effective and which were deceptive.
While dutifully working through the RTFM procedure (obligatory XKCD), I was not only pleased at the virtue signaled by DuckDuckGo being hardcoded in as their site-specific search tool, but also found this gem while searching the mailing list:
You're having no success because you're trying ramdom[sic] shit from the Internet. About 99,999% of the strongSwan related information on third party sites is wither well ng or of questinable quality[sic]. Don't get your information from any place but the project's website.
Kind regards
Noel
Great tonic for erstwhile tedium with hipstergrammer cargo cult explanations. Let's hope this post doesn't fall into the bottom \(\frac{99999}{100000}\) (yes, gratuitous MathJax) of strongSwan info.
Setup
I considered writing this post in SaltStack rather than plain command line, since if you're serious about what you're doing, you would not be deploying new systems without some kind of audit/control like salt or one of its inferior peers. I decided to favor the salt-illiterate.
Note
The following assumes you are going to run two Ubuntu 16.04 VMs and will install the strongSwan version packaged for that platform (currently 5.3.5-1ubuntu3.5).
Note
This is a worked reproduction of strongSwan's IKEv1 PSK testcase. I chose it because I need to site-to-site connect to a 3rd party, who uses this config profile. I chose to setup two strongSwan VMs so that I can more easily learn VPN technology generally and strongSwan specifically while controlling all the variables.
Note
Depending on how you configure strongSwan, you and/or strongSwan could be editing iptables rules.
Instantiate two Ubuntu 16.04 VMs on your favorite cloud/hypervisor. Name one
moon
and the othersun
.-
Install the package on both VMs.
(moon/sun) # apt update ; apt install strongswan
-
Setup main config
ipsec.conf
.(moon) # mv /etc/ipsec.conf /etc/ipsec.conf.original (moon) # cat > /etc/ipsec.conf config setup conn %default ikelifetime=60m keylife=20m rekeymargin=3m keyingtries=1 authby=secret keyexchange=ikev1 conn MyVPN left=192.168.0.1 # moon's normal IP addr leftsubnet=10.1.0.0/16 leftid=moon leftfirewall=yes # These two configs trigger the necessary iptables rules lefthostaccess=yes # for moon<-->sun traffic from strongSwan's up/down script right=192.168.0.2 # sun's normal IP addr rightsubnet=10.2.0.0/16 rightid=sun auto=add
(sun) # mv /etc/ipsec.conf /etc/ipsec.conf.original (sun) # cat > /etc/ipsec.conf config setup conn %default ikelifetime=60m keylife=20m rekeymargin=3m keyingtries=1 authby=secret keyexchange=ikev1 conn MyVPN left=192.168.0.2 # Swap left/right leftsubnet=10.2.0.0/16 leftid=sun leftfirewall=yes lefthostaccess=yes right=192.168.0.1 rightsubnet=10.1.0.0/16 rightid=moon auto=add
-
Setup secret config
ipsec.secrets
.(moon/sun) # mv /etc/ipsec.secrets /etc/ipsec.secrets.original (moon/sun) # cat > /etc/ipsec.secrets : PSK TBTBidVXSOQ1bTF8R81gtjQb0T97KfDJ3D4avvM32RZ744d6miaAAFiazJPQgWu8Fw9rduirfy7nIzLxN4loxqrqrLwAJ5JodQbWuEk3koQ0DbeoOLRr1zIVcdJHJWZB
-
Add the VPN IP addresses to
moon
andsun
. Make sure the subnet (/16
) matches what you've configured inipsec.conf
.(moon) # ip address add 10.1.0.1/16 dev eth0
(sun) # ip address add 10.2.0.1/16 dev eth0
-
Add iptables rules for
esp
,ah
, andisakmp
.(moon/sun) # iptables -A INPUT -i eth0 -p esp -j ACCEPT (moon/sun) # iptables -A INPUT -i eth0 -p ah -j ACCEPT (moon/sun) # iptables -A INPUT -i eth0 -p udp -m udp --dport isakmp -j ACCEPT (moon/sun) # iptables -A OUTPUT -o eth0 -p udp -m udp --dport isakmp -j ACCEPT
-
Start the connection.
(moon) # systemctl restart strongswan.service (moon) # ipsec statusall # Ensure MyVPN is listed (moon) # ipsec up MyVPN
-
Profit. An exercise for the reader: repeat the following from
sun
and replace10.2.0.1
with10.1.0.1
.(moon) # ping -c 1 10.2.0.1 PING 10.2.0.1 (10.2.0.1) 56(84) bytes of data. 64 bytes from 10.2.0.1: icmp_seq=1 ttl=64 time=180 ms --- 10.2.0.1 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 180.300/180.300/180.300/0.000 ms (moon) # telnet 10.2.0.1 22 Trying 10.2.0.1... Connected to 10.2.0.1. Escape character is '^]'. SSH-2.0-OpenSSH_7.2p2 Ubuntu-4ubuntu2.4 ^] telnet> Connection closed.
Comments
This is obviously not a finished example: the IP addresses, iptables rules, and VPN connection are not saved across reboots; two random VMs connected by a VPN is not very useful for real work; the systems are flagrantly not salted. The purpose is to illustrate the minimum necessary to get from nothing to a successful VPN connection, or at least imbue enough comprehension to make reading strongSwan's test cases productive and provide a working starting place.
Getting Help
strongSwan has what might appear to be an overly punitive RTFM policy, but given the complexity of the problem space a comprehensively detailed report seems like a reasonable context prerequisite for assistance. The one minor annoyance with this situation is that strongSwan does not provide a tool to gather most/all of this info for you, which is why I wrote the following script. It assumes you're running with the deprecated stroke plugin on Ubuntu with Systemd and have charon logging setup.
Note
If your Ubuntu VM has AppArmor enabled, you may need to place the following AppArmor config for charon logging to work:
(moon/sun) # cat > /etc/apparmor.d/local/usr.lib.ipsec.charon /var/log/charon_debug.log w,
#!/usr/bin/env python3 from subprocess import run, Popen, PIPE from datetime import datetime import time import shlex def log(stmt): print('=== {} ==='.format(stmt)) conn = 'MyVPN' pre_cmds = [ 'ipsec down {}'.format(conn), 'rm -f /var/log/charon_debug.log', 'systemctl restart strongswan.service', ] for pre_cmd in pre_cmds: log(pre_cmd) run(pre_cmd.split()) time.sleep(1) start = datetime.utcnow() cmds = [ 'ipsec up {}'.format(conn), 'journalctl --all --priority 7 --since "{}" --unit strongswan.service'.format(start.isoformat(sep=' ')[:19]), 'cat /var/log/charon_debug.log', 'cat /etc/ipsec.conf', 'ipsec statusall', 'iptables-save', 'ip6tables-save', 'ip route show table all', 'ip address show', 'ping -c 5 -I 10.1.0.1 10.2.0.1', ] with open('strongswan_report.md', 'w') as report: for cmd in cmds: time.sleep(1) log(cmd) proc = Popen(shlex.split(cmd), stdout=PIPE) report.write('\n### `{}`\n'.format(cmd)) report.write('```\n') report.write(proc.stdout.read().decode()) report.write('```\n')
The resulting strongswan_report.md
file should give you all you need to ask
for help on the mailing list
or in the IRC channel.
Remember to paste responsibly.