mirror of
https://github.com/WGDashboard/WGDashboard-PRW.git
synced 2026-08-03 14:32:56 +00:00
feat: add docker files and fix dayjs build bug
This commit is contained in:
@@ -0,0 +1,145 @@
|
|||||||
|
#
|
||||||
|
# AWG GOLANG BUILDING STAGE
|
||||||
|
# Base: Alpine
|
||||||
|
#
|
||||||
|
|
||||||
|
# Pull the current golang-alpine image.
|
||||||
|
FROM golang:1.25-alpine3.23 AS awg-go
|
||||||
|
|
||||||
|
# Install build-dependencies.
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
git \
|
||||||
|
gcc \
|
||||||
|
musl-dev
|
||||||
|
|
||||||
|
# Standard working directory for WGDashboard
|
||||||
|
RUN mkdir -p /workspace && \
|
||||||
|
git clone https://github.com/WGDashboard/amneziawg-go /workspace/awg
|
||||||
|
|
||||||
|
# Enable CGO compilation for AmneziaWG
|
||||||
|
ENV CGO_ENABLED=1
|
||||||
|
|
||||||
|
# Change directory
|
||||||
|
WORKDIR /workspace/awg
|
||||||
|
# Compile the binaries
|
||||||
|
RUN go version && \
|
||||||
|
go mod download && \
|
||||||
|
go mod verify && \
|
||||||
|
go build -ldflags '-linkmode external -extldflags "-fno-PIC -static"' -v -o /usr/bin
|
||||||
|
#
|
||||||
|
# AWG TOOLS BUILDING STAGE
|
||||||
|
# Base: Alpine
|
||||||
|
#
|
||||||
|
FROM alpine:3.23 AS awg-tools
|
||||||
|
|
||||||
|
# Install needed dependencies.
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
make \
|
||||||
|
git \
|
||||||
|
build-base \
|
||||||
|
linux-headers \
|
||||||
|
ca-certificates
|
||||||
|
|
||||||
|
# Get the workspace ready
|
||||||
|
RUN mkdir -p /workspace && \
|
||||||
|
git clone https://github.com/WGDashboard/amneziawg-tools /workspace/awg-tools
|
||||||
|
|
||||||
|
# Change directory
|
||||||
|
WORKDIR /workspace/awg-tools/src
|
||||||
|
# Compile and change permissions
|
||||||
|
RUN make && chmod +x wg*
|
||||||
|
|
||||||
|
#
|
||||||
|
# PIP DEPENDENCY BUILDING
|
||||||
|
# Base: Alpine
|
||||||
|
#
|
||||||
|
|
||||||
|
# Use the python-alpine image for building pip dependencies
|
||||||
|
FROM python:3.14-alpine3.23 AS pip-builder
|
||||||
|
|
||||||
|
ARG TARGETPLATFORM
|
||||||
|
|
||||||
|
# Add the build dependencies and create a Python virtual environment.
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
build-base \
|
||||||
|
pkgconfig \
|
||||||
|
python3-dev \
|
||||||
|
postgresql-dev \
|
||||||
|
libffi-dev \
|
||||||
|
libpq \
|
||||||
|
linux-headers \
|
||||||
|
rust \
|
||||||
|
cargo \
|
||||||
|
&& mkdir -p /opt/wgdashboard/src \
|
||||||
|
&& python3 -m venv /opt/wgdashboard/src/venv
|
||||||
|
|
||||||
|
# Copy the requirements file into the build layer.
|
||||||
|
COPY ./src/requirements.txt /opt/wgdashboard/src
|
||||||
|
RUN if [ "$TARGETPLATFORM" = "linux/arm/v7" ]; then \
|
||||||
|
sed -i 's|psycopg\[binary\]|psycopg[c]|' /opt/wgdashboard/src/requirements.txt; \
|
||||||
|
fi; \
|
||||||
|
cat /opt/wgdashboard/src/requirements.txt
|
||||||
|
|
||||||
|
# Install the pip packages
|
||||||
|
RUN . /opt/wgdashboard/src/venv/bin/activate && \
|
||||||
|
pip3 install --upgrade pip && \
|
||||||
|
pip3 install -r /opt/wgdashboard/src/requirements.txt
|
||||||
|
|
||||||
|
#
|
||||||
|
# WGDashboard RUNNING STAGE
|
||||||
|
# Base: Alpine
|
||||||
|
#
|
||||||
|
|
||||||
|
# Running with the python-alpine image.
|
||||||
|
FROM python:3.14-alpine3.23 AS final
|
||||||
|
LABEL maintainer="dselen@nerthus.nl"
|
||||||
|
|
||||||
|
# Install only the runtime dependencies
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
iproute2 iptables \
|
||||||
|
bash curl procps openrc \
|
||||||
|
tzdata wireguard-tools envsubst
|
||||||
|
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||||
|
|
||||||
|
# Copy only the final binaries from the AWG builder stages
|
||||||
|
COPY --from=awg-go /usr/bin/amneziawg-go /usr/bin/amneziawg-go
|
||||||
|
COPY --from=awg-tools /workspace/awg-tools/src/wg /usr/bin/awg
|
||||||
|
COPY --from=awg-tools /workspace/awg-tools/src/wg-quick/linux.bash /usr/bin/awg-quick
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
ARG wg_net="10.0.0.1"
|
||||||
|
ARG wg_subn="24"
|
||||||
|
ARG wg_port="51820"
|
||||||
|
ENV TZ="Europe/Amsterdam" \
|
||||||
|
global_dns="9.9.9.9" \
|
||||||
|
wgd_port="10086" \
|
||||||
|
public_ip="" \
|
||||||
|
WGDASH=/opt/wgdashboard
|
||||||
|
|
||||||
|
# Create directories needed for operation
|
||||||
|
RUN mkdir /data /configs -p ${WGDASH}/src /etc/amnezia/amneziawg \
|
||||||
|
&& echo "name_servers=${global_dns}" >> /etc/resolvconf.conf
|
||||||
|
|
||||||
|
# Copy the venv and source files from local compiled locations or repos
|
||||||
|
COPY ./src ${WGDASH}/src
|
||||||
|
COPY --from=pip-builder /opt/wgdashboard/src/venv /opt/wgdashboard/src/venv
|
||||||
|
COPY ./docker/templates/wg0.conf /tmp/wg0.conf.template
|
||||||
|
COPY ./docker/templates/wgdashboard-oidc-providers.json /tmp/wg-dashboard-oidc-providers.json.template
|
||||||
|
# Copy in the runtime script, essential.
|
||||||
|
COPY ./docker/entrypoint.sh /entrypoint.sh
|
||||||
|
|
||||||
|
# First WireGuard interface template
|
||||||
|
RUN export out_adapt=$(ip -o -4 route show to default | awk '{print $NF}') \
|
||||||
|
&& envsubst < /tmp/wg0.conf.template > /configs/wg0.conf.template \
|
||||||
|
&& chmod 600 /configs/wg0.conf.template \
|
||||||
|
&& cat /configs/wg0.conf.template
|
||||||
|
|
||||||
|
# Set a healthcheck to determine the container its health
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||||
|
CMD sh -c 'pgrep gunicorn > /dev/null && pgrep tail > /dev/null' || exit 1
|
||||||
|
|
||||||
|
# Expose ports on the container
|
||||||
|
EXPOSE 10086
|
||||||
|
WORKDIR $WGDASH/src
|
||||||
|
|
||||||
|
ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]
|
||||||
@@ -0,0 +1,217 @@
|
|||||||
|
# WGDashboard Docker Explanation:
|
||||||
|
Author: @DaanSelen<br>
|
||||||
|
|
||||||
|
This document delves into how the WGDashboard Docker container has been built.<br>
|
||||||
|
Of course there are two stages (simply said), 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 the container is started. <br>
|
||||||
|
In this example, [WireGuard](https://www.wireguard.com/) is integrated into the container itself, so it should be a run-and-go(/out-of-the-box) experience.<br>
|
||||||
|
For more details on the source-code specific to this Docker image, refer to the source files, they have lots of comments.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<img
|
||||||
|
src="https://wgdashboard-resources.tor1.cdn.digitaloceanspaces.com/Logos/Logo-2-Rounded-512x512.png"
|
||||||
|
alt="WG-Dashboard Logo"
|
||||||
|
title="WG-Dashboard Logo"
|
||||||
|
style="display: block; margin: 0 auto;"
|
||||||
|
width="150"
|
||||||
|
height="150"
|
||||||
|
/>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
To get the container running you either pull the pre-made image from a remote repository, there are 2 official options.<br>
|
||||||
|
|
||||||
|
- ghcr.io/wgdashboard/wgdashboard:<tag>
|
||||||
|
- docker.io/donaldzou/wgdashboard:<tag>
|
||||||
|
|
||||||
|
> tags should be either: latest, main, <version>, <branch-name> (if built) or <commit-sha>.
|
||||||
|
|
||||||
|
From there either use the environment variables described below as parameters or use the Docker Compose file: `compose.yaml`.<br>
|
||||||
|
Be careful, the default generated WireGuard configuration file uses port 51820/udp. So make sure to use this port if you want to use it out of the box.<br>
|
||||||
|
Otherwise edit the configuration file in WGDashboard under `Configuration Settings` -> `Edit Raw Configuration File`.
|
||||||
|
|
||||||
|
> Otherwise you need to enter the container and edit: `/etc/wireguard/wg0.conf`.
|
||||||
|
|
||||||
|
# WGDashboard: 🐳 Docker Deployment Guide
|
||||||
|
|
||||||
|
To run the container, you can either pull the image from the Github Container Registry (ghcr.io), Docker Hub (docker.io) or build it yourself. The image is available at:
|
||||||
|
|
||||||
|
> `docker.io` is in most cases automatically resolved by the Docker application. Therefor you can ofter specify: `donaldzou/wgdashboard:latest`
|
||||||
|
|
||||||
|
### 🔧 Quick Docker Run Command
|
||||||
|
|
||||||
|
Here's an example to get it up and running quickly:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
--name wgdashboard \
|
||||||
|
--restart unless-stopped \
|
||||||
|
-p 10086:10086/tcp \
|
||||||
|
-p 51820:51820/udp \
|
||||||
|
--cap-add NET_ADMIN \
|
||||||
|
ghcr.io/wgdashboard/wgdashboard:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
> ⚠️ The default WireGuard port is `51820/udp`. If you change this, update the `/etc/wireguard/wg0.conf` accordingly.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 📦 Docker Compose Alternative (see the [compose file](./compose.yaml))
|
||||||
|
|
||||||
|
You can also use Docker Compose for easier configuration:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
wgdashboard:
|
||||||
|
image: ghcr.io/wgdashboard/wgdashboard:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
container_name: wgdashboard
|
||||||
|
ports:
|
||||||
|
- 10086:10086/tcp
|
||||||
|
- 51820:51820/udp
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- aconf:/etc/amnezia/amneziawg
|
||||||
|
- conf:/etc/wireguard
|
||||||
|
- data:/data
|
||||||
|
|
||||||
|
cap_add:
|
||||||
|
- NET_ADMIN
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
aconf:
|
||||||
|
conf:
|
||||||
|
data:
|
||||||
|
```
|
||||||
|
|
||||||
|
> 📁 You can customize the **volume paths** on the host to fit your needs. The example above uses Docker volumes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Updating the Container
|
||||||
|
|
||||||
|
Updating the WGDashboard container should be through 'The Docker Way' - by pulling the newest/newer image and replacing this old one.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ Environment Variables
|
||||||
|
|
||||||
|
| Variable | Accepted Values | Default | Example | Description |
|
||||||
|
| ------------------ | ---------------------------------------- | ----------------------- | --------------------- | ----------------------------------------------------------------------- |
|
||||||
|
| `tz` | Timezone | `Europe/Amsterdam` | `America/New_York` | Sets the container's timezone. Useful for accurate logs and scheduling. |
|
||||||
|
| `global_dns` | IPv4 and IPv6 addresses | `9.9.9.9` | `8.8.8.8`, `1.1.1.1` | Default DNS for WireGuard clients. |
|
||||||
|
| `public_ip` | Public IP address | Retrieved automatically | `253.162.134.73` | Used to generate accurate client configs. Needed if container is NAT’d. |
|
||||||
|
| `wgd_port` | Any port that is allowed for the process | `10086` | `443` | This port is used to set the WGDashboard web port. |
|
||||||
|
| `username` | Any non‐empty string | `-` | `admin` | Username for the WGDashboard web interface account. |
|
||||||
|
| `password` | Any non‐empty string | `-` | `s3cr3tP@ss` | Password for the WGDashboard web interface account (stored hashed). |
|
||||||
|
| `enable_totp` | `true`, `false` | `true` | `false` | Enable TOTP‐based two‐factor authentication for the account. |
|
||||||
|
| `wg_autostart` | Wireguard interface name | `false` | `true` | Auto‐start the WireGuard client when the container launches. |
|
||||||
|
| `email_server` | SMTP server address | `-` | `smtp.gmail.com` | SMTP server for sending email notifications. |
|
||||||
|
| `email_port` | SMTP port number | `-` | `587` | Port for connecting to the SMTP server. |
|
||||||
|
| `email_encryption` | `TLS`, `SSL`, etc. | `-` | `TLS` | Encryption method for email communication. |
|
||||||
|
| `email_username` | Any non-empty string | `-` | `user@example.com` | Username for SMTP authentication. |
|
||||||
|
| `email_password` | Any non-empty string | `-` | `app_password` | Password for SMTP authentication. |
|
||||||
|
| `email_from` | Valid email address | `-` | `noreply@example.com` | Email address used as the sender for notifications. |
|
||||||
|
| `email_template` | Path to template file | `-` | `your-template` | Custom template for email notifications. |
|
||||||
|
| `database_type` | `sqlite`, `postgresql`, `mariadb+mariadbconnector`, etc. | `-` | `postgresql` | Type of [sqlalchemy database engine](https://docs.sqlalchemy.org/en/21/core/engines.html). |
|
||||||
|
| `database_host` | Any non-empty string | `-` | `localhost` | IP-Address or hostname of the SQL-database server. |
|
||||||
|
| `database_port` | Any non-empty string (or int for port) | `-` | `5432` | Port for the database communication. |
|
||||||
|
| `database_username`| Valid database username | `-` | `database_user` | Database user username. |
|
||||||
|
| `database_password`| Valid database password | `-` | `database_password` | Database user password. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔐 Port Forwarding Note
|
||||||
|
|
||||||
|
When using multiple WireGuard interfaces, remember to **open their respective ports** on the host.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
```yaml
|
||||||
|
# Individual mapping
|
||||||
|
- 51821:51821/udp
|
||||||
|
|
||||||
|
# Or port range
|
||||||
|
- 51820-51830:51820-51830/udp
|
||||||
|
```
|
||||||
|
|
||||||
|
> 🚨 **Security Tip:** Only expose ports you actually use.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ Building the Image Yourself
|
||||||
|
|
||||||
|
To build from source:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/WGDashboard/WGDashboard.git
|
||||||
|
cd WGDashboard
|
||||||
|
docker build . -f docker/Dockerfile -t yourname/wgdashboard:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
Example output:
|
||||||
|
```shell
|
||||||
|
docker images
|
||||||
|
|
||||||
|
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||||
|
yourname/wgdashboard latest c96fd96ee3b3 42 minutes ago 314MB
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧱 Dockerfile Overview
|
||||||
|
|
||||||
|
Here's a brief overview of the Dockerfile stages used in the image build:
|
||||||
|
|
||||||
|
### 1. **Build Tools & Go Compilation**
|
||||||
|
|
||||||
|
```Dockerfile
|
||||||
|
FROM golang:1.24 AS compiler
|
||||||
|
WORKDIR /go
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y ...
|
||||||
|
RUN git clone ... && make
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. **Binary Copy to Scratch**
|
||||||
|
|
||||||
|
```Dockerfile
|
||||||
|
FROM scratch AS bins
|
||||||
|
COPY --from=compiler /go/amneziawg-go/amneziawg-go /amneziawg-go
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. **Final Alpine Container Setup**
|
||||||
|
|
||||||
|
```Dockerfile
|
||||||
|
FROM alpine:latest
|
||||||
|
COPY --from=bins ...
|
||||||
|
RUN apk update && apk add --no-cache ...
|
||||||
|
COPY ./src ${WGDASH}/src
|
||||||
|
COPY ./docker/entrypoint.sh /entrypoint.sh
|
||||||
|
...
|
||||||
|
EXPOSE 10086
|
||||||
|
ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Entrypoint Overview
|
||||||
|
|
||||||
|
### Major Functions:
|
||||||
|
|
||||||
|
- **`ensure_installation`**: Sets up the app, database, and Python environment.
|
||||||
|
- **`set_envvars`**: Writes `wg-dashboard.ini` and applies environment variables.
|
||||||
|
- **`start_core`**: Starts the main WGDashboard service.
|
||||||
|
- **`ensure_blocking`**: Tails the error log to keep the container process alive.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Final Notes
|
||||||
|
|
||||||
|
- Use `docker logs wgdashboard` for troubleshooting.
|
||||||
|
- Access the web interface via `http://your-ip:10086` (or whichever port you specified in the compose).
|
||||||
|
- The first time run will auto-generate WireGuard keys and configs (configs are generated from the template).
|
||||||
|
|
||||||
|
## Closing remarks:
|
||||||
|
|
||||||
|
For feedback please submit an issue to the repository. Or message dselen@nerthus.nl.
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
services:
|
||||||
|
wgdashboard:
|
||||||
|
# Since the github organisation we recommend the ghcr.io.
|
||||||
|
# Alternatively we also still push to docker.io under donaldzou/wgdashboard.
|
||||||
|
# Both share the exact same tags. So they should be interchangable.
|
||||||
|
image: ghcr.io/wgdashboard/wgdashboard:v4.3.2-dev
|
||||||
|
|
||||||
|
# Make sure to set the restart policy. Because for a VPN its important to come back IF it crashes.
|
||||||
|
restart: unless-stopped
|
||||||
|
container_name: wgdashboard
|
||||||
|
|
||||||
|
# Environment variables can be used to configure certain values at startup. Without having to configure it from the dashboard.
|
||||||
|
# By default its all disabled, but uncomment the following lines to apply these. (uncommenting is removing the # character)
|
||||||
|
# Refer to the documentation on https://wgdashboard.dev/ for more info on what everything means.
|
||||||
|
#environment:
|
||||||
|
#- wg_autostart=wg0
|
||||||
|
#- tz= # <--- Set container timezone, default: Europe/Amsterdam.
|
||||||
|
#- public_ip= # <--- Set public IP to ensure the correct one is chosen, defaulting to the IP give by ifconfig.me.
|
||||||
|
#- wgd_port= # <--- Set the port WGDashboard will use for its web-server.
|
||||||
|
|
||||||
|
# The following section, ports is very important for exposing more than one Wireguard/AmneziaWireguard interfaces.
|
||||||
|
# Once you create a new configuration and assign a port in the dashboard, don't forget to add it to the ports as well.
|
||||||
|
# Quick-tip: most Wireguard VPN tunnels use UDP. WGDashboard uses HTTP, so tcp.
|
||||||
|
ports:
|
||||||
|
- 10087:10086/tcp
|
||||||
|
- 51820:51820/udp
|
||||||
|
|
||||||
|
# Volumes can be configured however you'd like. The default is using docker volumes.
|
||||||
|
# If you want to use local paths, replace the path before the : with your path.
|
||||||
|
volumes:
|
||||||
|
- aconf:/etc/amnezia/amneziawg
|
||||||
|
- conf:/etc/wireguard
|
||||||
|
- data:/data
|
||||||
|
|
||||||
|
# Needed for network administration.
|
||||||
|
cap_add:
|
||||||
|
- NET_ADMIN
|
||||||
|
|
||||||
|
# The following configuration is linked to the above default volumes.
|
||||||
|
volumes:
|
||||||
|
aconf:
|
||||||
|
conf:
|
||||||
|
data:
|
||||||
@@ -0,0 +1,329 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
config_file="/data/wg-dashboard.ini"
|
||||||
|
runtime_pid=""
|
||||||
|
|
||||||
|
trap 'stop_service' SIGTERM
|
||||||
|
|
||||||
|
# Hash password with bcrypt
|
||||||
|
hash_password() {
|
||||||
|
${WGDASH}/src/venv/bin/python3 -c "import bcrypt; print(bcrypt.hashpw('$1'.encode(), bcrypt.gensalt(12)).decode())"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to set or update section/key/value in the INI file
|
||||||
|
set_ini() {
|
||||||
|
local section="$1" key="$2" value="$3"
|
||||||
|
local current_value
|
||||||
|
|
||||||
|
# Add section if it doesn't exist
|
||||||
|
grep -q "^\[${section}\]" "$config_file" \
|
||||||
|
|| printf "\n[%s]\n" "${section}" >> "$config_file"
|
||||||
|
|
||||||
|
# Check current value if key exists
|
||||||
|
if grep -q "^[[:space:]]*${key}[[:space:]]*=" "$config_file"; then
|
||||||
|
current_value=$(grep "^[[:space:]]*${key}[[:space:]]*=" "$config_file" | cut -d= -f2- | xargs)
|
||||||
|
|
||||||
|
# Dont display actual value if it's a password field
|
||||||
|
if [[ "$key" == *"password"* ]]; then
|
||||||
|
if [ "$current_value" = "$value" ]; then
|
||||||
|
echo "- $key is already set correctly (value hidden)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
sed -i "/^\[${section}\]/,/^\[/{s|^[[:space:]]*${key}[[:space:]]*=.*|${key} = ${value}|}" "$config_file"
|
||||||
|
echo "- Updated $key (value hidden)"
|
||||||
|
else
|
||||||
|
if [ "$current_value" = "$value" ]; then
|
||||||
|
echo "- $key is already set correctly ($value)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
sed -i "/^\[${section}\]/,/^\[/{s|^[[:space:]]*${key}[[:space:]]*=.*|${key} = ${value}|}" "$config_file"
|
||||||
|
echo "- Updated $key to: $value"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
sed -i "/^\[${section}\]/a ${key} = ${value}" "$config_file"
|
||||||
|
|
||||||
|
# Don't display actual value if it's a password field
|
||||||
|
if [[ "$key" == *"password"* ]]; then
|
||||||
|
echo "- Added new setting $key (value hidden)"
|
||||||
|
else
|
||||||
|
echo "- Added new setting $key: $value"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_service() {
|
||||||
|
echo "[WGDashboard] Stopping WGDashboard..."
|
||||||
|
|
||||||
|
local max_rounds="10"
|
||||||
|
local round="0"
|
||||||
|
local runtime_pid=""
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
round=$((round + 1))
|
||||||
|
|
||||||
|
if [[ -f ${WGDASH}/src/gunicorn.pid ]]; then
|
||||||
|
runtime_pid=$(cat ${WGDASH}/src/gunicorn.pid)
|
||||||
|
|
||||||
|
echo "Running as PID: ${runtime_pid}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $round -eq $max_rounds ]]; then
|
||||||
|
echo "Reached breaking point!"
|
||||||
|
return 1
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 0.5s
|
||||||
|
done
|
||||||
|
|
||||||
|
kill $runtime_pid
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "------------------------- START ----------------------------"
|
||||||
|
echo "Starting the WGDashboard Docker container."
|
||||||
|
|
||||||
|
ensure_installation() {
|
||||||
|
echo "Quick-installing..."
|
||||||
|
|
||||||
|
# Make the wgd.sh script executable.
|
||||||
|
chmod +x "${WGDASH}"/src/wgd.sh
|
||||||
|
cd "${WGDASH}"/src || exit
|
||||||
|
|
||||||
|
# Github issue: https://github.com/donaldzou/WGDashboard/issues/723
|
||||||
|
echo "Checking for stale pids..."
|
||||||
|
if [[ -f ${WGDASH}/src/gunicorn.pid ]]; then
|
||||||
|
echo "Found stale pid, removing..."
|
||||||
|
rm ${WGDASH}/src/gunicorn.pid
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Removing clear shell command from the wgd.sh script to enhance docker logging.
|
||||||
|
echo "Removing clear command from wgd.sh for better Docker logging."
|
||||||
|
sed -i '/clear/d' ./wgd.sh
|
||||||
|
|
||||||
|
# PERSISTENCE FOR databases directory
|
||||||
|
# Create required directories and links
|
||||||
|
if [ ! -d "/data/db" ]; then
|
||||||
|
echo "Creating database dir"
|
||||||
|
mkdir -p /data/db
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -L "${WGDASH}/src/db" ]] && [[ -d "${WGDASH}/src/db" ]]; then
|
||||||
|
echo "Removing ${WGDASH}/src/db since its not a symbolic link."
|
||||||
|
rm -rfv "${WGDASH}/src/db"
|
||||||
|
fi
|
||||||
|
if [[ -L "${WGDASH}/src/db" ]]; then
|
||||||
|
echo "${WGDASH}/src/db is a symbolic link."
|
||||||
|
else
|
||||||
|
ln -sv /data/db "${WGDASH}/src/db"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# PERSISTENCE FOR wg-dashboard-oidc-providers.json
|
||||||
|
if [ ! -f "/data/wg-dashboard-oidc-providers.json" ]; then
|
||||||
|
echo "Creating wg-dashboard-oidc-providers.json file"
|
||||||
|
cp -v /tmp/wg-dashboard-oidc-providers.json.template /data/wg-dashboard-oidc-providers.json
|
||||||
|
fi
|
||||||
|
if [[ ! -L "${WGDASH}/src/wg-dashboard-oidc-providers.json" ]] && [[ -f "${WGDASH}/src/wg-dashboard-oidc-providers.json" ]]; then
|
||||||
|
echo "Removing ${WGDASH}/src/wg-dashboard-oidc-providers.json since its not a symbolic link."
|
||||||
|
rm -fv "${WGDASH}/src/wg-dashboard-oidc-providers.json"
|
||||||
|
fi
|
||||||
|
if [[ -L "${WGDASH}/src/wg-dashboard-oidc-providers.json" ]]; then
|
||||||
|
echo "${WGDASH}/src/wg-dashboard-oidc-providers.json is a symbolic link."
|
||||||
|
else
|
||||||
|
ln -sv /data/wg-dashboard-oidc-providers.json "${WGDASH}/src/wg-dashboard-oidc-providers.json"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# PERSISTENCE FOR wg-dashboard.ini
|
||||||
|
if [ ! -f "${config_file}" ]; then
|
||||||
|
echo "Creating wg-dashboard.ini file"
|
||||||
|
touch "${config_file}"
|
||||||
|
fi
|
||||||
|
if [[ ! -L "${WGDASH}/src/wg-dashboard.ini" ]] && [[ -f "${WGDASH}/src/wg-dashboard.ini" ]]; then
|
||||||
|
echo "Removing ${WGDASH}/src/wg-dashboard.ini since its not a symbolic link."
|
||||||
|
rm -fv "${WGDASH}/src/wg-dashboard.ini"
|
||||||
|
fi
|
||||||
|
if [[ -L "${WGDASH}/src/wg-dashboard.ini" ]]; then
|
||||||
|
echo "${WGDASH}/src/wg-dashboard.ini is a symbolic link."
|
||||||
|
else
|
||||||
|
ln -sv "${config_file}" "${WGDASH}/src/wg-dashboard.ini"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Setup WireGuard if needed
|
||||||
|
if [ -z "$(ls -A /etc/wireguard)" ]; then
|
||||||
|
cp -a "/configs/wg0.conf.template" "/etc/wireguard/wg0.conf"
|
||||||
|
|
||||||
|
echo "Setting a secure private key."
|
||||||
|
local privateKey
|
||||||
|
privateKey=$(wg genkey)
|
||||||
|
sed -i "s|^PrivateKey *=.*$|PrivateKey = ${privateKey}|g" /etc/wireguard/wg0.conf
|
||||||
|
|
||||||
|
echo "Done setting template."
|
||||||
|
else
|
||||||
|
echo "Existing wg0 configuration file found, using that."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
set_envvars() {
|
||||||
|
printf "\n------------- SETTING ENVIRONMENT VARIABLES ----------------\n"
|
||||||
|
|
||||||
|
# Check if config file is empty
|
||||||
|
if [ ! -s "${config_file}" ]; then
|
||||||
|
echo "Config file is empty. Creating initial structure."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Checking basic configuration:"
|
||||||
|
set_ini Peers peer_global_dns "${global_dns}"
|
||||||
|
|
||||||
|
if [ -z "${public_ip}" ]; then
|
||||||
|
public_ip=$(curl -s https://ifconfig.me)
|
||||||
|
if [ -z "${public_ip}" ]; then
|
||||||
|
echo "Using fallback public IP resolution website"
|
||||||
|
public_ip=$(curl -s https://api.ipify.org)
|
||||||
|
fi
|
||||||
|
if [ -z "${public_ip}" ]; then
|
||||||
|
echo "Failed to resolve publicly. Using private address."
|
||||||
|
public_ip=$(hostname -i)
|
||||||
|
fi
|
||||||
|
echo "Automatically detected public IP: ${public_ip}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
set_ini Peers remote_endpoint "${public_ip}"
|
||||||
|
set_ini Server app_port "${wgd_port}"
|
||||||
|
|
||||||
|
# Account settings - process all parameters
|
||||||
|
[[ -n "$username" ]] && echo "Configuring user account:"
|
||||||
|
# Basic account variables
|
||||||
|
[[ -n "$username" ]] && set_ini Account username "${username}"
|
||||||
|
|
||||||
|
if [[ -n "$password" ]]; then
|
||||||
|
echo "- Setting password"
|
||||||
|
set_ini Account password "$(hash_password "${password}")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Additional account variables
|
||||||
|
[[ -n "$enable_totp" ]] && set_ini Account enable_totp "${enable_totp}"
|
||||||
|
[[ -n "$totp_verified" ]] && set_ini Account totp_verified "${totp_verified}"
|
||||||
|
[[ -n "$totp_key" ]] && set_ini Account totp_key "${totp_key}"
|
||||||
|
|
||||||
|
# Welcome session
|
||||||
|
[[ -n "$welcome_session" ]] && set_ini Other welcome_session "${welcome_session}"
|
||||||
|
# If username and password are set but welcome_session isn't, disable it
|
||||||
|
if [[ -n "$username" && -n "$password" && -z "$welcome_session" ]]; then
|
||||||
|
set_ini Other welcome_session "false"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Autostart WireGuard
|
||||||
|
if [[ -n "$wg_autostart" ]]; then
|
||||||
|
echo "Configuring WireGuard autostart:"
|
||||||
|
set_ini WireGuardConfiguration autostart "${wg_autostart}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Database (check if any settings need to be configured)
|
||||||
|
database_vars=("database_type" "database_host" "database_port" "database_username" "database_password")
|
||||||
|
for var in "${database_vars[@]}"; do
|
||||||
|
if [ -n "${!var}" ]; then
|
||||||
|
echo "Configuring database settings:"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Database (iterate through all possible fields)
|
||||||
|
database_fields=("type:database_type" "host:database_host" "port:database_port"
|
||||||
|
"username:database_username" "password:database_password")
|
||||||
|
|
||||||
|
for field_pair in "${database_fields[@]}"; do
|
||||||
|
IFS=: read -r field var <<< "$field_pair"
|
||||||
|
[[ -n "${!var}" ]] && set_ini Database "$field" "${!var}"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Email (check if any settings need to be configured)
|
||||||
|
email_vars=("email_server" "email_port" "email_encryption" "email_username" "email_password" "email_from" "email_template")
|
||||||
|
for var in "${email_vars[@]}"; do
|
||||||
|
if [ -n "${!var}" ]; then
|
||||||
|
echo "Configuring email settings:"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Email (iterate through all possible fields)
|
||||||
|
email_fields=("server:email_server" "port:email_port" "encryption:email_encryption"
|
||||||
|
"username:email_username" "email_password:email_password"
|
||||||
|
"send_from:email_from" "email_template:email_template")
|
||||||
|
|
||||||
|
for field_pair in "${email_fields[@]}"; do
|
||||||
|
IFS=: read -r field var <<< "$field_pair"
|
||||||
|
[[ -n "${!var}" ]] && set_ini Email "$field" "${!var}"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Start service and monitor logs
|
||||||
|
start_and_monitor() {
|
||||||
|
printf "\n---------------------- STARTING CORE -----------------------\n"
|
||||||
|
|
||||||
|
# Due to resolvconf resetting the DNS we echo back the one we defined (or fallback to default).
|
||||||
|
resolvconf -u
|
||||||
|
|
||||||
|
# Due to some instances complaining about this, making sure its there every time.
|
||||||
|
mkdir -p /dev/net
|
||||||
|
mknod /dev/net/tun c 10 200
|
||||||
|
chmod 600 /dev/net/tun
|
||||||
|
|
||||||
|
# Actually starting WGDashboard
|
||||||
|
echo "Starting WGDashboard directly with Gunicorn..."
|
||||||
|
|
||||||
|
[[ ! -d ${WGDASH}/src/log ]] && mkdir ${WGDASH}/src/log
|
||||||
|
[[ ! -d ${WGDASH}/src/download ]] && mkdir ${WGDASH}/src/download
|
||||||
|
|
||||||
|
${WGDASH}/src/venv/bin/gunicorn --config ${WGDASH}/src/gunicorn.conf.py
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Loading WGDashboard failed... Look above for details."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Wait a second before continuing, to give the python program some time to get ready.
|
||||||
|
echo -e "\nEnsuring container continuation."
|
||||||
|
|
||||||
|
local max_rounds="10"
|
||||||
|
local round="0"
|
||||||
|
|
||||||
|
# Hang in there for 10s for Gunicorn to get ready
|
||||||
|
while true; do
|
||||||
|
round=$((round + 1))
|
||||||
|
|
||||||
|
local latest_error=$(ls -t ${WGDASH}/src/log/error_*.log 2> /dev/null | head -n 1)
|
||||||
|
|
||||||
|
if [[ $round -eq $max_rounds ]]; then
|
||||||
|
echo "Reached breaking point!"
|
||||||
|
break
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z $latest_error ]]; then
|
||||||
|
echo -e "Logs not yet present! Retrying in 1 second!"
|
||||||
|
sleep 1s
|
||||||
|
|
||||||
|
else
|
||||||
|
break
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z $latest_error ]]; then
|
||||||
|
echo -e "No error logs founds... Please investigate.\nExiting in 3 minutes..."
|
||||||
|
sleep 180s
|
||||||
|
exit 1
|
||||||
|
|
||||||
|
else
|
||||||
|
tail -f "$latest_error" &
|
||||||
|
tail_pid=$!
|
||||||
|
|
||||||
|
wait $tail_pid
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main execution flow
|
||||||
|
ensure_installation
|
||||||
|
set_envvars
|
||||||
|
start_and_monitor
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
[Interface]
|
||||||
|
Address = ${wg_net}/24
|
||||||
|
PrivateKey =
|
||||||
|
PostUp = iptables -t nat -I POSTROUTING 1 -s ${wg_net}/24 -o ${out_adapt} -j MASQUERADE; iptables -I FORWARD -i wg0 -o wg0 -j DROP
|
||||||
|
PreDown = iptables -t nat -D POSTROUTING -s ${wg_net}/24 -o ${out_adapt} -j MASQUERADE; iptables -D FORWARD -i wg0 -o wg0 -j DROP
|
||||||
|
ListenPort = ${wg_port}
|
||||||
|
SaveConfig = true
|
||||||
|
DNS = ${global_dns}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"Admin": {
|
||||||
|
"Provider": {
|
||||||
|
"client_id": "",
|
||||||
|
"client_secret": "",
|
||||||
|
"issuer": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Client": {
|
||||||
|
"Provider": {
|
||||||
|
"client_id": "",
|
||||||
|
"client_secret": "",
|
||||||
|
"issuer": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
import {computed, ref} from "vue";
|
import {computed, ref} from "vue";
|
||||||
import ConfigurationQRCode from "@/components/Configuration/configurationQRCode.vue";
|
import ConfigurationQRCode from "@/components/Configuration/configurationQRCode.vue";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import Duration from 'dayjs/plugin/Duration'
|
import duration from 'dayjs/plugin/duration'
|
||||||
dayjs.extend(Duration);
|
dayjs.extend(duration);
|
||||||
const props = defineProps([
|
const props = defineProps([
|
||||||
'config'
|
'config'
|
||||||
])
|
])
|
||||||
@@ -113,4 +113,4 @@ const emits = defineEmits(['select'])
|
|||||||
background-color: #28a745 !important;
|
background-color: #28a745 !important;
|
||||||
box-shadow: 0 0 0 .2rem #28a74545;
|
box-shadow: 0 0 0 .2rem #28a74545;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user