A simple web interface for WireGuard VPN that allows using existing configurations or setting up new ones.
- Adding and removing clients via the web interface with live QR preview
- Tracking status in the interface
- Reloading WireGuard via the web interface
- Working with multiple
.conffiles (interfaces) - Written in JS, with a maximally simple and open interface, allowing on-the-fly edits without the need for building
- Rotating runtime verification tokens (no hardcoded secrets in the repo)
- Client secrets are stored encrypted in
.data/peers.json - Does not use any databases (data is stored in JSON)
- Applying a configuration (e.g., after adding a client) requires a WireGuard reload
- Requires NodeJS to be installed, and preferably PM2 for automatic restart support
WireGuard and NodeJS are required. The guide below is for Ubuntu.
sudo apt install wireguardThe easiest way to install the required version of NodeJS is via NVM. This project was tested on NodeJS v.20.10, but it will likely work on older versions as well (probably even on version 12), so you can try installing NodeJS via apt. Below are example commands for installing NVM:
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
source ~/.bashrcYou can check if nvm is installed by running nvm -v. After that, install the recommended NodeJS version:
nvm install 20.10.0Clone the repository into a convenient folder (here, for example, it's /var/@gratio/wg):
git clone https://github.com/Gratio-tech/WireguardControl.git /var/@gratio/wgNavigate to the created folder and install the dependencies, then build:
cd /var/@gratio/wg
npm i
npm run buildNavigate to the previously created folder and start the server:
cd /var/@gratio/wg
npm run startDon't forget to specify your settings in the file config.example.json (on first launch it will be renamed to config.json file). You should set the default WG-interface and add your server's IP (in VPN network) to allowedOrigins. Also ensure that the webServerPort used by this server by default (8877) is open in the firewall (if you have port blocking enabled).
BE SURE TO CHANGE ALL DEFAULT SECRETS!
Note that you can generate a random key for yourself directly in bash, for example in the following ways:
# Using openssl:
openssl rand -base64 16
# Using /dev/urandom and base64:
head -c 16 /dev/urandom | base64Or run it in the developer console in the browser, opening the Wireguard Control web interface:
// The forge library is used in public\index.html for encryption
forge.util.encode64(forge.random.getBytes(16));After this, the interface will be accessible in your browser at your server's address, for example, http://10.8.2.1:9876/.
If you did this before manually creating the first client for WireGuard, you need to add your public IP and the webServerPort not blocked by your firewall to allowedOrigins.
Next, add the server script to autostart. There are several ways to do this, but the most convenient and simple is to use the pm2 tool, which, among other things, allows for load distribution and memory usage monitoring.
npm install pm2 -g
cd /var/@gratio/wg && pm2 start demon.json --watch --ignore-watch="node_modules"
pm2 startup
pm2 saveNow, to monitor the server's status, simply run pm2 monit.
Additional client data is stored in the .data folder in the peers.json file in the following format:
{
"PEER1_PUBLIC_KEY":{"name":"PEER1_NAME"},
"PEER2_PUBLIC_KEY":{"name":"PEER2_NAME"}
}When loading, Wireguard-Control searches for all available configs in /etc/wireguard, parses them, and loads them into memory, so the system does not access configuration files when requesting status.
The config.json file (created from config.example.json on the first launch) now contains the following fields:
| Key | Description |
|---|---|
defaultInterface |
Interface name (without .conf) that will be selected by default |
frontServerPort |
Port used by the Express server |
allowedOrigins |
List of URLs allowed for CORS |
frontendPasskey |
Key that is used to encrypt API responses in the browser (enter it on the UI to read data) |
dns |
Array of DNS servers that will be inserted into generated configs |
clientEncryptionPass |
Passphrase used to AES-encrypt client private keys and preshared keys in .data/peers.json |
runtimeRotationMinutes |
Interval for regenerating the runtime verification script (public/assets/runtime.js) |
The project can be installed and updated via NPM, which simplifies deployment and updates:
# Install globally
npm install -g @gratio/wg
# Create a project folder and navigate to it
mkdir /var/@gratio/wg
cd /var/@gratio/wg
# Initialize the project (copies necessary files)
@gratio/wg init
# Edit config.json
# BE SURE TO CHANGE ALL DEFAULT SECRETS!
nano config.json
# Start the server
@gratio/wg serveThe @gratio/wg init command copies to the current directory:
public/— static web interface filesconfig.example.json— configuration exampledemon.json— PM2 configuration file
After initialization, all commands are executed from this directory. The server will automatically create a .data folder to store client information.
@gratio/wg init # Initialize project (copies files to current directory)
@gratio/wg serve # Start the server
@gratio/wg init-config # Create config.json from config.example.json (if it doesn't exist)
@gratio/wg help # Show help messagenpm install pm2 -g
cd /var/@gratio/wg
pm2 start demon.json
pm2 startup
pm2 saveThe demon.json file is already configured to work with the globally installed package.
Additional client data is stored in the .data folder in the peers.json file. Each entry now also contains encrypted secretKey and presharedKey fields, so even if somebody reads the file they still need clientEncryptionPass from config.json to obtain real values. Only the readable metadata (name, IP, interface) is kept in plain-text.