Merge branch 'donaldzou:main' into main

This commit is contained in:
heiye1024 2024-07-22 09:01:35 +08:00 committed by GitHub
commit 1c267b732b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 565 additions and 58 deletions

1
.github/FUNDING.yml vendored
View file

@ -1,3 +1,4 @@
# These are supported funding model platforms
github: [donaldzou]
patreon: DonaldDonnyZou

70
.github/workflows/codeql-analysis.yml vendored Normal file
View file

@ -0,0 +1,70 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ main ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]
schedule:
- cron: '30 5 * * 4'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript', 'python' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://git.io/codeql-language-support
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

6
.gitignore vendored
View file

@ -1,5 +1,4 @@
.vscode/sftp.json
src/.vscode/sftp.json
.vscode
.DS_Store
.idea
src/db
@ -15,4 +14,5 @@ venv/**
log/**
release/*
src/db/wgdashboard.db
.jshintrc
.jshintrc
node_modules/

View file

@ -1,3 +1,19 @@
### Development on v4
Hi Everyone, I've decided to promote the long time working `v3.1` to `v4`! This version will be a huge update on the structure of the app. The frontend will be completely handle on the client side with Vue.js, and communicate with the server via REST Api. With this design, it could be possible for the frontend app to connect to multiple server in the future. Stay tune to this new verion, I'm working very hard on this while balancing school and work. If you would like know whats going on, you can check out [this branch](https://github.com/donaldzou/WGDashboard/tree/v4) 😊
> For more information, you can visit: https://github.com/users/donaldzou/projects/2
#### A preview on v4.0 🥹
![v4.0 Preview](https://raw.githubusercontent.com/donaldzou/WGDashboard/main/img/ScreenRecording2024-03-31at12.47.43AM-ezgif.com-video-to-gif-converter.gif)
<hr>
##### Known issue on WGDashboard `v3.0 - v3.0.6`
- [IPv6 in WireGuard might not fully support.](https://github.com/donaldzou/WGDashboard/issues/167)
<hr>
<p align="center">
<img alt="WGDashboard" src="img/logo.png" width="128">
</p>
@ -9,7 +25,8 @@
</p>
<p align="center">
<a href="https://github.com/donaldzou/wireguard-dashboard/releases/latest"><img src="https://img.shields.io/github/v/release/donaldzou/wireguard-dashboard"></a>
<a href="https://wakatime.com/badge/user/45f53c7c-9da9-4cb0-85d6-17bd38cc748b/project/5334ae20-e9a6-4c55-9fea-52d4eb9dfba6"><img src="https://wakatime.com/badge/user/45f53c7c-9da9-4cb0-85d6-17bd38cc748b/project/5334ae20-e9a6-4c55-9fea-52d4eb9dfba6.svg" alt="wakatime"></a>
<a href="https://wakatime.com/badge/github/donaldzou/WGDashboard"><img src="https://wakatime.com/badge/github/donaldzou/WGDashboard.svg" alt="wakatime"></a>
<a href="https://hits.seeyoufarm.com"><img src="https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Fdonaldzou%2FWGDashboard&count_bg=%2379C83D&title_bg=%23555555&icon=github.svg&icon_color=%23E7E7E7&title=Visitor&edge_flat=false"/></a>
</p>
<p align="center">Monitoring WireGuard is not convinient, need to login into server and type <code>wg show</code>. That's why this platform is being created, to view all configurations and manage them in a easier way.</p>
<p align="center"><small>Note: This project is not affiliate to the official WireGuard Project ;)</small></p>
@ -90,7 +107,8 @@
- **WireGuard** and **WireGuard-Tools (`wg-quick`)** are installed.
> Don't know how? Check this <a href="https://www.wireguard.com/install/">official documentation</a>
- Net Tools (`net-tools`) is installed.
- You can verify this by checking if `ifconfig` return a list of network interface. Such as `eth0`
- Configuration files under **`/etc/wireguard`**, but please note the following sample
```ini
@ -115,7 +133,7 @@
1. Download WGDashboard
```shell
git clone -b v3.0.5 https://github.com/donaldzou/WGDashboard.git wgdashboard
git clone -b v3.0.6.2 https://github.com/donaldzou/WGDashboard.git wgdashboard
2. Open the WGDashboard folder
@ -301,7 +319,7 @@ Since version 2.0, WGDashboard will be using a configuration file called `wg-das
| `app_ip` | IP address the dashboard will run with | `0.0.0.0` | Yes |
| `app_port` | Port the the dashboard will run with | `10086` | Yes |
| `auth_req` | Does the dashboard need authentication to access, if `auth_req = false` , user will not be access the **Setting** tab due to security consideration. **User can only edit the file directly in system**. | `true` | **No** |
| `version` | Dashboard Version | `v3.0.5` | **No** |
| `version` | Dashboard Version | `v3.0.6` | **No** |
| `dashboard_refresh_interval` | How frequent the dashboard will refresh on the configuration page | `60000ms` | Yes |
| `dashboard_sort` | How configuration is sorting | `status` | Yes |
| | | | |
@ -360,6 +378,7 @@ Endpoint = 0.0.0.0:51820
```bash
./wgd.sh update
chmod +x ./wgd.sh
```
> If this doesn't work, please use the method below. Sorry about that :(
@ -375,7 +394,7 @@ Endpoint = 0.0.0.0:51820
2. Update the dashboard
```shell
git pull https://github.com/donaldzou/WGDashboard.git v3.0.5 --force
git pull https://github.com/donaldzou/WGDashboard.git v3.0.6 --force
```
3. Install
@ -420,10 +439,58 @@ Starting with `v3.0`, you can simply do `./wgd.sh update` !! (I hope, lol)
## ⏰ Changelog
#### v3.0.6 - Mar 22, 2022
**Fixed Bug**
- When wgdashboard is running behind a proxy server, redirecting could cause using http while proxy is using https [❤️ from #161]
#### v3.0.5 - Jan 31, 2022
**Quick Fix**
- Fixed public key does not match when user used an existing private key
- Sorry for the wrong version number that causing the dashboard ask for update after updating.
#### v3.0.3 - Jan 23, 2022
- Fixed when dashboard configuration file cannot be found after a fresh install. [❤️ from #132 ]
#### v3.0 - Jan 18, 2022
- 🎉 **New Features**
- **Moved from TinyDB to SQLite**: SQLite provide a better performance and loading speed when getting peers! Also avoided crashing the database due to **race condition**.
- **Added Gunicorn WSGI Server**: This could provide more stable on handling HTTP request, and more flexibility in the future (such as HTTPS support). **BIG THANKS to @pgalonza :heart:**
- **Add Peers by Bulk:** User can add peers by bulk, just simply set the amount and click add.
- **Delete Peers by Bulk**: User can delete peers by bulk, without deleting peers one by one.
- **Download Peers in Zip**: User can download all *downloadable* peers in a zip.
- **Added Pre-shared Key to peers:** Now each peer can add with a pre-shared key to enhance security. Previously added peers can add the pre-shared key through the peer setting button.
- **Redirect Back to Previous Page:** The dashboard will now redirect you back to your previous page if the current session got timed out and you need to sign in again.
- **Added Some [🥘 Experimental Functions](https://github.com/donaldzou/WGDashboard#-experimental-functions)**
- 🪚 **Bug Fixed**
- [IP Sorting range issues #99](https://github.com/donaldzou/WGDashboard/issues/99) [❤️ @barryboom]
- [INvalid character written to tunnel json file #108](https://github.com/donaldzou/WGDashboard/issues/108) [❤️ @ikidd]
- [Add IPv6 #91](https://github.com/donaldzou/WGDashboard/pull/91) [❤️ @pgalonza]
- [Added MTU and PersistentKeepalive to QR code and download files #112](https://github.com/donaldzou/WGDashboard/pull/112) [:heart: @reafian]
- **And many other bugs provided by our beloved users** :heart:
- **🧐 Other Changes**
- **Key generating moved to front-end**: No longer need to use the server's WireGuard to generate keys, thanks to the `wireguard.js` from the [official repository](https://git.zx2c4.com/wireguard-tools/tree/contrib/keygen-html/wireguard.js)!
- **Peer transfer calculation**: each peer will now show all transfer amount (previously was only showing transfer amount from the last configuration start-up).
- **UI adjustment on running peers**: peers will have a new style indicating that it is running.
- **`wgd.sh` finally can update itself**: So now user could update the whole dashboard from `wgd.sh`, with the `update` command.
- **Minified JS and CSS files**: Although only a small changes on the file size, but I think is still a good practice to save a bit of bandwidth ;)
*And many other small changes for performance and bug fixes! :laughing:*
> If you have any other brilliant ideas for this project, please shout it in here [#129](https://github.com/donaldzou/WGDashboard/issues/129) :heart:
**For users who is using `v2.x.x` please be sure to read [this](https://github.com/donaldzou/WGDashboard#please-note-for-user-who-is-using-v231-or-below) before updating WGDashboard ;)**
#### v2.3.1 - Sep 8, 2021
- Updated dashboard's name to **WGDashboard**!!
#### v2.3 - Sep 8, 2021
- 🎉 **New Features**
@ -505,7 +572,8 @@ Bug Fixed:
- [jQuery](https://jquery.com) `v3.5.1`
- Python
- [Flask](https://pypi.org/project/Flask/) `v2.0.1`
- [ifcfg](https://pypi.org/project/ifcfg/) `v0.21`
- [ifcfg](https://pypi.org/project/ifcfg/) `v0.24`
- [psutil](https://pypi.org/project/psutil/) `v5.9.8`
- [icmplib](https://pypi.org/project/icmplib/) `v2.1.1`
- [flask-qrcode](https://pypi.org/project/Flask-QRcode/) `v3.0.0`

10
SECURITY.md Normal file
View file

@ -0,0 +1,10 @@
# Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| 5.1.x | :white_check_mark: |
| 5.0.x | :x: |
| 4.0.x | :white_check_mark: |
| < 4.0 | :x: |

82
docker/Docker-explain.md Normal file
View file

@ -0,0 +1,82 @@
# WG-Dashboard Docker Explanation:
Author: DaanSelen<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.
I have tried to embed some new features such as `isolated_peers` and interface startup on container-start (through `enable_wg0`).
<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>
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 \
--restart unless-stopped \
-e enable_wg0=true \
-e isolated_peers=true \
-p 10086:10086/tcp \
-p 51820:51820/udp \
--cap-add NET_ADMIN \
repo.nerthus.nl/app/wireguard-dashboard:latest
```
<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
volumes:
conf:
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.
## Working with the container and environment variables:
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.

77
docker/Dockerfile Normal file
View file

@ -0,0 +1,77 @@
# Pull from small Debian stable image.
FROM debian:stable-slim
LABEL maintainer="dselen@nerthus.nl"
# Declaring environment variables, change Peernet to an address you like, standard is a 24 bit subnet.
ENV wg_net="10.0.0.1"
# wg_net is used functionally as an ARG for its environment variable nature, do not change unless you know what you are doing.
# Following ENV variables are changable on container runtime because /entrypoint.sh handles that. See compose.yaml for more info.
ENV tz="Europe/Amsterdam"
ENV global_dns="1.1.1.1"
ENV enable_wg0="false"
ENV isolated_peers="true"
ENV public_ip="0.0.0.0"
# Doing basic system maintenance. Change the timezone to the desired timezone.
RUN ln -sf /usr/share/zoneinfo/${tz} /etc/localtime
# Doing package management operations, such as upgrading
RUN apt-get update && apt-get upgrade -y \
&& apt-get install -y --no-install-recommends curl \
git \
iproute2 \
iptables \
iputils-ping \
openresolv \
procps \
python3 \
python3-pip \
python3-venv \
traceroute \
wireguard \
wireguard-tools \
&& apt-get remove linux-image-* --autoremove -y \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Removing the Linux Image package to preserve space on the image, for this reason also deleting apt lists, to be able to install packages: run apt update.
# Using WGDASH -- like wg_net functionally as a ARG command. But it is needed in entrypoint.sh so it needs to be exported as environment variable.
ENV WGDASH=/opt/wireguarddashboard
RUN python3 -m venv ${WGDASH}/venv
# Doing WireGuard Dashboard installation measures. Modify the git clone command to get the preferred version, with a specific branch for example.
RUN . ${WGDASH}/venv/bin/activate \
&& git clone https://github.com/donaldzou/WGDashboard.git ${WGDASH}/app \
&& pip3 install -r ${WGDASH}/app/src/requirements.txt \
&& chmod +x ${WGDASH}/app/src/wgd.sh \
&& .${WGDASH}/app/src/wgd.sh install
# Set the volume to be used for persistency.
VOLUME /etc/wireguard
# Generate basic WireGuard interface. Echoing the WireGuard interface config for readability, adjust if you want it for efficiency.
# Also setting the pipefail option, verbose: https://github.com/hadolint/hadolint/wiki/DL4006.
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN wg genkey | tee /etc/wireguard/wg0_privatekey \
&& echo "[Interface]" > /wg0.conf \
&& echo "SaveConfig = true" >> /wg0.conf \
&& echo "Address = ${wg_net}/24" >> /wg0.conf \
&& echo "PrivateKey = $(cat /etc/wireguard/wg0_privatekey)" >> /wg0.conf \
&& echo "PostUp = iptables -t nat -I POSTROUTING 1 -s ${wg_net}/24 -o $(ip -o -4 route show to default | awk '{print $NF}') -j MASQUERADE" >> /wg0.conf \
&& echo "PostUp = iptables -I FORWARD -i wg0 -o wg0 -j DROP" >> /wg0.conf \
&& echo "PreDown = iptables -t nat -D POSTROUTING 1" >> /wg0.conf \
&& echo "PreDown = iptables -D FORWARD -i wg0 -o wg0 -j DROP" >> /wg0.conf \
&& echo "ListenPort = 51820" >> /wg0.conf \
#&& echo "DNS = ${global_dns}" >> /wg0.conf \
&& rm /etc/wireguard/wg0_privatekey
# Defining a way for Docker to check the health of the container. In this case: checking the login URL.
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 CMD curl -f http://localhost:10086/signin || exit 1
# Copy the basic entrypoint.sh script.
COPY entrypoint.sh /entrypoint.sh
# Exposing the default WireGuard Dashboard port for web access.
EXPOSE 10086
ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]

23
docker/compose.yaml Normal file
View file

@ -0,0 +1,23 @@
services:
wireguard-dashboard:
image: repo.nerthus.nl/app/wireguard-dashboard:latest
restart: unless-stopped
container_name: wire-dash
environment:
#- tz= # <--- Set container timezone, default: Europe/Amsterdam.
#- global_dns= # <--- Set global DNS address, default: 1.1.1.1.
- enable_wg0=true # <--- If true, wg0 will be started on container startup. default: false.
- isolated_peers=false # <--- When set to true, it disallows peers to talk to eachother, setting to false, allows it, default: true.
#- public_ip= # <--- Set public IP to ensure the correct one is chosen, defaulting to the IP give by ifconfig.me.
ports:
- 10086:10086/tcp
- 51820:51820/udp
volumes:
- conf:/etc/wireguard
- app:/opt/wireguarddashboard/app
cap_add:
- NET_ADMIN
volumes:
conf:
app:

109
docker/entrypoint.sh Normal file
View file

@ -0,0 +1,109 @@
#!/bin/bash
echo "Starting the WireGuard Dashboard Docker container."
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."
rm /opt/wireguarddashboard/app/src/gunicorn.pid
else
echo "No remains found, continuing."
fi
}
start_core() {
# This first step is to ensure the wg0.conf file exists, and if not, then its copied over from the ephemeral container storage.
if [ ! -f "/etc/wireguard/wg0.conf" ]; then
cp "/wg0.conf" "/etc/wireguard/wg0.conf"
echo "WireGuard interface file copied over."
else
echo "WireGuard interface file looks to already be existing."
fi
echo "Activating Python venv and executing the WireGuard Dashboard service."
. "${WGDASH}"/venv/bin/activate
cd "${WGDASH}"/app/src || return # If changing the directory fails (permission or presence error), then bash will exist this function, causing the WireGuard Dashboard to not be succesfully launched.
bash wgd.sh start
# The following section takes care of the firewall rules regarding the 'isolated_peers' feature, which allows or drops packets destined from the wg0 to the wg0 interface.
if [ "${isolated_peers,,}" = "false" ]; then
echo "Isolated peers disabled, adjusting."
sed -i '/PostUp = iptables -I FORWARD -i wg0 -o wg0 -j DROP/d' /etc/wireguard/wg0.conf
sed -i '/PreDown = iptables -D FORWARD -i wg0 -o wg0 -j DROP/d' /etc/wireguard/wg0.conf
elif [ "${isolated_peers,,}" = "true" ]; then
upblocking=$(grep -c "PostUp = iptables -I FORWARD -i wg0 -o wg0 -j DROP" /etc/wireguard/wg0.conf)
downblocking=$(grep -c "PreDown = iptables -D FORWARD -i wg0 -o wg0 -j DROP" /etc/wireguard/wg0.conf)
if [ "$upblocking" -lt 1 ] && [ "$downblocking" -lt 1 ]; then
echo "Isolated peers enabled, adjusting."
sed -i '/PostUp = iptables -t nat -I POSTROUTING 1 -s/a PostUp = iptables -I FORWARD -i wg0 -o wg0 -j DROP' /etc/wireguard/wg0.conf
sed -i '/PreDown = iptables -t nat -D POSTROUTING 1 -s/a PreDown = iptables -D FORWARD -i wg0 -o wg0 -j DROP' /etc/wireguard/wg0.conf
fi
fi
# The following section takes care of
if [ "${enable_wg0,,}" = "true" ]; then
echo "Preference for wg0 to be turned on found."
wg-quick up wg0
else
echo "Preference for wg0 to be turned off found."
fi
}
set_envvars() {
echo "Setting relevant variables for operation."
# If the timezone is different, for example in North-America or Asia.
if [ "${tz}" != "$(cat /etc/timezone)" ]; then
echo "Changing timezone."
ln -sf /usr/share/zoneinfo/"${tz}" /etc/localtime
echo "${tz}" > /etc/timezone
fi
# Changing the DNS used for clients and the dashboard itself.
if [ "${global_dns}" != "$(grep "peer_global_dns = " /opt/wireguarddashboard/app/src/wg-dashboard.ini | awk '{print $NF}')" ]; then
echo "Changing default dns."
#sed -i "s/^DNS = .*/DNS = ${global_dns}/" /etc/wireguard/wg0.conf # Uncomment if you want to have DNS on server-level.
sed -i "s/^peer_global_dns = .*/peer_global_dns = ${global_dns}/" /opt/wireguarddashboard/app/src/wg-dashboard.ini
fi
# Setting the public IP of the WireGuard Dashboard container host. If not defined, it will trying fetching it using a curl to ifconfig.me.
if [ "${public_ip}" = "0.0.0.0" ]; then
default_ip=$(curl -s ifconfig.me)
echo "Trying to fetch the Public-IP using ifconfig.me: ${default_ip}"
sed -i "s/^remote_endpoint = .*/remote_endpoint = ${default_ip}/" /opt/wireguarddashboard/app/src/wg-dashboard.ini
elif [ "${public_ip}" != "$(grep "remote_endpoint = " /opt/wireguarddashboard/app/src/wg-dashboard.ini | awk '{print $NF}')" ]; then
echo "Setting the Public-IP using given variable: ${public_ip}"
sed -i "s/^remote_endpoint = .*/remote_endpoint = ${public_ip}/" /opt/wireguarddashboard/app/src/wg-dashboard.ini
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/app/src/log" -mindepth 1 -maxdepth 1 -type f | read -r; then
latestErrLog=$(find /opt/wireguarddashboard/app/src/log -name "error_*.log" | head -n 1)
latestAccLog=$(find /opt/wireguarddashboard/app/src/log -name "access_*.log" | head -n 1)
tail -f "${latestErrLog}" "${latestAccLog}"
fi
# Blocking command in case of erroring. So the container does not quit.
sleep infinity
}
# Execute functions for the WireGuard Dashboard services, then set the environment variables
clean_up
start_core
set_envvars
ensure_blocking

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB

