Compare commits

..

No commits in common. "master" and "v1.0" have entirely different histories.
master ... v1.0

19 changed files with 162 additions and 255 deletions

View file

@ -5,10 +5,10 @@ This is my (LARE-MNT) implementation of an auto peering system
It consists of two parts: It consists of two parts:
1. the "server" 1. the "server"
- handling user auth (via kioubit) - handling user auth (via kioubit)
- add/edit/delete peerings - add/edit/delete peerings (#todo)
- communicate new/update peerings with nodes - communicate new/update peerings with nodes #todo
2. the "nodes daemon" 2. the "nodes daemon" #todo
- reveives new/updated peering configs from the "server" - reveives new/updated peering configs from the "server" #todo
## Installation ## Installation

View file

@ -27,7 +27,7 @@ Type=simple
Restart=on-failure Restart=on-failure
RestartSec=5s RestartSec=5s
WorkingDirectory=</path/to/autopeering/>web WorkingDirectory=</path/to/autopeering/>web
ExecStart=</path/to/autopeering>/web/start.sh ExecStart=start.sh
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
@ -72,7 +72,7 @@ Type=simple
Restart=on-failure Restart=on-failure
RestartSec=5s RestartSec=5s
WorkingDirectory=</path/to/autopeering>/nodes WorkingDirectory=</path/to/autopeering>/nodes
ExecStart=</path/to/autopeering>/nodes/start.sh ExecStart=start.sh
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View file

@ -130,53 +130,52 @@ class PeeringManager:
def __generate_wg_conf(self, peering: dict): def __generate_wg_conf(self, peering: dict):
return render_template("wireguard.template.conf", peering=peering) return render_template("wireguard.template.conf", peering=peering)
def __generate_bird_conf(self, peering: dict): def __generate_bird_conf(self, peering:dict):
peering["MNT"] = peering["MNT"].replace("-","_")
return render_template("bgp-peer.template.conf", peering=peering) return render_template("bgp-peer.template.conf", peering=peering)
def __install_peering(self, mode: str, peering: dict): def __install_peering(self, mode: str, peering: dict):
if mode == "add": if mode == "add":
wg_conf = self.__generate_wg_conf(peering) wg_conf = self.__generate_wg_conf(peering)
bgp_conf = self.__generate_bird_conf(peering) bgp_conf = self.__generate_bird_conf(peering)
with open(f"{self.__config['wg-configs']}/dn42_{peering['ASN'][-6:] if len(peering['ASN']) >=6 else peering['ASN']}.conf", "w") as wg_file: with open(f"{self.__config['wg-configs']}/dn42_{peering['MNT'][:-4].lower()}.conf", "w") as wg_file:
wg_file.write(wg_conf) wg_file.write(wg_conf)
wg_enable = subprocess.run(self.__config["wg-commands"]["enable"].replace("{PEERING}",peering['ASN'][-6:] if len(peering["ASN"]) >=6 else peering["ASN"]).split(" ")) wg_enable = subprocess.run(self.__config["wg-commands"]["enable"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
print(wg_enable) print(wg_enable)
wg_up = subprocess.run(self.__config["wg-commands"]["up"].replace("{PEERING}",peering['ASN'][-6:] if len(peering["ASN"]) >=6 else peering["ASN"]).split(" ")) wg_up = subprocess.run(self.__config["wg-commands"]["up"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
print(wg_up) print(wg_up)
time.sleep(3) time.sleep(5)
with open(f"{self.__config['bird-peers']}/dn42_{peering['MNT'][:-4].lower()}_{peering['ASN'][-4:]}.conf", "w") as bgp_file: with open(f"{self.__config['bird-peers']}/dn42_{peering['MNT'][:-4].lower()}.conf", "w") as bgp_file:
bgp_file.write(bgp_conf) bgp_file.write(bgp_conf)
bgp_reload = subprocess.run(self.__config["bird-reload"].replace("{PEERING}",peering['ASN'][-6:] if len(peering["ASN"]) >=6 else peering["ASN"]).split(" ")) bgp_reload = subprocess.run(self.__config["bird-reload"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
print(bgp_reload) print(bgp_reload)
return 200 return 200
elif mode == "update": elif mode == "update":
wg_conf = self.__generate_wg_conf(peering) wg_conf = self.__generate_wg_conf(peering)
bgp_conf = self.__generate_bird_conf(peering) bgp_conf = self.__generate_bird_conf(peering)
with open(f"{self.__config['wg-configs']}/dn42_{peering['ASN'][-6:] if len(peering['ASN']) >=6 else peering['ASN']}.conf", "w") as wg_file: with open(f"{self.__config['wg-configs']}/dn42_{peering['MNT'][:-4].lower()}.conf", "w") as wg_file:
wg_file.write(wg_conf) wg_file.write(wg_conf)
wg_down = subprocess.run(self.__config["wg-commands"]["down"].replace("{PEERING}",peering['ASN'][-6:] if len(peering["ASN"]) >=6 else peering["ASN"]).split(" ")) wg_down = subprocess.run(self.__config["wg-commands"]["down"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
print(wg_down) print(wg_down)
wg_up = subprocess.run(self.__config["wg-commands"]["up"].replace("{PEERING}",peering['ASN'][-6:] if len(peering["ASN"]) >=6 else peering["ASN"]).split(" ")) wg_up = subprocess.run(self.__config["wg-commands"]["up"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
print(wg_up) print(wg_up)
time.sleep(3) time.sleep(5)
with open(f"{self.__config['bird-peers']}/dn42_{peering['MNT'][:-4].lower()}_{peering['ASN'][-4:]}.conf", "w") as bgp_file: with open(f"{self.__config['bird-peers']}/dn42_{peering['MNT'][:-4].lower()}.conf", "w") as bgp_file:
bgp_file.write(bgp_conf) bgp_file.write(bgp_conf)
bgp_reload = subprocess.run(self.__config["bird-reload"].replace("{PEERING}",peering['ASN'][-6:] if len(peering["ASN"]) >=6 else peering["ASN"]).split(" ")) bgp_reload = subprocess.run(self.__config["bird-reload"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
print(bgp_reload) print(bgp_reload)
return 200 return 200
elif mode == "delete": elif mode == "delete":
os.remove(f"{self.__config['bird-peers']}/dn42_{peering['MNT'][:-4].lower()}_{peering['ASN'][-4:]}.conf") os.remove(f"{self.__config['bird-peers']}/dn42_{peering['MNT'][:-4].lower()}.conf")
bgp_reload = subprocess.run(self.__config["bird-reload"].replace("{PEERING}",peering['ASN'][-6:] if len(peering["ASN"]) >=6 else peering["ASN"]).split(" ")) bgp_reload = subprocess.run(self.__config["bird-reload"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
print(bgp_reload) print(bgp_reload)
time.sleep(3) time.sleep(5)
wg_down = subprocess.run(self.__config["wg-commands"]["down"].replace("{PEERING}",peering['ASN'][-6:] if len(peering["ASN"]) >=6 else peering["ASN"]).split(" ")) wg_down = subprocess.run(self.__config["wg-commands"]["down"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
print(wg_down) print(wg_down)
wg_disable = subprocess.run(self.__config["wg-commands"]["disable"].replace("{PEERING}",peering['ASN'][-6:] if len(peering["ASN"]) >=6 else peering["ASN"]).split(" ")) wg_disable = subprocess.run(self.__config["wg-commands"]["disable"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
print(wg_disable) print(wg_disable)
return 200 return 200
@ -229,6 +228,7 @@ class PeeringManager:
def update_peering(self, ASN, wg_key, MNT=NotSpecified, node=NotSpecified, endpoint=NotSpecified, ipv6ll=NotSpecified, ipv4=NotSpecified, ipv6=NotSpecified, bgp_mp=NotSpecified, bgp_enh=NotSpecified): def update_peering(self, ASN, wg_key, MNT=NotSpecified, node=NotSpecified, endpoint=NotSpecified, ipv6ll=NotSpecified, ipv4=NotSpecified, ipv6=NotSpecified, bgp_mp=NotSpecified, bgp_enh=NotSpecified):
asn = ASN asn = ASN
try: try:
if not asn in self.peerings: if not asn in self.peerings:
return False, 404 return False, 404
@ -239,10 +239,8 @@ class PeeringManager:
for pNr in range(len(self.peerings[asn])): for pNr in range(len(self.peerings[asn])):
if self.peerings[asn][pNr]["node"] == node: if self.peerings[asn][pNr]["node"] == node:
old_peering = self.peerings[asn][pNr] old_peering = self.peerings[asn][pNr]
new_peering = self.peerings[asn][pNr] = {"MNT": MNT if MNT!=NotSpecified else old_peering["MNT"], "ASN": asn, "node": config["nodename"], new_peering = self.peerings[asn][pNr] = {"MNT": MNT if MNT!=NotSpecified else old_peering["MNT"], "ASN": asn, "node": config["nodename"], "wg_key": wg_key,
"wg_key": wg_key,"endpoint": endpoint if endpoint!=NotSpecified else old_peering["endpoint"], "endpoint": endpoint if endpoint!=NotSpecified else old_peering["endpoint"], "ipv6ll": ipv6ll if ipv6ll != NotSpecified else old_peering["ipv6ll"], "ipv4": ipv4 if ipv4 != NotSpecified else old_peering["ipv4"], "ipv6": ipv6 if ipv6 != NotSpecified else old_peering["ipv6"], "bgp_mp": bgp_mp if bgp_mp != NotSpecified else old_peering["bgp_mp"], "bgp_enh": bgp_enh if bgp_enh != NotSpecified else old_peering["bgp_enh"]}
"ipv6ll": ipv6ll if ipv6ll != NotSpecified else old_peering["ipv6ll"], "ipv4": ipv4 if ipv4 != NotSpecified else old_peering["ipv4"], "ipv6": ipv6 if ipv6 != NotSpecified else old_peering["ipv6"],
"bgp_mp": bgp_mp if bgp_mp != NotSpecified else old_peering["bgp_mp"], "bgp_enh": bgp_enh if bgp_enh != NotSpecified else old_peering["bgp_enh"]}
success = True success = True
if not success: if not success:
return False, 404 return False, 404
@ -303,9 +301,9 @@ class PeeringsRoute(Resource):
requested_peerings = peerings.get_peerings_by_asn(args["ASN"]) requested_peerings = peerings.get_peerings_by_asn(args["ASN"])
if requested_peerings: if requested_peerings:
return {"success": True, "ASN": args["ASN"], "peerings": requested_peerings}, 200 return {"success": True, "asn": args["ASN"], "peerings": requested_peerings}, 200
else: else:
return {"success": False, "ASN": args["ASN"], "error": "not found", "peerings": []}, 404 return {"success": False, "asn": args["ASN"], "error": "not found", "peerings": []}, 404
@check_ACL() @check_ACL()
def post(self): def post(self):
@ -323,8 +321,8 @@ class PeeringsRoute(Resource):
parser.add_argument('ipv6ll') parser.add_argument('ipv6ll')
parser.add_argument('ipv4') parser.add_argument('ipv4')
parser.add_argument('ipv6') parser.add_argument('ipv6')
parser.add_argument('bgp_mp', type = bool) parser.add_argument('bgp_mp')
parser.add_argument('bgp_enh', type = bool) parser.add_argument('bgp_enh')
args = parser.parse_args() # parse arguments to dictionary args = parser.parse_args() # parse arguments to dictionary
if not peerings.exists(asn=args["ASN"], wg_key=args["wg_key"]): if not peerings.exists(asn=args["ASN"], wg_key=args["wg_key"]):
@ -355,8 +353,8 @@ class PeeringsRoute(Resource):
parser.add_argument('ipv6ll', store_missing=False) parser.add_argument('ipv6ll', store_missing=False)
parser.add_argument('ipv4', store_missing=False) parser.add_argument('ipv4', store_missing=False)
parser.add_argument('ipv6', store_missing=False) parser.add_argument('ipv6', store_missing=False)
parser.add_argument('bgp_mp', type=bool, store_missing=False) parser.add_argument('bgp_mp', store_missing=False)
parser.add_argument('bgp_enh', type=bool, store_missing=False) parser.add_argument('bgp_enh', store_missing=False)
args = parser.parse_args() # parse arguments to dictionary args = parser.parse_args() # parse arguments to dictionary
print(args) print(args)
if peerings.exists(asn=args["ASN"], wg_key=args["wg_key"]): if peerings.exists(asn=args["ASN"], wg_key=args["wg_key"]):
@ -404,4 +402,4 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View file

@ -9,11 +9,11 @@
"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
"wg-configs": "/etc/wireguard/", // optional, default: "/etc/wireguard/"; directory where the wireguard configs are located "wg-configs": "/etc/wireguard/", // optional, default: "/etc/wireguard/"; directory where the wireguard configs are located
"wg-commands": { // {PEERING} will get replaced with the lowercase mnter without "-MNT" followed by the last four digits of the ASn "wg-commands": { // {MNT} will get replaced with the lowercase mnter without "-MNT"
"enable": "./wg-services.sh enable {PEERING}", //command to execute for enabling the wg-interface "enable": "systemctl enable wg-quick@dn42_{MNT}", //command to execute for enabling the wg-interface
"up": "./wg-services.sh start {PEERING}", //command to execute for starting the wg-interface "up": "systemctl start wg-quick@dn42_{MNT}", //command to execute for starting the wg-interface
"down": "./wg-services.sh stop {PEERING}", //command to execute for stopping the wg-interface "down": "systemctl stop wg-quick@dn42_{MNT}", //command to execute for stopping the wg-interface
"disable": "./wg-services.sh disable {PEERING}" //command to execute for disabling the wg-interface "disable": "systemctl disable wg-quick@dn42_{MNT}" //command to execute for disabling the wg-interface
}, },
"bird-peers": "/etc/bird/peers/", // optional, default: "/etc/bird/peers/"; directory where bird peers are to be located "bird-peers": "/etc/bird/peers/", // optional, default: "/etc/bird/peers/"; directory where bird peers are to be located
"bird-reload": "birdc configure", // optional, default: "birdc configure"; command to reconfigure bird or other bgp daemon "bird-reload": "birdc configure", // optional, default: "birdc configure"; command to reconfigure bird or other bgp daemon

0
nodes/start.sh Executable file → Normal file
View file

View file

@ -1,7 +1,7 @@
{% if peering["bgp_mp"] == True %} {% if peering["bgp_mp"] %}
protocol bgp dn42_{{peering["MNT"][:-4].lower()}}_{{peering["ASN"][-4:]}} from dnpeers { protocol bgp dn42_{{peering["MNT"][:-4].lower()}} from dnpeers {
neighbor {{peering["ipv6ll"] or peering["ipv4"] or peering["ipv6"]}} as {{peering["ASN"]}}; neighbor {{peering["ipv6ll"]}} as {{peering["ASN"]}};
interface {% if peering['ASN'].__len__() >=6 %}"dn42_{{peering['ASN'][-6:]}}"{% else %}"dn42_{{peering['asn']}}"{% endif %}; interface "dn42_{{peering['MNT'][:-4].lower()}}";
passive off; passive off;
ipv4 { ipv4 {
@ -16,21 +16,19 @@ protocol bgp dn42_{{peering["MNT"][:-4].lower()}}_{{peering["ASN"][-4:]}} from d
#import where dn42_import_filter(x,y,z); #import where dn42_import_filter(x,y,z);
#export where dn42_export_filter(x,y,z); #export where dn42_export_filter(x,y,z);
#} #}
extended next hop off; extended next hop {% if peering["bgp_enh"] %}on{%else%}off{%endif%};
}; };
}; };
{% elif peering["bgp_mp"] == False %} {%else%}
protocol bgp dn42_{{peering["MNT"][:-4].lower()}}_{{peering['ASN'][-4:]}}_4 from dnpeers { protocol bgp dn42_{{peering["MNT"][:-4].lower()}}_4 from dnpeers {
neighbor {{peering["ipv4"]}} as {{peering["ASN"]}}; neighbor {{peering["ipv4"]}} as {{peering["ASN"]}};
interface {% if peering['ASN'].__len__() >=6 %}"dn42_{{peering['ASN'][-6:]}}"{% else %}"dn42_{{peering['asn']}}"{% endif %};
passive off; passive off;
#import where dn42_import_filter(x,y,z); #import where dn42_import_filter(x,y,z);
#export where dn42_export_filter(x,y,z); #export where dn42_export_filter(x,y,z);
}; };
protocol bgp dn42_{{peering["MNT"][:-4].lower()}}_{{peering['ASN'][-4:]}}_6 from dnpeers { protocol bgp dn42_{{peering["MNT"][:-4].lower()}}_6 from dnpeers {
neighbor {% peering["ipv6"] != None %}{{peering["ipv6"]}}{% else %}{{peering["ipv6ll"]}}{% endif %} as {{peering["ASN"]}}; neighbor {{peering["ipv6"]}} as {{peering["ASN"]}};
interface {% if peering['ASN'].__len__() >=6 %}"dn42_{{peering['ASN'][-6:]}}"{% else %}"dn42_{{peering['asn']}}"{% endif %};
passive off; passive off;
#import where dn42_import_filter(x,y,z); #import where dn42_import_filter(x,y,z);
#export where dn42_export_filter(x,y,z); #export where dn42_export_filter(x,y,z);

View file

@ -2,7 +2,7 @@
PostUp = wg set %i private-key /etc/wireguard/dn42.priv PostUp = wg set %i private-key /etc/wireguard/dn42.priv
ListenPort = 2{{peering["ASN"][-4:]}} ListenPort = 2{{peering["ASN"][-4:]}}
{% if peering["ipv4"] %}PostUp = /sbin/ip addr add dev %i 172.22.125.130/32 peer {{peering["ipv4"]}}/32 {% if peering["ipv4"] %}PostUp = /sbin/ip addr add dev %i 172.22.125.130/32 peer {{peering["ipv4"]}}/32
{%endif%}{% if peering["ipv6"] %}PostUp = /sbin/ip addr add dev %i fd63:5d40:47e5::130/128 peer {{peering["ipv6"]}}/128 {%endif%}{% if peering["ipv6"] %}PostUp = /sbin/ip addr add dev %i fe63:5d40:47e5::130/128 peer {{peering["ipv6"]}}/128
{%endif%}{% if peering["ipv6ll"] %}PostUp = /sbin/ip addr add dev %i fe80::3035:130/128 peer {{peering["ipv6ll"]}}/128{%endif%} {%endif%}{% if peering["ipv6ll"] %}PostUp = /sbin/ip addr add dev %i fe80::3035:130/128 peer {{peering["ipv6ll"]}}/128{%endif%}
Table = off Table = off

0
nodes/wg-services.sh Executable file → Normal file
View file

View file

@ -43,16 +43,12 @@ class AuthVerifyer ():
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"
elif user_data["domain"] != self.domain.replace("https://",""):
return False, "invalid domain"
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
return False, "invalid JSON" return False, "invalid JSON"
except KeyError: except KeyError:
return False, "value not found in JSON" return False, "value not found in JSON"
logging.debug(user_data) logging.debug(user_data)
# use mnt[0] as mnt
user_data["mnt"] = user_data["mnt"][0]
return True, user_data return True, user_data

View file

@ -1,19 +1,14 @@
#! /usr/bin/env python3 #! /usr/bin/env python3
# flask
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
# other utils
import json import json
import os import os
import base64 import base64
# potential errors on base64 decode: binascii.Error
import binascii
import logging import logging
import random 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
# self written modules
import kioubit_verify import kioubit_verify
from peering_manager import PeeringManager from peering_manager import PeeringManager
@ -54,21 +49,21 @@ class Config (dict):
except json.decoder.JSONDecodeError: except json.decoder.JSONDecodeError:
raise SyntaxError(f"no valid JSON found in '{cf.name}'") raise SyntaxError(f"no valid JSON found in '{cf.name}'")
if "flask-template-dir" not in self._config: if not "flask-template-dir" in self._config:
self._config["flask-template-dir"] = "../frontend" self._config["flask-template-dir"] = "../frontend"
if "debug-mode" not in self._config: if not "debug-mode" in self._config:
self._config["debug-mode"] = False self._config["debug-mode"] = False
if "base-dir" not in self._config: if not "base-dir" in self._config:
self._config["base-dir"] = "/" self._config["base-dir"] = "/"
if "peerings-data" not in self._config: if not "peerings-data" in self._config:
self._config["peering-data"] = "./peerings" self._config["peering-data"] = "./peerings"
if "nodes" not in self._config: if not "nodes" in self._config:
self,_config = {} self,_config = {}
for node in self._config["nodes"]: for node in self._config["nodes"]:
if "capacity" not in self._config["nodes"][node]: if not "capacity" in self._config["nodes"][node]:
self._config["nodes"][node]["capacity"] = -1 self._config["nodes"][node]["capacity"] = -1
logging.info(self._config) logging.info(self._config)
@ -86,8 +81,7 @@ def check_peering_data(form):
# check if all required (and enabled) options are specified # check if all required (and enabled) options are specified
try: try:
# force "peer-asn" to be the same as in the login data except if login is admin (admin get the peer-asn field enabled in the form) new_peering["peer-asn"] = session["user-data"]["asn"]
new_peering["peer-asn"] = session["user-data"]["asn"] if "peer-asn" not in request.form or session["login"] != config["MNT"] else form["peer-asn"]
new_peering["peer-wgkey"] = form["peer-wgkey"] new_peering["peer-wgkey"] = form["peer-wgkey"]
if "peer-endpoint-enabled" in form and form["peer-endpoint-enabled"] == "on": if "peer-endpoint-enabled" in form and form["peer-endpoint-enabled"] == "on":
new_peering["peer-endpoint"] = form["peer-endpoint"] new_peering["peer-endpoint"] = form["peer-endpoint"]
@ -131,7 +125,7 @@ def check_peering_data(form):
wg_key_invalid = True wg_key_invalid = True
try: try:
base64.b64decode(new_peering["peer-wgkey"]) base64.b64decode(new_peering["peer-wgkey"])
except binascii.Error: except:
wg_key_invalid = True wg_key_invalid = True
if wg_key_invalid: if wg_key_invalid:
return False, "invalid wireguard Key" return False, "invalid wireguard Key"
@ -140,7 +134,7 @@ def check_peering_data(form):
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
@ -154,13 +148,13 @@ def check_peering_data(form):
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 ipv6ll.version != 6: if not ipv6ll.version == 6:
raise ValueError() raise ValueError()
if not ipv6ll.is_link_local: if not ipv6ll.is_link_local:
raise ValueError() 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 ipv4.version != 4: if not ipv4.version == 4:
raise ValueError() raise ValueError()
if ipv4.is_link_local: if ipv4.is_link_local:
pass pass
@ -169,21 +163,16 @@ def check_peering_data(form):
raise ValueError() raise ValueError()
is_in_allowed = False is_in_allowed = False
if session["user-data"]["allowed4"]: if session["user-data"]["allowed4"]:
if not isinstance(session["user-data"]["allowed4"],list): for allowed4 in session["user-data"]["allowed4"]:
allowed4 = session["user-data"]["allowed4"]
if ipv4 in ip_network(allowed4): if ipv4 in ip_network(allowed4):
is_in_allowed = True is_in_allowed = True
else:
for allowed4 in session["user-data"]["allowed4"]:
if ipv4 in ip_network(allowed4):
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: else:
raise ValueError() 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 ipv6.version != 6: if not ipv6.version == 6:
raise ValueError() raise ValueError()
if not ipv6.is_private: if not ipv6.is_private:
raise ValueError() raise ValueError()
@ -191,25 +180,19 @@ def check_peering_data(form):
raise ValueError() raise ValueError()
is_in_allowed = False is_in_allowed = False
if session["user-data"]["allowed6"]: if session["user-data"]["allowed6"]:
if not isinstance(session["user-data"]["allowed6"],list): for allowed6 in session["user-data"]["allowed6"]:
allowed6 = session["user-data"]["allowed6"]
if ipv6 in ip_network(allowed6): if ipv6 in ip_network(allowed6):
is_in_allowed = True is_in_allowed = True
else:
for allowed6 in session["user-data"]["allowed6"]:
if ipv6 in ip_network(allowed6):
is_in_allowed = True
if not is_in_allowed: if not is_in_allowed:
return False, "supplied ipv6 addr not in allowed ip range" return False, "supplied ipv6 addr not in allowed ip range"
except ValueError as e: except ValueError:
print(e)
return False, "invalid ip address(es) supplied" return False, "invalid ip address(es) supplied"
# check bgp options # check bgp options
try: try:
if new_peering["bgp-mp"] is False and new_peering["bgp-enh"] is True: if new_peering["bgp-mp"] == False and new_peering["bgp-enh"] == True:
return False, "extended next hop requires multiprotocol bgp" return False, "extended next hop requires multiprotocol bgp"
if new_peering["bgp-mp"] is False: if new_peering["bgp-mp"] == False:
if not (new_peering["peer-v4"] and (new_peering["peer-v6"] or new_peering["peer-v6ll"])): if not (new_peering["peer-v4"] and (new_peering["peer-v6"] or new_peering["peer-v6ll"])):
return False, "ipv4 and ipv6 addresses required when not having MP-BGP" return False, "ipv4 and ipv6 addresses required when not having MP-BGP"
except ValueError: except ValueError:
@ -221,7 +204,7 @@ def auth_required():
def wrapper(f): def wrapper(f):
@wraps(f) @wraps(f)
def decorated(*args, **kwargs): def decorated(*args, **kwargs):
if "login" not in session: if not "login" in session:
request_url = f"{config['base-dir']}{request.full_path}".replace("//", "/") request_url = f"{config['base-dir']}{request.full_path}".replace("//", "/")
return redirect(f"{config['base-dir']}login?return={request_url}") return redirect(f"{config['base-dir']}login?return={request_url}")
else: else:
@ -271,7 +254,7 @@ def login():
session["return_url"] = request.args["return"] if "return" in request.args else "" 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"]) return render_template("login.html", session=session, config=config, return_addr=session["return_url"])
elif request.method == "POST" and (config["debug-mode"] or session["login"] == config["MNT"]): elif request.method == "POST" and config["debug-mode"]:
try: try:
print(request.form) print(request.form)
if request.form["theanswer"] != "42": if request.form["theanswer"] != "42":
@ -299,7 +282,7 @@ def login():
allowed6 = None allowed6 = None
session["user-data"] = {'asn': asn, 'allowed4': allowed4, session["user-data"] = {'asn': asn, 'allowed4': allowed4,
'allowed6': allowed6, 'mnt': mnt, 'authtype': "debug"} 'allowed6': allowed6, 'mnt': mnt, 'authtype': "debug"}
session["login"] = mnt if "login" not in session else session["login"] 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"
@ -320,14 +303,12 @@ def peerings_delete():
return render_template("peerings-delete.html", session=session, config=config, request_args=request.args) 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)}" return f"{request.method} /peerings/delete?{str(request.args)}{str(request.form)}"
elif request.method in ["POST", "DELETE"]: elif request.method in ["POST", "DELETE"]:
if request.form["confirm"] != "on": 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") return render_template("peerings-delete.html", session=session, config=config, request_args=request.args, msg="you have to confirm the deletion first")
# mnt=None, if admin if not peerings.exists(request.args["asn"], request.args["node"], mnt=session["login"]):
if not peerings.exists(request.args["asn"], request.args["node"], mnt=session["user-data"]["mnt"] if session["login"] != config["MNT"] else None):
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") 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)) print(str(request))
# mnt=None, if admin if not peerings.delete_peering(request.args["asn"], request.args["node"], mnt=session["login"]):
if not peerings.delete_peering(request.args["asn"], request.args["node"], mnt=session["user-data"]["mnt"] if session["login"] != config["MNT"] else None):
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") 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", session["msg"] = {"msg": "peer-del",
"node": request.args["node"], "asn": request.args["asn"]} "node": request.args["node"], "asn": request.args["asn"]}
@ -340,9 +321,9 @@ def peerings_delete():
def peerings_edit(): def peerings_edit():
print(session) print(session)
if request.method == "GET": if request.method == "GET":
if "node" not 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 peering selected, please click one of the buttons above") return render_template("peerings-edit.html", session=session, config=config, peerings=peerings, msg="no peering selected, please click one of the buttons above")
mnt_peerings = peerings.get_peerings_by_mnt(session["user-data"]["mnt"]) mnt_peerings = peerings.get_peerings_by_mnt(session["login"])
# print(mnt_peerings) # print(mnt_peerings)
if "node" in request.args and request.args["node"] in config["nodes"]: if "node" in request.args and request.args["node"] in config["nodes"]:
selected_peering = None selected_peering = None
@ -358,14 +339,14 @@ def peerings_edit():
elif request.method == "POST": elif request.method == "POST":
print(request.args) print(request.args)
print(request.form) print(request.form)
if "node" not 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 peering selected, please click one of the buttons above") return render_template("peerings-edit.html", session=session, config=config, peerings=peerings, msg="no peering selected, 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 selected_peering = None
mnt_peerings = peerings.get_peerings_by_mnt(session["user-data"]["mnt"]) mnt_peerings = peerings.get_peerings_by_mnt(session["login"])
for p in mnt_peerings: for p in mnt_peerings:
if p["node"] == request.args["node"] and p["ASN"] == request.args["asn"]: if p["node"] == request.args["node"] and p["ASN"] == request.args["asn"]:
selected_peering = p selected_peering = p
@ -373,7 +354,7 @@ def peerings_edit():
break 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, selected_peering=selected_peering), 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"]["asn"], request.args["node"], session["user-data"]["mnt"], 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)", selected_peering=selected_peering), 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")
@ -392,17 +373,15 @@ def peerings_new():
elif request.method == "POST": elif request.method == "POST":
print(request.args) print(request.args)
print(request.form) print(request.form)
if "node" not 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
success, code = peerings.add_peering(session["user-data"]["asn"], request.args["node"], session["user-data"]["mnt"], 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"]):
print(f"{success}, {code}") return render_template("peerings-new.html", session=session, config=config, peerings=peerings, msg="this ASN already has a peering with the requested node"), 400
if not success:
return render_template("peerings-new.html", session=session, config=config, peerings=peerings, msg="this ASN already has a peering with the requested node or something failed in the backend, please retry later"), code
return redirect(f"{config['base-dir']}peerings") return redirect(f"{config['base-dir']}peerings")
@ -421,14 +400,8 @@ def peerings_view():
elif request.method == "DELETE": elif request.method == "DELETE":
return peerings_delete() return peerings_delete()
else: else:
# shouldn't get here
abort(405) abort(405)
@app.route("/hidden", methods=["GET", "POST", "DELETE"])
@auth_required()
def hidden_page():
...
@app.route("/") @app.route("/")
def index(): def index():

View file

@ -15,21 +15,14 @@ class NodeCommunicator:
self.__api_addr = config["api-con"] self.__api_addr = config["api-con"]
def update(self, action: str, peering: dict): def update(self, action: str, peering: dict):
try: if action == "add":
if action == "add": print(requests.post(f"{self.__api_addr}peerings", json=peering))
req = requests.post(f"{self.__api_addr}peerings", json=peering, timeout=10) elif action == "update":
elif action == "update": print(requests.put(f"{self.__api_addr}peerings", json=peering))
req = requests.put(f"{self.__api_addr}peerings", json=peering, timeout=10) elif action == "delete":
elif action == "delete": print(requests.delete(f"{self.__api_addr}peerings", json=peering))
req = requests.delete(f"{self.__api_addr}peerings", json=peering, timeout=10) else:
else: return 400
return 400
# should only get here if a request was "successful" (not necessarily http code 200)
return req.status_code
except requests.exceptions.ReadTimeout:
# if it took more than timeout for the client to respond
return 504
class PeeringManager: class PeeringManager:
@ -51,22 +44,15 @@ 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({"mntner": {}, "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)
# migration due to typo in "mnter" key: except json.decoder.JSONDecodeError:
if "mnter" in self.peerings: with open(self.__peering_file, "w") as p:
print("WARN: migrating peering data (Typo mnter)") json.dump({"mnter": {}, "asn": {}}, p)
self.peerings = {"mntner": self.peerings["mnter"], "asn": self.peerings["asn"]} with open(self.__peering_file, "r") as p:
except json.decoder.JSONDecodeError as e: self.peerings = json.load(p)
print("CRITICAL: potentially corrupted peering database, aborting:")
print(e.args)
exit(1)
# with open(self.__peering_file, "w") as p:
# json.dump({"mntner": {}, "asn": {}}, p)
# with open(self.__peering_file, "r") as p:
# self.peerings = json.load(p)
# self.peerings = {} # self.peerings = {}
# missing_peerings = False # missing_peerings = False
@ -92,35 +78,37 @@ class PeeringManager:
new_peering: if mode=="update" the new peering to update to new_peering: if mode=="update" the new peering to update to
""" """
if peering["node"] in self._nodes: if peering["node"] in self._nodes:
return_code = self._nodes[peering["node"]].update(action=action, peering = peering if not new_peering else new_peering) #thread = threading.Thread(target=
if return_code in [200, 201]: self._nodes[peering["node"]].update(#), kwargs={
return True, return_code action=action, peering = peering if not new_peering else new_peering#, })
else: )
return False, return_code #thread.start()
#self._threads.append(thread)
else: else:
return False, 404 return False
def _update_amounts(self): def _update_amounts(self):
__new = {} __new = {}
for asn in self.peerings["asn"]: for asn in self.peerings["asn"]:
for peering in self.peerings["asn"][asn]: for peering in self.peerings["asn"][asn]:
if peering["node"] not in __new: if not peering["node"] in __new:
__new[peering["node"]] = 0 __new[peering["node"]] = 0
__new[peering["node"]] += 1 __new[peering["node"]] += 1
self._amounts = __new self._amounts = __new
def amount_by_node(self, node_name: str): def amount_by_node(self, node_name: str):
if self._amounts is None: if self._amounts == None:
self._update_amounts() self._update_amounts()
try: try:
return self._amounts[node_name] return self._amounts[node_name]
except KeyError: except KeyError:
return 0 return 0
def exists(self, asn, node, mnt=None, wg_key=None, endpoint=None, ipv6ll=None, ipv4=None, ipv6=None, bgp_mp=None, bgp_enh=None): 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""" """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 # 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["mntner"] and asn in self.peerings["mntner"][mnt]): if mnt and not (mnt in self.peerings["mnter"] and asn in self.peerings["mnter"][mnt]):
return False return False
selected_peerings = self.peerings["asn"][asn] selected_peerings = self.peerings["asn"][asn]
# check if the ASn even has peerings # check if the ASn even has peerings
@ -138,37 +126,27 @@ class PeeringManager:
# print(self.peerings) # print(self.peerings)
try: try:
out = [] out = []
# if admin is logged in: return all for asn in self.peerings["mnter"][mnt]:
if mnt == self.__config["MNT"]: try:
for mntner in self.peerings["mntner"]: for peering in self.peerings["asn"][asn]:
for asn in self.peerings["mntner"][mntner]: out.append(peering)
try: except KeyError as e:
for peering in self.peerings["asn"][asn]: pass
out.append(peering)
except KeyError:
pass
else:
for asn in self.peerings["mntner"][mnt]:
try:
for peering in self.peerings["asn"][asn]:
out.append(peering)
except KeyError:
pass
return out return out
except KeyError: except KeyError:
return {} return {}
def add_peering(self, asn, node, mnt, wg_key, endpoint=None, ipv6ll=None, ipv4=None, ipv6=None, bgp_mp=True, bgp_enh=True)-> tuple[bool,int]: 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 asn not in self.peerings["mntner"][mnt]: if not asn in self.peerings["mnter"][mnt]:
# ... and add it if it hasn't # ... and add it if it hasn't
self.peerings[mnt].append(asn) self.peerings[mnt].append(asn)
except KeyError: except KeyError:
# ... and cerate it if it doesn't have any yet # ... and cerate it if it doesn't have any yet
self.peerings["mntner"][mnt] = [asn] self.peerings["mnter"][mnt] = [asn]
try: try:
if asn not in self.peerings["asn"]: if not asn in self.peerings["asn"]:
self.peerings["asn"][asn] = [] self.peerings["asn"][asn] = []
except KeyError: except KeyError:
self.peerings["asn"][asn] = [] self.peerings["asn"][asn] = []
@ -176,78 +154,54 @@ 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: if peering["node"] == node:
return False, 409 return False
peering = {"MNT": mnt, "ASN": asn, "node": node, "wg_key": wg_key, "endpoint": endpoint, peering = {"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} "ipv6ll": ipv6ll, "ipv4": ipv4, "ipv6": ipv6, "bgp_mp": bgp_mp, "bgp_enh": bgp_enh}
success, code = self._update_nodes("add", peering=peering)
if not success and code != 409:
# if something failed notify user and don't add it to the list
return False, code
elif code == 409:
# apparently this peering already exists in a similar way, so we'll try updating it
success, code = self._update_nodes("update", peering=peering)
if not success:
return False, code
self.peerings["asn"][asn].append(peering) self.peerings["asn"][asn].append(peering)
self._update_nodes("add", peering=peering)
self._save_peerings() self._save_peerings()
return True, code return True
def update_peering(self, asn, node, mnt, 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 asn not in self.peerings["mntner"][mnt]: if not asn in self.peerings["mnter"][mnt]:
# ... and add it if it hasn't # ... and add it if it hasn't
self.peerings[mnt].append(asn) self.peerings[mnt].append(asn)
except KeyError: except KeyError:
# ... and create it if it doesn't have any yet # ... and cerate it if it doesn't have any yet
self.peerings["mntner"][mnt] = [asn] self.peerings["mnter"][mnt] = [asn]
try: try:
# there are no peerings for this asn -> can't edit nothing... if not asn in self.peerings["asn"]:
if asn not in self.peerings["asn"]: return False
return False, 404
except KeyError: except KeyError:
# this should only happen if "asn" not in self.peerings return False
return False, 404
found = 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:
old_peering = self.peerings["asn"][asn][pNr] old_peering = self.peerings["asn"][asn][pNr]
new_peering = self.peerings["asn"][asn][pNr] = {"MNT": mnt, "ASN": asn, "node": node, "wg_key": wg_key, new_peering = 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} "endpoint": endpoint, "ipv6ll": ipv6ll, "ipv4": ipv4, "ipv6": ipv6, "bgp_mp": bgp_mp, "bgp_enh": bgp_enh}
found = True success = True
peering_number = pNr if not success:
if not found: return False
return False, 404
# notify the node
success, code = self._update_nodes("update", old_peering, new_peering=new_peering)
if not success and code != 404:
# revert updating peering
self.peerings["asn"][asn][peering_number] = old_peering
return False, code
elif code == 404:
# for some reason the node doesn't yet know about this peering, we'l try adding it
success, code = self._update_nodes("add", old_peering, new_peering=new_peering)
if not success:
# revert updating peering
self.peerings["asn"][asn][peering_number] = old_peering
return False, code
self._save_peerings() self._save_peerings()
return True, code self._update_nodes("update", old_peering, new_peering=new_peering)
return True
def delete_peering(self, asn, node, mnt, wg_key=None): def delete_peering(self, asn, node, mnt, wg_key=None):
if not self.exists(asn, node, mnt=mnt, wg_key=wg_key): if not self.exists(asn, node, mnt=mnt, wg_key=wg_key):
return False, 404 return False
for p in self.peerings["asn"][asn]: for p in self.peerings["asn"][asn]:
if p["node"] == node: if p["node"] == node:
if wg_key and p["wg_key"] != wg_key:
continue
self.peerings["asn"][asn].remove(p) self.peerings["asn"][asn].remove(p)
self._save_peerings() self._save_peerings()
success, code = self._update_nodes("delete", peering=p) self._update_nodes("delete", peering=p)
print(f"DELETE: {asn} on {node}: {success}, {code}") return True
return success, code
# if nothing got found (should have been catched by self.exists) # if nothing got found (should have been catched by self.exists)
return False, 404 return False

View file

@ -5,7 +5,7 @@
<form action="https://dn42.g-load.eu/auth/"> <form action="https://dn42.g-load.eu/auth/">
<link <link
rel="stylesheet" rel="stylesheet"
href="{{config['base-dir']}}static/auth.css" href="https://dn42.g-load.eu/auth/assets/button-font/auth.css"
/> />
<input <input
type="hidden" type="hidden"
@ -15,20 +15,13 @@
/> />
<button <button
type="submit" type="submit"
class="kioubit-btn-dark" class="kioubit-btn kioubit-btn-primary kioubit-btn-dark kioubit-btn-main"
> >
<object <span class="icon-kioubit-auth"></span>Authenticate with Kioubit.dn42
width="35px"
height="35px"
type="image/svg+xml"
data="{{config['base-dir']}}static/auth.svg"
class="kioubit-btn-logo"
></object>
Authenticate with Kioubit.dn42
</button> </button>
</form> </form>
{% if config["debug-mode"] or ("login" in session and session["login"] == config["MNT"])%} {% if config["debug-mode"] %}
<form action="" method="post" class="flex"> <form action="" method="post" class="flex">
<label for="debug" <label for="debug"
>Debug login, if you see this in Production contact {{config["MNT"]}}</label >Debug login, if you see this in Production contact {{config["MNT"]}}</label

View file

@ -155,7 +155,7 @@
let enh_anabled = document.getElementById("bgp-extended-next-hop").checked; let enh_anabled = document.getElementById("bgp-extended-next-hop").checked;
if (enh_anabled) { if (enh_anabled) {
example_config_bird2_enh4.innerHTML = "on"; example_config_bird2_enh4.innerHTML = "on";
//example_config_bird2_enh6.innerHTML = "on"; example_config_bird2_enh6.innerHTML = "on";
} else { } else {
example_config_bird2_enh4.innerHTML = "off"; example_config_bird2_enh4.innerHTML = "off";
example_config_bird2_enh6.innerHTML = "off"; example_config_bird2_enh6.innerHTML = "off";
@ -174,9 +174,9 @@
<div> <div>
{% for peering in mnt_peerings %} {% for peering in mnt_peerings %}
<a href="?node={{peering['node']}}&asn={% if session['login'] != config['MNT'] %}{{session['user-data']['asn']}}{%else%}{{peering['asn']}}{%endif%}"> <a href="?node={{peering['node']}}&asn={{session['user-data']['asn']}}">
<button {% if selected_peering %}{% if selected_peering == peering['node'] %}class="button-selected"{% endif %}{% endif %}> <button {% if selected_peering %}{% if selected_peering == peering['node'] %}class="button-selected"{% endif %}{% endif %}>
{% if session['login'] == config['MNT'] %}{{peering['ASN']}}<br>{%endif%}with<br>{{peering["node"]}} with<br>{{peering["node"]}}
</button> </button>
{% if selected_node == peering['node'] %} {% if selected_node == peering['node'] %}
{% set selected_peering = peering %} {% set selected_peering = peering %}
@ -190,7 +190,7 @@
<tr> <tr>
<td><label for="peer-asn">Your ASN</label></td> <td><label for="peer-asn">Your ASN</label></td>
<td></td> <td></td>
<td><input type="text" name="peer-asn" id="peer-asn" {% if session["login"] != config["MNT"] %}disabled="disabled"{%endif%} value="{% if session['login'] != config['MNT'] %}{{session['user-data']['asn']}}{%else%}{{selected_peering['ASN']}}{%endif%}"></td> <td><input type="text" name="peer-asn" id="peer-asn" disabled="disabled" value="{{session['user-data']['asn']}}"></td>
</tr> </tr>
<tr> <tr>
<td><h4>Wireguard</h4></td> <td><h4>Wireguard</h4></td>
@ -205,22 +205,22 @@
<tr> <tr>
<td><label for="peer-endpoint">your Endpoint</label></td> <td><label for="peer-endpoint">your Endpoint</label></td>
<td><input type="checkbox" name="peer-endpoint-enabled" id="peer-endpoint-enabled" {% if selected_peering %}{% if selected_peering["endpoint"] %} checked {% endif %}{%endif%}></td> <td><input type="checkbox" name="peer-endpoint-enabled" id="peer-endpoint-enabled" {% if selected_peering %}{% if selected_peering["endpoint"] %} checked {% endif %}{%endif%}></td>
<td><input type="text" name="peer-endpoint" id="peer-endpoint" onchange="return update_from_endpoint()" placeholder="node.example.org:1234" {% if selected_peering %}{% if selected_peering["endpoint"] %}value="{{selected_peering['endpoint']}}"{% endif %}{%endif%}></td> <td><input type="text" name="peer-endpoint" id="peer-endpoint" onchange="return update_from_endpoint()" {% if selected_peering %}{% if selected_peering["endpoint"] %}value="{{selected_peering['endpoint']}}"{% endif %}{%endif%}></td>
</tr> </tr>
<tr> <tr>
<td><label for="peer-v6ll">your ipv6 LinkLocal</label></td> <td><label for="peer-v6ll">your ipv6 LinkLocal</label></td>
<td><input type="checkbox" name="peer-v6ll-enabled" id="peer-v6ll-enabled" onchange="return update_from_v6ll()" placeholder="fe80::xxxx (recommended/preferred)" title="default when using extended next hop" {% if selected_peering %}{% if selected_peering["ipv6ll"] %} checked {% endif %}{%endif%}></td> <td><input type="checkbox" name="peer-v6ll-enabled" id="peer-v6ll-enabled" onchange="return update_from_v6ll()"{% if selected_peering %}{% if selected_peering["ipv6ll"] %} checked {% endif %}{%endif%}></td>
<td><input type="text" name="peer-v6ll" id="peer-v6ll" onchange="return update_from_v6ll()"{% if selected_peering %}{% if selected_peering["ipv6ll"] %}value="{{selected_peering['ipv6ll']}}" {% endif %}{%endif%}></td> <td><input type="text" name="peer-v6ll" id="peer-v6ll" onchange="return update_from_v6ll()"{% if selected_peering %}{% if selected_peering["ipv6ll"] %}value="{{selected_peering['ipv6ll']}}" {% endif %}{%endif%}></td>
</tr> </tr>
<tr> <tr>
<td><label for="peer-v4">your ipv4</label></td> <td><label for="peer-v4">your ipv4</label></td>
<td><input type="checkbox" name="peer-v4-enabled" id="peer-v4-enabled" onchange="return update_from_v4()" {% if selected_peering %}{% if selected_peering["ipv4"] %} checked {% endif %}{%endif%}></td> <td><input type="checkbox" name="peer-v4-enabled" id="peer-v4-enabled" onchange="return update_from_v4()" {% if selected_peering %}{% if selected_peering["ipv4"] %} checked {% endif %}{%endif%}></td>
<td><input type="text" name="peer-v4" id="peer-v4" onchange="return update_from_v4()" placeholder="172.2x.yyy.zzz" title="only required when not using extended next hop or not MultiProtocol" {% if selected_peering %}{% if selected_peering["ipv4"] %}value="{{selected_peering['ipv4']}}" {% endif %}{%endif%}></td> <td><input type="text" name="peer-v4" id="peer-v4" onchange="return update_from_v4()" {% if selected_peering %}{% if selected_peering["ipv4"] %}value="{{selected_peering['ipv4']}}" {% endif %}{%endif%}></td>
</tr> </tr>
<tr> <tr>
<td><label for="peer-v6">your ipv6</label></td> <td><label for="peer-v6">your ipv6</label></td>
<td><input type="checkbox" name="peer-v6-enabled" id="peer-v6-enabled" onchange="return update_from_v6()"{% if selected_peering %}{% if selected_peering["ipv6"] %} checked {% endif %}{%endif%}></td> <td><input type="checkbox" name="peer-v6-enabled" id="peer-v6-enabled" onchange="return update_from_v6()"{% if selected_peering %}{% if selected_peering["ipv6"] %} checked {% endif %}{%endif%}></td>
<td><input type="text" name="peer-v6" id="peer-v6" onchange="return update_from_v6()" placeholder="fdxx:yyyy:zzzz:..." title="only required when ipv6 LinkLocal and/or MultiProtocol BGP are not supported" {% if selected_peering %}{% if selected_peering["ipv6"] %}value="{{selected_peering['ipv6']}}" {% endif %}{%endif%}></td> <td><input type="text" name="peer-v6" id="peer-v6" onchange="return update_from_v6()" {% if selected_peering %}{% if selected_peering["ipv6"] %}value="{{selected_peering['ipv6']}}" {% endif %}{%endif%}></td>
</tr> </tr>
<tr> <tr>
<td><h4>BGP</h4></td> <td><h4>BGP</h4></td>
@ -266,7 +266,7 @@ protocol bgp dn42_{{config["MNT"][:-4].lower()}} from dnpeers {
extended next hop <span id="example-config-bird2-enh4">on</span>; extended next hop <span id="example-config-bird2-enh4">on</span>;
}; };
ipv6 { ipv6 {
extended next hop <span id="example-config-bird2-enh6">off</span>; extended next hop <span id="example-config-bird2-enh6">on</span>;
}; };
} }
</pre> </pre>

View file

@ -155,7 +155,7 @@
let enh_anabled = document.getElementById("bgp-extended-next-hop").checked; let enh_anabled = document.getElementById("bgp-extended-next-hop").checked;
if (enh_anabled) { if (enh_anabled) {
example_config_bird2_enh4.innerHTML = "on"; example_config_bird2_enh4.innerHTML = "on";
//example_config_bird2_enh6.innerHTML = "on"; example_config_bird2_enh6.innerHTML = "on";
} else { } else {
example_config_bird2_enh4.innerHTML = "off"; example_config_bird2_enh4.innerHTML = "off";
example_config_bird2_enh6.innerHTML = "off"; example_config_bird2_enh6.innerHTML = "off";
@ -187,7 +187,7 @@
<tr> <tr>
<td><label for="peer-asn">Your ASN</label></td> <td><label for="peer-asn">Your ASN</label></td>
<td></td> <td></td>
<td><input type="text" name="peer-asn" id="peer-asn" {% if session["login"] != config["MNT"] %}disabled="disabled"{%endif%} value="{{session['user-data']['asn']}}"></td> <td><input type="text" name="peer-asn" id="peer-asn" disabled="disabled" value="{{session['user-data']['asn']}}"></td>
</tr> </tr>
<tr> <tr>
<td><h4>Wireguard</h4></td> <td><h4>Wireguard</h4></td>
@ -202,22 +202,22 @@
<tr> <tr>
<td><label for="peer-endpoint">your Endpoint</label></td> <td><label for="peer-endpoint">your Endpoint</label></td>
<td><input type="checkbox" name="peer-endpoint-enabled" id="peer-endpoint-enabled" checked></td> <td><input type="checkbox" name="peer-endpoint-enabled" id="peer-endpoint-enabled" checked></td>
<td><input type="text" name="peer-endpoint" id="peer-endpoint" onchange="return update_from_endpoint()" placeholder="node.example.org:1234"></td> <td><input type="text" name="peer-endpoint" id="peer-endpoint" onchange="return update_from_endpoint()"></td>
</tr> </tr>
<tr> <tr>
<td><label for="peer-v6ll">your ipv6 LinkLocal</label></td> <td><label for="peer-v6ll">your ipv6 LinkLocal</label></td>
<td><input type="checkbox" name="peer-v6ll-enabled" id="peer-v6ll-enabled" onchange="return update_from_v6ll()" checked></td> <td><input type="checkbox" name="peer-v6ll-enabled" id="peer-v6ll-enabled" onchange="return update_from_v6ll()" checked></td>
<td><input type="text" name="peer-v6ll" id="peer-v6ll" onchange="return update_from_v6ll()" placeholder="fe80::xxxx (recommended/preferred)" title="default when using extended next hop"></td> <td><input type="text" name="peer-v6ll" id="peer-v6ll" onchange="return update_from_v6ll()"></td>
</tr> </tr>
<tr> <tr>
<td><label for="peer-v4">your ipv4</label></td> <td><label for="peer-v4">your ipv4</label></td>
<td><input type="checkbox" name="peer-v4-enabled" id="peer-v4-enabled" onchange="return update_from_v4()"></td> <td><input type="checkbox" name="peer-v4-enabled" id="peer-v4-enabled"onchange="return update_from_v4()"></td>
<td><input type="text" name="peer-v4" id="peer-v4" onchange="return update_from_v4()" placeholder="172.2x.yyy.zzz" title="only required when not using extended next hop or not MultiProtocol"></td> <td><input type="text" name="peer-v4" id="peer-v4"onchange="return update_from_v4()"></td>
</tr> </tr>
<tr> <tr>
<td><label for="peer-v6">your ipv6</label></td> <td><label for="peer-v6">your ipv6</label></td>
<td><input type="checkbox" name="peer-v6-enabled" id="peer-v6-enabled" onchange="return update_from_v6()"></td> <td><input type="checkbox" name="peer-v6-enabled" id="peer-v6-enabled"onchange="return update_from_v6()"></td>
<td><input type="text" name="peer-v6" id="peer-v6" onchange="return update_from_v6()" placeholder="fdxx:yyyy:zzzz:..." title="only required when ipv6 LinkLocal and/or MultiProtocol BGP are not supported"></td> <td><input type="text" name="peer-v6" id="peer-v6"onchange="return update_from_v6()"></td>
</tr> </tr>
<tr> <tr>
<td><h4>BGP</h4></td> <td><h4>BGP</h4></td>
@ -263,7 +263,7 @@ protocol bgp dn42_{{config["MNT"][:-4].lower()}} from dnpeers {
extended next hop <span id="example-config-bird2-enh4">on</span>; extended next hop <span id="example-config-bird2-enh4">on</span>;
}; };
ipv6 { ipv6 {
extended next hop <span id="example-config-bird2-enh6">off</span>; extended next hop <span id="example-config-bird2-enh6">on</span>;
}; };
} }
</pre> </pre>

View file

@ -5,7 +5,7 @@
> >
</div> </div>
<div class="flex flex-row"> <div class="flex flex-row">
{% for peering in peerings.get_peerings_by_mnt(session["user-data"]["mnt"]) %} {% 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>

View file

@ -1 +0,0 @@
.kioubit-btn-dark,.kioubit-btn-light{font-weight:400;font-size:1rem;line-height:1.5;padding:.5em;display:flex;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.kioubit-btn-dark{color:#fff;background-color:#343a40;vertical-align:middle;border:1px solid transparent;border-radius:.4rem;align-items:center}.kioubit-btn-dark:hover{color:#fff;background-color:#651fff;border-color:#1d2124}.kioubit-btn-dark:focus,.kioubit-btn-light:focus{outline:0;box-shadow:0 0 0 .2rem rgba(82,88,93,.5)}.kioubit-btn-light{color:#fafafa;background-color:#2962ff;border:1px solid transparent;border-radius:.4rem;align-items:center}.kioubit-btn-light:hover{color:#fff;background-color:#311b92;border-color:#1d2124}.kioubit-btn-logo{margin-right:.5em;filter:invert(100%) sepia(5%) saturate(7480%) hue-rotate(81deg) brightness(125%) contrast(106%)}

View file

@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -64 1024 1024">
<path d="M375.899 935.372C149.064 861.488 15.555 660.577 28.517 410.41 37.59 225.053 133.509 77.286 295.535-1.782c80.365-40.182 95.919-42.775 216.466-42.775 116.658 0 136.101 3.889 197.023 36.294C899.566 91.545 994.189 262.643 982.523 485.59c-9.073 185.357-104.992 333.124-267.018 412.192-73.884 37.59-101.104 42.775-197.023 45.367-60.922 1.296-124.435-1.296-142.582-7.777zm268.314-124.435c67.403-28.516 167.21-129.62 198.319-203.504 40.182-93.327 40.182-226.835 1.296-314.977-60.922-138.694-198.319-230.724-344.79-230.724-97.215 0-156.841 24.628-238.501 101.104-84.253 79.068-120.547 164.618-121.843 285.165-1.296 270.906 264.425 462.744 505.519 362.937zM311.089 448V156.354h89.438l7.777 256.648 107.585-128.324c102.4-123.139 108.881-128.324 158.137-128.324 28.516 0 51.848 2.592 51.848 5.185 0 3.889-58.329 72.587-128.324 152.952L469.226 460.962l98.511 110.177c54.441 60.922 110.177 123.139 124.435 139.99l24.628 28.516h-50.552c-49.256 0-55.737-6.481-154.248-121.843L408.304 494.663l-3.889 123.139-3.889 121.843h-89.438V447.999z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -1,4 +1,3 @@
Flask Flask
waitress waitress
pyopenssl pyopenssl
requests

0
web/start.sh Executable file → Normal file
View file