From e586979f9da8a74ca6909fdc42adda4642d4cc11 Mon Sep 17 00:00:00 2001 From: DaanSelen Date: Wed, 4 Mar 2026 14:31:01 +0100 Subject: [PATCH] feat: add docker files and fix dayjs build bug --- docker/Dockerfile | 145 ++++++++ docker/README.md | 217 ++++++++++++ docker/compose.yaml | 43 +++ docker/entrypoint.sh | 329 ++++++++++++++++++ docker/templates/wg0.conf | 8 + .../templates/wgdashboard-oidc-providers.json | 16 + .../Configuration/configuration.vue | 6 +- 7 files changed, 761 insertions(+), 3 deletions(-) create mode 100644 docker/Dockerfile create mode 100644 docker/README.md create mode 100644 docker/compose.yaml create mode 100644 docker/entrypoint.sh create mode 100644 docker/templates/wg0.conf create mode 100644 docker/templates/wgdashboard-oidc-providers.json diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..ad78f13 --- /dev/null +++ b/docker/Dockerfile @@ -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"] diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 0000000..f32e8f4 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,217 @@ +# WGDashboard Docker Explanation: +Author: @DaanSelen
+ +This document delves into how the WGDashboard Docker container has been built.
+Of course there are two stages (simply said), one before run-time and one at/after run-time.
+The `Dockerfile` describes how the container image is made, and the `entrypoint.sh` is executed after the container is started.
+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.
+For more details on the source-code specific to this Docker image, refer to the source files, they have lots of comments. + +
+WG-Dashboard Logo +
+ +To get the container running you either pull the pre-made image from a remote repository, there are 2 official options.
+ +- ghcr.io/wgdashboard/wgdashboard: +- docker.io/donaldzou/wgdashboard: + +> tags should be either: latest, main, , (if built) or . + +From there either use the environment variables described below as parameters or use the Docker Compose file: `compose.yaml`.
+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.
+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. diff --git a/docker/compose.yaml b/docker/compose.yaml new file mode 100644 index 0000000..2c5f37a --- /dev/null +++ b/docker/compose.yaml @@ -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: diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100644 index 0000000..daef69c --- /dev/null +++ b/docker/entrypoint.sh @@ -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 diff --git a/docker/templates/wg0.conf b/docker/templates/wg0.conf new file mode 100644 index 0000000..4d7779c --- /dev/null +++ b/docker/templates/wg0.conf @@ -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} diff --git a/docker/templates/wgdashboard-oidc-providers.json b/docker/templates/wgdashboard-oidc-providers.json new file mode 100644 index 0000000..3764d0d --- /dev/null +++ b/docker/templates/wgdashboard-oidc-providers.json @@ -0,0 +1,16 @@ +{ + "Admin": { + "Provider": { + "client_id": "", + "client_secret": "", + "issuer": "" + } + }, + "Client": { + "Provider": { + "client_id": "", + "client_secret": "", + "issuer": "" + } + } +} diff --git a/src/static/client/src/components/Configuration/configuration.vue b/src/static/client/src/components/Configuration/configuration.vue index f146a5d..29fca2d 100644 --- a/src/static/client/src/components/Configuration/configuration.vue +++ b/src/static/client/src/components/Configuration/configuration.vue @@ -2,8 +2,8 @@ import {computed, ref} from "vue"; import ConfigurationQRCode from "@/components/Configuration/configurationQRCode.vue"; import dayjs from "dayjs"; -import Duration from 'dayjs/plugin/Duration' -dayjs.extend(Duration); +import duration from 'dayjs/plugin/duration' +dayjs.extend(duration); const props = defineProps([ 'config' ]) @@ -113,4 +113,4 @@ const emits = defineEmits(['select']) background-color: #28a745 !important; box-shadow: 0 0 0 .2rem #28a74545; } - \ No newline at end of file +