This commit is contained in:
Noxcis 2024-09-19 14:36:01 +08:00 committed by GitHub
commit 5a48828368
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 250 additions and 92 deletions

View file

@ -1,7 +1,8 @@
# Pull from small Debian stable image.
# Pull from small secure alpine image.
FROM alpine:latest AS builder
LABEL maintainer="dselen@nerthus.nl"
LABEL Maintainer="dselen@nerthus.nl"
LABEL Deployment-Engineer="NOXCIS"
WORKDIR /opt/wireguarddashboard/src
@ -9,11 +10,12 @@ RUN apk update && \
apk add --no-cache sudo gcc musl-dev rust cargo linux-headers
COPY ./docker/alpine/builder.sh /opt/wireguarddashboard/src/
COPY ./docker/alpine/requirements.txt /opt/wireguarddashboard/src/
COPY ./docker/alpine/builder_requirements.txt /opt/wireguarddashboard/src/
RUN chmod u+x /opt/wireguarddashboard/src/builder.sh
RUN /opt/wireguarddashboard/src/builder.sh
FROM alpine:latest
WORKDIR /opt/wireguarddashboard/src
@ -26,6 +28,12 @@ RUN apk update && \
apk add --no-cache iptables ip6tables && \
chmod u+x /opt/wireguarddashboard/src/entrypoint.sh
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD curl -f http://localhost:10086/signin || exit 1
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD sh -c 'pgrep gunicorn > /dev/null && pgrep tail > /dev/null' || exit 1
ENTRYPOINT ["/opt/wireguarddashboard/src/entrypoint.sh"]
ENTRYPOINT ["/opt/wireguarddashboard/src/entrypoint.sh"]
CMD ["docker_start"]
# Define the stop signal to be used by Docker
STOPSIGNAL SIGTERM

View file

@ -2,20 +2,30 @@ services:
wireguard-dashboard:
build: ./
container_name: wiregate
container_name: wg-dashboard
cap_add:
- NET_ADMIN
- SYS_MODULE
restart: unless-stopped
environment:
- wg_net=10.0.0.1/24
- wg_port=51820
- WGD_USER=admin
- WGD_PASS=admin
- WGD_NET=10.0.0.1/24
- WGD_PORT=51820
- WGD_APP_PORT=10086
- WGD_REMOTE_ENDPOINT=0.0.0.0
- WGD_DNS="1.1.1.1, 1.0.0.1"
- WGD_PEER_ENDPOINT_ALLOWED_IP=0.0.0.0/0
- WGD_KEEP_ALIVE=21
- WGD_MTU=1420
- WGD_WELCOME_SESSION=false #set to true for welcome setup
volumes:
- wgd_configs:/etc/wireguard
- wgd_app:/opt/wireguarddashboard/src
ports:
- 10086:10086/tcp
- 51820:51820/udp
- 10086:10086/tcp
- 51820:51820/udp
# Add Port Map for New Configs and Restart Container to Apply
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1

View file