View file

@ -4,7 +4,6 @@ Under Apache-2.0 License
"""
import sqlite3
from flask import g
import configparser
import hashlib
import ipaddress
@ -22,7 +21,8 @@ from datetime import datetime, timedelta
from operator import itemgetter
# PIP installed library
import ifcfg
from flask import Flask, request, render_template, redirect, url_for, session, jsonify
import psutil
from flask import Flask, request, render_template, redirect, url_for, session, jsonify, g
from flask_qrcode import QRcode
from icmplib import ping, traceroute
@ -31,22 +31,27 @@ from util import regex_match, check_DNS, check_Allowed_IPs, check_remote_endpoin
check_IP_with_range, clean_IP_with_range
# Dashboard Version
DASHBOARD_VERSION = 'v3.0.5'
DASHBOARD_VERSION = 'v3.0.6.2'
# WireGuard's configuration path
WG_CONF_PATH = None
# Dashboard Config Name
configuration_path = os.getenv('CONFIGURATION_PATH', '.')
DB_PATH = os.path.join(configuration_path, 'db')
if not os.path.isdir(DB_PATH):
os.mkdir(DB_PATH)
DASHBOARD_CONF = os.path.join(configuration_path, 'wg-dashboard.ini')
# Upgrade Required
UPDATE = None
# Flask App Configuration
app = Flask("WGDashboard")
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 5206928
app.secret_key = secrets.token_urlsafe(16)
app.config['TEMPLATES_AUTO_RELOAD'] = True
# Enable QR Code Generator
QRcode(app)
@ -99,7 +104,6 @@ def get_conf_peer_key(config_name):
return config_name + " is not running."
# Get numbers of connected peer of a configuration
def get_conf_running_peer_number(config_name):
"""
Get number of running peers on wireguard interface.
@ -128,7 +132,6 @@ def get_conf_running_peer_number(config_name):
return running
# Read [Interface] section from configuration file
def read_conf_file_interface(config_name):
"""
Get interface settings.
@ -161,7 +164,6 @@ def read_conf_file(config_name):
@rtype: dict
"""
# Read Configuration File Start
conf_location = WG_CONF_PATH + "/" + config_name + ".conf"
f = open(conf_location, 'r')
file = f.read().split("\n")
@ -294,6 +296,7 @@ def get_endpoint(config_name):
count += 2
def get_allowed_ip(conf_peer_data, config_name):
"""
Get allowed ips from all peers of a configuration
@ -470,8 +473,8 @@ def get_conf_status(config_name):
@param config_name:
@return: Return a string indicate the running status
"""
ifconfig = dict(ifcfg.interfaces().items())
return "running" if config_name in ifconfig.keys() else "stopped"
addrs = psutil.net_if_addrs()
return "running" if config_name in addrs else "stopped"
def get_conf_list():
@ -626,7 +629,6 @@ def close_DB(exception):
g.db.close()
# Before request
@app.before_request
def auth_req():
"""
@ -652,7 +654,10 @@ def auth_req():
else:
session['message'] = ""
conf.clear()
return redirect("/signin?redirect=" + str(request.url))
redirectURL = str(request.url)
redirectURL = redirectURL.replace("http://", "")
redirectURL = redirectURL.replace("https://", "")
return redirect("/signin?redirect=" + redirectURL)
else:
if request.endpoint in ['signin', 'signout', 'auth', 'settings', 'update_acct', 'update_pwd',
'update_app_ip_port', 'update_wg_conf_path']:
@ -678,7 +683,7 @@ def signin():
if "message" in session:
message = session['message']
session.pop("message")
return render_template('signin.html', message=message)
return render_template('signin.html', message=message, version=DASHBOARD_VERSION)
# Sign Out
@ -868,7 +873,7 @@ def update_pwd():
config.set("Account", "password", hashlib.sha256(request.form['repnewpass'].encode()).hexdigest())
try:
set_dashboard_conf(config)
session['message'] = "Password update successfully!"
session['message'] = "Password updated successfully!"
session['message_status'] = "success"
config.clear()
return redirect(url_for("settings"))
@ -883,7 +888,7 @@ def update_pwd():
config.clear()
return redirect(url_for("settings"))
else:
session['message'] = "Your Password does not match."
session['message'] = "Your Passwords do not match."
session['message_status'] = "danger"
config.clear()
return redirect(url_for("settings"))
@ -1092,7 +1097,7 @@ def add_peer_bulk(config_name):
if not amount.isdigit() or int(amount) < 1:
return "Amount must be integer larger than 0"
amount = int(amount)
if not check_DNS(dns_addresses):
if len(dns_addresses) > 0 and not check_DNS(dns_addresses):
return "DNS formate is incorrect. Example: 1.1.1.1"
if not check_Allowed_IPs(endpoint_allowed_ip):
return "Endpoint Allowed IPs format is incorrect."
@ -1155,7 +1160,7 @@ def add_peer(config_name):
enable_preshared_key = data["enable_preshared_key"]
preshared_key = data['preshared_key']
keys = get_conf_peer_key(config_name)
if len(public_key) == 0 or len(dns_addresses) == 0 or len(allowed_ips) == 0 or len(endpoint_allowed_ip) == 0:
if len(public_key) == 0 or len(allowed_ips) == 0 or len(endpoint_allowed_ip) == 0:
return "Please fill in all required box."
if not isinstance(keys, list):
return config_name + " is not running."
@ -1166,7 +1171,7 @@ def add_peer(config_name):
.fetchone()
if check_dup_ip[0] != 0:
return "Allowed IP already taken by another peer."
if not check_DNS(dns_addresses):
if len(dns_addresses) > 0 and not check_DNS(dns_addresses):
return "DNS formate is incorrect. Example: 1.1.1.1"
if not check_Allowed_IPs(endpoint_allowed_ip):
return "Endpoint Allowed IPs format is incorrect."
@ -1258,7 +1263,7 @@ def save_peer_setting(config_name):
check_ip = check_repeat_allowed_ip(id, allowed_ip, config_name)
if not check_IP_with_range(endpoint_allowed_ip):
return jsonify({"status": "failed", "msg": "Endpoint Allowed IPs format is incorrect."})
if not check_DNS(dns_addresses):
if len(dns_addresses) > 0 and not check_DNS(dns_addresses):
return jsonify({"status": "failed", "msg": "DNS format is incorrect."})
if len(data['MTU']) == 0 or not data['MTU'].isdigit():
return jsonify({"status": "failed", "msg": "MTU format is not correct."})
@ -1417,12 +1422,30 @@ def download_all(config_name):
filename = filename + "_" + config_name
psk = ""
if preshared_key != "":
psk = "\nPresharedKey = " + preshared_key
psk = "PresharedKey = " + preshared_key
return_data = f'''[Interface]
PrivateKey = {private_key}
Address = {allowed_ip}
MTU = {str(mtu_value)}
return_data = "[Interface]\nPrivateKey = " + private_key + "\nAddress = " + allowed_ip + "\nDNS = " + \
dns_addresses + "\nMTU = " + str(mtu_value) + "\n\n[Peer]\nPublicKey = " + \
public_key + "\nAllowedIPs = " + endpoint_allowed_ip + "\nEndpoint = " + \
endpoint + "\nPersistentKeepalive = " + str(keepalive) + psk
'''
if len(dns_addresses) > 0:
return_data += f'DNS = {dns_addresses}'
return_data += f'''
[Peer]
PublicKey = {public_key}
AllowedIPs = {endpoint_allowed_ip}
Endpoint = {endpoint}
PersistentKeepalive = {str(keepalive)}
{psk}
'''
# return_data = "[Interface]\nPrivateKey = " + private_key + "\nAddress = " + allowed_ip + "\nDNS = " + \
# dns_addresses + "\nMTU = " + str(mtu_value) + "\n\n[Peer]\nPublicKey = " + \
# public_key + "\nAllowedIPs = " + endpoint_allowed_ip + "\nEndpoint = " + \
# endpoint + "\nPersistentKeepalive = " + str(keepalive) + psk
data.append({"filename": f"{filename}.conf", "content": return_data})
return jsonify({"status": True, "peers": data, "filename": f"{config_name}.zip"})
@ -1470,12 +1493,30 @@ def download(config_name):
filename = filename + "_" + config_name
psk = ""
if preshared_key != "":
psk = "\nPresharedKey = " + preshared_key
psk = "PresharedKey = " + preshared_key
return_data = "[Interface]\nPrivateKey = " + private_key + "\nAddress = " + allowed_ip + "\nDNS = " + \
dns_addresses + "\nMTU = " + str(mtu_value) + "\n\n[Peer]\nPublicKey = " + \
public_key + "\nAllowedIPs = " + endpoint_allowed_ip + "\nEndpoint = " + \
endpoint + "\nPersistentKeepalive = " + str(keepalive) + psk
return_data = f'''[Interface]
PrivateKey = {private_key}
Address = {allowed_ip}
MTU = {str(mtu_value)}
'''
if len(dns_addresses) > 0:
return_data += f'DNS = {dns_addresses}'
return_data += f'''
[Peer]
PublicKey = {public_key}
AllowedIPs = {endpoint_allowed_ip}
Endpoint = {endpoint}
PersistentKeepalive = {str(keepalive)}
{psk}
'''
# return_data = "[Interface]\nPrivateKey = " + private_key + "\nAddress = " + allowed_ip + "\nDNS = " + \
# dns_addresses + "\nMTU = " + str(mtu_value) + "\n\n[Peer]\nPublicKey = " + \
# public_key + "\nAllowedIPs = " + endpoint_allowed_ip + "\nEndpoint = " + \
# endpoint + "\nPersistentKeepalive = " + str(keepalive) + psk
return jsonify({"status": True, "filename": f"{filename}.conf", "content": return_data})
return jsonify({"status": False, "filename": "", "content": ""})
@ -1700,7 +1741,6 @@ def get_host_bind():
config.read('wg-dashboard.ini')
app_ip = config.get("Server", "app_ip")
app_port = config.get("Server", "app_port")
return app_ip, app_port

View file

@ -1,5 +1,6 @@
Flask
ifcfg
psutil
icmplib
flask-qrcode
gunicorn

View file

@ -1,5 +1,6 @@
body {
font-size: .875rem;
/*font-family: 'Poppins', sans-serif;*/
}
.feather {
@ -407,8 +408,26 @@ main{
.btn{
border-radius: 8px;
/*padding: 0.6rem 0.9em;*/
}
#username, #password{
padding: 0.6rem calc( 0.9rem + 32px );
height: inherit;
}
label[for="username"], label[for="password"]{
font-size: 1rem;
margin: 0 !important;
transform: translateY(30px) translateX(16px);
padding: 0;
}
/*label[for="password"]{*/
/* transform: translateY(32px) translateX(16px);*/
/*}*/
.modal-content{
border-radius: 10px;
}

File diff suppressed because one or more lines are too long

View file

@ -155,7 +155,7 @@
let $enable_preshare_key = $("#enable_preshare_key");
let data_list = [$new_add_DNS, $new_add_endpoint_allowed_ip,$new_add_MTU, $new_add_keep_alive];
if ($new_add_amount.val() > 0 && !$new_add_amount.hasClass("is-invalid")){
if ($new_add_DNS.val() !== "" && $new_add_endpoint_allowed_ip.val() !== ""){
if ($new_add_endpoint_allowed_ip.val() !== ""){
let conf = $add_peer.getAttribute('conf_id');
let keys = [];
for (let i = 0; i < $new_add_amount.val(); i++) {
@ -633,7 +633,7 @@ $add_peer.addEventListener("click",function(){
let $enable_preshare_key = $("#enable_preshare_key");
$add_peer.setAttribute("disabled","disabled");
$add_peer.innerHTML = "Adding...";
if ($allowed_ips.val() !== "" && $public_key.val() !== "" && $new_add_DNS.val() !== "" && $new_add_endpoint_allowed_ip.val() !== ""){
if ($allowed_ips.val() !== "" && $public_key.val() !== "" && $new_add_endpoint_allowed_ip.val() !== ""){
let conf = $add_peer.getAttribute('conf_id');
let data_list = [$private_key, $allowed_ips, $new_add_name, $new_add_DNS, $new_add_endpoint_allowed_ip,$new_add_MTU, $new_add_keep_alive];
data_list.forEach((ele) => ele.attr("disabled", "disabled"));
@ -924,7 +924,7 @@ $("#save_peer_setting").on("click",function (){
let $peer_mtu = $("#peer_mtu");
let $peer_keep_alive = $("#peer_keep_alive");
if ($peer_DNS_textbox.val() !== "" &&
if (
$peer_allowed_ip_textbox.val() !== "" && $peer_endpoint_allowed_ips.val() !== ""){
let peer_id = $(this).attr("peer_id");
let conf_id = $(this).attr("conf_id");

View file

@ -192,7 +192,7 @@
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="new_add_DNS">DNS <code>(Required)</code></label>
<label for="new_add_DNS">DNS</label>
<input type="text" class="form-control" id="new_add_DNS" value="{{ DNS }}">
</div>
</div>
@ -297,7 +297,7 @@
</div>
<div class="col-sm-6">
<div class="mb-3">
<label for="peer_DNS_textbox" class="form-label">DNS <code>(Required)</code></label>
<label for="peer_DNS_textbox" class="form-label">DNS</label>
<input type="text" class="form-control" id="peer_DNS_textbox">
</div>
</div>
@ -410,8 +410,8 @@
{% include "tools.html" %}
</body>
{% include "footer.html" %}
<script src="{{ url_for('static',filename='js/wireguard.min.js') }}"></script>
<script src="{{ url_for('static',filename='js/configuration.min.js') }}"></script>
<script src="{{ url_for('static',filename='js/wireguard.js') }}"></script>
<script src="{{ url_for('static',filename='js/configuration.js') }}"></script>
<script>
/* global peers */
let load_timeout;

View file

@ -1,3 +1,3 @@
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-fQybjgWLrvvRgtW6bFlB7jaZrFsaBXjsOMm/tB9LTS58ONXgqbR9W8oWht/amnpF" crossorigin="anonymous"></script>
<script src="{{ url_for('static',filename='js/tools.min.js') }}"></script>
<script src="{{ url_for('static',filename='js/tools.js') }}"></script>

View file

@ -14,29 +14,32 @@
<div class="container-fluid login-container-fluid">
<main role="main" class="container login-container">
<div class="login-box" style="margin: auto !important;">
<h1 class="text-center">Sign In</h1>
<h1 class="text-center">Sign in</h1>
<h5 class="text-center">to WGDashboard</h5>
<form style="margin-left: auto !important; margin-right: auto !important; max-width: 500px;" action="/auth" method="post">
{% if message != "" %}
<div class="alert alert-warning" role="alert">You need to sign in first</div>
{% endif %}
<div class="alert alert-danger d-none" role="alert"></div>
<div class="alert alert-danger d-none" role="alert" style="margin-top: 1rem; margin-bottom: 0rem;"></div>
<div class="form-group">
<label for="username" class="text-left" style="font-size: 1rem"><i class="bi bi-person-circle"></i> Username</label>
<input type="text" class="form-control" id="username" name="username" required>
<label for="username" class="text-left" style="font-size: 1rem"><i class="bi bi-person-circle"></i></label>
<input type="text" class="form-control" id="username" name="username" placeholder="Your username" required>
</div>
<div class="form-group">
<label for="password" class="text-left" style="font-size: 1rem"><i class="bi bi-key-fill"></i> Password</label>
<input type="password" class="form-control" id="password" name="password" required>
<label for="password" class="text-left" style="font-size: 1rem"><i class="bi bi-key-fill"></i></label>
<input type="password" class="form-control" id="password" name="password" placeholder="Your password" required>
</div>
<button type="submit" class="btn btn-dark" style="width: 100%;">Sign In</button>
<button type="submit" class="btn btn-dark" style="width: 100%">Sign In</button>
</form>
</div>
</main>
</div>
<small class="text-muted" style="position: fixed; bottom: 0; width: 100%; text-align: center; margin-bottom: 2rem">Version: {{ version }}</small>
</body>
{% include "footer.html" %}
<script>
$("button").on("click", function(e){
let loginButton = $('button[type="submit"]');
loginButton.on("click", function(e){
e.preventDefault();
let $password = $("#password");
let $username = $("#username");
@ -44,7 +47,7 @@
let check = true
for (let i = 0; i < req.length; i++){
if ($(req[i]).val().length === 0){
$("button").html("Sign In");
loginButton.html("Sign In");
check = false;
$(req[i]).addClass("is-invalid");
break;
@ -64,13 +67,17 @@
if (res.status === true){
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.get("redirect")){
window.location.replace(urlParams.get("redirect"))
if (document.URL.substring(0, 5) == "http:"){
window.location.replace(`http://${urlParams.get("redirect")}`)
}else if (document.URL.substring(0, 5) == "https"){
window.location.replace(`https://${urlParams.get("redirect")}`)
}
}else{
window.location.replace("/");
}
}else{
$(".alert").html(res.msg).removeClass("d-none");
$("button").html("Sign In").removeAttr("disabled");
$(".alert").html(res.msg).removeClass("d-none").fadeIn();
loginButton.html("Sign In").removeAttr("disabled");
$("input[required]").addClass("is-invalid");
}
});