add ability to delete peerings
+ format all files
This commit is contained in:
parent
ff4865f4b6
commit
85f8c936be
10 changed files with 455 additions and 236 deletions
|
@ -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 = ""
|
||||
|
@ -24,8 +29,8 @@ class AuthVerifyer ():
|
|||
|
||||
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}")
|
||||
|
@ -36,7 +41,7 @@ class AuthVerifyer ():
|
|||
|
||||
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=",
|
||||
#signature = 'MIGHAkFy1m+9ahjIc5cJk/p+RiXJbhbWT5rPSJNg9Q3c8UTAM4F7lz2OqdWHw6GZN5NQgvqm6OB3Y751djYwCd54y2Kn4wJCAcBaOrtSclxkGIleVx183PhTnSr97r2F089PsDzNXIBvH5pYUwvJX7hG0op0f5tPm7fl12HOOrr8Q6kWW+XTrgGX'
|
||||
|
|
|
@ -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,7 +36,8 @@ 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]
|
||||
|
||||
|
@ -50,6 +60,7 @@ class Config (dict):
|
|||
self._config["peering-data"] = "./peerings"
|
||||
logging.info(self._config)
|
||||
|
||||
|
||||
class PeeringManager:
|
||||
|
||||
def __init__(self, peerings_file):
|
||||
|
@ -60,14 +71,14 @@ 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)
|
||||
json.dump({"mnter": {}, "asn": {}}, p)
|
||||
try:
|
||||
with open(self._peering_file,"r") as p:
|
||||
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:
|
||||
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]:
|
||||
|
@ -118,13 +153,16 @@ class PeeringManager:
|
|||
|
||||
# deny more than one peering per ASN to one node
|
||||
for peering in self.peerings["asn"][asn]:
|
||||
if peering["node"] == node: return False
|
||||
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.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]:
|
||||
|
@ -142,22 +180,38 @@ class PeeringManager:
|
|||
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"]
|
||||
|
@ -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
|
||||
|
@ -208,29 +262,32 @@ def check_peering_data(form):
|
|||
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)
|
||||
|
@ -287,13 +350,13 @@ def kioubit_auth():
|
|||
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,16 +367,18 @@ 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":
|
||||
|
@ -325,47 +390,66 @@ def login():
|
|||
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():
|
||||
|
||||
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"])
|
||||
|
||||
@app.route("/peerings/edit", methods=["GET", "POST"])
|
||||
@auth_required()
|
||||
def peerings_edit():
|
||||
print(session)
|
||||
|
@ -386,19 +470,21 @@ def peerings_edit():
|
|||
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)
|
||||
|
@ -406,23 +492,24 @@ def peerings_new():
|
|||
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)
|
||||
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)
|
||||
|
@ -430,7 +517,7 @@ def peerings_view():
|
|||
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)
|
||||
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()
|
|
@ -1,22 +1,29 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>{{config["MNT"]}} Autopeering</title>
|
||||
<link rel="stylesheet" href="{{config['base-dir']}}static/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="flex flex-row"><div></div><a href="{{config['base-dir']}}">{{config["MNT"]}} Autopeering</a>{% if "login" in session %} <div><a href="{{config['base-dir']}}peerings">manage</a> <a href="{{config['base-dir']}}logout">logout</a></div>{% else %} <a href="{{config['base-dir']}}login?return=/peerings">login</a>{%endif%}</header>
|
||||
<div class="content flex">
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
<link rel="stylesheet" href="{{config['base-dir']}}static/style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<header class="flex flex-row">
|
||||
<div></div>
|
||||
<a href="{{config['base-dir']}}">{{config["MNT"]}} Autopeering</a>{% if
|
||||
"login" in session %}
|
||||
<div>
|
||||
<a href="{{config['base-dir']}}peerings">manage</a>
|
||||
<a href="{{config['base-dir']}}logout">logout</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<a href="{{config['base-dir']}}login?return=/peerings">login</a>{%endif%}
|
||||
</header>
|
||||
<div class="content flex">{% block content %} {% endblock %}</div>
|
||||
<footer class="flex">
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div> 🄯 LARE-MNT</div>
|
||||
<div>🄯 LARE-MNT</div>
|
||||
</footer>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
|
@ -1,8 +1,5 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div>
|
||||
</div>
|
||||
{% extends 'base.html' %} {% block content %}
|
||||
<div></div>
|
||||
<div>
|
||||
<table>
|
||||
<thead>
|
||||
|
|
|
@ -1,31 +1,64 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% if msg %}
|
||||
<div style="background-color: red;">
|
||||
{{msg}}
|
||||
</div>
|
||||
{% extends 'base.html' %} {% block content %} {% if msg %}
|
||||
<div style="background-color: red">{{msg}}</div>
|
||||
{% endif %}
|
||||
|
||||
<form action="https://dn42.g-load.eu/auth/">
|
||||
<link rel="stylesheet" href="https://dn42.g-load.eu/auth/assets/button-font/auth.css">
|
||||
<input type="hidden" id="return" name="return" value='{{"http://"+config["domain"]+"/api/auth/kverify"}}'>
|
||||
<button type="submit" class="kioubit-btn kioubit-btn-primary kioubit-btn-dark kioubit-btn-main">
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://dn42.g-load.eu/auth/assets/button-font/auth.css"
|
||||
/>
|
||||
<input
|
||||
type="hidden"
|
||||
id="return"
|
||||
name="return"
|
||||
value='{{"http://"+config["domain"]+"/api/auth/kverify"}}'
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
class="kioubit-btn kioubit-btn-primary kioubit-btn-dark kioubit-btn-main"
|
||||
>
|
||||
<span class="icon-kioubit-auth"></span>Authenticate with Kioubit.dn42
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{% if config["debug-mode"] %}
|
||||
<form action="" method="post" class="flex">
|
||||
<label for="debug">Debug login, if you see this in Production contact {{config["MNT"]}}</label><br>
|
||||
<input type="text" name="mnt" id="mnt" placeholder="YOUR-MNT" required><br>
|
||||
<input type="text" name="asn" id="asn" placeholder="AS4242420000" required><br>
|
||||
<input type="text" name="allowed4" id="allowed4" placeholder="ipv4 subnet (optional)"><br>
|
||||
<input type="text" name="allowed6" id="allowed6" placeholder="ipv6 subnet (optional)"><br>
|
||||
<input type="number" name="theanswer" id="theanswer" placeholder="The answer for everything" required><br>
|
||||
<input type="submit" value="login">
|
||||
<label for="debug"
|
||||
>Debug login, if you see this in Production contact {{config["MNT"]}}</label
|
||||
><br />
|
||||
<input
|
||||
type="text"
|
||||
name="mnt"
|
||||
id="mnt"
|
||||
placeholder="YOUR-MNT"
|
||||
required
|
||||
/><br />
|
||||
<input
|
||||
type="text"
|
||||
name="asn"
|
||||
id="asn"
|
||||
placeholder="AS4242420000"
|
||||
required
|
||||
/><br />
|
||||
<input
|
||||
type="text"
|
||||
name="allowed4"
|
||||
id="allowed4"
|
||||
placeholder="ipv4 subnet (optional)"
|
||||
/><br />
|
||||
<input
|
||||
type="text"
|
||||
name="allowed6"
|
||||
id="allowed6"
|
||||
placeholder="ipv6 subnet (optional)"
|
||||
/><br />
|
||||
<input
|
||||
type="number"
|
||||
name="theanswer"
|
||||
id="theanswer"
|
||||
placeholder="The answer for everything"
|
||||
required
|
||||
/><br />
|
||||
<input type="submit" value="login" />
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
{% endif %} {% endblock %}
|
||||
|
|
45
web/frontend/peerings-delete.html
Normal file
45
web/frontend/peerings-delete.html
Normal file
|
@ -0,0 +1,45 @@
|
|||
{% extends 'base.html' %} {% block content %}
|
||||
|
||||
<form class="flex" action="" method="post">
|
||||
<div id="warning" style="background-color: yellow">
|
||||
{% if msg %}{{msg}}{% else %}confirm the deletion of this peering{% endif %}
|
||||
</div>
|
||||
<table>
|
||||
<tr>
|
||||
<td><label for="asn">ASn</label></td>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
id="asn"
|
||||
readonly="readonly"
|
||||
value="{{request_args['asn']}}"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="node">node</label></td>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
id="node"
|
||||
readonly="readonly"
|
||||
value="{{request_args['node']}}"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="confirm">confirm</label></td>
|
||||
<td>
|
||||
<input type="checkbox" name="confirm" id="delete-confirm" required />
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div>
|
||||
<a href="../peerings">
|
||||
<input type="button" class="button-green" value="back"
|
||||
/></a>
|
||||
<button type="submit" class="button-red">confirm</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
|
@ -182,7 +182,7 @@
|
|||
{% endfor %}
|
||||
</div>
|
||||
<form action="" method="post" class="flex" onsubmit="return form_validate(this)">
|
||||
<div id="peer-invalid-note" style="background-color:red;">{% if msg %}{{msg}}{%endif%}</div>
|
||||
<div id="peer-invalid-note" style="background-color:red;">{% if msg %}{{msg}}{%elif not selected_node%}please select one of the nodes above{%endif%}</div>
|
||||
<table>
|
||||
<tr>
|
||||
<td><label for="peer-asn">Your ASN</label></td>
|
||||
|
@ -284,7 +284,7 @@ protocol bgp dn42_{{config["MNT"][:-4].lower()}}_v6 from dnpeers {
|
|||
};
|
||||
}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<script>
|
||||
document.onload();
|
||||
|
|
|
@ -1,39 +1,58 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
{% extends 'base.html' %} {% block content %}
|
||||
<div>
|
||||
<a href="peerings/new"><button>add new</button></a>
|
||||
<a href="peerings/new"
|
||||
><button class="button-green default-border-color">add new</button></a
|
||||
>
|
||||
</div>
|
||||
<div class="flex flex-row">
|
||||
{% for peering in peerings.get_peerings_by_mnt(session["login"]) %}
|
||||
{% for peering in peerings.get_peerings_by_mnt(session["login"]) %}
|
||||
<div class="peering">
|
||||
<div>
|
||||
<div>Node: {{peering["node"]}}</div>
|
||||
</div>
|
||||
<div>
|
||||
<table>
|
||||
<tr><td>ASN:</td><td>{{peering["ASN"]}}</td></tr>
|
||||
<tr><td>WG-PublicKey:</td><td>{{peering["wg_key"][:8]}}...</td></tr>
|
||||
<tr>
|
||||
<td>ASN:</td>
|
||||
<td>{{peering["ASN"]}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>WG-PublicKey:</td>
|
||||
<td>{{peering["wg_key"][:8]}}...</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div>
|
||||
<table>
|
||||
{% if peering["ipv6ll"] %}<tr><td>ipv6 linklocal:</td><td>{{peering["ipv6ll"]}}</td></tr>{% endif %}
|
||||
{% if peering["ipv4"] %}<tr><td>ipv4:</td><td>{{peering["ipv4"]}}</td></tr>{% endif %}
|
||||
{% if peering["ipv6"] %}<tr><td>ipv6:</td><td>{{peering["ipv6"]}}</td></tr>{% endif %}
|
||||
{% if peering["ipv6ll"] %}
|
||||
<tr>
|
||||
<td>ipv6 linklocal:</td>
|
||||
<td>{{peering["ipv6ll"]}}</td>
|
||||
</tr>
|
||||
{% endif %} {% if peering["ipv4"] %}
|
||||
<tr>
|
||||
<td>ipv4:</td>
|
||||
<td>{{peering["ipv4"]}}</td>
|
||||
</tr>
|
||||
{% endif %} {% if peering["ipv6"] %}
|
||||
<tr>
|
||||
<td>ipv6:</td>
|
||||
<td>{{peering["ipv6"]}}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
</div>
|
||||
<!-- <div>{{peering}}</div> -->
|
||||
<div>
|
||||
<a href="peerings/edit?node={{peering['node']}}&asn={{peering['ASN']}}">
|
||||
<button class="peering-edit">edit</button>
|
||||
<button class="button-blue">edit</button>
|
||||
</a>
|
||||
<a href="peerings/delete?node={{peering['node']}}&asn={{peering['ASN']}}">
|
||||
<button class="peering-delete">delete</button>
|
||||
<button class="button-red">delete</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -17,6 +17,7 @@ header {
|
|||
background-color: var(--other-background);
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
@ -27,6 +28,7 @@ a {
|
|||
margin: auto;
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
|
||||
.content>* {
|
||||
padding: 5%;
|
||||
}
|
||||
|
@ -35,6 +37,7 @@ a {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flex-row {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
@ -54,6 +57,7 @@ footer {
|
|||
height: 40px;
|
||||
background: var(--other-background);
|
||||
}
|
||||
|
||||
footer.flex {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
@ -63,14 +67,14 @@ footer.flex {
|
|||
padding: 1%;
|
||||
}
|
||||
|
||||
.example-config > pre{
|
||||
.example-config>pre {
|
||||
border-color: var(--other-background);
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
border-style: groove;
|
||||
}
|
||||
|
||||
form > div > * {
|
||||
form>div>* {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
|
@ -85,40 +89,59 @@ form > div > * {
|
|||
width: 100%;
|
||||
margin: 10px;
|
||||
}
|
||||
.peering > div {
|
||||
|
||||
.peering>div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.peering > div > * {
|
||||
|
||||
.peering>div>* {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
button,
|
||||
input[type=button] {
|
||||
background-color: #00000020;
|
||||
border-color: var(--text-color);
|
||||
border-width: 5px;
|
||||
padding: 10px;
|
||||
}
|
||||
button.button-selected{
|
||||
|
||||
button.button-selected,
|
||||
input[type=button].button-selected {
|
||||
background-color: var(--other-background);
|
||||
color: var(--text-color-dark);
|
||||
}
|
||||
|
||||
.peering>* {
|
||||
width:auto;
|
||||
width: auto;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.peering-edit {
|
||||
.button-blue {
|
||||
border-color: lightblue;
|
||||
}
|
||||
|
||||
.peering-edit:hover {
|
||||
.button-blue:hover {
|
||||
background-color: #87cefaaa;
|
||||
}
|
||||
|
||||
.peering-delete {
|
||||
.button-red {
|
||||
border-color: darkred;
|
||||
}
|
||||
.peering-delete:hover {
|
||||
|
||||
.button-red:hover {
|
||||
background-color: #ff0000aa;
|
||||
}
|
||||
|
||||
.button-green {
|
||||
border-color: lightgreen;
|
||||
}
|
||||
|
||||
.button-green:hover {
|
||||
background-color: greenyellow;
|
||||
}
|
||||
|
||||
.default-border-color {
|
||||
border-color: inherit;
|
||||
}
|
Loading…
Add table
Reference in a new issue