From 9d476af384fed3db4e473a6a05b87c0547d077fe Mon Sep 17 00:00:00 2001 From: Donald Cheng Hong Zou Date: Sat, 14 Aug 2021 23:30:05 -0400 Subject: [PATCH] v2.2beta almost done!!!!! --- README.md | 13 +++++- src/dashboard.py | 70 +++++++++++++++++++++----------- src/requirements.txt | 2 +- src/static/js/configuration.js | 50 ++++++++++++++++++++--- src/templates/configuration.html | 21 ++-------- src/templates/get_conf.html | 18 ++++---- src/templates/header.html | 2 +- src/templates/index.html | 6 ++- src/templates/settings.html | 5 ++- 9 files changed, 128 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 62ca409..2fc2927 100644 --- a/README.md +++ b/README.md @@ -20,15 +20,26 @@ ## 📣 What's New: Version v2.2 - 🎉 **New Features** + - **Add new peers**: Now you can add peers directly on dashboard, it will generate a pair of private key and public key. You can also set its DNS, endpoint allowed IPs. Both can set a default value in the setting page. [❤️ in [#44](https://github.com/donaldzou/wireguard-dashboard/issues/44)] - **QR Code:** You can add the private key in peer setting of your existed peer to create a QR code. Or just create a new one, dashboard will now be able to auto generate a private key and public key ;) Don't worry, all keys will be generated on your machine, and **will delete all key files after they got generated**. [❤️ in [#29](https://github.com/donaldzou/wireguard-dashboard/issues/29)] - **Peer configuration file download:** Same as QR code, you now can download the peer configuration file, so you don't need to manually input all the details on the peer machine! [❤️ in [#40](https://github.com/donaldzou/wireguard-dashboard/issues/40)] + - **Search peers**: You can now search peers by their name. - **Autostart on boot:** Added a tutorial on how to start the dashboard to on boot! Please read the [tutorial below](#autostart-wireguard-dashboard-on-boot). [❤️ in [#29](https://github.com/donaldzou/wireguard-dashboard/issues/29)] + - **Click to copy**: You can now click and copy all peer's public key and configuration's public key. + - And many more... - 🪚 **Bug Fixed** - When there are comments in the wireguard config file, will cause the dashboard to crash. - Used regex to search for config files. - **🧐 Other Changes** - Moved all external CSS and JavaScript file to local hosting (Except Bootstrap Icon, due to large amount of SVG files). - - Updated `Flask` from`v1.1.2` to `v2.0.1`, and `Jinja` from `v2.10.1` to `v3.0.1` + - Updated Python dependencies + - Flask: `v1.1.2 => v2.0.1` + - Jinja: `v2.10.1 => v3.0.1` + - icmplib: `v2.1.1 => v3.0.1` + - Updated CSS/JS dependencies + - Bootstrap: `v4.5.3 => v4.6.0` + - UI adjustment + - Adjusted how peers will display in larger screens, used to be 1 row per peer, now is 3 peers in 1 row.
diff --git a/src/dashboard.py b/src/dashboard.py index c1a1de3..7f96f80 100644 --- a/src/dashboard.py +++ b/src/dashboard.py @@ -66,6 +66,23 @@ def is_match(regex, text): return pattern.search(text) is not None +def read_conf_file_interface(config_name): + conf_location = wg_conf_path + "/" + config_name + ".conf" + f = open(conf_location, 'r') + file = f.read().split("\n") + data = {} + peers_start = 0 + for i in range(len(file)): + if not is_match("#(.*)", file[i]): + if len(file[i]) > 0: + if file[i] != "[Interface]": + tmp = re.split(r'\s*=\s*', file[i], 1) + if len(tmp) == 2: + data[tmp[0]] = tmp[1] + f.close() + return data + + def read_conf_file(config_name): # Read Configuration File Start conf_location = wg_conf_path + "/" + config_name + ".conf" @@ -195,7 +212,7 @@ def get_all_peers_data(config_name): "id": i['PublicKey'], "private_key": "", "DNS": "1.1.1.1", - "endpoint_allowed_ip":"0.0.0.0/0", + "endpoint_allowed_ip": "0.0.0.0/0", "name": "", "total_receive": 0, "total_sent": 0, @@ -357,14 +374,23 @@ def checkAllowedIP(public_key, ip, config_name): else: return {'status': 'success'} + def checkIp(ip): - return is_match("((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}",ip) + return is_match("((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}", ip) + + def cleanIp(ip): - return ip.replace(' ','') + return ip.replace(' ', '') + + def cleanIpWithRange(ip): return cleanIp(ip).split(',') + + def checkIpWithRange(ip): return is_match("((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|\/)){4}(0|8|16|24|32)(,|$)", ip) + + def checkAllowedIPs(ip): ip = cleanIpWithRange(ip) for i in ip: @@ -372,8 +398,6 @@ def checkAllowedIPs(ip): return True - - @app.before_request def auth_req(): conf = configparser.ConfigParser(strict=False) @@ -431,8 +455,8 @@ def settings(): return render_template('settings.html', conf=get_conf_list(), message=message, status=status, app_ip=config.get("Server", "app_ip"), app_port=config.get("Server", "app_port"), required_auth=required_auth, wg_conf_path=config.get("Server", "wg_conf_path"), - peer_global_DNS=config.get("Peers","peer_global_DNS"), - peer_endpoint_allowed_ip=config.get("Peers","peer_endpoint_allowed_ip")) + peer_global_DNS=config.get("Peers", "peer_global_DNS"), + peer_endpoint_allowed_ip=config.get("Peers", "peer_endpoint_allowed_ip")) @app.route('/auth', methods=['POST']) @@ -473,6 +497,7 @@ def update_acct(): config.clear() return redirect(url_for("settings")) + @app.route('/update_peer_default_config', methods=['POST']) def update_peer_default_config(): config = configparser.ConfigParser(strict=False) @@ -492,13 +517,11 @@ def update_peer_default_config(): # Check Endpoint Allowed IPs ip = request.form['peer_endpoint_allowed_ip'] if not checkAllowedIPs(ip): - session['message'] = "Peer Endpoint Allowed IPs Format Incorrect. Example: 192.168.1.1/32 or 192.168.1.1/32,192.168.1.2/32" + session[ + 'message'] = "Peer Endpoint Allowed IPs Format Incorrect. Example: 192.168.1.1/32 or 192.168.1.1/32,192.168.1.2/32" session['message_status'] = "danger" return redirect(url_for("settings")) - - - config.set("Peers", "peer_endpoint_allowed_ip", ','.join(cleanIpWithRange(ip))) config.set("Peers", "peer_global_DNS", request.form['peer_global_DNS']) try: @@ -679,6 +702,8 @@ def conf(config_name): @app.route('/get_config/', methods=['GET']) def get_conf(config_name): + config_interface = read_conf_file_interface(config_name) + search = request.args.get('search') if len(search) == 0: search = "" search = urllib.parse.unquote(search) @@ -693,12 +718,14 @@ def get_conf(config_name): "public_key": get_conf_pub_key(config_name), "listen_port": get_conf_listen_port(config_name), "running_peer": get_conf_running_peer_number(config_name), + "conf_address": config_interface['Address'] } if conf_data['status'] == "stopped": conf_data['checked'] = "nope" else: conf_data['checked'] = "checked" - return render_template('get_conf.html', conf_data=conf_data, wg_ip=wg_ip, sort_tag=sort, dashboard_refresh_interval=int(config.get("Server", "dashboard_refresh_interval"))) + return render_template('get_conf.html', conf_data=conf_data, wg_ip=wg_ip, sort_tag=sort, + dashboard_refresh_interval=int(config.get("Server", "dashboard_refresh_interval"))) @app.route('/switch/', methods=['GET']) @@ -731,6 +758,9 @@ def add_peer(config_name): endpoint_allowed_ip = data['endpoint_allowed_ip'] DNS = data['DNS'] keys = get_conf_peer_key(config_name) + if len(public_key) == 0 or len(DNS) == 0 or len(allowed_ips) == 0 or len(endpoint_allowed_ip) == 0: + return "Please fill in all required box." + if type(keys) != list: return config_name + " is not running." if public_key in keys: @@ -750,7 +780,8 @@ def add_peer(config_name): stderr=subprocess.STDOUT) status = subprocess.check_output("wg-quick save " + config_name, shell=True, stderr=subprocess.STDOUT) get_all_peers_data(config_name) - db.update({"name": data['name'], "private_key": data['private_key'], "DNS": data['DNS'], "endpoint_allowed_ip": endpoint_allowed_ip}, + db.update({"name": data['name'], "private_key": data['private_key'], "DNS": data['DNS'], + "endpoint_allowed_ip": endpoint_allowed_ip}, peers.id == public_key) db.close() return "true" @@ -804,7 +835,6 @@ def save_peer_setting(config_name): return jsonify(check_key) if check_ip['status'] == "failed": return jsonify(check_ip) - try: if allowed_ip == "": allowed_ip = '""' @@ -815,16 +845,13 @@ def save_peer_setting(config_name): if change_ip.decode("UTF-8") != "": return jsonify({"status": "failed", "msg": change_ip.decode("UTF-8")}) - - db.update({"name": name, "private_key": private_key, "DNS": DNS, "endpoint_allowed_ip":endpoint_allowed_ip}, peers.id == id) + db.update( + {"name": name, "private_key": private_key, "DNS": DNS, "endpoint_allowed_ip": endpoint_allowed_ip}, + peers.id == id) db.close() return jsonify({"status": "success", "msg": ""}) except subprocess.CalledProcessError as exc: return jsonify({"status": "failed", "msg": str(exc.output.decode("UTF-8").strip())}) - - - - else: return jsonify({"status": "failed", "msg": "This peer does not exist."}) @@ -881,7 +908,6 @@ def download(config_name): private_key = peer['private_key'] allowed_ip = peer['allowed_ip'] DNS = peer['DNS'] - filename = peer['name'] if len(filename) == 0: filename = "Untitled_Peers" @@ -922,7 +948,6 @@ def init_dashboard(): config['Account']['username'] = 'admin' if "password" not in config['Account']: config['Account']['password'] = '8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918' - if "Server" not in config: config['Server'] = {} if 'wg_conf_path' not in config['Server']: @@ -939,7 +964,6 @@ def init_dashboard(): config['Server']['dashboard_refresh_interval'] = '15000' if 'dashboard_sort' not in config['Server']: config['Server']['dashboard_sort'] = 'status' - if "Peers" not in config: config['Peers'] = {} if 'peer_global_DNS' not in config['Peers']: diff --git a/src/requirements.txt b/src/requirements.txt index 8127a46..511897c 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -1,5 +1,5 @@ Flask==2.0.1 tinydb==4.3.0 ifcfg==0.21 -icmplib==2.1.1 +icmplib==3.0.1 flask-qrcode==3.0.0 \ No newline at end of file diff --git a/src/static/js/configuration.js b/src/static/js/configuration.js index 8a5270d..329dabd 100644 --- a/src/static/js/configuration.js +++ b/src/static/js/configuration.js @@ -58,8 +58,17 @@ $("#re_generate_key").click(function (){ }) $("#save_peer").click(function(){ - if ($("#allowed_ips") !== "" && $("#public_key") !== ""){ + $(this).attr("disabled","disabled") + $(this).html("Saving...") + + if ($("#allowed_ips").val() !== "" && $("#public_key").val() !== "" && $("#new_add_DNS").val() !== "" && $("#new_add_endpoint_allowed_ip").val() != ""){ var conf = $(this).attr('conf_id') + var data_list = [$("#private_key"), $("#allowed_ips"), $("#new_add_name"), $("#new_add_DNS"), $("#new_add_endpoint_allowed_ip")] + for (var i = 0; i < data_list.length; i++){ + data_list[i].attr("disabled", "disabled") + } + + $.ajax({ method: "POST", url: "/add_peer/"+conf, @@ -84,6 +93,9 @@ $("#save_peer").click(function(){ } } }) + }else{ + $("#add_peer_alert").html("Please fill in all required box."); + $("#add_peer_alert").removeClass("d-none"); } }) var qrcodeModal = new bootstrap.Modal(document.getElementById('qrcode_modal'), { @@ -108,6 +120,8 @@ $("body").on("click", ".btn-delete-peer", function(){ }) $("#delete_peer").click(function(){ + $(this).attr("disabled","disabled") + $(this).html("Deleting...") var peer_id = $(this).attr("peer_id"); var config = $(this).attr("conf_id"); $.ajax({ @@ -127,6 +141,8 @@ $("#delete_peer").click(function(){ load_data($('#search_peer_textbox').val()); $('#alertToast').toast('show'); $('#alertToast .toast-body').html("Peer deleted!"); + $("#delete_peer").removeAttr("disabled") + $("#delete_peer").html("Delete") } } }) @@ -189,9 +205,16 @@ $("#peer_private_key_textbox").change(function(){ $("#save_peer_setting").click(function (){ $(this).attr("disabled","disabled") $(this).html("Saving...") - if ($("#peer_DNS_textbox").val() !== "" && $("#peer_allowed_ip_textbox").val() !== ""){ + if ($("#peer_DNS_textbox").val() !== "" && + $("#peer_allowed_ip_textbox").val() !== "" && + $("#peer_endpoint_allowed_ips").val() != "" + ){ var peer_id = $(this).attr("peer_id"); var conf_id = $(this).attr("conf_id"); + var data_list = [$("#peer_name_textbox"), $("#peer_DNS_textbox"), $("#peer_private_key_textbox"), $("#peer_allowed_ip_textbox"), $("#peer_endpoint_allowed_ips")] + for (var i = 0; i < data_list.length; i++){ + data_list[i].attr("disabled", "disabled") + } $.ajax({ method: "POST", url: "/save_peer_setting/"+conf_id, @@ -218,8 +241,14 @@ $("#save_peer_setting").click(function (){ } $("#save_peer_setting").removeAttr("disabled") $("#save_peer_setting").html("Save") + for (var i = 0; i < data_list.length; i++){ + data_list[i].removeAttr("disabled") + } } }) + }else{ + $("#setting_peer_alert").html("Please fill in all required box."); + $("#setting_peer_alert").removeClass("d-none"); } @@ -288,7 +317,16 @@ function copyToClipboard(element) { $temp.remove(); } - -// $(".key").mouseenter(function(){ -// -// }) +$("body").on("click", ".update_interval", function(){ + $.ajax({ + method:"POST", + data: "interval="+$(this).attr("refresh-interval"), + url: "/update_dashboard_refresh_interval", + success: function (res){ + location.reload() + } + }) + }); +$("body").on("click", ".refresh", function (){ + load_data($('#search_peer_textbox').val()); +}); diff --git a/src/templates/configuration.html b/src/templates/configuration.html index f1d001c..300c24d 100644 --- a/src/templates/configuration.html +++ b/src/templates/configuration.html @@ -69,14 +69,14 @@
- +
- +
@@ -228,7 +228,6 @@ function load_data(search){ startProgressBar() - $.ajax({ method: "GET", url: "/get_config/"+conf_name+"?search="+encodeURIComponent(search), @@ -237,12 +236,10 @@ }, success: function (response){ $("#config_body").html(response); - {#$("[refresh-interval={{ dashboard_refresh_interval }}]").addClass("active")#} $("#search_peer_textbox").css("display", "block") if (bar.css("width") !== "0%"){ endProgressBar() } - } }) } @@ -253,19 +250,7 @@ }, {{dashboard_refresh_interval}}) }); - $("body").on("click", ".update_interval", function(){ - $.ajax({ - method:"POST", - data: "interval="+$(this).attr("refresh-interval"), - url: "/update_dashboard_refresh_interval", - success: function (res){ - location.reload() - } - }) - }); - $("body").on("click", ".refresh", function (){ - load_data($('#search_peer_textbox').val()); - }); + \ No newline at end of file diff --git a/src/templates/get_conf.html b/src/templates/get_conf.html index 75789fd..30d79a6 100644 --- a/src/templates/get_conf.html +++ b/src/templates/get_conf.html @@ -49,6 +49,10 @@ LISTEN PORT
{{conf_data['listen_port']}}
+
+ ADDRESS +
{{conf_data['conf_address']}}
+

@@ -66,12 +70,12 @@

-
- - - - - +
+ + + + +
@@ -151,7 +155,7 @@ - +
diff --git a/src/templates/header.html b/src/templates/header.html index 4c3427e..c63fd7a 100644 --- a/src/templates/header.html +++ b/src/templates/header.html @@ -1,7 +1,7 @@ - Wireguard Dashboard - {{ title }} + Wireguard Dashboard | {{ title }} diff --git a/src/templates/index.html b/src/templates/index.html index 8dcf8f7..52ad2ad 100644 --- a/src/templates/index.html +++ b/src/templates/index.html @@ -1,5 +1,9 @@ -{% include "header.html" %} +{% with %} + {% set title="Home" %} + {% include "header.html"%} +{% endwith %} + {% include "navbar.html" %}
diff --git a/src/templates/settings.html b/src/templates/settings.html index f4ddf15..f9af059 100644 --- a/src/templates/settings.html +++ b/src/templates/settings.html @@ -1,5 +1,8 @@ -{% include "header.html" %} +{% with %} + {% set title="Settings" %} + {% include "header.html"%} +{% endwith %} {% include "navbar.html" %}