diff --git a/README.md b/README.md
index a282d2d6..71168146 100644
--- a/README.md
+++ b/README.md
@@ -8,11 +8,13 @@
You have found the easiest way to install & manage WireGuard on any Linux host!
+
-
+
## Features
+
* All-in-one: WireGuard + Web UI.
* Easy installation, simple to use.
* List, create, edit, delete, enable & disable clients.
@@ -26,6 +28,7 @@ You have found the easiest way to install & manage WireGuard on any Linux host!
* UI_TRAFFIC_STATS (default off)
* UI_SHOW_LINKS (default off)
* WG_ENABLE_EXPIRES_TIME (default off)
+* Prometheus metrics support
## Requirements
@@ -34,7 +37,7 @@ You have found the easiest way to install & manage WireGuard on any Linux host!
## Versions
-We provide more then 1 docker image to get, this will help you decide which one is best for you.
+We provide more then 1 docker image to get, this will help you decide which one is best for you.
For **stable** versions instead of nightly or development please read **README** from the **production** branch!
| tag | Branch | Example | Description |
@@ -62,7 +65,7 @@ And log in again.
To automatically install & run wg-easy, simply run:
-```
+```bash
docker run -d \
--name=wg-easy \
-e LANG=de \
@@ -87,6 +90,8 @@ To automatically install & run wg-easy, simply run:
The Web UI will now be available on `http://0.0.0.0:51821`.
+The Prometheus metrics will now be available on `http://0.0.0.0:51821/metrics`. Grafana dashboard [21733](https://grafana.com/grafana/dashboards/21733-wireguard/)
+
> 💡 Your configuration files will be saved in `~/.wg-easy`
WireGuard Easy can be launched with Docker Compose as well - just download
@@ -109,7 +114,7 @@ These options can be configured by setting environment variables using `-e KEY="
| `WG_HOST` | - | `vpn.myserver.com` | The public hostname of your VPN server. |
| `WG_DEVICE` | `eth0` | `ens6f0` | Ethernet device the wireguard traffic should be forwarded through. |
| `WG_PORT` | `51820` | `12345` | The public UDP port of your VPN server. WireGuard will listen on that (othwise default) inside the Docker container. |
-| `WG_CONFIG_PORT`| `51820` | `12345` | The UDP port used on [Home Assistant Plugin](https://github.com/adriy-be/homeassistant-addons-jdeath/tree/main/wgeasy)
+| `WG_CONFIG_PORT`| `51820` | `12345` | The UDP port used on [Home Assistant Plugin](https://github.com/adriy-be/homeassistant-addons-jdeath/tree/main/wgeasy)
| `WG_MTU` | `null` | `1420` | The MTU the clients will use. Server uses default WG MTU. |
| `WG_PERSISTENT_KEEPALIVE` | `0` | `25` | Value in seconds to keep the "connection" open. If this value is 0, then connections won't be kept alive. |
| `WG_DEFAULT_ADDRESS` | `10.8.0.x` | `10.6.0.x` | Clients IP address range. |
@@ -126,7 +131,7 @@ These options can be configured by setting environment variables using `-e KEY="
| `UI_SHOW_LINKS` | `false` | `true` | Enable display of a short download link in Web UI |
| `MAX_AGE` | `0` | `1440` | The maximum age of Web UI sessions in minutes. `0` means that the session will exist until the browser is closed. |
| `UI_ENABLE_SORT_CLIENTS` | `false` | `true` | Enable UI sort clients by name |
-
+| `ENABLE_PROMETHEUS_METRICS` | `true` | `true` | Enable Prometheus metrics `http://0.0.0.0:51821/metrics` and `http://0.0.0.0:51821/metrics/json`|
> If you change `WG_PORT`, make sure to also change the exposed port.
diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml
index bd4a836d..2bc01ff0 100644
--- a/docker-compose.dev.yml
+++ b/docker-compose.dev.yml
@@ -13,5 +13,5 @@ services:
- NET_ADMIN
- SYS_MODULE
environment:
- # - PASSWORD=p
+ # - PASSWORD_HASH=$$2y$$10$$hBCoykrB95WSzuV4fafBzOHWKu9sbyVa34GJr8VV5R/pIelfEMYyG # 'foobar123'
- WG_HOST=192.168.1.233
diff --git a/docker-compose.yml b/docker-compose.yml
index 9de55e32..7d1e9d3f 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -30,6 +30,7 @@ services:
# - UI_SHOW_LINKS=true
# - UI_ENABLE_SORT_CLIENTS=true
# - WG_ENABLE_EXPIRES_TIME=true
+ # - ENABLE_PROMETHEUS_METRICS=false
image: ghcr.io/wg-easy/wg-easy
container_name: wg-easy
diff --git a/src/config.js b/src/config.js
index 6168e589..068caa39 100644
--- a/src/config.js
+++ b/src/config.js
@@ -41,3 +41,4 @@ module.exports.UI_CHART_TYPE = process.env.UI_CHART_TYPE || 0;
module.exports.UI_SHOW_LINKS = process.env.UI_SHOW_LINKS || 'false';
module.exports.UI_ENABLE_SORT_CLIENTS = process.env.UI_ENABLE_SORT_CLIENTS || 'false';
module.exports.WG_ENABLE_EXPIRES_TIME = process.env.WG_ENABLE_EXPIRES_TIME || 'false';
+module.exports.ENABLE_PROMETHEUS_METRICS = process.env.ENABLE_PROMETHEUS_METRICS || 'true';
diff --git a/src/lib/Server.js b/src/lib/Server.js
index 40b9bb1f..8d5aebcd 100644
--- a/src/lib/Server.js
+++ b/src/lib/Server.js
@@ -36,6 +36,7 @@ const {
UI_SHOW_LINKS,
UI_ENABLE_SORT_CLIENTS,
WG_ENABLE_EXPIRES_TIME,
+ ENABLE_PROMETHEUS_METRICS,
} = require('../config');
const requiresPassword = !!PASSWORD_HASH;
@@ -286,6 +287,20 @@ module.exports = class Server {
const { expireDate } = await readBody(event);
await WireGuard.updateClientExpireDate({ clientId, expireDate });
return { success: true };
+ }))
+ .get('/metrics', defineEventHandler(async (event) => {
+ setHeader(event, 'Content-Type', 'text/plain');
+ if (ENABLE_PROMETHEUS_METRICS === 'true') {
+ return WireGuard.getMetrics();
+ }
+ return '';
+ }))
+ .get('/metrics/json', defineEventHandler(async (event) => {
+ setHeader(event, 'Content-Type', 'application/json');
+ if (ENABLE_PROMETHEUS_METRICS === 'true') {
+ return WireGuard.getMetricsJSON();
+ }
+ return '';
}));
const safePathJoin = (base, target) => {
diff --git a/src/lib/WireGuard.js b/src/lib/WireGuard.js
index 120dd238..613190fa 100644
--- a/src/lib/WireGuard.js
+++ b/src/lib/WireGuard.js
@@ -158,6 +158,7 @@ ${client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : ''
latestHandshakeAt: null,
transferRx: null,
transferTx: null,
+ endpoint: null,
}));
// Loop WireGuard status
@@ -186,6 +187,7 @@ ${client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : ''
client.latestHandshakeAt = latestHandshakeAt === '0'
? null
: new Date(Number(`${latestHandshakeAt}000`));
+ client.endpoint = endpoint === '(none)' ? null : endpoint;
client.transferRx = Number(transferRx);
client.transferTx = Number(transferTx);
client.persistentKeepalive = persistentKeepalive;
@@ -398,4 +400,75 @@ Endpoint = ${WG_HOST}:${WG_CONFIG_PORT}`;
}
}
+ async getMetrics() {
+ const clients = await this.getClients();
+ let wireguardPeerCount = 0;
+ let wireguardEnabledPeersCount = 0;
+ let wireguardConnectedPeersCount = 0;
+ let wireguardSentBytes = '';
+ let wireguardReceivedBytes = '';
+ let wireguardLatestHandshakeSeconds = '';
+ for (const client of Object.values(clients)) {
+ wireguardPeerCount++;
+ if (client.enabled === true) {
+ wireguardEnabledPeersCount++;
+ }
+ if (client.endpoint !== null) {
+ wireguardConnectedPeersCount++;
+ }
+ wireguardSentBytes += `wireguard_sent_bytes{interface="wg0",enabled="${client.enabled}",address="${client.address}",name="${client.name}"} ${Number(client.transferTx)}\n`;
+ wireguardReceivedBytes += `wireguard_received_bytes{interface="wg0",enabled="${client.enabled}",address="${client.address}",name="${client.name}"} ${Number(client.transferRx)}\n`;
+ wireguardLatestHandshakeSeconds += `wireguard_latest_handshake_seconds{interface="wg0",enabled="${client.enabled}",address="${client.address}",name="${client.name}"} ${client.latestHandshakeAt ? (new Date().getTime() - new Date(client.latestHandshakeAt).getTime()) / 1000 : 0}\n`;
+ }
+
+ let returnText = '# HELP wg-easy and wireguard metrics\n';
+
+ returnText += '\n# HELP wireguard_configured_peers\n';
+ returnText += '# TYPE wireguard_configured_peers gauge\n';
+ returnText += `wireguard_configured_peers{interface="wg0"} ${Number(wireguardPeerCount)}\n`;
+
+ returnText += '\n# HELP wireguard_enabled_peers\n';
+ returnText += '# TYPE wireguard_enabled_peers gauge\n';
+ returnText += `wireguard_enabled_peers{interface="wg0"} ${Number(wireguardEnabledPeersCount)}\n`;
+
+ returnText += '\n# HELP wireguard_connected_peers\n';
+ returnText += '# TYPE wireguard_connected_peers gauge\n';
+ returnText += `wireguard_connected_peers{interface="wg0"} ${Number(wireguardConnectedPeersCount)}\n`;
+
+ returnText += '\n# HELP wireguard_sent_bytes Bytes sent to the peer\n';
+ returnText += '# TYPE wireguard_sent_bytes counter\n';
+ returnText += `${wireguardSentBytes}`;
+
+ returnText += '\n# HELP wireguard_received_bytes Bytes received from the peer\n';
+ returnText += '# TYPE wireguard_received_bytes counter\n';
+ returnText += `${wireguardReceivedBytes}`;
+
+ returnText += '\n# HELP wireguard_latest_handshake_seconds UNIX timestamp seconds of the last handshake\n';
+ returnText += '# TYPE wireguard_latest_handshake_seconds gauge\n';
+ returnText += `${wireguardLatestHandshakeSeconds}`;
+
+ return returnText;
+ }
+
+ async getMetricsJSON() {
+ const clients = await this.getClients();
+ let wireguardPeerCount = 0;
+ let wireguardEnabledPeersCount = 0;
+ let wireguardConnectedPeersCount = 0;
+ for (const client of Object.values(clients)) {
+ wireguardPeerCount++;
+ if (client.enabled === true) {
+ wireguardEnabledPeersCount++;
+ }
+ if (client.endpoint !== null) {
+ wireguardConnectedPeersCount++;
+ }
+ }
+ return {
+ wireguard_configured_peers: Number(wireguardPeerCount),
+ wireguard_enabled_peers: Number(wireguardEnabledPeersCount),
+ wireguard_connected_peers: Number(wireguardConnectedPeersCount),
+ };
+ }
+
};