diff --git a/README.md b/README.md index 72dde6c16..e0057e4d5 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ To automatically install & run wg-easy, simply run: --name=wg-easy-m3 \ -e LANG=de \ -e WG_HOST=<🚨YOUR_SERVER_IP> \ - -e PASSWORD=<🚨YOUR_ADMIN_PASSWORD> \ + -e PASSWORD_HASH=<🚨YOUR_ADMIN_PASSWORD_HASH> \ -e PORT=51821 \ -e WG_PORT=51820 \ -v ~/.wg-easy:/etc/wireguard \ @@ -84,7 +84,7 @@ To automatically install & run wg-easy, simply run: > 💡 Replace `YOUR_SERVER_IP` with your WAN IP, or a Dynamic DNS hostname. > -> 💡 Replace `YOUR_ADMIN_PASSWORD` with a password to log in on the Web UI. +> 💡 Replace `YOUR_ADMIN_PASSWORD_HASH` with a bcrypt password hash to log in on the Web UI. The Web UI will now be available on `http://0.0.0.0:51821`. @@ -106,7 +106,8 @@ These options can be configured by setting environment variables using `-e KEY=" | - | - | - | - | | `PORT` | `51821` | `6789` | TCP port for Web UI. | | `WEBUI_HOST` | `0.0.0.0` | `localhost` | IP address web UI binds to. | -| `PASSWORD` | - | `foobar123` | When set, requires a password when logging in to the Web UI. | +| `PASSWORD_HASH` | - | `$2y$05$Ci...` | When set, requires a password when logging in to the Web UI. | +| `PASSWORD` (deprecated) | - | `foobar123` | When set, requires a password when logging in to the Web UI. *(Not used if `PASSWORD_HASH` is set)* | | `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. | diff --git a/src/config.js b/src/config.js index bf7b84b40..40b70ddfd 100644 --- a/src/config.js +++ b/src/config.js @@ -6,6 +6,7 @@ module.exports.RELEASE = release; module.exports.PORT = process.env.PORT || '51821'; module.exports.WEBUI_HOST = process.env.WEBUI_HOST || '0.0.0.0'; module.exports.PASSWORD = process.env.PASSWORD; +module.exports.PASSWORD_HASH = process.env.PASSWORD_HASH; module.exports.WG_PATH = process.env.WG_PATH || '/etc/wireguard/'; module.exports.WG_DEVICE = process.env.WG_DEVICE || 'eth0'; module.exports.WG_HOST = process.env.WG_HOST; diff --git a/src/lib/Server.js b/src/lib/Server.js index 1a7953ad8..932b2ffa2 100644 --- a/src/lib/Server.js +++ b/src/lib/Server.js @@ -29,11 +29,40 @@ const { WEBUI_HOST, RELEASE, PASSWORD, + PASSWORD_HASH, LANG, UI_TRAFFIC_STATS, UI_CHART_TYPE, } = require('../config'); +const requiresPassword = !!PASSWORD || !!PASSWORD_HASH; + +/** + * Checks if `password` matches the PASSWORD_HASH. + * + * For backward compatibility it also allows `password` to match the clear text PASSWORD, + * but only if no PASSWORD_HASH is provided. + * + * If both enviornment variables are not set, the password is always invalid. + * + * @param {string} password String to test + * @returns {boolean} true if matching environment, otherwise false + */ +const isPasswordValid = (password) => { + if (typeof password !== 'string') { + return false; + } + + if (PASSWORD_HASH) { + return bcrypt.compareSync(password, PASSWORD_HASH); + } + if (PASSWORD) { + return password === PASSWORD; + } + + return false; +}; + module.exports = class Server { constructor() { @@ -72,7 +101,6 @@ module.exports = class Server { // Authentication .get('/api/session', defineEventHandler((event) => { - const requiresPassword = !!process.env.PASSWORD; const authenticated = requiresPassword ? !!(event.node.req.session && event.node.req.session.authenticated) : true; @@ -85,14 +113,16 @@ module.exports = class Server { .post('/api/session', defineEventHandler(async (event) => { const { password } = await readBody(event); - if (typeof password !== 'string') { + if (!requiresPassword) { + // if no password is required, the API should never be called. + // Do not automatically authenticate the user. throw createError({ status: 401, - message: 'Missing: Password', + message: 'Invalid state', }); } - if (password !== PASSWORD) { + if (!isPasswordValid(password)) { throw createError({ status: 401, message: 'Incorrect Password', @@ -110,7 +140,7 @@ module.exports = class Server { // WireGuard app.use( fromNodeMiddleware((req, res, next) => { - if (!PASSWORD || !req.url.startsWith('/api/')) { + if (!requiresPassword || !req.url.startsWith('/api/')) { return next(); } @@ -119,7 +149,7 @@ module.exports = class Server { } if (req.url.startsWith('/api/') && req.headers['authorization']) { - if (bcrypt.compareSync(req.headers['authorization'], bcrypt.hashSync(PASSWORD, 10))) { + if (isPasswordValid(req.headers['authorization'])) { return next(); } return res.status(401).json({