Wireguard config over ssh
-
I'm looking to implement management of the wireguard service running on pfsense through scripting.
I am particularly interested in adding and removing peers.
I understand that I can issue commands via ssh to query existing peers, their assigned ips and even wg to add peers, but I'm not sure how to ensure that those peers are saved and that they appear properly in the wireguard configuration menu on the web gui.
I see that there is also /usr/local/etc/wireguard/tun_wg0.conf which contains all peers and which I can edit, but adding a peer there doesn't seem to make it show up in the web gui either.Thanks,
Mike -
@mike_vc I have exactly the same question.
I suspect that this is not a "supported" option, but for me, that is fine as I am looking to configure ephemeral pfSense VMs when automating some lab environments to create a Remote Access VPN (so adding clients automatically, but ideally also configuring the tunnel or resetting its private/public key). We have done this previously with an OpenVPN server installation on Ubuntu, but pfSense fits much better with the lab so makes much more sense to use it if possible (and pfSense is just great).
I did start looking at Wireguard site itself (https://www.wireguard.com/quickstart/) but got distracted.
If you come across anything, @mike_vc , I would be grateful if you would post back.
-
One thing I can see being a little bit of an issue is when trying to add peers in a automated way is to ensure that the "Allowed IP" for the peer is configured correctly. It will just take a little bit of lateral thinking.
-
@mike_vc NOTE what I have done below is liable to cause all sort of issues if you get it wrong, but for me I still think there is value in figuring out this hacky way to do things. I managed to cause a couple of crashes through poor modification of the XML elements! BE WARNED!
I have been playing around with the
wg
command (see https://git.zx2c4.com/wireguard-tools/about/src/man/wg.8) and whilst you can create separate config files and even get them functioning usingwg setconf
orwg syncconf
, as soon as you restart the WireGuard service of pfSense, the settings are reset to the original values. I even overwrote the conf file/usr/local/etc/wireguard/tun_wg0.conf
, but this too is reset.The information looks to be stored in the
/conf/config.xml
file. There is a<wireguard>.....</wireguard>
section which contains all of the configuration seen in the GUI. I have successfully hand updated the tunnel<privatekey>...</privatekey>
and<publickey>...</publickey>
elements, but you would need to modify the<peers>...</peers>
section. I'm sure there would be a cleversh
script out there that might be able to be modified to this, but I haven't looked yet. To update theiconfig.xml
file, I usedviconfig
(although myvi
skills are weak).It's also possible to control the WireGuard service via the command line, for example:
Stop WireGuard service
/usr/local/sbin/pfSsh.php playback svc stop WireGuard
Start
/usr/local/sbin/pfSsh.php playback svc start WireGuard
So, whilst not there yet, it's a start!
-
@swinster This might be what I was looking for. I wasn't aware of the /conf/config.xml containing the <wiregaurd> section.
I'll play around with it and post back if I can figure out how to acquire "next available" ip, and add peer via that config. -
@mike_vc FWIW, I figured out that the "PHP Shell + pfSense tools" option might also help when editing the XML file, which is option 12 when login into the console/SSH as root/admin (although I think you can also run similar stuff from the standard shell using
/usr/local/sbin/pfSsh.php
). Also, see https://docs.netgate.com/pfsense/en/latest/development/php-shell.htmlFor example, to set the config for the
privatekey
andpublickey
for the tunnel, I can run:$config['installedpackages']['wireguard']['tunnels']['item']['0']['privatekey'] = "XXXXXXXXXXXX"; $config['installedpackages']['wireguard']['tunnels']['item']['0']['publickey'] = "YYYYYYYYYYYY"; write_config(); exec; playback svc restart WireGuard exit
This sets the values of the relevant
privatekey
andpublickey
elements withinconfig.xml
, saves the config to disk, then restarts theWireGuard
service (capitalisation is important, I found), and exits the PHP shell (apparently, you should only run oneplayback
script for each PHP shell session, albeit you can run multipleplayback
scripts from the standard shell.After you have run these commands, you will see the update in the GUI. The changes will only come into effect once the
WireGuard
service has been restarted.I also found this post (https://serverfault.com/questions/790254/pfsense-shell-apply-config-modification-without-reboot) that might help in the construction of a shell script along with a PHP config script that edits arrays (which will be required for adding peers)
Now, I am no Linux, PHP or Shell script expert. I am just muddling through but hopefully can build something to allow a script to be run by passing in the relevant peer keys via SSH and grabbing the server public key via SCP. I am much more comfortable in PowerShell and found this (https://www.powershellgallery.com/packages/pfSense/0.9.6), which I might be able to modify to do something similar, but I haven't looked too deeply yet.
I am still concerned that you need to define a peer IP address within the tunnel (both on pfSense and in the client config). My lab client machines are identical clones, and I do not know what order they will connect in. I need some way to assign an IP within the tunnel range to be used by each client as it connects. It's a shame this doesn't happen automatically (I think it does in OpenVPN). Perhaps, I will have a simple text file with a pool of IPs on pfSense and remove one each time a client connects.
I don't mind hacking like this because the entire lab gets reset and redeployed when done.
-
-
@mike_vc , dod you get anywhere with this?
-
@swinster
Here's as far as I've gotten so far. It's "seemingly" doing everything right, and not returning any errors, but then it also fails to create a new peer, and I haven't figured out where to go from here.
Mind that this is an interactive script that expects you to press Y, but should be easy to adopt to say take an email address as parameter instead and then email the config to that address.
It also assumes that you have a /24 subnet for your wireguard clients (for now).#!/bin/sh DNS="10.2.10.10, mydomain.com" ALLOWEDIPS="10.2.10.0/24" ENDPOINT="wireguard.mydomain.com:51820" ## Usage #./wg-add-peer.sh <username> # check that only 1 argument is given if [ $# -ne 1 ]; then echo "illegal number of parameters\nUsage\n$0 <username>" exit 1 fi # Get tunnel name tunnel=`xmllint --xpath "string(/pfsense/installedpackages/wireguard/tunnels/item/name)" /conf/config.xml` # Get the first 3 actets subnet=`xmllint --xpath 'string(/pfsense/installedpackages/wireguard/tunnels/item/addresses/row/address)' /conf/config.xml | cut -f-3 -d'.'` # Get count of existing peers peer_count=`xmllint --xpath "count(/pfsense/installedpackages/wireguard/peers/item)" /conf/config.xml` find_next_ip() { # Assume the first integer in last octet belongs to our tunnel interface ip seq=2 # Find next available integer for i in `xmllint --xpath "//pfsense/installedpackages/wireguard/peers/item//allowedips/row/address" /conf/config.xml | sed 's/<*.address>//g' | sort -t . -k 1,1n -k 2,2n -k 3,3n -k 4,4n | cut -f4 -d'.'`; do if [ $i != $seq ]; then echo $i return $i fi seq=$((seq+1)) done echo $seq return $seq } next_ip="$subnet.$(find_next_ip)" #Generate keys private_key=$(wg genkey) public_key=$(echo "$private_key" | wg pubkey) cat > /tmp/pfSsh_script.tmp << EOF \$newPeer['enabled'] = 'yes'; \$newPeer['tun'] = '$tunnel'; \$newPeer['descr'] = '$1'; \$newPeer['persistentkeepalive'] = ''; \$newPeer['publickey'] = '$public_key'; \$newPeer['presharedkey'] = ''; \$newPeerIP['address'] = '$next_ip'; \$newPeerIP['mask'] = '32'; \$newPeerIP['descr'] = ''; \$config['installedpackages']['wireguard']['peers']['item']][] = \$newPeer; \$config['installedpackages']['wireguard']['peers']['item']['$peer_count']['allowedips']['row'][] = \$newPeerIP pfSense shell: parse_config(true); pfSense shell: write_config(); pfSense shell: exec; playback svc restart WireGuard exit EOF cat > "$1-wg.conf" << EOL [Interface] PrivateKey = $private_key Address = $next_ip/32 DNS = $DNS [Peer] PublicKey = $(wg|grep "public key"|rev|cut -d' ' -f1|rev) AllowedIPs = $ALLOWEDIPS Endpoint = $ENDPOINT PersistentKeepalive = 15 EOL echo "About to run the following pfSsh.php script:" cat /tmp/pfSsh_script.tmp read -r -p $'Confirm by pressing y... ' key if [ "$key" == 'y' ] || [ "$key" == 'Y' ]; then /usr/local/sbin/pfSsh.php < /tmp/pfSsh_script.tmp rm -f /tmp/pfSsh_script.tmp echo "$1-wg.conf:" cat "$1-wg.conf" else # Anything else pressed, do whatever else. echo User input not y... exit 1 fi