@ -1,82 +1,144 @@
# WG-Dashboard Docker Explanation:
Author: DaanSelen<br>
Author: Noxcis<br>
This document delves into how the WG-Dashboard Docker container has been built.<br>
Of course there are two stages, one before run-time and one at/after run-time.<br>
The `Dockerfile` describes how the container image is made, and the `entrypoint.sh` is executed after running the container. <br>
In this example, WireGuard is integrated into the container itself, so it should be a run-and-go.<br>
For more details on the source-code specific to this Docker image, refer to the source files, they have lots of comments.
The Image is two stage docker build based on alpine where psutil and bcrypt are compiled in the first stage before being copied to the final stage. This is done to maintain a small image footprint as bcrypt and psutil require gcc and supporting libraries.
I have tried to embed some new features such as `isolated_peers` and interface startup on container-start (through `enable_wg0`).
The `Dockerfile` describes how the container image is made, and the `entrypoint.sh` is executed to run the container. <br>
<img src="https://raw.githubusercontent.com/donaldzou/WGDashboard/main/img/logo.png" alt="WG-Dashboard Logo" title="WG-Dashboard Logo" width="150" height="150" />
In this example, WireGuard is integrated into the container itself, so it is a compose up and done.<br>
For more details on the source-code specific to this Docker image, refer to the source files, google, stackedit, reddit & ChatGPT until your curiosity is satisfied.
<img src="https://raw.githubusercontent.com/donaldzou/WGDashboard/main/img/logo.png" alt="WG-Dashboard Logo" title="WG-Dashboard Logo" width="150" height="150" />
## Getting the container running:
To get the container running you either pull the image from the repository, at the moment: `repo.nerthus.nl/app/wireguard-dashboard:latest`.<br>
To get the container running you either pull the image from the repository, at the moment: `noxcis/wgdashboard:4.0.2`. **Check DockerHub For Updated Tags**<br>
From there either use the environment variables describe below as parameters or use the Docker Compose file: `compose.yaml`.
An example of a simple command to get the container running is show below:<br>
```shell
docker run -d \
--name wireguard-dashboard \
--name wg-dashboard \
--cap-add NET_ADMIN \
--cap-add SYS_MODULE \
--restart unless-stopped \
-e enable_wg0=true \
-e isolated_peers=true \
-e WGD_USER=admin \
-e WGD_PASS=admin \
-e WGD_NET=10.0.0.1/24 \
-e WGD_PORT=51820 \
-e WGD_APP_PORT=10086 \
-e WGD_REMOTE_ENDPOINT=0.0.0.0 \
-e WGD_DNS="1.1.1.1, 1.0.0.1" \
-e WGD_PEER_ENDPOINT_ALLOWED_IP=0.0.0.0/0 \
-e WGD_KEEP_ALIVE=21 \
-e WGD_MTU=1420 \
-e WGD_WELCOME_SESSION=false \
-v wgd_configs:/etc/wireguard \
-v wgd_app:/opt/wireguarddashboard/src \
-p 10086:10086/tcp \
-p 51820:51820/udp \
--cap-add NET_ADMIN \
repo.nerthus.nl/app/wireguard-dashboard:latest
--sysctl net.ipv4.ip_forward=1 \
--sysctl net.ipv4.conf.all.src_valid_mark=1 \
noxcis/wgdashboard:4.0.2
```
<br>
If you want to use Compose instead of a raw Docker command, refer to the example in the `compose.yaml` or the one pasted below:
<br><br>
```yaml
services:
wireguard-dashboard:
image: repo.nerthus.nl/app/wireguard-dashboard:latest
restart: unless-stopped
container_name: wire-dash
environment:
#- tz=
#- global_dns=
- enable_wg0=true
- isolated_peers=false
#- public_ip=
ports:
- 10086:10086/tcp
- 51820:51820/udp
volumes:
- conf:/etc/wireguard
- app:/opt/wireguarddashboard/app
cap_add:
- NET_ADMIN
wireguard-dashboard:
# build: ./ #Uncomment & comment out line below to build your own Image
image: noxcis/wgdashboard:4.0.2
container_name: wg-dashboard
cap_add:
- NET_ADMIN
- SYS_MODULE
restart: unless-stopped
environment:
- WGD_USER=admin
- WGD_PASS=admin
- WGD_NET=10.0.0.1/24
- WGD_PORT=51820
- WGD_APP_PORT=10086
- WGD_REMOTE_ENDPOINT=0.0.0.0
- WGD_DNS="1.1.1.1, 1.0.0.1"
- WGD_PEER_ENDPOINT_ALLOWED_IP=0.0.0.0/0
- WGD_KEEP_ALIVE=21
- WGD_MTU=1420
- WGD_WELCOME_SESSION=false #set to true for welcome setup
volumes:
- wgd_configs:/etc/wireguard
- wgd_app:/opt/wireguarddashboard/src
ports:
- 10086:10086/tcp
- 51820:51820/udp
# Add Port Map for New Configs and Restart Container to Apply
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1
volumes:
conf:
app:
wgd_configs:
wgd_app:
```
If you want to customize the yaml, make sure the core stays the same, but for example volume PATHs can be freely changed.<br>
This setup is just generic and will use the Docker volumes.
If you want to customize the yaml, make sure to adjust your ports accordingly in respect to the dashboard and your wireguard configs. Your Wireguard & Dashboard Config will persist across container updates as long as the wgd_configs & wdg_app volumes are not deleted.
**TIPS**
> The Dashboard can be reset by deleting the **wgd_app** volume while maintaining configs and peers in the **wgd_configs** volume.
This setup is meant to be persistent and can be made ephemeral for development purpose or etc, by commenting out the docker volume section.
## Working with the container and environment variables:
| Environment variable | Default value | Example |
| -------------- | ------- | ------- |
|WGD_USER | admin | james |
|WGD_PASS | admin | ScottsMan49 |
|WGD_NET | 10.0.0.1/24 | 10.0.2.0/24 |
|WGD_PORT | 51820 | 4201 |
|WGD_APP_PORT | 10086 | 8000 |
|WGD_REMOTE_ENDPOINT | 0.0.0.0 | localhost |
|WGD_DNS | "1.1.1.1, 1.0.0.1" | "8.8.8.8, 8.8.4.4" |
|WGD_PEER_ENDPOINT_ALLOWED_IP | 0.0.0.0/0 |192.168.15.0/24, 10.0.1.0/24 |
|WGD_KEEP_ALIVE | 21 | 0
|WGD_MTU | 1420 | 1412
|WGD_WELCOME_SESSION | false | true
Once the container is running, the installation process is essentially the same as running it on bare-metal.<br>
So go to the assign TCP port in this case HTTP, like the default 10086 one in the example and log into the WEB-GUI.<br>
| Environment variable | Accepted arguments | Default value | Verbose |
| -------------- | ------- | ------- | ------- |
| tz | Europe/Amsterdam or any confirming timezone notation. | Europe/Amsterdam | Sets the timezone of the Docker container. This is to timesync the container to any other processes which would need it. |
| global_dns | Any IPv4 address, such as my personal recommendation: 9.9.9.9 (QUAD9) | 1.1.1.1 | Set the default DNS given to clients once they connect to the WireGuard tunnel (VPN).
| enable_wg0 | `true` or `false` | `false` | Enables or disables the starting of the WireGuard interface on container 'boot-up'.
| isolated_peers | `true` or `false` | `true` | For security the default is true, and it disables peers to ping or reach eachother, the WireGuard interface IS able to reach the peers (Done through `iptables`).
| public_ip | Any IPv4 (public recommended) address, such as the one returned by default | Default uses the return of `curl ifconfig.me` | To reach your VPN from outside your own network, you need WG-Dashboard to know what your public IP-address is, otherwise it will generate faulty config files for clients.
## Closing remarks:
For feedback please submit an issue to the repository. Or message dselen@nerthus.nl.
For feedback please submit an issue to the repository. Or message dselen@nerthus.nl.

