mirror of
https://github.com/donaldzou/WGDashboard.git
synced 2025-10-03 15:56:17 +00:00
Compare commits
57 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
9e38137e76 | ||
|
210f5eabc9 | ||
|
5f5c91a8ff | ||
|
05c7121c8a | ||
|
551a8e9588 | ||
|
8fe5507ff8 | ||
|
4aed647865 | ||
|
74ab7aaa3d | ||
|
dcf2055851 | ||
|
734a4b5e00 | ||
|
da70fac0b6 | ||
|
3d6ecba4f5 | ||
|
86220694ce | ||
|
77f31177c8 | ||
|
33ff91aea8 | ||
|
2c9cade70a | ||
|
d7d7a84bd5 | ||
|
990463fbea | ||
|
0d9a8ba6f7 | ||
|
edbaf3ac82 | ||
|
cd84674ae0 | ||
|
f60ea43f29 | ||
|
76f547a726 | ||
|
dffe5e0819 | ||
|
fb9ef0c547 | ||
|
d52cd2b17c | ||
|
cefa80f317 | ||
|
191ff1abec | ||
|
3bb86493cc | ||
|
d1d3151e1e | ||
|
f9dc9ebdb3 | ||
|
3b478bcc2d | ||
|
77bb78c381 | ||
|
cafa97f502 | ||
|
0d858493d5 | ||
|
99fb07c6b3 | ||
|
eaad971c0a | ||
|
377abd87fd | ||
|
af71176296 | ||
|
69737177ef | ||
|
efae1222c1 | ||
|
3caea1a903 | ||
|
a43478d627 | ||
|
ed2c3f43c7 | ||
|
fcfd816cec | ||
|
2e3977e59c | ||
|
3c68430336 | ||
|
43afb86fa8 | ||
|
194ccbdbb4 | ||
|
7139e230cf | ||
|
120d3b9f54 | ||
|
712460a040 | ||
|
be5594f1c9 | ||
|
0a885117db | ||
|
5b73654544 | ||
|
1485b78b7b | ||
|
bfec57172a |
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@@ -1,3 +1,4 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [donaldzou]
|
||||
patreon: DonaldDonnyZou
|
||||
|
70
.github/workflows/codeql-analysis.yml
vendored
Normal file
70
.github/workflows/codeql-analysis.yml
vendored
Normal 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
6
.gitignore
vendored
@@ -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/
|
115
README.md
115
README.md
@@ -1,3 +1,17 @@
|
||||
### 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) 😊
|
||||
|
||||
#### A preview on v4.0 🥹
|
||||
|
||||

