Why do I need Wireguard on an antiquated server?
Some time ago I took on admin of a club server running Debian 8 Jessie (EOL 17 Jun 2018) hosting a small PHP web app that's quite important to the club. With recent PHP2Shell vulnerabilities (CVE-2026-109618 and CVE-2026-109619), hosting the app on open internet is suicidal. Yes, there was CloudFlare proxy up front with WAF rules, but hey - WAF is temp fix and can by bypassed as new attack paths emerge, and I didn't want to take risk.
Best way to protect it, except wholesale system update to get to lated Debian 13, battling broken updates, breaking changes, etc. is to take the app off public network and expose on VPN. Fallback plan would be mTLS for legitimate users but that would require installing software from long archived repos (good luck!)
Wireguard-go or Wireguard-rs?
No difference in real terms, but pick whichever one floats your boat - I know you have a favourite.
I got both, cross-compiled for cpu architecture I needed and linked statically so I didn't need anything more than binaries. This allowed me to work around installing DKMS kernel modules, wg-tools or other packages - basically a self-contained solution, as much as can be...
CAVEAT - neither of those two uses standard wireguard config files, instead you configure server by writing to a unix socket file, and format of those commands is different than normal config file, and rather poorly documented (hence this writeup).
Reality check, gotchas and getting it to work
To start with, Debian 8 has tun module, my server had netcat version that doesn't support -U flag needed to work with unix socket, so had to write python scripts to handle writes/reads, and Python 3 is old 3.4.x and doesn't support f-strings (introduced in 3.6) so had to use older format() approach.
The big PITA was that wireguard-go which I somehow left in place doesn't take keys in normal format, instead it's in hex - more fun, and don't forget the keys are Curve25519 based!
Key generaion:
Option 1 - install wg-tools on another host
That's what I've done being lazy
Option 2 - use openssl to generate key material (didn't work on old Debian though)
openssl genpkey -algorithm X25519 -outform DER | tail -c 32 | openssl base64 > privatekey
openssl pkey -inform DER -pubout -outform DER -in <(openssl pkey -inform DER -in <(openssl base64 -d -in privatekey | awk '{printf "\x30\x2e\x02\x01\x00\x30\x05\x06\x03\x2b\x65\x6e\x04\x22\x04\x20"}{print}') ) | tail -c 32 | openssl base64 > publickey
Convert keys from base64 format to hex acceptable in UAPI:
wg genkey > private.key && cat private.keywg pubkey < private.key > public.key && cat public.keycat public.key | base64 -d | xxd -p -c 32
What you get from xxd is the hex string that needs to go into UAPI statement.
Wireguard-go setup (brain dump)
Binary at /etc/wireguard/bin/wireguard-go, config files in /etc/wireguard folder.
UAPI interface is simplistic, but operational. First define private key of server, listening port and then each public_key is followed by settings for particular client, until next public_key is defined and so on.
The /etc/wireguard/uapi.conf contains config commands that will be injected into socket:
set=1private_key=88...1471listen_port=51820public_key=4b...673aallowed_ip=10.1.1.2/32public_key=2c...2122allowed_ip=10.1.1.3/32public_key=3e...0949allowed_ip=10.1.1.4/32public_key=80...2b17allowed_ip=10.1.1.5/32
To start/reload wireguard I wrote a small script that first stops any running instance, starts clean one up, sets up IP networking on wg0 and then injects UAPI into socket using Python.
/etc/wireguard/run.sh:
#!/bin/bashrm /var/run/wireguard/wg0.sock /var/run/wireguard/wireguard.pid 2>/dev/nullsleep 1/etc/wireguard/bin/wireguard-go wg0WG_PID=`ps ax | grep wireguard-go|grep -v grep | awk '{print $1}'`echo $WG_PID > /var/run/wireguard/wireguard.pidsleep 1ip address add 10.1.1.1/24 dev wg0ip link set up dev wg0sleep 1/usr/bin/python3 /etc/wireguard/wg-configure.py
/etc/wireguard/wg-configure.py:
import socket, sys
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.connect("/var/run/wireguard/wg0.sock")
s.sendall(open("/etc/wireguard/uapi.conf", "rb").read())
s.close()
/etc/systemd/system/wireguard.service:
[Unit]Description=WireGuard User-Space VPN (wg0)After=network.target network-online.target ufw.serviceWants=network-online.target
Before=ssh.service apache2.service postgresql.service
[Service]Type=forkingExecStart=/etc/wireguard/run.sh
# Tell systemd how to find and kill the background process when stopping the servicePIDFile=/var/run/wireguard/wireguard.pidTimeoutSec=30Restart=on-failure
ExecStop=/sbin/ip link set down dev wg0ExecStop=/bin/rm -f /var/run/wireguard/wg0.sock /var/run/wireguard/wireguard.pid
[Install]WantedBy=multi-user.target
Sample client config:
[Interface]PrivateKey = 8...w=Address = 10.19.1.3/24[Peer]PublicKey = F...M=AllowedIPs = 10.1.1.1/24Endpoint = x.x.x.x:51820PersistentKeepalive = 25
Finally set firewall rules to whatever is needed in given scenario. I wanted apache to listen on wg0 only, hence wireguard had to start before web and database servers.
Fun fact - deleting wg0.sock file stops the wireguard server - love the clean unix way of doing so :-)
No comments:
Post a Comment