add ability to delete peerings
+ format all files + fix "selected_peering" not being defined in /peerings/edit
This commit is contained in:
parent
ff4865f4b6
commit
537400f77a
10 changed files with 464 additions and 238 deletions
|
@ -1,29 +1,29 @@
|
||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"<nodename>": {
|
"<nodename>": {
|
||||||
"endpoint": "<clearnet-fqdn/ip-address>", //optional, recommended, default: None/null
|
"endpoint": "<clearnet-fqdn/ip-address>", //optional, recommended, default: None/null
|
||||||
"api-con": "http://<node-(internal)-ip/hostname>:<port>/", // required
|
"api-con": "http://<node-(internal)-ip/hostname>:<port>/", // required
|
||||||
"#comment": "/* from here: data to be displayed on the webinterface */",
|
"#comment": "/* from here: data to be displayed on the webinterface */",
|
||||||
"country": "...", // Countrycode: 2 capital letters
|
"country": "...", // Countrycode: 2 capital letters
|
||||||
"city": "...",
|
"city": "...",
|
||||||
"wg-key": "...=", // pubkey of node; required
|
"wg-key": "...=", // pubkey of node; required
|
||||||
"internal-v4": "172.2x.xxx.xxx", //at least one ipv{4,6} addr required
|
"internal-v4": "172.2x.xxx.xxx", //at least one ipv{4,6} addr required
|
||||||
"internal-v6": "fdxx:...",
|
"internal-v6": "fdxx:...",
|
||||||
"internal-v4ll": "169.254.xxx.xxx",
|
"internal-v4ll": "169.254.xxx.xxx",
|
||||||
"internal-v6ll": "fe80::...",
|
"internal-v6ll": "fe80::...",
|
||||||
"note": "...", //optional, special precausions, like only supporting a specific amount of peers/ipv{4,6} in clearnet, etc
|
"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))
|
"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
|
"MNT": "YOUR-MNT", // your MNT tag
|
||||||
"ASN": "424242000", //Your ASN (used to generate default peer ListenPorts)
|
"ASN": "424242000", //Your ASN (used to generate default peer ListenPorts)
|
||||||
"listen": "0.0.0.0",
|
"listen": "0.0.0.0",
|
||||||
"port": 8042,
|
"port": 8042,
|
||||||
"domain": "example.org", // domain to use for kioubit verification service
|
"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 "/"
|
"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
|
"peerings": "/path/to/peering-config.json", // optional; default "$PWD/peerings", directory to save existing peerings to
|
||||||
"production": true, //optional, default true;
|
"production": true, //optional, default true;
|
||||||
"debug-mode": false, // optional; whethet to enable debugging; default false
|
"debug-mode": false, // optional; whethet to enable debugging; default false
|
||||||
"flask-secret-key": "<secret-please-replace>", // secret key for session cookies
|
"flask-secret-key": "<secret-please-replace>", // secret key for session cookies
|
||||||
"flask-template-dir": "../frontend/" // optional; default "../frontend"
|
"flask-template-dir": "../frontend/" // optional; default "../frontend"
|
||||||
}
|
}
|
|
@ -1,15 +1,20 @@
|
||||||
#! /usr/bin/env python3
|
#! /usr/bin/env python3
|
||||||
|
|
||||||
import base64, os, json, time, logging
|
import base64
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import logging
|
||||||
import OpenSSL
|
import OpenSSL
|
||||||
from OpenSSL.crypto import load_publickey, FILETYPE_PEM, verify, X509
|
from OpenSSL.crypto import load_publickey, FILETYPE_PEM, verify, X509
|
||||||
|
|
||||||
|
|
||||||
PUBKEY_FILE = os.path.dirname(__file__)+"/kioubit-auth-pubkey.pem"
|
PUBKEY_FILE = os.path.dirname(__file__)+"/kioubit-auth-pubkey.pem"
|
||||||
|
|
||||||
|
|
||||||
class AuthVerifyer ():
|
class AuthVerifyer ():
|
||||||
|
|
||||||
def __init__(self,domain, pubkey=PUBKEY_FILE):
|
def __init__(self, domain, pubkey=PUBKEY_FILE):
|
||||||
self.domain = domain
|
self.domain = domain
|
||||||
with open(pubkey) as pk:
|
with open(pubkey) as pk:
|
||||||
pk_content = ""
|
pk_content = ""
|
||||||
|
@ -24,8 +29,8 @@ class AuthVerifyer ():
|
||||||
|
|
||||||
def verify(self, params, signature):
|
def verify(self, params, signature):
|
||||||
# logging.debug(type(sig))
|
# logging.debug(type(sig))
|
||||||
#OpenSSL_verify(self.pubkey, sig
|
# OpenSSL_verify(self.pubkey, sig
|
||||||
#, base64.b64decode(params), "sha512")
|
# , base64.b64decode(params), "sha512")
|
||||||
sig = base64.b64decode(signature)
|
sig = base64.b64decode(signature)
|
||||||
logging.info(f"sig: {sig}")
|
logging.info(f"sig: {sig}")
|
||||||
logging.info(f"params: {params}")
|
logging.info(f"params: {params}")
|
||||||
|
@ -36,7 +41,7 @@ class AuthVerifyer ():
|
||||||
|
|
||||||
try:
|
try:
|
||||||
user_data = json.loads(base64.b64decode(params))
|
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"
|
return False, "Signature to old"
|
||||||
except json.decoder.JSONDecodeError:
|
except json.decoder.JSONDecodeError:
|
||||||
# we shouldn't get here unless kioubit's service is misbehaving
|
# we shouldn't get here unless kioubit's service is misbehaving
|
||||||
|
@ -46,11 +51,12 @@ class AuthVerifyer ():
|
||||||
logging.debug(user_data)
|
logging.debug(user_data)
|
||||||
return True, user_data
|
return True, user_data
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
example_com_verifier = AuthVerifyer("example.com")
|
example_com_verifier = AuthVerifyer("example.com")
|
||||||
logging.info (example_com_verifier.verify(
|
logging.info(example_com_verifier.verify(
|
||||||
params=b"eyJhc24iOiI0MjQyNDIzMDM1IiwidGltZSI6MTY2ODI2NjkyNiwiYWxsb3dlZDQiOiIxNzIuMjIuMTI1LjEyOFwvMjYsMTcyLjIwLjAuODFcLzMyIiwiYWxsb3dlZDYiOiJmZDYzOjVkNDA6NDdlNTo6XC80OCxmZDQyOmQ0MjpkNDI6ODE6OlwvNjQiLCJtbnQiOiJMQVJFLU1OVCIsImF1dGh0eXBlIjoibG9naW5jb2RlIiwiZG9tYWluIjoic3ZjLmJ1cmJsZS5kbjQyIn0=",
|
params=b"eyJhc24iOiI0MjQyNDIzMDM1IiwidGltZSI6MTY2ODI2NjkyNiwiYWxsb3dlZDQiOiIxNzIuMjIuMTI1LjEyOFwvMjYsMTcyLjIwLjAuODFcLzMyIiwiYWxsb3dlZDYiOiJmZDYzOjVkNDA6NDdlNTo6XC80OCxmZDQyOmQ0MjpkNDI6ODE6OlwvNjQiLCJtbnQiOiJMQVJFLU1OVCIsImF1dGh0eXBlIjoibG9naW5jb2RlIiwiZG9tYWluIjoic3ZjLmJ1cmJsZS5kbjQyIn0=",
|
||||||
signature=b"MIGIAkIBAmwz3sQ1vOkH8+8e0NJ8GsUqKSaazIWmYDp60sshlTo7gCAopZOZ6/+tD6s+oEGM1i5mKGbHgK9ROATQLHxUZecCQgCa2N828uNn76z1Yg63/c7veMVIiK4l1X9TCUepJnJ3mCto+7ogCP+2vQm6GHipSNRF4wnt6tZbir0HZvrqEnRAmA=="
|
signature=b"MIGIAkIBAmwz3sQ1vOkH8+8e0NJ8GsUqKSaazIWmYDp60sshlTo7gCAopZOZ6/+tD6s+oEGM1i5mKGbHgK9ROATQLHxUZecCQgCa2N828uNn76z1Yg63/c7veMVIiK4l1X9TCUepJnJ3mCto+7ogCP+2vQm6GHipSNRF4wnt6tZbir0HZvrqEnRAmA=="
|
||||||
) )
|
))
|
||||||
#params = "eyJhc24iOiI0MjQyNDIzMDM1IiwidGltZSI6MTY2ODI1NjI5NSwiYWxsb3dlZDQiOiIxNzIuMjIuMTI1LjEyOFwvMjYsMTcyLjIwLjAuODFcLzMyIiwiYWxsb3dlZDYiOiJmZDYzOjVkNDA6NDdlNTo6XC80OCxmZDQyOmQ0MjpkNDI6ODE6OlwvNjQiLCJtbnQiOiJMQVJFLU1OVCIsImF1dGh0eXBlIjoibG9naW5jb2RlIiwiZG9tYWluIjoic3ZjLmJ1cmJsZS5kbjQyIn0=",
|
#params = "eyJhc24iOiI0MjQyNDIzMDM1IiwidGltZSI6MTY2ODI1NjI5NSwiYWxsb3dlZDQiOiIxNzIuMjIuMTI1LjEyOFwvMjYsMTcyLjIwLjAuODFcLzMyIiwiYWxsb3dlZDYiOiJmZDYzOjVkNDA6NDdlNTo6XC80OCxmZDQyOmQ0MjpkNDI6ODE6OlwvNjQiLCJtbnQiOiJMQVJFLU1OVCIsImF1dGh0eXBlIjoibG9naW5jb2RlIiwiZG9tYWluIjoic3ZjLmJ1cmJsZS5kbjQyIn0=",
|
||||||
#signature = 'MIGHAkFy1m+9ahjIc5cJk/p+RiXJbhbWT5rPSJNg9Q3c8UTAM4F7lz2OqdWHw6GZN5NQgvqm6OB3Y751djYwCd54y2Kn4wJCAcBaOrtSclxkGIleVx183PhTnSr97r2F089PsDzNXIBvH5pYUwvJX7hG0op0f5tPm7fl12HOOrr8Q6kWW+XTrgGX'
|
#signature = 'MIGHAkFy1m+9ahjIc5cJk/p+RiXJbhbWT5rPSJNg9Q3c8UTAM4F7lz2OqdWHw6GZN5NQgvqm6OB3Y751djYwCd54y2Kn4wJCAcBaOrtSclxkGIleVx183PhTnSr97r2F089PsDzNXIBvH5pYUwvJX7hG0op0f5tPm7fl12HOOrr8Q6kWW+XTrgGX'
|
||||||
|
|
|
@ -2,21 +2,30 @@
|
||||||
|
|
||||||
from flask import Flask, Response, redirect, render_template, request, session, abort
|
from flask import Flask, Response, redirect, render_template, request, session, abort
|
||||||
import werkzeug.exceptions as werkzeug_exceptions
|
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 functools import wraps
|
||||||
from ipaddress import ip_address, ip_network, IPv4Network, IPv6Network
|
from ipaddress import ip_address, ip_network, IPv4Network, IPv6Network
|
||||||
import kioubit_verify
|
import kioubit_verify
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Config (dict):
|
class Config (dict):
|
||||||
def __init__(self, configfile:str = None):
|
def __init__(self, configfile: str = None):
|
||||||
if configfile:
|
if configfile:
|
||||||
self.configfile = configfile
|
self.configfile = configfile
|
||||||
else:
|
else:
|
||||||
if os.path.exists("./config.json"): self.configfile = "./config.json"
|
if os.path.exists("./config.json"):
|
||||||
elif os.path.exists("/etc/dn42-autopeer/config.json"): self.configfile = "/etc/dn42-autopeer/config,json"
|
self.configfile = "./config.json"
|
||||||
else: raise FileNotFoundError("no config file found in ./config.json or /etc/dn42-autopeer/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._load_config()
|
||||||
self.keys = self._config.keys
|
self.keys = self._config.keys
|
||||||
#self.__getitem__ = self._config.__getitem__
|
#self.__getitem__ = self._config.__getitem__
|
||||||
|
@ -27,7 +36,8 @@ class Config (dict):
|
||||||
|
|
||||||
def __delitem__(self, v):
|
def __delitem__(self, v):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
super().__delitem__(self,v)
|
super().__delitem__(self, v)
|
||||||
|
|
||||||
def __getitem__(self, k):
|
def __getitem__(self, k):
|
||||||
return self._config[k]
|
return self._config[k]
|
||||||
|
|
||||||
|
@ -50,6 +60,7 @@ class Config (dict):
|
||||||
self._config["peering-data"] = "./peerings"
|
self._config["peering-data"] = "./peerings"
|
||||||
logging.info(self._config)
|
logging.info(self._config)
|
||||||
|
|
||||||
|
|
||||||
class PeeringManager:
|
class PeeringManager:
|
||||||
|
|
||||||
def __init__(self, peerings_file):
|
def __init__(self, peerings_file):
|
||||||
|
@ -60,14 +71,14 @@ class PeeringManager:
|
||||||
def _load_peerings(self):
|
def _load_peerings(self):
|
||||||
if not os.path.exists(self._peering_file):
|
if not os.path.exists(self._peering_file):
|
||||||
with open(self._peering_file, "x") as p:
|
with open(self._peering_file, "x") as p:
|
||||||
json.dump({"mnter":{},"asn":{}}, p)
|
json.dump({"mnter": {}, "asn": {}}, p)
|
||||||
try:
|
try:
|
||||||
with open(self._peering_file,"r") as p:
|
with open(self._peering_file, "r") as p:
|
||||||
self.peerings = json.load(p)
|
self.peerings = json.load(p)
|
||||||
except json.decoder.JSONDecodeError:
|
except json.decoder.JSONDecodeError:
|
||||||
with open(self._peering_file, "w") as p:
|
with open(self._peering_file, "w") as p:
|
||||||
json.dump({"mnter":{},"asn":{}}, p)
|
json.dump({"mnter": {}, "asn": {}}, p)
|
||||||
with open(self._peering_file,"r") as p:
|
with open(self._peering_file, "r") as p:
|
||||||
self.peerings = json.load(p)
|
self.peerings = json.load(p)
|
||||||
|
|
||||||
# self.peerings = {}
|
# self.peerings = {}
|
||||||
|
@ -87,6 +98,30 @@ class PeeringManager:
|
||||||
with open(self._peering_file, "w") as p:
|
with open(self._peering_file, "w") as p:
|
||||||
json.dump(self.peerings, p, indent=4)
|
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):
|
def get_peerings_by_mnt(self, mnt):
|
||||||
# print(self.peerings)
|
# print(self.peerings)
|
||||||
try:
|
try:
|
||||||
|
@ -101,7 +136,7 @@ class PeeringManager:
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return {}
|
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
|
# check if this MNT already has a/this asn
|
||||||
try:
|
try:
|
||||||
if not asn in self.peerings["mnter"][mnt]:
|
if not asn in self.peerings["mnter"][mnt]:
|
||||||
|
@ -118,13 +153,16 @@ class PeeringManager:
|
||||||
|
|
||||||
# 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]:
|
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()
|
self._save_peerings()
|
||||||
return True
|
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
|
# check if this MNT already has a/this asn
|
||||||
try:
|
try:
|
||||||
if not asn in self.peerings["mnter"][mnt]:
|
if not asn in self.peerings["mnter"][mnt]:
|
||||||
|
@ -142,22 +180,38 @@ class PeeringManager:
|
||||||
success = False
|
success = False
|
||||||
for pNr in range(len(self.peerings["asn"][asn])):
|
for pNr in range(len(self.peerings["asn"][asn])):
|
||||||
if self.peerings["asn"][asn][pNr]["node"] == node:
|
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
|
success = True
|
||||||
if not success: return False
|
if not success:
|
||||||
|
return False
|
||||||
self._save_peerings()
|
self._save_peerings()
|
||||||
return True
|
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()
|
config = Config()
|
||||||
peerings = PeeringManager(config["peerings"])
|
peerings = PeeringManager(config["peerings"])
|
||||||
kverifyer = kioubit_verify.AuthVerifyer(config["domain"])
|
kverifyer = kioubit_verify.AuthVerifyer(config["domain"])
|
||||||
|
|
||||||
|
|
||||||
def check_peering_data(form):
|
def check_peering_data(form):
|
||||||
new_peering = {}
|
new_peering = {}
|
||||||
# errors = 0
|
# errors = 0
|
||||||
|
|
||||||
## check if all required (and enabled) options are specified
|
# check if all required (and enabled) options are specified
|
||||||
try:
|
try:
|
||||||
new_peering["peer-asn"] = session["user-data"]["asn"]
|
new_peering["peer-asn"] = session["user-data"]["asn"]
|
||||||
new_peering["peer-wgkey"] = form["peer-wgkey"]
|
new_peering["peer-wgkey"] = form["peer-wgkey"]
|
||||||
|
@ -197,7 +251,7 @@ def check_peering_data(form):
|
||||||
|
|
||||||
print(new_peering)
|
print(new_peering)
|
||||||
|
|
||||||
## check wireguard key
|
# check wireguard key
|
||||||
wg_key_invalid = False
|
wg_key_invalid = False
|
||||||
if len(new_peering["peer-wgkey"]) != 44:
|
if len(new_peering["peer-wgkey"]) != 44:
|
||||||
wg_key_invalid = True
|
wg_key_invalid = True
|
||||||
|
@ -208,29 +262,32 @@ def check_peering_data(form):
|
||||||
if wg_key_invalid:
|
if wg_key_invalid:
|
||||||
return False, "invalid wireguard Key"
|
return False, "invalid wireguard Key"
|
||||||
|
|
||||||
## check endpoint
|
# check endpoint
|
||||||
if new_peering["peer-endpoint"]:
|
if new_peering["peer-endpoint"]:
|
||||||
if not new_peering["peer-endpoint"].split(":")[-1].isnumeric():
|
if not new_peering["peer-endpoint"].split(":")[-1].isnumeric():
|
||||||
return False, "no port number in endpoint"
|
return False, "no port number in endpoint"
|
||||||
elif len(new_peering["peer-endpoint"].split(":")) < 2 and not "." in new_peering["peer-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"
|
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:
|
try:
|
||||||
if not (new_peering["peer-v6ll"] or new_peering["peer-v4"] or new_peering["peer-v6"]):
|
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"
|
return False, "at least one of the ip addresses must be enabled and specified"
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return False, "one of the values isn't valid"
|
return False, "one of the values isn't valid"
|
||||||
|
|
||||||
## check if supplied ip addresses are valid
|
# check if supplied ip addresses are valid
|
||||||
try:
|
try:
|
||||||
if new_peering["peer-v6ll"]:
|
if new_peering["peer-v6ll"]:
|
||||||
ipv6ll = ip_address(new_peering["peer-v6ll"])
|
ipv6ll = ip_address(new_peering["peer-v6ll"])
|
||||||
if not ipv6ll.version == 6: raise ValueError()
|
if not ipv6ll.version == 6:
|
||||||
if not ipv6ll.is_link_local: raise ValueError()
|
raise ValueError()
|
||||||
|
if not ipv6ll.is_link_local:
|
||||||
|
raise ValueError()
|
||||||
if new_peering["peer-v4"]:
|
if new_peering["peer-v4"]:
|
||||||
ipv4 = ip_address(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:
|
if ipv4.is_link_local:
|
||||||
pass
|
pass
|
||||||
elif ipv4.is_private:
|
elif ipv4.is_private:
|
||||||
|
@ -243,12 +300,16 @@ def check_peering_data(form):
|
||||||
is_in_allowed = True
|
is_in_allowed = True
|
||||||
if not is_in_allowed:
|
if not is_in_allowed:
|
||||||
return False, "supplied ipv4 addr not in allowed ip range"
|
return False, "supplied ipv4 addr not in allowed ip range"
|
||||||
else: raise ValueError()
|
else:
|
||||||
|
raise ValueError()
|
||||||
if new_peering["peer-v6"]:
|
if new_peering["peer-v6"]:
|
||||||
ipv6 = ip_address(new_peering["peer-v6"])
|
ipv6 = ip_address(new_peering["peer-v6"])
|
||||||
if not ipv6.version == 6: raise ValueError()
|
if not ipv6.version == 6:
|
||||||
if not ipv6.is_private: raise ValueError()
|
raise ValueError()
|
||||||
if ipv6.is_link_local: raise ValueError()
|
if not ipv6.is_private:
|
||||||
|
raise ValueError()
|
||||||
|
if ipv6.is_link_local:
|
||||||
|
raise ValueError()
|
||||||
is_in_allowed = False
|
is_in_allowed = False
|
||||||
if session["user-data"]["allowed6"]:
|
if session["user-data"]["allowed6"]:
|
||||||
for allowed6 in session["user-data"]["allowed6"].split(","):
|
for allowed6 in session["user-data"]["allowed6"].split(","):
|
||||||
|
@ -269,6 +330,8 @@ def check_peering_data(form):
|
||||||
except ValueError:
|
except ValueError:
|
||||||
...
|
...
|
||||||
return True, new_peering
|
return True, new_peering
|
||||||
|
|
||||||
|
|
||||||
def auth_required():
|
def auth_required():
|
||||||
def wrapper(f):
|
def wrapper(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
|
@ -287,13 +350,13 @@ def kioubit_auth():
|
||||||
params = request.args["params"]
|
params = request.args["params"]
|
||||||
signature = request.args["signature"]
|
signature = request.args["signature"]
|
||||||
except KeyError:
|
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)
|
success, msg = kverifyer.verify(params, signature)
|
||||||
try: logging.debug(base64.b64decode(params))
|
try:
|
||||||
except: logging.debug("invalid Base64 data provided")
|
logging.debug(base64.b64decode(params))
|
||||||
|
except:
|
||||||
|
logging.debug("invalid Base64 data provided")
|
||||||
|
|
||||||
if success:
|
if success:
|
||||||
session["user-data"] = msg
|
session["user-data"] = msg
|
||||||
|
@ -304,16 +367,18 @@ def kioubit_auth():
|
||||||
return redirect(f"{config['base-dir']}peerings")
|
return redirect(f"{config['base-dir']}peerings")
|
||||||
else:
|
else:
|
||||||
try:
|
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:
|
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")
|
@app.route("/logout")
|
||||||
def logout():
|
def logout():
|
||||||
session.clear()
|
session.clear()
|
||||||
return redirect("/")
|
return redirect("/")
|
||||||
|
|
||||||
@app.route("/login",methods=["GET","POST"])
|
|
||||||
|
@app.route("/login", methods=["GET", "POST"])
|
||||||
def login():
|
def login():
|
||||||
print(session)
|
print(session)
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
|
@ -325,47 +390,66 @@ def login():
|
||||||
print(request.form)
|
print(request.form)
|
||||||
if request.form["theanswer"] != "42":
|
if request.form["theanswer"] != "42":
|
||||||
msg = "what is the answer for everything?"
|
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"]
|
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 = request.form["asn"]
|
||||||
asn = asn[2:] if asn[:2].lower() == "as" else asn
|
asn = asn[2:] if asn[:2].lower() == "as" else asn
|
||||||
int(asn)
|
int(asn)
|
||||||
if "allowed4" in request.form:
|
if "allowed4" in request.form:
|
||||||
allowed4 = request.form["allowed4"]
|
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:
|
for v4_range in v4_ranges:
|
||||||
IPv4Network(v4_range)
|
IPv4Network(v4_range)
|
||||||
else:
|
else:
|
||||||
allowed4 = None
|
allowed4 = None
|
||||||
if "allowed6" in request.form:
|
if "allowed6" in request.form:
|
||||||
allowed6 = request.form["allowed6"]
|
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:
|
for v6_range in v6_ranges:
|
||||||
IPv6Network(v6_range)
|
IPv6Network(v6_range)
|
||||||
else:
|
else:
|
||||||
allowed6 = None
|
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
|
session["login"] = mnt
|
||||||
return redirect(session["return_url"])
|
return redirect(session["return_url"])
|
||||||
except ValueError:
|
except ValueError:
|
||||||
msg = "at least one of the values provided is wrong/invalid"
|
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:
|
except KeyError:
|
||||||
msg = "not all required field were specified"
|
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"]:
|
elif request.method == "POST" and not config["debug-mode"]:
|
||||||
abort(405)
|
abort(405)
|
||||||
return redirect(request.args["return"])
|
return redirect(request.args["return"])
|
||||||
|
|
||||||
|
|
||||||
@app.route("/peerings/delete", methods=["GET","DELETE"])
|
@app.route("/peerings/delete", methods=["GET", "POST", "DELETE"])
|
||||||
@auth_required()
|
@auth_required()
|
||||||
def peerings_delete():
|
def peerings_delete():
|
||||||
|
|
||||||
return f"{request.method} /peerings/delete?{str(request.args)}{str(request.form)}"
|
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()
|
@auth_required()
|
||||||
def peerings_edit():
|
def peerings_edit():
|
||||||
print(session)
|
print(session)
|
||||||
|
@ -386,19 +470,28 @@ def peerings_edit():
|
||||||
print(request.args)
|
print(request.args)
|
||||||
print(request.form)
|
print(request.form)
|
||||||
if not "node" in request.args or not request.args["node"]:
|
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)
|
peering_valid, peering_or_msg = check_peering_data(request.form)
|
||||||
print(peering_valid)
|
print(peering_valid)
|
||||||
print(peering_or_msg)
|
print(peering_or_msg)
|
||||||
|
selected_peering = None
|
||||||
|
mnt_peerings = peerings.get_peerings_by_mnt(session["login"])
|
||||||
|
for p in mnt_peerings:
|
||||||
|
if p["node"] == request.args["node"] and p["ASN"] == request.args["asn"]:
|
||||||
|
selected_peering = p
|
||||||
|
print(p)
|
||||||
|
break
|
||||||
if not peering_valid:
|
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, selected_peering=selected_peering), 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"]):
|
if not peerings.update_peering(session["user-data"]["asn"], request.args["node"], session["login"], 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)", selected_peering=selected_peering), 400
|
||||||
|
|
||||||
return redirect(f"{config['base-dir']}peerings")
|
return redirect(f"{config['base-dir']}peerings")
|
||||||
return f"{request.method} /peerings/edit?{str(request.args)}{str(request.form)}"
|
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()
|
@auth_required()
|
||||||
def peerings_new():
|
def peerings_new():
|
||||||
print(session)
|
print(session)
|
||||||
|
@ -406,23 +499,24 @@ def peerings_new():
|
||||||
if "node" in request.args and request.args["node"] in config["nodes"]:
|
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)
|
return render_template("peerings-new.html", config=config, selected_node=request.args["node"], peerings=peerings)
|
||||||
else:
|
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":
|
elif request.method == "POST":
|
||||||
print(request.args)
|
print(request.args)
|
||||||
print(request.form)
|
print(request.form)
|
||||||
if not "node" in request.args or not request.args["node"]:
|
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)
|
peering_valid, peering_or_msg = check_peering_data(request.form)
|
||||||
|
|
||||||
if not peering_valid:
|
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"]):
|
if not peerings.add_peering(session["user-data"]["asn"], request.args["node"], session["login"], 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")
|
return redirect(f"{config['base-dir']}peerings")
|
||||||
|
|
||||||
@app.route("/peerings", methods=["GET","POST","DELETE"])
|
|
||||||
|
@app.route("/peerings", methods=["GET", "POST", "DELETE"])
|
||||||
@auth_required()
|
@auth_required()
|
||||||
def peerings_view():
|
def peerings_view():
|
||||||
print(session)
|
print(session)
|
||||||
|
@ -430,7 +524,7 @@ def peerings_view():
|
||||||
if "node" in request.args and request.args["node"] in config["nodes"]:
|
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)
|
return render_template("peerings.html", config=config, selected_node=request.args["node"], peerings=peerings)
|
||||||
else:
|
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":
|
elif request.method == "POST":
|
||||||
return peerings_new()
|
return peerings_new()
|
||||||
elif request.method == "DELETE":
|
elif request.method == "DELETE":
|
||||||
|
@ -438,6 +532,7 @@ def peerings_view():
|
||||||
else:
|
else:
|
||||||
abort(405)
|
abort(405)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
def index():
|
def index():
|
||||||
print(session)
|
print(session)
|
||||||
|
@ -446,19 +541,20 @@ def index():
|
||||||
# print (node)
|
# print (node)
|
||||||
return render_template("index.html", session=session, config=config._config)
|
return render_template("index.html", session=session, config=config._config)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
app.static_folder= config["flask-template-dir"]+"/static/"
|
app.static_folder = config["flask-template-dir"]+"/static/"
|
||||||
app.template_folder=config["flask-template-dir"]
|
app.template_folder = config["flask-template-dir"]
|
||||||
app.secret_key = config["flask-secret-key"]
|
app.secret_key = config["flask-secret-key"]
|
||||||
if "production" in config and config["production"] == False:
|
if "production" in config and config["production"] == False:
|
||||||
logging.getLogger(__name__).setLevel(0)
|
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:
|
else:
|
||||||
from waitress import serve
|
from waitress import serve
|
||||||
logging.getLogger(__name__).setLevel(logging.INFO)
|
logging.getLogger(__name__).setLevel(logging.INFO)
|
||||||
serve(app, host=config["listen"], port=config["port"])
|
serve(app, host=config["listen"], port=config["port"])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
|
@ -1,22 +1,29 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>{{config["MNT"]}} Autopeering</title>
|
<title>{{config["MNT"]}} Autopeering</title>
|
||||||
<link rel="stylesheet" href="{{config['base-dir']}}static/style.css">
|
<link rel="stylesheet" href="{{config['base-dir']}}static/style.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<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>
|
<header class="flex flex-row">
|
||||||
<div class="content flex">
|
<div></div>
|
||||||
{% block content %}
|
<a href="{{config['base-dir']}}">{{config["MNT"]}} Autopeering</a>{% if
|
||||||
{% endblock %}
|
"login" in session %}
|
||||||
</div>
|
<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">
|
<footer class="flex">
|
||||||
<div></div>
|
<div></div>
|
||||||
<div></div>
|
<div></div>
|
||||||
<div> 🄯 LARE-MNT</div>
|
<div>🄯 LARE-MNT</div>
|
||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -1,28 +1,25 @@
|
||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %} {% block content %}
|
||||||
|
<div></div>
|
||||||
{% block content %}
|
|
||||||
<div>
|
<div>
|
||||||
</div>
|
<table>
|
||||||
<div>
|
<thead>
|
||||||
<table>
|
<tr>
|
||||||
<thead>
|
<th>NodeName</th>
|
||||||
<tr>
|
<th>Country</th>
|
||||||
<th>NodeName</th>
|
<th>City</th>
|
||||||
<th>Country</th>
|
<th>peerings</th>
|
||||||
<th>City</th>
|
<th>Peer!</th>
|
||||||
<th>peerings</th>
|
</tr>
|
||||||
<th>Peer!</th>
|
</thead>
|
||||||
</tr>
|
{% for node in config["nodes"] %}
|
||||||
</thead>
|
<tr>
|
||||||
{% for node in config["nodes"] %}
|
<td>{{node}}</td>
|
||||||
<tr>
|
<td>{{config["nodes"][node]["country"]}}</td>
|
||||||
<td>{{node}}</td>
|
<td>{{config["nodes"][node]["city"]}}</td>
|
||||||
<td>{{config["nodes"][node]["country"]}}</td>
|
<td></td>
|
||||||
<td>{{config["nodes"][node]["city"]}}</td>
|
<td><a href="peerings/new?node={{node}}">peer</a></td>
|
||||||
<td></td>
|
</tr>
|
||||||
<td><a href="peerings/new?node={{node}}">peer</a></td>
|
{% endfor %}
|
||||||
</tr>
|
</table>
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -1,31 +1,64 @@
|
||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %} {% block content %} {% if msg %}
|
||||||
|
<div style="background-color: red">{{msg}}</div>
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
{% if msg %}
|
|
||||||
<div style="background-color: red;">
|
|
||||||
{{msg}}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<form action="https://dn42.g-load.eu/auth/">
|
<form action="https://dn42.g-load.eu/auth/">
|
||||||
<link rel="stylesheet" href="https://dn42.g-load.eu/auth/assets/button-font/auth.css">
|
<link
|
||||||
<input type="hidden" id="return" name="return" value='{{"http://"+config["domain"]+"/api/auth/kverify"}}'>
|
rel="stylesheet"
|
||||||
<button type="submit" class="kioubit-btn kioubit-btn-primary kioubit-btn-dark kioubit-btn-main">
|
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
|
<span class="icon-kioubit-auth"></span>Authenticate with Kioubit.dn42
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{% if config["debug-mode"] %}
|
{% if config["debug-mode"] %}
|
||||||
<form action="" method="post" class="flex">
|
<form action="" method="post" class="flex">
|
||||||
<label for="debug">Debug login, if you see this in Production contact {{config["MNT"]}}</label><br>
|
<label for="debug"
|
||||||
<input type="text" name="mnt" id="mnt" placeholder="YOUR-MNT" required><br>
|
>Debug login, if you see this in Production contact {{config["MNT"]}}</label
|
||||||
<input type="text" name="asn" id="asn" placeholder="AS4242420000" required><br>
|
><br />
|
||||||
<input type="text" name="allowed4" id="allowed4" placeholder="ipv4 subnet (optional)"><br>
|
<input
|
||||||
<input type="text" name="allowed6" id="allowed6" placeholder="ipv6 subnet (optional)"><br>
|
type="text"
|
||||||
<input type="number" name="theanswer" id="theanswer" placeholder="The answer for everything" required><br>
|
name="mnt"
|
||||||
<input type="submit" value="login">
|
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>
|
</form>
|
||||||
{% endif %}
|
{% endif %} {% endblock %}
|
||||||
|
|
||||||
{% 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 %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
<form action="" method="post" class="flex" onsubmit="return form_validate(this)">
|
<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>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td><label for="peer-asn">Your ASN</label></td>
|
<td><label for="peer-asn">Your ASN</label></td>
|
||||||
|
@ -283,8 +283,8 @@ protocol bgp dn42_{{config["MNT"][:-4].lower()}}_v6 from dnpeers {
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
document.onload();
|
document.onload();
|
||||||
|
|
|
@ -1,39 +1,58 @@
|
||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %} {% block content %}
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div>
|
<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>
|
||||||
<div class="flex flex-row">
|
<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 class="peering">
|
||||||
<div>
|
<div>
|
||||||
<div>Node: {{peering["node"]}}</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>
|
|
||||||
</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 %}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<!-- <div>{{peering}}</div> -->
|
|
||||||
<div>
|
|
||||||
<a href="peerings/edit?node={{peering['node']}}&asn={{peering['ASN']}}">
|
|
||||||
<button class="peering-edit">edit</button>
|
|
||||||
</a>
|
|
||||||
<a href="peerings/delete?node={{peering['node']}}&asn={{peering['ASN']}}">
|
|
||||||
<button class="peering-delete">delete</button>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
<div>
|
||||||
|
<table>
|
||||||
|
<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 %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<!-- <div>{{peering}}</div> -->
|
||||||
|
<div>
|
||||||
|
<a href="peerings/edit?node={{peering['node']}}&asn={{peering['ASN']}}">
|
||||||
|
<button class="button-blue">edit</button>
|
||||||
|
</a>
|
||||||
|
<a href="peerings/delete?node={{peering['node']}}&asn={{peering['ASN']}}">
|
||||||
|
<button class="button-red">delete</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -17,6 +17,7 @@ header {
|
||||||
background-color: var(--other-background);
|
background-color: var(--other-background);
|
||||||
height: 50px;
|
height: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
@ -27,6 +28,7 @@ a {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
padding-bottom: 50px;
|
padding-bottom: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content>* {
|
.content>* {
|
||||||
padding: 5%;
|
padding: 5%;
|
||||||
}
|
}
|
||||||
|
@ -35,6 +37,7 @@ a {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flex-row {
|
.flex-row {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
|
@ -54,6 +57,7 @@ footer {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
background: var(--other-background);
|
background: var(--other-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
footer.flex {
|
footer.flex {
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
|
@ -63,14 +67,14 @@ footer.flex {
|
||||||
padding: 1%;
|
padding: 1%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.example-config > pre{
|
.example-config>pre {
|
||||||
border-color: var(--other-background);
|
border-color: var(--other-background);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border-style: groove;
|
border-style: groove;
|
||||||
}
|
}
|
||||||
|
|
||||||
form > div > * {
|
form>div>* {
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,40 +89,59 @@ form > div > * {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
}
|
}
|
||||||
.peering > div {
|
|
||||||
|
.peering>div {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
.peering > div > * {
|
|
||||||
|
.peering>div>* {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button,
|
||||||
|
input[type=button] {
|
||||||
background-color: #00000020;
|
background-color: #00000020;
|
||||||
border-color: var(--text-color);
|
border-color: var(--text-color);
|
||||||
border-width: 5px;
|
border-width: 5px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
button.button-selected{
|
|
||||||
|
button.button-selected,
|
||||||
|
input[type=button].button-selected {
|
||||||
background-color: var(--other-background);
|
background-color: var(--other-background);
|
||||||
color: var(--text-color-dark);
|
color: var(--text-color-dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
.peering>* {
|
.peering>* {
|
||||||
width:auto;
|
width: auto;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.peering-edit {
|
.button-blue {
|
||||||
border-color: lightblue;
|
border-color: lightblue;
|
||||||
}
|
}
|
||||||
|
|
||||||
.peering-edit:hover {
|
.button-blue:hover {
|
||||||
background-color: #87cefaaa;
|
background-color: #87cefaaa;
|
||||||
}
|
}
|
||||||
|
|
||||||
.peering-delete {
|
.button-red {
|
||||||
border-color: darkred;
|
border-color: darkred;
|
||||||
}
|
}
|
||||||
.peering-delete:hover {
|
|
||||||
|
.button-red:hover {
|
||||||
background-color: #ff0000aa;
|
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