|
||||
|
||||
|
||||
<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 +23,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>
|
||||
@@ -19,7 +34,7 @@
|
||||
- 🎉 **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.
|
||||
- **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.
|
||||
@@ -28,8 +43,8 @@
|
||||
|
||||
- 🪚 **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]
|
||||
- [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**
|
||||
@@ -90,7 +105,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 +131,7 @@
|
||||
1. Download WGDashboard
|
||||
|
||||
```shell
|
||||
git clone -b v3.0 https://github.com/donaldzou/WGDashboard.git wgdashboard
|
||||
git clone -b v3.0.6 https://github.com/donaldzou/WGDashboard.git wgdashboard
|
||||
|
||||
2. Open the WGDashboard folder
|
||||
|
||||
@@ -301,7 +317,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` | **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 |
|
||||
| | | | |
|
||||
@@ -348,17 +364,45 @@ Endpoint = 0.0.0.0:51820
|
||||
|
||||
<hr>
|
||||
|
||||
#### Update Method 1 (For `v3.0` or above)
|
||||
|
||||
1. Change your directory to `wgdashboard/src`
|
||||
|
||||
```bash
|
||||
cd wgdashboard/src
|
||||
```
|
||||
|
||||
2. Update the dashboard with the following
|
||||
|
||||
```bash
|
||||
./wgd.sh update
|
||||
chmod +x ./wgd.sh
|
||||
```
|
||||
|
||||
> If this doesn't work, please use the method below. Sorry about that :(
|
||||
|
||||
#### Update Method 2
|
||||
|
||||
|
||||
1. Change your directory to `wgdashboard`
|
||||
|
||||
|
||||
```shell
|
||||
cd wgdashboard
|
||||
cd wgdashboard/src
|
||||
```
|
||||
|
||||
2. Update the dashboard
|
||||
```shell
|
||||
git pull https://github.com/donaldzou/WGDashboard.git v3.0 --force
|
||||
git pull https://github.com/donaldzou/WGDashboard.git v3.0.6 --force
|
||||
```
|
||||
|
||||
3. Install
|
||||
|
||||
```shell
|
||||
./wgd.sh install
|
||||
```
|
||||
|
||||
|
||||
|
||||
Starting with `v3.0`, you can simply do `./wgd.sh update` !! (I hope, lol)
|
||||
|
||||
## 🥘 Experimental Functions
|
||||
@@ -393,10 +437,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**
|
||||
@@ -478,7 +570,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
10
SECURITY.md
Normal 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: |
|
Binary file not shown.
After Width: | Height: | Size: 3.8 MiB |
105
src/dashboard.py
105
src/dashboard.py
@@ -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'
|
||||
DASHBOARD_VERSION = 'v3.0.6'
|
||||
|
||||
# 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)
|
||||
|
||||
@@ -66,9 +71,9 @@ def get_dashboard_conf():
|
||||
Get dashboard configuration
|
||||
@return: configparser.ConfigParser
|
||||
"""
|
||||
config = configparser.ConfigParser(strict=False)
|
||||
config.read(DASHBOARD_CONF)
|
||||
return config
|
||||
r_config = configparser.ConfigParser(strict=False)
|
||||
r_config.read(DASHBOARD_CONF)
|
||||
return r_config
|
||||
|
||||
|
||||
def set_dashboard_conf(config):
|
||||
@@ -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")
|
||||
@@ -190,7 +192,7 @@ def read_conf_file(config_name):
|
||||
conf_peer_data["Peers"].append({})
|
||||
elif peer > -1:
|
||||
if len(i) > 0:
|
||||
tmp = re.split('\s*=\s*', i, 1)
|
||||
tmp = re.split(r'\s*=\s*', i, 1)
|
||||
if len(tmp) == 2:
|
||||
conf_peer_data["Peers"][peer][tmp[0]] = tmp[1]
|
||||
|
||||
@@ -224,10 +226,10 @@ def get_latest_handshake(config_name):
|
||||
status = "stopped"
|
||||
if int(data_usage[count + 1]) > 0:
|
||||
g.cur.execute("UPDATE %s SET latest_handshake = '%s', status = '%s' WHERE id='%s'"
|
||||
% (config_name, str(minus).split(".", maxsplit=1)[0], status, data_usage[count]))
|
||||
% (config_name, str(minus).split(".", maxsplit=1)[0], status, data_usage[count]))
|
||||
else:
|
||||
g.cur.execute("UPDATE %s SET latest_handshake = '(None)', status = '%s' WHERE id='%s'"
|
||||
% (config_name, status, data_usage[count]))
|
||||
% (config_name, status, data_usage[count]))
|
||||
count += 2
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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,11 +1741,11 @@ 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
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
init_dashboard()
|
||||
UPDATE = check_update()
|
||||
config = configparser.ConfigParser(strict=False)
|
||||
config.read('wg-dashboard.ini')
|
||||
|
@@ -1,5 +1,6 @@
|
||||
Flask
|
||||
ifcfg
|
||||
psutil
|
||||
icmplib
|
||||
flask-qrcode
|
||||
gunicorn
|
||||
|
BIN
src/static/.DS_Store
vendored
BIN
src/static/.DS_Store
vendored
Binary file not shown.
@@ -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;
|
||||
}
|
||||
|
2
src/static/css/dashboard.min.css
vendored
2
src/static/css/dashboard.min.css
vendored
File diff suppressed because one or more lines are too long
@@ -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++) {
|
||||
@@ -338,8 +338,12 @@
|
||||
* Load Peers from server to configuration page
|
||||
* @param searchString
|
||||
*/
|
||||
let d1 = new Date();
|
||||
let time = 0;
|
||||
let count = 0;
|
||||
function loadPeers(searchString){
|
||||
startProgressBar();
|
||||
d1 = new Date();
|
||||
$.ajax({
|
||||
method: "GET",
|
||||
url: `/get_config/${conf_name}?search=${encodeURIComponent(searchString)}`,
|
||||
@@ -354,6 +358,12 @@
|
||||
$(".dot.dot-stopped").attr("title","Peer Disconnected").tooltip();
|
||||
$("i[data-toggle='tooltip']").tooltip();
|
||||
endProgressBar();
|
||||
let d2 = new Date();
|
||||
let seconds = (d2 - d1);
|
||||
time += seconds;
|
||||
count += 1;
|
||||
console.log(`Average ${time/count}ms`);
|
||||
$("#peer_loading_time").html(`Peer Loading Time: ${seconds}ms`);
|
||||
}).fail(function(){
|
||||
noResponding();
|
||||
});
|
||||
@@ -623,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"));
|
||||
@@ -914,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");
|
||||
|
2
src/static/js/configuration.min.js
vendored
2
src/static/js/configuration.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -171,6 +171,17 @@
|
||||
return String.fromCharCode.apply(null, base64);
|
||||
}
|
||||
|
||||
function base64ToKey(base64) {
|
||||
let binary_string = window.atob(base64);
|
||||
let len = binary_string.length;
|
||||
let bytes = new Uint8Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
bytes[i] = binary_string.charCodeAt(i);
|
||||
}
|
||||
let uint8 = new Uint8Array(bytes.buffer);
|
||||
return uint8;
|
||||
}
|
||||
|
||||
function putU32(b, n)
|
||||
{
|
||||
b.push(n & 0xff, (n >>> 8) & 0xff, (n >>> 16) & 0xff, (n >>> 24) & 0xff);
|
||||
@@ -282,7 +293,8 @@
|
||||
};
|
||||
},
|
||||
generatePublicKey: function (privateKey){
|
||||
return keyToBase64(generatePublicKey(privateKey))
|
||||
privateKey = base64ToKey(privateKey);
|
||||
return keyToBase64(generatePublicKey(privateKey));
|
||||
},
|
||||
|
||||
generateZipFiles: function(res){
|
||||
|
2
src/static/js/wireguard.min.js
vendored
2
src/static/js/wireguard.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -11,7 +11,6 @@
|
||||
</div>
|
||||
</div>
|
||||
{% include "navbar.html" %}
|
||||
|
||||
<div class="container-fluid" id="right_body">
|
||||
{% include "sidebar.html" %}
|
||||
<div class="col-md-9 ml-sm-auto col-lg-10 px-md-4 mt-4 mb-4">
|
||||
@@ -120,6 +119,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row peer_list"></div>
|
||||
<small id="peer_loading_time" class="text-muted"></small>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
@@ -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;
|
||||
|
@@ -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>
|
||||
|
@@ -14,41 +14,46 @@
|
||||
<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 req = $("input[required]");
|
||||
let loginButton = $('button[type="submit"]');
|
||||
loginButton.on("click", function(e){
|
||||
e.preventDefault();
|
||||
let $password = $("#password");
|
||||
let $username = $("#username");
|
||||
let req = [$password, $username];
|
||||
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;
|
||||
$("input[required]").addClass("is-invalid");
|
||||
$(req[i]).addClass("is-invalid");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (check){
|
||||
e.preventDefault();
|
||||
$(this).html("Signing In...").attr("disabled", "disabled");
|
||||
$.ajax({
|
||||
url: "/auth",
|
||||
@@ -62,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");
|
||||
}
|
||||
});
|
||||
|
1
src/wgd.sh
Executable file → Normal file
1
src/wgd.sh
Executable file → Normal file
@@ -33,6 +33,7 @@ help () {
|
||||
}
|
||||
|
||||
_check_and_set_venv(){
|
||||
# This function will not be using in v3.0
|
||||
# deb/ubuntu users: might need a 'apt install python3.8-venv'
|
||||
# set up the local environment
|
||||
APP_ROOT=`pwd`
|
||||
|
Reference in New Issue
Block a user