From 85f8c936beb31b5bc27b93fbd23646db7aaa38cc Mon Sep 17 00:00:00 2001 From: lare Date: Wed, 7 Dec 2022 17:00:52 +0100 Subject: [PATCH] add ability to delete peerings + format all files --- web/backend/config.sample.json | 56 +++---- web/backend/kioubit_verify.py | 28 ++-- web/backend/main.py | 259 ++++++++++++++++++++---------- web/frontend/base.html | 41 +++-- web/frontend/index.html | 49 +++--- web/frontend/login.html | 77 ++++++--- web/frontend/peerings-delete.html | 45 ++++++ web/frontend/peerings-new.html | 6 +- web/frontend/peerings.html | 85 ++++++---- web/frontend/static/style.css | 45 ++++-- 10 files changed, 455 insertions(+), 236 deletions(-) create mode 100644 web/frontend/peerings-delete.html diff --git a/web/backend/config.sample.json b/web/backend/config.sample.json index 61446cb..788dab9 100644 --- a/web/backend/config.sample.json +++ b/web/backend/config.sample.json @@ -1,29 +1,29 @@ { - "nodes": { - "": { - "endpoint": "", //optional, recommended, default: None/null - "api-con": "http://:/", // required - "#comment": "/* from here: data to be displayed on the webinterface */", - "country": "...", // Countrycode: 2 capital letters - "city": "...", - "wg-key": "...=", // pubkey of node; required - "internal-v4": "172.2x.xxx.xxx", //at least one ipv{4,6} addr required - "internal-v6": "fdxx:...", - "internal-v4ll": "169.254.xxx.xxx", - "internal-v6ll": "fe80::...", - "note": "...", //optional, special precausions, like only supporting a specific amount of peers/ipv{4,6} in clearnet, etc - "capacity": 100 //optional, default: -1 (infinite); estimated capacity of that node (i.e. OPENVZ(7) only has userspace WG (which consumes memory for every interface created)) - } - }, - "MNT": "YOUR-MNT", // your MNT tag - "ASN": "424242000", //Your ASN (used to generate default peer ListenPorts) - "listen": "0.0.0.0", - "port": 8042, - "domain": "example.org", // domain to use for kioubit verification service - "base-dir": "/", //optional:directury for which it is reachable (if behind some sort of reverse proxy) default "/" - "peerings": "/path/to/peering-config.json", // optional; default "$PWD/peerings", directory to save existing peerings to - "production": true, //optional, default true; - "debug-mode": false, // optional; whethet to enable debugging; default false - "flask-secret-key": "", // secret key for session cookies - "flask-template-dir": "../frontend/" // optional; default "../frontend" -} \ No newline at end of file + "nodes": { + "": { + "endpoint": "", //optional, recommended, default: None/null + "api-con": "http://:/", // required + "#comment": "/* from here: data to be displayed on the webinterface */", + "country": "...", // Countrycode: 2 capital letters + "city": "...", + "wg-key": "...=", // pubkey of node; required + "internal-v4": "172.2x.xxx.xxx", //at least one ipv{4,6} addr required + "internal-v6": "fdxx:...", + "internal-v4ll": "169.254.xxx.xxx", + "internal-v6ll": "fe80::...", + "note": "...", //optional, special precausions, like only supporting a specific amount of peers/ipv{4,6} in clearnet, etc + "capacity": 100 //optional, default: -1 (infinite); estimated capacity of that node (i.e. OPENVZ(7) only has userspace WG (which consumes memory for every interface created)) + } + }, + "MNT": "YOUR-MNT", // your MNT tag + "ASN": "424242000", //Your ASN (used to generate default peer ListenPorts) + "listen": "0.0.0.0", + "port": 8042, + "domain": "example.org", // domain to use for kioubit verification service + "base-dir": "/", //optional:directury for which it is reachable (if behind some sort of reverse proxy) default "/" + "peerings": "/path/to/peering-config.json", // optional; default "$PWD/peerings", directory to save existing peerings to + "production": true, //optional, default true; + "debug-mode": false, // optional; whethet to enable debugging; default false + "flask-secret-key": "", // secret key for session cookies + "flask-template-dir": "../frontend/" // optional; default "../frontend" +} diff --git a/web/backend/kioubit_verify.py b/web/backend/kioubit_verify.py index 11fe87c..69a7a63 100644 --- a/web/backend/kioubit_verify.py +++ b/web/backend/kioubit_verify.py @@ -1,15 +1,20 @@ #! /usr/bin/env python3 -import base64, os, json, time, logging +import base64 +import os +import json +import time +import logging import OpenSSL from OpenSSL.crypto import load_publickey, FILETYPE_PEM, verify, X509 PUBKEY_FILE = os.path.dirname(__file__)+"/kioubit-auth-pubkey.pem" + class AuthVerifyer (): - def __init__(self,domain, pubkey=PUBKEY_FILE): + def __init__(self, domain, pubkey=PUBKEY_FILE): self.domain = domain with open(pubkey) as pk: pk_content = "" @@ -19,13 +24,13 @@ class AuthVerifyer (): pkey = load_publickey(FILETYPE_PEM, pk_content) self.x509 = X509() self.x509.set_pubkey(pkey) - + logging.debug(self.x509) - + def verify(self, params, signature): # logging.debug(type(sig)) - #OpenSSL_verify(self.pubkey, sig - #, base64.b64decode(params), "sha512") + # OpenSSL_verify(self.pubkey, sig + # , base64.b64decode(params), "sha512") sig = base64.b64decode(signature) logging.info(f"sig: {sig}") logging.info(f"params: {params}") @@ -33,10 +38,10 @@ class AuthVerifyer (): verify(self.x509, sig, params, 'sha512') except OpenSSL.crypto.Error: return False, "Signature Failed" - + try: user_data = json.loads(base64.b64decode(params)) - if (time.time() - user_data["time"] )> 60: + if (time.time() - user_data["time"]) > 60: return False, "Signature to old" except json.decoder.JSONDecodeError: # we shouldn't get here unless kioubit's service is misbehaving @@ -46,11 +51,12 @@ class AuthVerifyer (): logging.debug(user_data) return True, user_data + if __name__ == "__main__": example_com_verifier = AuthVerifyer("example.com") - logging.info (example_com_verifier.verify( + logging.info(example_com_verifier.verify( params=b"eyJhc24iOiI0MjQyNDIzMDM1IiwidGltZSI6MTY2ODI2NjkyNiwiYWxsb3dlZDQiOiIxNzIuMjIuMTI1LjEyOFwvMjYsMTcyLjIwLjAuODFcLzMyIiwiYWxsb3dlZDYiOiJmZDYzOjVkNDA6NDdlNTo6XC80OCxmZDQyOmQ0MjpkNDI6ODE6OlwvNjQiLCJtbnQiOiJMQVJFLU1OVCIsImF1dGh0eXBlIjoibG9naW5jb2RlIiwiZG9tYWluIjoic3ZjLmJ1cmJsZS5kbjQyIn0=", signature=b"MIGIAkIBAmwz3sQ1vOkH8+8e0NJ8GsUqKSaazIWmYDp60sshlTo7gCAopZOZ6/+tD6s+oEGM1i5mKGbHgK9ROATQLHxUZecCQgCa2N828uNn76z1Yg63/c7veMVIiK4l1X9TCUepJnJ3mCto+7ogCP+2vQm6GHipSNRF4wnt6tZbir0HZvrqEnRAmA==" - ) ) -#params = "eyJhc24iOiI0MjQyNDIzMDM1IiwidGltZSI6MTY2ODI1NjI5NSwiYWxsb3dlZDQiOiIxNzIuMjIuMTI1LjEyOFwvMjYsMTcyLjIwLjAuODFcLzMyIiwiYWxsb3dlZDYiOiJmZDYzOjVkNDA6NDdlNTo6XC80OCxmZDQyOmQ0MjpkNDI6ODE6OlwvNjQiLCJtbnQiOiJMQVJFLU1OVCIsImF1dGh0eXBlIjoibG9naW5jb2RlIiwiZG9tYWluIjoic3ZjLmJ1cmJsZS5kbjQyIn0=", + )) +#params = "eyJhc24iOiI0MjQyNDIzMDM1IiwidGltZSI6MTY2ODI1NjI5NSwiYWxsb3dlZDQiOiIxNzIuMjIuMTI1LjEyOFwvMjYsMTcyLjIwLjAuODFcLzMyIiwiYWxsb3dlZDYiOiJmZDYzOjVkNDA6NDdlNTo6XC80OCxmZDQyOmQ0MjpkNDI6ODE6OlwvNjQiLCJtbnQiOiJMQVJFLU1OVCIsImF1dGh0eXBlIjoibG9naW5jb2RlIiwiZG9tYWluIjoic3ZjLmJ1cmJsZS5kbjQyIn0=", #signature = 'MIGHAkFy1m+9ahjIc5cJk/p+RiXJbhbWT5rPSJNg9Q3c8UTAM4F7lz2OqdWHw6GZN5NQgvqm6OB3Y751djYwCd54y2Kn4wJCAcBaOrtSclxkGIleVx183PhTnSr97r2F089PsDzNXIBvH5pYUwvJX7hG0op0f5tPm7fl12HOOrr8Q6kWW+XTrgGX' diff --git a/web/backend/main.py b/web/backend/main.py index 3b260c4..e84be04 100644 --- a/web/backend/main.py +++ b/web/backend/main.py @@ -2,21 +2,30 @@ from flask import Flask, Response, redirect, render_template, request, session, abort import werkzeug.exceptions as werkzeug_exceptions -import json, os, base64, logging, random +import json +import os +import base64 +import logging +import random from functools import wraps from ipaddress import ip_address, ip_network, IPv4Network, IPv6Network import kioubit_verify app = Flask(__name__) + class Config (dict): - def __init__(self, configfile:str = None): + def __init__(self, configfile: str = None): if configfile: self.configfile = configfile else: - if os.path.exists("./config.json"): self.configfile = "./config.json" - elif os.path.exists("/etc/dn42-autopeer/config.json"): self.configfile = "/etc/dn42-autopeer/config,json" - else: raise FileNotFoundError("no config file found in ./config.json or /etc/dn42-autopeer/config.json") + if os.path.exists("./config.json"): + self.configfile = "./config.json" + elif os.path.exists("/etc/dn42-autopeer/config.json"): + self.configfile = "/etc/dn42-autopeer/config,json" + else: + raise FileNotFoundError( + "no config file found in ./config.json or /etc/dn42-autopeer/config.json") self._load_config() self.keys = self._config.keys #self.__getitem__ = self._config.__getitem__ @@ -27,29 +36,31 @@ class Config (dict): def __delitem__(self, v): raise NotImplementedError() - super().__delitem__(self,v) + super().__delitem__(self, v) + def __getitem__(self, k): return self._config[k] - + def _load_config(self): with open(self.configfile) as cf: try: self._config = json.load(cf) except json.decoder.JSONDecodeError: raise SyntaxError(f"no valid JSON found in '{cf.name}'") - + if not "flask-template-dir" in self._config: - self._config["flask-template-dir"] = "../frontend" - + self._config["flask-template-dir"] = "../frontend" + if not "debug-mode" in self._config: - self._config["debug-mode"] = False + self._config["debug-mode"] = False if not "base-dir" in self._config: self._config["base-dir"] = "/" - + if not "peerings-data" in self._config: self._config["peering-data"] = "./peerings" logging.info(self._config) + class PeeringManager: def __init__(self, peerings_file): @@ -59,15 +70,15 @@ class PeeringManager: def _load_peerings(self): if not os.path.exists(self._peering_file): - with open(self._peering_file, "x") as p: - json.dump({"mnter":{},"asn":{}}, p) - try: - with open(self._peering_file,"r") as p: + with open(self._peering_file, "x") as p: + json.dump({"mnter": {}, "asn": {}}, p) + try: + with open(self._peering_file, "r") as p: self.peerings = json.load(p) except json.decoder.JSONDecodeError: - with open(self._peering_file, "w") as p: - json.dump({"mnter":{},"asn":{}}, p) - with open(self._peering_file,"r") as p: + with open(self._peering_file, "w") as p: + json.dump({"mnter": {}, "asn": {}}, p) + with open(self._peering_file, "r") as p: self.peerings = json.load(p) # self.peerings = {} @@ -87,6 +98,30 @@ class PeeringManager: with open(self._peering_file, "w") as p: json.dump(self.peerings, p, indent=4) + def _update_nodes(mode, peering, new_peering=None): + """mode: "add","update","delete + peering: peering to send to node (included in peering) + new_peering: if mode=="update" the new peering to update to + """ + pass + + def exists(self, asn, node, mnt=None, wg_key=None, endpoint=None, ipv6ll=None, ipv4=None, ipv6=None, bgp_mp=True, bgp_enh=True): + """checks if a peerings with specific data already exists""" + # check if mnt is specified, already exists in the database and if that mnt has the specified ASn -> if not: return False + if mnt and not (mnt in self.peerings["mnter"] and asn in self.peerings["mnter"][mnt]): + return False + selected_peerings = self.peerings["asn"][asn] + # check if the ASn even has peerings + if len(selected_peerings) == 0: + return False + for p in selected_peerings: + if p["node"] == node: + if (not wg_key or p["wg_key"] == wg_key) and (not endpoint or p["endpoint"] == endpoint) \ + and (not ipv6ll or p["ipv6ll"] == ipv6ll) and (not ipv4 or p["ipv4"] == ipv4) and (not ipv6 or p["ipv6"] == ipv6)\ + and (not bgp_mp or p["bgp_mp"] == bgp_mp) and (not bgp_enh or p["bgp_enh"] == bgp_enh): + return True + return False + def get_peerings_by_mnt(self, mnt): # print(self.peerings) try: @@ -101,7 +136,7 @@ class PeeringManager: except KeyError: return {} - def add_peering(self, mnt, asn, node, wg_key, endpoint=None, ipv6ll=None, ipv4=None, ipv6=None, bgp_mp=True, bgp_enh=True): + def add_peering(self, asn, node, mnt, wg_key, endpoint=None, ipv6ll=None, ipv4=None, ipv6=None, bgp_mp=True, bgp_enh=True): # check if this MNT already has a/this asn try: if not asn in self.peerings["mnter"][mnt]: @@ -115,16 +150,19 @@ class PeeringManager: self.peerings["asn"][asn] = [] except KeyError: self.peerings["asn"][asn] = [] - - # deny more than one peering per ASN to one node + + # deny more than one peering per ASN to one node for peering in self.peerings["asn"][asn]: - if peering["node"] == node: return False - - self.peerings["asn"][asn].append({"MNT":mnt,"ASN":asn, "node": node, "wg_key":wg_key, "endpoint": endpoint,"ipv6ll":ipv6ll,"ipv4":ipv4,"ipv6":ipv6, "bgp_mp":bgp_mp, "bgp_enh":bgp_enh}) + if peering["node"] == node: + return False + + self.peerings["asn"][asn].append({"MNT": mnt, "ASN": asn, "node": node, "wg_key": wg_key, "endpoint": endpoint, + "ipv6ll": ipv6ll, "ipv4": ipv4, "ipv6": ipv6, "bgp_mp": bgp_mp, "bgp_enh": bgp_enh}) self._save_peerings() return True - def update_peering(self, mnt, asn, node, wg_key, endpoint=None, ipv6ll=None, ipv4=None, ipv6=None, bgp_mp=True, bgp_enh=True): + + def update_peering(self, asn, node, mnt, wg_key, endpoint=None, ipv6ll=None, ipv4=None, ipv6=None, bgp_mp=True, bgp_enh=True): # check if this MNT already has a/this asn try: if not asn in self.peerings["mnter"][mnt]: @@ -138,26 +176,42 @@ class PeeringManager: return False except KeyError: return False - + success = False for pNr in range(len(self.peerings["asn"][asn])): if self.peerings["asn"][asn][pNr]["node"] == node: - self.peerings["asn"][asn][pNr] = {"MNT":mnt,"ASN":asn, "node": node, "wg_key":wg_key, "endpoint": endpoint,"ipv6ll":ipv6ll,"ipv4":ipv4,"ipv6":ipv6, "bgp_mp":bgp_mp, "bgp_enh":bgp_enh} + self.peerings["asn"][asn][pNr] = {"MNT": mnt, "ASN": asn, "node": node, "wg_key": wg_key, + "endpoint": endpoint, "ipv6ll": ipv6ll, "ipv4": ipv4, "ipv6": ipv6, "bgp_mp": bgp_mp, "bgp_enh": bgp_enh} success = True - if not success: return False + if not success: + return False self._save_peerings() return True - + + def delete_peering(self, asn, node, mnt, wg_key=None): + if not self.exists(asn, node, mnt=mnt, wg_key=wg_key): + return False + for p in self.peerings["asn"][asn]: + if p["node"] == node: + if wg_key and p["wg_key"] != wg_key: + continue + self.peerings["asn"][asn].remove(p) + self._save_peerings() + return True + # if nothing got found (should have been catched by self.exists) + return False + config = Config() peerings = PeeringManager(config["peerings"]) kverifyer = kioubit_verify.AuthVerifyer(config["domain"]) + def check_peering_data(form): new_peering = {} # errors = 0 - - ## check if all required (and enabled) options are specified + + # check if all required (and enabled) options are specified try: new_peering["peer-asn"] = session["user-data"]["asn"] new_peering["peer-wgkey"] = form["peer-wgkey"] @@ -167,7 +221,7 @@ def check_peering_data(form): raise ValueError("peer-endpoint") else: new_peering["peer-endpoint"] = None - + if "peer-v6ll-enabled" in form and form["peer-v6ll-enabled"] == "on": new_peering["peer-v6ll"] = form["peer-v6ll"] if new_peering["peer-v6ll"] == "": @@ -197,7 +251,7 @@ def check_peering_data(form): print(new_peering) - ## check wireguard key + # check wireguard key wg_key_invalid = False if len(new_peering["peer-wgkey"]) != 44: wg_key_invalid = True @@ -205,32 +259,35 @@ def check_peering_data(form): base64.b64decode(new_peering["peer-wgkey"]) except: wg_key_invalid = True - if wg_key_invalid: + if wg_key_invalid: return False, "invalid wireguard Key" - ## check endpoint + # check endpoint if new_peering["peer-endpoint"]: if not new_peering["peer-endpoint"].split(":")[-1].isnumeric(): return False, "no port number in endpoint" elif len(new_peering["peer-endpoint"].split(":")) < 2 and not "." in new_peering["peer-endpoint"]: return False, "endpoint doesn't look like a ip address or fqdn" - ## check if at least one ip is specified/enabled + # check if at least one ip is specified/enabled try: if not (new_peering["peer-v6ll"] or new_peering["peer-v4"] or new_peering["peer-v6"]): return False, "at least one of the ip addresses must be enabled and specified" except KeyError: return False, "one of the values isn't valid" - ## check if supplied ip addresses are valid + # check if supplied ip addresses are valid try: if new_peering["peer-v6ll"]: ipv6ll = ip_address(new_peering["peer-v6ll"]) - if not ipv6ll.version == 6: raise ValueError() - if not ipv6ll.is_link_local: raise ValueError() + if not ipv6ll.version == 6: + raise ValueError() + if not ipv6ll.is_link_local: + raise ValueError() if new_peering["peer-v4"]: ipv4 = ip_address(new_peering["peer-v4"]) - if not ipv4.version == 4: raise ValueError() + if not ipv4.version == 4: + raise ValueError() if ipv4.is_link_local: pass elif ipv4.is_private: @@ -243,12 +300,16 @@ def check_peering_data(form): is_in_allowed = True if not is_in_allowed: return False, "supplied ipv4 addr not in allowed ip range" - else: raise ValueError() + else: + raise ValueError() if new_peering["peer-v6"]: ipv6 = ip_address(new_peering["peer-v6"]) - if not ipv6.version == 6: raise ValueError() - if not ipv6.is_private: raise ValueError() - if ipv6.is_link_local: raise ValueError() + if not ipv6.version == 6: + raise ValueError() + if not ipv6.is_private: + raise ValueError() + if ipv6.is_link_local: + raise ValueError() is_in_allowed = False if session["user-data"]["allowed6"]: for allowed6 in session["user-data"]["allowed6"].split(","): @@ -269,6 +330,8 @@ def check_peering_data(form): except ValueError: ... return True, new_peering + + def auth_required(): def wrapper(f): @wraps(f) @@ -283,17 +346,17 @@ def auth_required(): @app.route("/api/auth/kverify", methods=["GET", "POST"]) def kioubit_auth(): - try: + try: params = request.args["params"] signature = request.args["signature"] except KeyError: - return render_template("login.html", session=session,config=config,return_addr=session["return_url"], msg='"params" or "signature" missing') + return render_template("login.html", session=session, config=config, return_addr=session["return_url"], msg='"params" or "signature" missing') - success, msg = kverifyer.verify(params, signature) - try: logging.debug(base64.b64decode(params)) - except: logging.debug("invalid Base64 data provided") - + try: + logging.debug(base64.b64decode(params)) + except: + logging.debug("invalid Base64 data provided") if success: session["user-data"] = msg @@ -304,68 +367,89 @@ def kioubit_auth(): return redirect(f"{config['base-dir']}peerings") else: try: - return render_template("login.html", session=session,config=config,return_addr=session["return_url"], msg=msg) + return render_template("login.html", session=session, config=config, return_addr=session["return_url"], msg=msg) except KeyError: - return render_template("login.html", session=session,config=config,return_addr=f"{config['base-dir']}peerings", msg=msg) + return render_template("login.html", session=session, config=config, return_addr=f"{config['base-dir']}peerings", msg=msg) + @app.route("/logout") def logout(): session.clear() return redirect("/") -@app.route("/login",methods=["GET","POST"]) + +@app.route("/login", methods=["GET", "POST"]) def login(): print(session) if request.method == "GET": session["return_url"] = request.args["return"] if "return" in request.args else "" - + return render_template("login.html", session=session, config=config, return_addr=session["return_url"]) elif request.method == "POST" and config["debug-mode"]: try: print(request.form) if request.form["theanswer"] != "42": msg = "what is the answer for everything?" - return render_template("login.html", session=session,config=config,return_addr=session["return_url"], msg=msg) + return render_template("login.html", session=session, config=config, return_addr=session["return_url"], msg=msg) mnt = request.form["mnt"] - if not mnt.upper().endswith("-MNT"): raise ValueError + if not mnt.upper().endswith("-MNT"): + raise ValueError asn = request.form["asn"] asn = asn[2:] if asn[:2].lower() == "as" else asn int(asn) if "allowed4" in request.form: allowed4 = request.form["allowed4"] - v4_ranges = allowed4.split(",") if "," in allowed4 else [allowed4] + v4_ranges = allowed4.split( + ",") if "," in allowed4 else [allowed4] for v4_range in v4_ranges: IPv4Network(v4_range) else: allowed4 = None if "allowed6" in request.form: allowed6 = request.form["allowed6"] - v6_ranges = allowed6.split(",") if "," in allowed6 else [allowed6] + v6_ranges = allowed6.split( + ",") if "," in allowed6 else [allowed6] for v6_range in v6_ranges: IPv6Network(v6_range) else: allowed6 = None - session["user-data"] = {'asn':asn,'allowed4': allowed4, 'allowed6': allowed6,'mnt':mnt, 'authtype': "debug"} + session["user-data"] = {'asn': asn, 'allowed4': allowed4, + 'allowed6': allowed6, 'mnt': mnt, 'authtype': "debug"} session["login"] = mnt return redirect(session["return_url"]) except ValueError: msg = "at least one of the values provided is wrong/invalid" - return render_template("login.html", session=session,config=config,return_addr=session["return_url"], msg=msg) + return render_template("login.html", session=session, config=config, return_addr=session["return_url"], msg=msg) except KeyError: msg = "not all required field were specified" - return render_template("login.html", session=session,config=config,return_addr=session["return_url"], msg=msg) + return render_template("login.html", session=session, config=config, return_addr=session["return_url"], msg=msg) elif request.method == "POST" and not config["debug-mode"]: abort(405) return redirect(request.args["return"]) - -@app.route("/peerings/delete", methods=["GET","DELETE"]) + +@app.route("/peerings/delete", methods=["GET", "POST", "DELETE"]) @auth_required() def peerings_delete(): - return f"{request.method} /peerings/delete?{str(request.args)}{str(request.form)}" - -@app.route("/peerings/edit", methods=["GET","POST"]) + if request.method == "GET": + return render_template("peerings-delete.html", session=session, config=config, request_args=request.args) + return f"{request.method} /peerings/delete?{str(request.args)}{str(request.form)}" + elif request.method in ["POST", "DELETE"]: + if not request.form["confirm"] == "on": + return render_template("peerings-delete.html", session=session, config=config, request_args=request.args, msg="you have to confirm the deletion first") + if not peerings.exists(request.args["asn"], request.args["node"], mnt=session["login"]): + return render_template("peerings-delete.html", session=session, config=config, request_args=request.args, msg="the peering you requested to delete doesn't exist (anymore) or you are not authorized to delete it") + print(str(request)) + if not peerings.delete_peering(request.args["asn"], request.args["node"], mnt=session["login"]): + return render_template("peerings-delete.html", session=session, config=config, request_args=request.args, msg="deletion of the peering requested failed, maybe you are not authorized or that peering doesn't exist") + session["msg"] = {"msg": "peer-del", + "node": request.args["node"], "asn": request.args["asn"]} + return redirect("../peerings") + return f"{request.method} /peerings/delete {request.args} {request.form}" + + +@app.route("/peerings/edit", methods=["GET", "POST"]) @auth_required() def peerings_edit(): print(session) @@ -380,57 +464,60 @@ def peerings_edit(): print(p) break return render_template("peerings-edit.html", session=session, config=config, mnt_peerings=mnt_peerings, selected_peering=selected_peering) - else: + else: return render_template("peerings-edit.html", session=session, config=config, mnt_peerings=mnt_peerings, selected_peering=None) elif request.method == "POST": print(request.args) print(request.form) if not "node" in request.args or not request.args["node"]: - return render_template("peerings-edit.html", session=session,config=config, peerings=peerings, msg="no node specified, please click one of the buttons above") + return render_template("peerings-edit.html", session=session, config=config, peerings=peerings, msg="no node specified, please click one of the buttons above") peering_valid, peering_or_msg = check_peering_data(request.form) print(peering_valid) print(peering_or_msg) if not peering_valid: - return render_template("peerings-edit.html", session=session,config=config, peerings=peerings, msg=peering_or_msg), 400 + return render_template("peerings-edit.html", session=session, config=config, peerings=peerings, msg=peering_or_msg), 400 if not peerings.update_peering(session["user-data"]["mnt"], session["user-data"]["asn"], request.args["node"], peering_or_msg["peer-wgkey"], peering_or_msg["peer-endpoint"], peering_or_msg["peer-v6ll"], peering_or_msg["peer-v4"], peering_or_msg["peer-v6"], peering_or_msg["bgp-mp"], peering_or_msg["bgp-enh"]): - return render_template("peerings-edit.html", session=session,config=config, peerings=peerings, msg="such a peering doesn't exist(yet)"), 400 + return render_template("peerings-edit.html", session=session, config=config, peerings=peerings, msg="such a peering doesn't exist(yet)"), 400 return redirect(f"{config['base-dir']}peerings") return f"{request.method} /peerings/edit?{str(request.args)}{str(request.form)}" -@app.route("/peerings/new", methods=["GET","POST"]) + + +@app.route("/peerings/new", methods=["GET", "POST"]) @auth_required() def peerings_new(): print(session) if request.method == "GET": if "node" in request.args and request.args["node"] in config["nodes"]: return render_template("peerings-new.html", config=config, selected_node=request.args["node"], peerings=peerings) - else: - return render_template("peerings-new.html", session=session,config=config, peerings=peerings) + else: + return render_template("peerings-new.html", session=session, config=config, peerings=peerings) elif request.method == "POST": print(request.args) print(request.form) if not "node" in request.args or not request.args["node"]: - return render_template("peerings-new.html", session=session,config=config, peerings=peerings, msg="no node specified, please click one of the buttons above") + return render_template("peerings-new.html", session=session, config=config, peerings=peerings, msg="no node specified, please click one of the buttons above") peering_valid, peering_or_msg = check_peering_data(request.form) if not peering_valid: - return render_template("peerings-new.html", session=session,config=config, peerings=peerings, msg=peering_or_msg), 400 + return render_template("peerings-new.html", session=session, config=config, peerings=peerings, msg=peering_or_msg), 400 if not peerings.add_peering(session["user-data"]["mnt"], session["user-data"]["asn"], request.args["node"], peering_or_msg["peer-wgkey"], peering_or_msg["peer-endpoint"], peering_or_msg["peer-v6ll"], peering_or_msg["peer-v4"], peering_or_msg["peer-v6"], peering_or_msg["bgp-mp"], peering_or_msg["bgp-enh"]): - return render_template("peerings-new.html", session=session,config=config, peerings=peerings, msg="this ASN already has a peering with the requested node"), 400 + return render_template("peerings-new.html", session=session, config=config, peerings=peerings, msg="this ASN already has a peering with the requested node"), 400 return redirect(f"{config['base-dir']}peerings") -@app.route("/peerings", methods=["GET","POST","DELETE"]) + +@app.route("/peerings", methods=["GET", "POST", "DELETE"]) @auth_required() def peerings_view(): print(session) if request.method == "GET": if "node" in request.args and request.args["node"] in config["nodes"]: return render_template("peerings.html", config=config, selected_node=request.args["node"], peerings=peerings) - else: - return render_template("peerings.html", session=session,config=config, peerings=peerings) + else: + return render_template("peerings.html", session=session, config=config, peerings=peerings) elif request.method == "POST": return peerings_new() elif request.method == "DELETE": @@ -438,6 +525,7 @@ def peerings_view(): else: abort(405) + @app.route("/") def index(): print(session) @@ -446,19 +534,20 @@ def index(): # print (node) return render_template("index.html", session=session, config=config._config) + def main(): - app.static_folder= config["flask-template-dir"]+"/static/" - app.template_folder=config["flask-template-dir"] + app.static_folder = config["flask-template-dir"]+"/static/" + app.template_folder = config["flask-template-dir"] app.secret_key = config["flask-secret-key"] if "production" in config and config["production"] == False: logging.getLogger(__name__).setLevel(0) - app.run(host=config["listen"], port=config["port"], debug=config["debug-mode"], threaded=True) + app.run(host=config["listen"], port=config["port"], + debug=config["debug-mode"], threaded=True) else: from waitress import serve logging.getLogger(__name__).setLevel(logging.INFO) serve(app, host=config["listen"], port=config["port"]) - if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/web/frontend/base.html b/web/frontend/base.html index 7c90ae6..31242a2 100644 --- a/web/frontend/base.html +++ b/web/frontend/base.html @@ -1,22 +1,29 @@ - - - - + + + + {{config["MNT"]}} Autopeering - - - -
{{config["MNT"]}} Autopeering{% if "login" in session %} {% else %} login{%endif%}
-
- {% block content %} - {% endblock %} -
+ + + +
+
+ {{config["MNT"]}} Autopeering{% if + "login" in session %} +
+ manage + logout +
+ {% else %} + login{%endif%} +
+
{% block content %} {% endblock %}
-
-
-
🄯 LARE-MNT
+
+
+
🄯 LARE-MNT
- - \ No newline at end of file + + diff --git a/web/frontend/index.html b/web/frontend/index.html index 24383ef..608f98c 100644 --- a/web/frontend/index.html +++ b/web/frontend/index.html @@ -1,28 +1,25 @@ -{% extends 'base.html' %} - -{% block content %} +{% extends 'base.html' %} {% block content %} +
+ + + + + + + + + + + {% for node in config["nodes"] %} + + + + + + + + {% endfor %} +
NodeNameCountryCitypeeringsPeer!
{{node}}{{config["nodes"][node]["country"]}}{{config["nodes"][node]["city"]}}peer
-
- - - - - - - - - - - {% for node in config["nodes"] %} - - - - - - - - {% endfor %} -
NodeNameCountryCitypeeringsPeer!
{{node}}{{config["nodes"][node]["country"]}}{{config["nodes"][node]["city"]}}peer
-
-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/web/frontend/login.html b/web/frontend/login.html index 76ea1b9..7d0f1ab 100644 --- a/web/frontend/login.html +++ b/web/frontend/login.html @@ -1,31 +1,64 @@ -{% extends 'base.html' %} - -{% block content %} - -{% if msg %} -
- {{msg}} -
+{% extends 'base.html' %} {% block content %} {% if msg %} +
{{msg}}
{% endif %}
- - - +
{% if config["debug-mode"] %}
-
-
-
-
-
-
- +
+
+
+
+
+
+
-{% endif %} - -{% endblock %} \ No newline at end of file +{% endif %} {% endblock %} diff --git a/web/frontend/peerings-delete.html b/web/frontend/peerings-delete.html new file mode 100644 index 0000000..a884c11 --- /dev/null +++ b/web/frontend/peerings-delete.html @@ -0,0 +1,45 @@ +{% extends 'base.html' %} {% block content %} + +
+
+ {% if msg %}{{msg}}{% else %}confirm the deletion of this peering{% endif %} +
+ + + + + + + + + + + + + +
+ +
+ +
+ +
+
+ + + +
+
+ +{% endblock %} diff --git a/web/frontend/peerings-new.html b/web/frontend/peerings-new.html index 9a045ae..d8ae06f 100644 --- a/web/frontend/peerings-new.html +++ b/web/frontend/peerings-new.html @@ -182,7 +182,7 @@ {% endfor %}
-
{% if msg %}{{msg}}{%endif%}
+
{% if msg %}{{msg}}{%elif not selected_node%}please select one of the nodes above{%endif%}
@@ -283,8 +283,8 @@ protocol bgp dn42_{{config["MNT"][:-4].lower()}}_v6 from dnpeers { }; } - - + +