diff --git a/README.md b/README.md index 1c7b5dce..60114c3f 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ You have found the easiest way to install & manage WireGuard on any Linux host! * Automatic Light / Dark Mode * Multilanguage Support * UI_TRAFFIC_STATS (default off) -* UI_SHOW_LINKS (default off) +* WG_ENABLE_ONE_TIME_LINKS (default off) * WG_ENABLE_EXPIRES_TIME (default off) * Prometheus metrics support @@ -126,7 +126,7 @@ These options can be configured by setting environment variables using `-e KEY=" | `LANG` | `en` | `de` | Web UI language (Supports: en, ua, ru, tr, no, pl, fr, de, ca, es, ko, vi, nl, is, pt, chs, cht, it, th, hi). | | `UI_TRAFFIC_STATS` | `false` | `true` | Enable detailed RX / TX client stats in Web UI | | `UI_CHART_TYPE` | `0` | `1` | UI_CHART_TYPE=0 # Charts disabled, UI_CHART_TYPE=1 # Line chart, UI_CHART_TYPE=2 # Area chart, UI_CHART_TYPE=3 # Bar chart | -| `UI_SHOW_LINKS` | `false` | `true` | Enable display of a short download link in Web UI | +| `WG_ENABLE_ONE_TIME_LINKS` | `false` | `true` | Enable display and generation of short one time download links (expire after 5 minutes) | | `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`| diff --git a/docker-compose.yml b/docker-compose.yml index 7d1e9d3f..aa138e29 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -27,7 +27,7 @@ services: # - WG_POST_DOWN=echo "Post Down" > /etc/wireguard/post-down.txt # - UI_TRAFFIC_STATS=true # - UI_CHART_TYPE=0 # (0 Charts disabled, 1 # Line chart, 2 # Area chart, 3 # Bar chart) - # - UI_SHOW_LINKS=true + # - WG_ENABLE_ONE_TIME_LINKS=true # - UI_ENABLE_SORT_CLIENTS=true # - WG_ENABLE_EXPIRES_TIME=true # - ENABLE_PROMETHEUS_METRICS=false diff --git a/src/config.js b/src/config.js index 068caa39..30bfbc05 100644 --- a/src/config.js +++ b/src/config.js @@ -38,7 +38,7 @@ iptables -D FORWARD -o wg0 -j ACCEPT; module.exports.LANG = process.env.LANG || 'en'; module.exports.UI_TRAFFIC_STATS = process.env.UI_TRAFFIC_STATS || 'false'; module.exports.UI_CHART_TYPE = process.env.UI_CHART_TYPE || 0; -module.exports.UI_SHOW_LINKS = process.env.UI_SHOW_LINKS || 'false'; +module.exports.WG_ENABLE_ONE_TIME_LINKS = process.env.WG_ENABLE_ONE_TIME_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 8c30caab..c50770da 100644 --- a/src/lib/Server.js +++ b/src/lib/Server.js @@ -33,7 +33,7 @@ const { LANG, UI_TRAFFIC_STATS, UI_CHART_TYPE, - UI_SHOW_LINKS, + WG_ENABLE_ONE_TIME_LINKS, UI_ENABLE_SORT_CLIENTS, WG_ENABLE_EXPIRES_TIME, ENABLE_PROMETHEUS_METRICS, @@ -107,9 +107,9 @@ module.exports = class Server { return `"${UI_CHART_TYPE}"`; })) - .get('/api/ui-show-links', defineEventHandler((event) => { + .get('/api/wg-enable-one-time-links', defineEventHandler((event) => { setHeader(event, 'Content-Type', 'application/json'); - return `${UI_SHOW_LINKS}`; + return `${WG_ENABLE_ONE_TIME_LINKS}`; })) .get('/api/ui-sort-clients', defineEventHandler((event) => { @@ -134,6 +134,12 @@ module.exports = class Server { }; })) .get('/cnf/:clientOneTimeLink', defineEventHandler(async (event) => { + if (WG_ENABLE_ONE_TIME_LINKS === 'false') { + throw createError({ + status: 404, + message: 'Invalid state', + }); + } const clientOneTimeLink = getRouterParam(event, 'clientOneTimeLink'); const clients = await WireGuard.getClients(); const client = clients.find((client) => client.oneTimeLink === clientOneTimeLink); @@ -255,6 +261,12 @@ module.exports = class Server { return { success: true }; })) .post('/api/wireguard/client/:clientId/generateOneTimeLink', defineEventHandler(async (event) => { + if (WG_ENABLE_ONE_TIME_LINKS === 'false') { + throw createError({ + status: 404, + message: 'Invalid state', + }); + } const clientId = getRouterParam(event, 'clientId'); if (clientId === '__proto__' || clientId === 'constructor' || clientId === 'prototype') { throw createError({ status: 403 }); diff --git a/src/lib/WireGuard.js b/src/lib/WireGuard.js index 694fca15..1aaed9a3 100644 --- a/src/lib/WireGuard.js +++ b/src/lib/WireGuard.js @@ -25,6 +25,7 @@ const { WG_PRE_DOWN, WG_POST_DOWN, WG_ENABLE_EXPIRES_TIME, + WG_ENABLE_ONE_TIME_LINKS, } = require('../config'); module.exports = class WireGuard { @@ -152,7 +153,8 @@ ${client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : '' ? new Date(client.expiredAt) : null, allowedIPs: client.allowedIPs, - oneTimeLink: client.oneTimeLink ? client.oneTimeLink : null, + oneTimeLink: client.oneTimeLink ?? null, + oneTimeLinkExpiresAt: client.oneTimeLinkExpiresAt ?? null, downloadableConfig: 'privateKey' in client, persistentKeepalive: null, latestHandshakeAt: null, @@ -312,6 +314,7 @@ Endpoint = ${WG_HOST}:${WG_CONFIG_PORT}`; const client = await this.getClient({ clientId }); const key = `${clientId}-${Math.floor(Math.random() * 1000)}`; client.oneTimeLink = Math.abs(CRC32.str(key)).toString(16); + client.oneTimeLinkExpiresAt = new Date(Date.now() + 5 * 60 * 1000); client.updatedAt = new Date(); await this.saveConfig(); } @@ -319,6 +322,7 @@ Endpoint = ${WG_HOST}:${WG_CONFIG_PORT}`; async eraseOneTimeLink({ clientId }) { const client = await this.getClient({ clientId }); client.oneTimeLink = null; + client.oneTimeLinkExpiresAt = null; client.updatedAt = new Date(); await this.saveConfig(); } @@ -398,8 +402,9 @@ Endpoint = ${WG_HOST}:${WG_CONFIG_PORT}`; async cronJobEveryMinute() { const config = await this.getConfig(); + let needSaveConfig = false; + // Expires Feature if (WG_ENABLE_EXPIRES_TIME === 'true') { - let needSaveConfig = false; for (const client of Object.values(config.clients)) { if (client.enabled !== true) continue; if (client.expiredAt !== null && new Date() > new Date(client.expiredAt)) { @@ -409,10 +414,22 @@ Endpoint = ${WG_HOST}:${WG_CONFIG_PORT}`; client.updatedAt = new Date(); } } - if (needSaveConfig) { - await this.saveConfig(); + } + // One Time Link Feature + if (WG_ENABLE_ONE_TIME_LINKS === 'true') { + for (const client of Object.values(config.clients)) { + if (client.oneTimeLink !== null && new Date() > new Date(client.oneTimeLinkExpiresAt)) { + debug(`Client ${client.id} One Time Link expired.`); + needSaveConfig = true; + client.oneTimeLink = null; + client.oneTimeLinkExpiresAt = null; + client.updatedAt = new Date(); + } } } + if (needSaveConfig) { + await this.saveConfig(); + } } async getMetrics() { diff --git a/src/www/index.html b/src/www/index.html index 72312870..a35e81ef 100644 --- a/src/www/index.html +++ b/src/www/index.html @@ -256,7 +256,7 @@ {{!uiTrafficStats ? " ยท " : ""}}{{new Date(client.latestHandshakeAt) | timeago}} -
+
{{document.location.protocol}}//{{document.location.host}}/cnf/{{client.oneTimeLink}}
@@ -385,7 +385,7 @@ -