Wag adds 2fa and device enrolment to wireguard.

It allows you to restrict routes based on 2fa, while allowing other routes to remain public as long as a client has a valid public key.


This work was very kindly supported by Aura Information Security.



The wireguard device must be running before wag is started.

iptables and wg-quick must be installed. Wag must be run as root, to manage iptables and the wireguard device itself.

Forwarding must be enabled in sysctl.

sysctl -w net.ipv4.ip_forward=1

It is a good idea to have SaveConfig set to true in the server configuration file, so that changes to the peers list made by wag will be saved.

Address =
SaveConfig = true
ListenPort = 51820
PrivateKey = <omitted>

Example /etc/wireguard/wg0.conf

systemctl start wg-quick@wg0

Setup instructions

git clone [email protected]:NHAS/wag.git
cd wag
go build

cp example_config config.json

sudo ./wag start

If running behind a reverse proxy, X-Forwarded-For must be set.


The root user is able to manage the wag server with the following command:

wag subcommand [-options]

All commands need to be able to load the config file. And thus support -config

start: starts the wag server

cleanup: Will remove all firewall forwards, and shutdown the wireguard device

registration: Deals with creating, deleting and listing the registration tokens

Usage of registration:
        Create a new enrolment token
        Delete existing enrolment token
        List tokens
  -token string
        Manually set registration token (Optional)
  -username string
        Username of device

devices: Manages MFA and device access

Usage of devices:
        Completely remove device blocks wireguard access
  -device string
        Device address
        List devices with 2fa entries
        Locked account/device access to mfa routes
        Reset locked account/device
        Get list of currently active authorised sessions

User guide

Starting wag

  1. Create your wg0.conf and start the service wg-quick@wg0
  2. Edit the configuration file WgDevName to WgDevName:wg0
  3. sudo ./wag start

Creating new registration tokens

First generate a token.

# ./wag registration -add -username tester

Then curl said token.

curl http://public.server.address:8082/register_device?key=e83253fd9962c68f73aa5088604f3f425d58a963bfb5c0889cca54d63a34b2e3

The service will return a fully templated response:

PrivateKey = <omitted>
Address =

Endpoint =  public.server.address:51820
PublicKey = pnvl40WiRt++0NucEGexlpfwWA8QzBYg2+8ZWZJvejA=
AllowedIPs =,,,
PersistentKeepAlive = 10

Which can then be written to a config file.

Entering MFA

To authenticate the user should browse to the servers vpn address, in this case, where they will be prompted for their 2fa code. The configuration file specifies how long a session can live for, before expiring.

Configuration file reference

Proxied: Respect the X-Forward-For directive, must ensure that you are securing the X-Forward-For directive in your reverse proxy HelpMail: The email address that is shown on the prompt page WgDevName: The wireguard tunnel device name that wag will manage Lockout: Number of times a person can attempt mfa authentication before their account locks ExternalAddress: The public address of the server, the place where wireguard is listening to the internet, and where clients can reach the /register_device endpoint SessionTimeoutMinutes: After authenticating, a device will be allowed to talk to privileged routes for this many minutes Webserver: Object that contains the public and tunnel listening addresses of the webserver WebServer.<endpoint>.ListenAddress: Listen address for endpoint WebServer.<endpoint>.CertPath: TLS Certificate path for endpoint WebServer.<endpoint>.KeyPath: TLS key for endpoint DatabaseLocation: Where to load the sqlite3 database from, it will be created if it does not exist Issuer: TOTP issuer, the name that will get added to the TOTP app Acls: Defines the Groups and Policies that restrict routes

Full config example

    "WgDevName": "wg0",
    "Lockout": 5,
    "HelpMail": "[email protected]",
    "SessionTimeoutMinutes": 10,
    "ExternalAddress": "",
    "DatabaseLocation": "devices.db",
    "Issuer": "",
    "Webserver": {
        "Public": {
            "ListenAddress": ""
        "Tunnel": {
            "ListenAddress": ""
    "Acls": {
        "Groups": {
            "group:nerds": [
        "Policies": {
            "*": {
                "Allow": [
            "username": {
                  "Allow":[ ""]
            "group:nerds": {
                "Mfa": [
                "Allow": [


  • Only supports clients with one AllowedIP, which is perfect for site to site, or client -> server based architecture.
  • IPv4 only.
  • Linux only


View Github