feat: PASSWORD_HASH helpers (#1180)
* feat: generate PASSWORD_HASH on the fly * remove PASSWORD environment variable in favor of PASSWORD_HASH * enhance password validity check server function * update Dockerfile to include building a binary for generating hashed password * update README with comprehensive Docker usage instructions hash generation * fix: try fix git action docker build * Dockerfile: use alpine-base image and install required build packages * rewrite in js * move files * fix: lint errors * some corrections --------- Co-authored-by: Philip H <47042125+pheiduck@users.noreply.github.com>
This commit is contained in:
commit
c28e5befa6
|
@ -26,6 +26,10 @@ COPY --from=build_node_modules /app /app
|
||||||
# than what runs inside of docker.
|
# than what runs inside of docker.
|
||||||
COPY --from=build_node_modules /node_modules /node_modules
|
COPY --from=build_node_modules /node_modules /node_modules
|
||||||
|
|
||||||
|
# Copy the needed wg-password scripts
|
||||||
|
COPY --from=build_node_modules /app/wgpw.sh /bin/wgpw
|
||||||
|
RUN chmod +x /bin/wgpw
|
||||||
|
|
||||||
# Install Linux packages
|
# Install Linux packages
|
||||||
RUN apk add --no-cache \
|
RUN apk add --no-cache \
|
||||||
dpkg \
|
dpkg \
|
||||||
|
|
|
@ -1,110 +1,34 @@
|
||||||
<!-- created by Mathys Lopinto (@mathys-lopinto) -->
|
# wg-password
|
||||||
# How to generate bcrypt hash
|
|
||||||
|
|
||||||
## Prerequisites
|
`wg-password` (wgpg) is a script that generates bcrypt password hashes for use with `wg-easy`, enhancing security by requiring passwords.
|
||||||
- Python 3
|
|
||||||
- bcrypt library
|
|
||||||
|
|
||||||
## Prerequisites Installation
|
## Features
|
||||||
### Windows
|
|
||||||
Download and install Python 3 from [official website](https://www.python.org/downloads/).
|
- Generate bcrypt password hashes.
|
||||||
Check "Add python.exe to PATH" before running "Install Now".
|
- Easily integrate with `wg-easy` to enforce password requirements.
|
||||||
|
|
||||||
|
## Usage with Docker
|
||||||
|
|
||||||
|
To generate a bcrypt password hash using Docker, run the following command:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker run ghcr.io/wg-easy/wg-easy wgpw YOUR_PASSWORD
|
||||||
|
PASSWORD_HASH='$2b$12$coPqCsPtcFO.Ab99xylBNOW4.Iu7OOA2/ZIboHN6/oyxca3MWo7fW' // litteraly YOUR_PASSWORD
|
||||||
|
```
|
||||||
|
|
||||||
|
## Important
|
||||||
|
|
||||||
|
Make sure to enclose your password in single quotes when you run a linux host.
|
||||||
|
|
||||||
Open Command Prompt (win + r, type "cmd" and press enter) and run the following command to install bcrypt library:
|
|
||||||
```bash
|
```bash
|
||||||
pip install bcrypt
|
$ echo $2b$12$coPqCsPtcF
|
||||||
|
b2
|
||||||
|
$ echo "$2b$12$coPqCsPtcF"
|
||||||
|
b2
|
||||||
|
$ echo '$2b$12$coPqCsPtcF'
|
||||||
|
$2b$12$coPqCsPtcF
|
||||||
```
|
```
|
||||||
|
|
||||||
### Debian based distributions
|
## LICENSE
|
||||||
```bash
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install python3 python3-pip
|
|
||||||
# If you use have install python using apt
|
|
||||||
sudo apt-get install python3-bcrypt
|
|
||||||
# If don't install python using apt
|
|
||||||
pip3 install bcrypt
|
|
||||||
# If you got externally-managed-environment error
|
|
||||||
pip3 install bcrypt --break-system-packages
|
|
||||||
```
|
|
||||||
|
|
||||||
### Fedora based distributions
|
[wg-easy license](./LICENSE)
|
||||||
```bash
|
|
||||||
sudo dnf update
|
|
||||||
sudo dnf install python3 python3-pip
|
|
||||||
# If you use have install python using dnf
|
|
||||||
sudo dnf install python3-bcrypt
|
|
||||||
# If don't install python using dnf
|
|
||||||
pip3 install bcrypt
|
|
||||||
# If you got externally-managed-environment error
|
|
||||||
pip3 install bcrypt --break-system-packages
|
|
||||||
```
|
|
||||||
|
|
||||||
### Arch Linux based distributions
|
|
||||||
```bash
|
|
||||||
sudo pacman -Syy
|
|
||||||
sudo pacman -S python python-pip
|
|
||||||
# If you use have install python using pacman
|
|
||||||
sudo pacman -S python-bcrypt
|
|
||||||
# If don't install python using pacman
|
|
||||||
pip3 install bcrypt
|
|
||||||
# If you got externally-managed-environment error
|
|
||||||
pip3 install bcrypt --break-system-packages
|
|
||||||
```
|
|
||||||
|
|
||||||
### macOS
|
|
||||||
```bash
|
|
||||||
brew install bcrypt
|
|
||||||
# If don't install bcrypt using homebrew
|
|
||||||
pip3 install bcrypt
|
|
||||||
# If you got externally-managed-environment error
|
|
||||||
pip3 install bcrypt --break-system-packages
|
|
||||||
```
|
|
||||||
|
|
||||||
## Generating bcrypt hash from the command line
|
|
||||||
You can use the following one-liner command to generate a bcrypt hash directly in the cmd/ terminal:
|
|
||||||
```bash
|
|
||||||
python3 -c "import bcrypt; password = b'your_password_here'; assert len(password) < 72, 'Password must be less than 72 bytes due to bcrypt limitation'; hashed = bcrypt.hashpw(password, bcrypt.gensalt()); print(f'The hashed password is: {hashed.decode()}'); docker_interpolation = hashed.decode().replace('$', '$'*2); print(f'The hashed password for a Docker env is: {docker_interpolation}')" # or python if you run this on Windows. CHANGE your_password_here BY YOUR PASSWORD
|
|
||||||
```
|
|
||||||
Please change ``your_password_here`` in the line by your own password.
|
|
||||||
|
|
||||||
## Generating bcrypt hash from an script file
|
|
||||||
### Do not name the file `bcrypt.py` as it will cause an error.
|
|
||||||
Create a python file with the following content:
|
|
||||||
```python
|
|
||||||
import bcrypt
|
|
||||||
|
|
||||||
# Initial password
|
|
||||||
password = b"your_password_here" # DO NOT REMOVE THE b
|
|
||||||
|
|
||||||
# Assert that the password is under 72 bytes
|
|
||||||
assert len(password) < 72, "Password must be less than 72 bytes due to bcrypt limitation"
|
|
||||||
|
|
||||||
# Generate a salt and hash the password
|
|
||||||
hashed = bcrypt.hashpw(password, bcrypt.gensalt())
|
|
||||||
|
|
||||||
# Print the hashed password
|
|
||||||
print(f'The hashed password is: {hashed.decode()}')
|
|
||||||
|
|
||||||
# Prepare the hashed password for Docker environment variables
|
|
||||||
docker_interpolation = hashed.decode().replace("$", "$$")
|
|
||||||
print(f'The hashed password for a Docker env is: {docker_interpolation}')
|
|
||||||
```
|
|
||||||
|
|
||||||
Replace `your_password_here` with the password you want to hash.
|
|
||||||
|
|
||||||
Run the python file and you will get the hashed password.
|
|
||||||
|
|
||||||
## Get the right hash
|
|
||||||
Copy the 2nd line of the output (after the : ) and use it as your hashed password.
|
|
||||||
|
|
||||||
__Exemple__
|
|
||||||
If the output is:
|
|
||||||
```txt
|
|
||||||
The hashed password is: $2b$12$NRiL4Kw4dKid.ix2WvZltOmaQBZjoX30shjHJXRVdEGshAxYWXXMe
|
|
||||||
The hashed password for an docker env is: $$2b$$12$$NRiL4Kw4dKid.ix2WvZltOmaQBZjoX30shjHJXRVdEGshAxYWXXMe
|
|
||||||
```
|
|
||||||
|
|
||||||
The docker line ``PASSWORD_HASH`` will be:
|
|
||||||
```txt
|
|
||||||
PASSWORD_HASH=$$2b$$12$$NRiL4Kw4dKid.ix2WvZltOmaQBZjoX30shjHJXRVdEGshAxYWXXMe
|
|
||||||
```
|
|
|
@ -80,7 +80,7 @@ To automatically install & run wg-easy, simply run:
|
||||||
|
|
||||||
> 💡 Replace `YOUR_SERVER_IP` with your WAN IP, or a Dynamic DNS hostname.
|
> 💡 Replace `YOUR_SERVER_IP` with your WAN IP, or a Dynamic DNS hostname.
|
||||||
>
|
>
|
||||||
> 💡 Replace `YOUR_ADMIN_PASSWORD_HASH` with a bcrypt password hash to log in on the Web UI. See How_to_generate_an_bcrypt_hash.md for know how generate the hash.
|
> 💡 Replace `YOUR_ADMIN_PASSWORD_HASH` with a bcrypt password hash to log in on the Web UI. See [How_to_generate_an_bcrypt_hash.md](./How_to_generate_an_bcrypt_hash.md) for know how generate the hash.
|
||||||
|
|
||||||
The Web UI will now be available on `http://0.0.0.0:51821`.
|
The Web UI will now be available on `http://0.0.0.0:51821`.
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ const { release } = require('./package.json');
|
||||||
module.exports.RELEASE = release;
|
module.exports.RELEASE = release;
|
||||||
module.exports.PORT = process.env.PORT || '51821';
|
module.exports.PORT = process.env.PORT || '51821';
|
||||||
module.exports.WEBUI_HOST = process.env.WEBUI_HOST || '0.0.0.0';
|
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.PASSWORD_HASH = process.env.PASSWORD_HASH;
|
||||||
module.exports.WG_PATH = process.env.WG_PATH || '/etc/wireguard/';
|
module.exports.WG_PATH = process.env.WG_PATH || '/etc/wireguard/';
|
||||||
module.exports.WG_DEVICE = process.env.WG_DEVICE || 'eth0';
|
module.exports.WG_DEVICE = process.env.WG_DEVICE || 'eth0';
|
||||||
|
|
|
@ -28,22 +28,18 @@ const {
|
||||||
PORT,
|
PORT,
|
||||||
WEBUI_HOST,
|
WEBUI_HOST,
|
||||||
RELEASE,
|
RELEASE,
|
||||||
PASSWORD,
|
|
||||||
PASSWORD_HASH,
|
PASSWORD_HASH,
|
||||||
LANG,
|
LANG,
|
||||||
UI_TRAFFIC_STATS,
|
UI_TRAFFIC_STATS,
|
||||||
UI_CHART_TYPE,
|
UI_CHART_TYPE,
|
||||||
} = require('../config');
|
} = require('../config');
|
||||||
|
|
||||||
const requiresPassword = !!PASSWORD || !!PASSWORD_HASH;
|
const requiresPassword = !!PASSWORD_HASH;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if `password` matches the PASSWORD_HASH.
|
* Checks if `password` matches the PASSWORD_HASH.
|
||||||
*
|
*
|
||||||
* For backward compatibility it also allows `password` to match the clear text PASSWORD,
|
* If environment variable is not set, the password is always invalid.
|
||||||
* 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
|
* @param {string} password String to test
|
||||||
* @returns {boolean} true if matching environment, otherwise false
|
* @returns {boolean} true if matching environment, otherwise false
|
||||||
|
@ -56,9 +52,6 @@ const isPasswordValid = (password) => {
|
||||||
if (PASSWORD_HASH) {
|
if (PASSWORD_HASH) {
|
||||||
return bcrypt.compareSync(password, PASSWORD_HASH);
|
return bcrypt.compareSync(password, PASSWORD_HASH);
|
||||||
}
|
}
|
||||||
if (PASSWORD) {
|
|
||||||
return password === PASSWORD;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Import needed libraries
|
||||||
|
import bcrypt from 'bcryptjs';
|
||||||
|
|
||||||
|
// Function to generate hash
|
||||||
|
const generateHash = async (password) => {
|
||||||
|
try {
|
||||||
|
const salt = await bcrypt.genSalt(12);
|
||||||
|
const hash = await bcrypt.hash(password, salt);
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(`PASSWORD_HASH='${hash}'`);
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Failed to generate hash : ${error}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to compare password with hash
|
||||||
|
const comparePassword = async (password, hash) => {
|
||||||
|
try {
|
||||||
|
const match = await bcrypt.compare(password, hash);
|
||||||
|
if (match) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log('Password matches the hash !');
|
||||||
|
} else {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log('Password does not match the hash.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Failed to compare password and hash : ${error}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
// Retrieve command line arguments
|
||||||
|
const args = process.argv.slice(2); // Ignore the first two arguments
|
||||||
|
if (args.length > 2) {
|
||||||
|
throw new Error('Usage : wgpw YOUR_PASSWORD [HASH]');
|
||||||
|
}
|
||||||
|
|
||||||
|
const [password, hash] = args;
|
||||||
|
if (password && hash) {
|
||||||
|
await comparePassword(password, hash);
|
||||||
|
} else if (password) {
|
||||||
|
await generateHash(password);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error(error);
|
||||||
|
// eslint-disable-next-line no-process-exit
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
})();
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# This script is intended to be run only inside a docker container, not on the development host machine
|
||||||
|
set -e
|
||||||
|
# proxy command
|
||||||
|
node /app/wgpw.mjs "$@"
|
Loading…
Reference in New Issue