View file

@ -6,13 +6,13 @@ pythonExecutable="python3"
_check_and_set_venv(){
VIRTUAL_ENV="./venv"
if [ ! -d $VIRTUAL_ENV ]; then
printf "[WGDashboard] Creating Python Virtual Environment under ./venv\n"
printf "[WGDashboard][Docker] Creating Python Virtual Environment under ./venv\n"
{ $pythonExecutable -m venv $VIRTUAL_ENV; } >> ./log/install.txt
fi
if ! $venv_python --version > /dev/null 2>&1
then
printf "[WGDashboard] %s Python Virtual Environment under ./venv failed to create. Halting now.\n" "$heavy_crossmark"
printf "[WGDashboard][Docker] %s Python Virtual Environment under ./venv failed to create. Halting now.\n" "$heavy_crossmark"
kill $TOP_PID
fi
@ -23,21 +23,21 @@ _check_and_set_venv(){
build_core () {
if [ ! -d "log" ]
then
printf "[WGDashboard] Creating ./log folder\n"
printf "[WGDashboard][Docker] Creating ./log folder\n"
mkdir "log"
fi
apk add --no-cache python3 net-tools python3-dev py3-virtualenv
_check_and_set_venv
printf "[WGDashboard] Upgrading Python Package Manage (PIP)\n"
printf "[WGDashboard][Docker] Upgrading Python Package Manage (PIP)\n"
{ date; python3 -m pip install --upgrade pip; printf "\n\n"; } >> ./log/install.txt
printf "[WGDashboard] Building Bcrypt & Psutil\n"
{ date; python3 -m pip install -r requirements.txt ; printf "\n\n"; } >> ./log/install.txt
printf "[WGDashboard] Build Successfull!\n"
printf "[WGDashboard] Clean Up Pip!\n"
printf "[WGDashboard][Docker] Building Bcrypt & Psutil\n"
{ date; python3 -m pip install -r builder_requirements.txt ; printf "\n\n"; } >> ./log/install.txt
printf "[WGDashboard][Docker] Build Successfull!\n"
printf "[WGDashboard][Docker] Clean Up Pip!\n"
{ date; rm -rf /opt/wireguarddashboard/src/venv/lib/python3.12/site-packages/pip* ; printf "\n\n"; } >> ./log/install.txt
}
build_core
build_core

View file

@ -1,2 +1,2 @@
bcrypt
psutil
psutil

View file

@ -26,6 +26,8 @@ from flask import Flask, request, render_template, session, g
from json import JSONEncoder
from flask_cors import CORS
from dotenv import load_dotenv
from icmplib import ping, traceroute
# Import other python files
@ -51,7 +53,20 @@ app = Flask("WGDashboard")
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 5206928
app.secret_key = secrets.token_urlsafe(32)
#Docker ENV ARGS Import
load_dotenv()
wgd_welcome = os.environ.get('WGD_WELCOME_SESSION')
wgd_app_port = os.environ.get('WGD_APP_PORT')
wgd_user = os.environ.get('WGD_USER')
wgd_pass = os.environ.get('WGD_PASS')
wgd_global_dns = os.environ.get('WGD_DNS')
wgd_peer_endpoint_allowed_ip = os.environ.get('WGD_PEER_ENDPOINT_ALLOWED_IP')
wgd_remote_endpoint = os.environ.get('WGD_REMOTE_ENDPOINT')
if wgd_remote_endpoint == '0.0.0.0':
default_interface = ifcfg.default_interface()
wgd_remote_endpoint = default_interface['inet']
wgd_keep_alive = os.environ.get('WGD_KEEP_ALIVE')
wgd_mtu = os.environ.get('WGD_MTU')
class ModelEncoder(JSONEncoder):
@ -1113,8 +1128,8 @@ class DashboardConfig:
self.hiddenAttribute = ["totp_key"]
self.__default = {
"Account": {
"username": "admin",
"password": "admin",
"username": wgd_user,
"password": wgd_pass,
"enable_totp": "false",
"totp_verified": "false",
"totp_key": pyotp.random_base32()
@ -1123,7 +1138,7 @@ class DashboardConfig:
"wg_conf_path": "/etc/wireguard",
"app_prefix": "",
"app_ip": "0.0.0.0",
"app_port": "10086",
"app_port": wgd_app_port,
"auth_req": "true",
"version": DASHBOARD_VERSION,
"dashboard_refresh_interval": "60000",
@ -1132,15 +1147,15 @@ class DashboardConfig:
"dashboard_api_key": "false"
},
"Peers": {
"peer_global_DNS": "1.1.1.1",
"peer_endpoint_allowed_ip": "0.0.0.0/0",
"peer_global_DNS": wgd_global_dns,
"peer_endpoint_allowed_ip": wgd_peer_endpoint_allowed_ip,
"peer_display_mode": "grid",
"remote_endpoint": ifcfg.default_interface()['inet'],
"peer_MTU": "1420",
"peer_keep_alive": "21"
"remote_endpoint": wgd_remote_endpoint,
"peer_MTU": wgd_mtu,
"peer_keep_alive": wgd_keep_alive
},
"Other": {
"welcome_session": "true"
"welcome_session": wgd_welcome
},
"Database":{
"type": "sqlite"

View file

@ -1,8 +1,17 @@
#!/bin/bash
# Trap the SIGTERM signal and call the stop_service function
trap 'stop_service' SIGTERM
echo "Starting the WireGuard Dashboard Docker container."
stop_service() {
echo "SIGTERM received. Stopping WireGuard Dashboard."
./wgd.sh stop
exit 0
}
clean_up() {
# Cleaning out previous data such as the .pid file and starting the WireGuard Dashboard. Making sure to use the python venv.
echo "Looking for remains of previous instances..."
if [ -f "/opt/wireguarddashboard/app/src/gunicorn.pid" ]; then
echo "Found old .pid file, removing."
@ -11,24 +20,25 @@ clean_up() {
echo "No remains found, continuing."
fi
}
ensure_blocking() {
sleep 1s
echo "Ensuring container continuation."
# This function checks if the latest error log is created and tails it for docker logs uses.
if find "/opt/wireguarddashboard/src/log" -mindepth 1 -maxdepth 1 -type f | read -r; then
latestErrLog=$(find /opt/wireguarddashboard/src/log -name "error_*.log" | head -n 1)
latestAccLog=$(find /opt/wireguarddashboard/src/log -name "access_*.log" | head -n 1)
tail -f "${latestErrLog}" "${latestAccLog}"
tail -f "${latestErrLog}" "${latestAccLog}" &
fi
# Blocking command in case of erroring. So the container does not quit.
sleep infinity
wait
}
{ date; clean_up; printf "\n\n"; } >> ./log/install.txt
chmod u+x /opt/wireguarddashboard/src/wgd.sh
/opt/wireguarddashboard/src/wgd.sh install
/opt/wireguarddashboard/src/wgd.sh docker_start
/opt/wireguarddashboard/src/wgd.sh docker_start &
SERVICE_PID=$!
ensure_blocking

View file

@ -5,4 +5,5 @@ pyotp
Flask
flask-cors
icmplib
gunicorn
gunicorn
python-dotenv

View file

@ -10,6 +10,7 @@ app_official_name="WGDashboard"
venv_python="./venv/bin/python3"
venv_gunicorn="./venv/bin/gunicorn"
pythonExecutable="python3"
svr_config="/etc/wireguard/wg0.conf"
heavy_checkmark=$(printf "\xE2\x9C\x94")
heavy_crossmark=$(printf "\xE2\x9C\x97")
@ -310,6 +311,7 @@ gunicorn_stop () {
start_wgd () {
_checkWireguard
set_env regular
gunicorn_start
}
@ -323,19 +325,67 @@ stop_wgd() {
startwgd_docker() {
_checkWireguard
printf "[WGDashboard][Docker] WireGuard configuration started\n"
{ date; start_core ; printf "\n\n"; } >> ./log/install.txt
printf "[WGDashboard][Docker] WGD Docker Started\n"
set_env docker
start_core
gunicorn_start
}
set_env() {
local env_file=".env"
local env_type="$1"
# Check if the env_file exists and is not empty
if [[ -f "$env_file" && -s "$env_file" ]]; then
printf "[WG-DASH ENVIORNMENT] %s Loading Enviornment File.\n" "$heavy_checkmark"
return 0
fi
# Create the env_file if it doesn't exist
if [[ ! -f "$env_file" ]]; then
touch "$env_file"
printf "[WG-DASH ENVIORNMENT] %s Enviornment File Missing, Creating ...\n" "$heavy_checkmark"
fi
# Clear the file to ensure it's updated with the latest values
> "$env_file"
if [[ "$env_type" == "docker" ]]; then
printf "WGD_WELCOME_SESSION=%s\n" "${WGD_WELCOME_SESSION}" >> "$env_file"
printf "WGD_APP_PORT=%s\n" "${WGD_APP_PORT}" >> "$env_file"
printf "WGD_USER=%s\n" "${WGD_USER}" >> "$env_file"
printf "WGD_PASS=%s\n" "${WGD_PASS}" >> "$env_file"
printf "WGD_REMOTE_ENDPOINT=%s\n" "${WGD_REMOTE_ENDPOINT}" >> "$env_file"
printf "WGD_DNS=%s\n" "${WGD_DNS}" >> "$env_file"
printf "WGD_PEER_ENDPOINT_ALLOWED_IP=%s\n" "${WGD_PEER_ENDPOINT_ALLOWED_IP}" >> "$env_file"
printf "WGD_KEEP_ALIVE=%s\n" "${WGD_KEEP_ALIVE}" >> "$env_file"
printf "WGD_MTU=%s\n" "${WGD_MTU}" >> "$env_file"
printf "WGD_PORT=%s\n" "${WGD_PORT}" >> "$env_file"
printf "WGD_NET=%s\n" "${WGD_NET}" >> "$env_file"
elif [[ "$env_type" == "regular" ]]; then
printf "WGD_WELCOME_SESSION=true\n" >> "$env_file"
printf "WGD_APP_PORT=10086\n" >> "$env_file"
printf "WGD_USER=admin\n" >> "$env_file"
printf "WGD_PASS=admin\n" >> "$env_file"
printf "WGD_REMOTE_ENDPOINT=0.0.0.0\n" >> "$env_file"
printf "WGD_DNS=1.1.1.1\n" >> "$env_file"
printf "WGD_PEER_ENDPOINT_ALLOWED_IP=0.0.0.0/0\n" >> "$env_file"
printf "WGD_KEEP_ALIVE=21\n" >> "$env_file"
printf "WGD_MTU=1420\n" >> "$env_file"
else
echo "Error: Invalid environment type. Use 'docker' or 'regular'."
return 1
fi
. .env
}
start_core() {
local iptable_dir="/opt/wireguarddashboard/src/iptable-rules"
# Check if wg0.conf exists in /etc/wireguard
if [[ ! -f /etc/wireguard/wg0.conf ]]; then
echo "[WGDashboard][Docker] wg0.conf not found. Running generate configuration."
printf "[WGDashboard][Docker] %s Wireguard Configuration Missing, Creating ....\n" "$heavy_checkmark"
newconf_wgd
else
echo "[WGDashboard][Docker] wg0.conf already exists. Skipping WireGuard configuration generation."
printf "[WGDashboard][Docker] %s Loading Wireguard Configuartions.\n" "$heavy_checkmark"
fi
# Re-assign config_files to ensure it includes any newly created configurations
local config_files=$(find /etc/wireguard -type f -name "*.conf")
@ -344,6 +394,8 @@ start_core() {
find /etc/wireguard -type f -name "*.conf" -exec chmod 600 {} \;
find "$iptable_dir" -type f -name "*.sh" -exec chmod +x {} \;
printf "[WGDashboard][Docker] %s Starting Wireguard Configuartions.\n" "$heavy_checkmark"
printf "%s\n" "$dashes"
# Start WireGuard for each config file
for file in $config_files; do
config_name=$(basename "$file" ".conf")
@ -354,8 +406,8 @@ start_core() {
newconf_wgd() {
local wg_port_listen=$wg_port
local wg_addr_range=$wg_net
local wg_port_listen=$WGD_PORT
local wg_addr_range=$WGD_NET
private_key=$(wg genkey)
public_key=$(echo "$private_key" | wg pubkey)
cat <<EOF >"/etc/wireguard/wg0.conf"