Compare commits
22 commits
Author | SHA1 | Date | |
---|---|---|---|
1f242ba597 | |||
d34136b42f | |||
b3337a5b3d | |||
067803081d | |||
376571c410 | |||
41e2290365 | |||
f01559844d | |||
b3eb047a6f | |||
1894a6dce9 | |||
09da5ec830 | |||
67f10102b3 | |||
23fa5ba1a6 | |||
4139c66381 | |||
d0a21adab4 | |||
cb03ba5556 | |||
c27951ef0c | |||
e6e38675b9 | |||
b44f89b664 | |||
c02e8ae3c0 | |||
ad9caf6798 | |||
b231b6a835 | |||
0f1cbb3a11 |
19 changed files with 255 additions and 162 deletions
|
@ -5,10 +5,10 @@ This is my (LARE-MNT) implementation of an auto peering system
|
|||
It consists of two parts:
|
||||
1. the "server"
|
||||
- handling user auth (via kioubit)
|
||||
- add/edit/delete peerings (#todo)
|
||||
- communicate new/update peerings with nodes #todo
|
||||
2. the "nodes daemon" #todo
|
||||
- reveives new/updated peering configs from the "server" #todo
|
||||
- add/edit/delete peerings
|
||||
- communicate new/update peerings with nodes
|
||||
2. the "nodes daemon"
|
||||
- reveives new/updated peering configs from the "server"
|
||||
|
||||
|
||||
## Installation
|
||||
|
|
|
@ -27,7 +27,7 @@ Type=simple
|
|||
Restart=on-failure
|
||||
RestartSec=5s
|
||||
WorkingDirectory=</path/to/autopeering/>web
|
||||
ExecStart=start.sh
|
||||
ExecStart=</path/to/autopeering>/web/start.sh
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
@ -72,7 +72,7 @@ Type=simple
|
|||
Restart=on-failure
|
||||
RestartSec=5s
|
||||
WorkingDirectory=</path/to/autopeering>/nodes
|
||||
ExecStart=start.sh
|
||||
ExecStart=</path/to/autopeering>/nodes/start.sh
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
|
|
@ -131,51 +131,52 @@ class PeeringManager:
|
|||
def __generate_wg_conf(self, peering: dict):
|
||||
return render_template("wireguard.template.conf", peering=peering)
|
||||
def __generate_bird_conf(self, peering: dict):
|
||||
peering["MNT"] = peering["MNT"].replace("-","_")
|
||||
return render_template("bgp-peer.template.conf", peering=peering)
|
||||
|
||||
def __install_peering(self, mode: str, peering: dict):
|
||||
if mode == "add":
|
||||
wg_conf = self.__generate_wg_conf(peering)
|
||||
bgp_conf = self.__generate_bird_conf(peering)
|
||||
with open(f"{self.__config['wg-configs']}/dn42_{peering['MNT'][:-4].lower()}.conf", "w") as wg_file:
|
||||
with open(f"{self.__config['wg-configs']}/dn42_{peering['ASN'][-6:] if len(peering['ASN']) >=6 else peering['ASN']}.conf", "w") as wg_file:
|
||||
wg_file.write(wg_conf)
|
||||
|
||||
wg_enable = subprocess.run(self.__config["wg-commands"]["enable"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
|
||||
wg_enable = subprocess.run(self.__config["wg-commands"]["enable"].replace("{PEERING}",peering['ASN'][-6:] if len(peering["ASN"]) >=6 else peering["ASN"]).split(" "))
|
||||
print(wg_enable)
|
||||
wg_up = subprocess.run(self.__config["wg-commands"]["up"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
|
||||
wg_up = subprocess.run(self.__config["wg-commands"]["up"].replace("{PEERING}",peering['ASN'][-6:] if len(peering["ASN"]) >=6 else peering["ASN"]).split(" "))
|
||||
print(wg_up)
|
||||
time.sleep(5)
|
||||
with open(f"{self.__config['bird-peers']}/dn42_{peering['MNT'][:-4].lower()}.conf", "w") as bgp_file:
|
||||
time.sleep(3)
|
||||
with open(f"{self.__config['bird-peers']}/dn42_{peering['MNT'][:-4].lower()}_{peering['ASN'][-4:]}.conf", "w") as bgp_file:
|
||||
bgp_file.write(bgp_conf)
|
||||
bgp_reload = subprocess.run(self.__config["bird-reload"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
|
||||
bgp_reload = subprocess.run(self.__config["bird-reload"].replace("{PEERING}",peering['ASN'][-6:] if len(peering["ASN"]) >=6 else peering["ASN"]).split(" "))
|
||||
print(bgp_reload)
|
||||
|
||||
return 200
|
||||
elif mode == "update":
|
||||
wg_conf = self.__generate_wg_conf(peering)
|
||||
bgp_conf = self.__generate_bird_conf(peering)
|
||||
with open(f"{self.__config['wg-configs']}/dn42_{peering['MNT'][:-4].lower()}.conf", "w") as wg_file:
|
||||
with open(f"{self.__config['wg-configs']}/dn42_{peering['ASN'][-6:] if len(peering['ASN']) >=6 else peering['ASN']}.conf", "w") as wg_file:
|
||||
wg_file.write(wg_conf)
|
||||
|
||||
wg_down = subprocess.run(self.__config["wg-commands"]["down"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
|
||||
wg_down = subprocess.run(self.__config["wg-commands"]["down"].replace("{PEERING}",peering['ASN'][-6:] if len(peering["ASN"]) >=6 else peering["ASN"]).split(" "))
|
||||
print(wg_down)
|
||||
wg_up = subprocess.run(self.__config["wg-commands"]["up"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
|
||||
wg_up = subprocess.run(self.__config["wg-commands"]["up"].replace("{PEERING}",peering['ASN'][-6:] if len(peering["ASN"]) >=6 else peering["ASN"]).split(" "))
|
||||
print(wg_up)
|
||||
time.sleep(5)
|
||||
with open(f"{self.__config['bird-peers']}/dn42_{peering['MNT'][:-4].lower()}.conf", "w") as bgp_file:
|
||||
time.sleep(3)
|
||||
with open(f"{self.__config['bird-peers']}/dn42_{peering['MNT'][:-4].lower()}_{peering['ASN'][-4:]}.conf", "w") as bgp_file:
|
||||
bgp_file.write(bgp_conf)
|
||||
bgp_reload = subprocess.run(self.__config["bird-reload"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
|
||||
bgp_reload = subprocess.run(self.__config["bird-reload"].replace("{PEERING}",peering['ASN'][-6:] if len(peering["ASN"]) >=6 else peering["ASN"]).split(" "))
|
||||
print(bgp_reload)
|
||||
|
||||
return 200
|
||||
elif mode == "delete":
|
||||
os.remove(f"{self.__config['bird-peers']}/dn42_{peering['MNT'][:-4].lower()}.conf")
|
||||
bgp_reload = subprocess.run(self.__config["bird-reload"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
|
||||
os.remove(f"{self.__config['bird-peers']}/dn42_{peering['MNT'][:-4].lower()}_{peering['ASN'][-4:]}.conf")
|
||||
bgp_reload = subprocess.run(self.__config["bird-reload"].replace("{PEERING}",peering['ASN'][-6:] if len(peering["ASN"]) >=6 else peering["ASN"]).split(" "))
|
||||
print(bgp_reload)
|
||||
time.sleep(5)
|
||||
wg_down = subprocess.run(self.__config["wg-commands"]["down"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
|
||||
time.sleep(3)
|
||||
wg_down = subprocess.run(self.__config["wg-commands"]["down"].replace("{PEERING}",peering['ASN'][-6:] if len(peering["ASN"]) >=6 else peering["ASN"]).split(" "))
|
||||
print(wg_down)
|
||||
wg_disable = subprocess.run(self.__config["wg-commands"]["disable"].replace("{MNT}",peering['MNT'][:-4].lower()).split(" "))
|
||||
wg_disable = subprocess.run(self.__config["wg-commands"]["disable"].replace("{PEERING}",peering['ASN'][-6:] if len(peering["ASN"]) >=6 else peering["ASN"]).split(" "))
|
||||
print(wg_disable)
|
||||
|
||||
return 200
|
||||
|
@ -228,7 +229,6 @@ 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):
|
||||
asn = ASN
|
||||
|
||||
try:
|
||||
if not asn in self.peerings:
|
||||
return False, 404
|
||||
|
@ -239,8 +239,10 @@ class PeeringManager:
|
|||
for pNr in range(len(self.peerings[asn])):
|
||||
if self.peerings[asn][pNr]["node"] == node:
|
||||
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"], "wg_key": wg_key,
|
||||
"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"]}
|
||||
new_peering = self.peerings[asn][pNr] = {"MNT": MNT if MNT!=NotSpecified else old_peering["MNT"], "ASN": asn, "node": config["nodename"],
|
||||
"wg_key": wg_key,"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"]}
|
||||
success = True
|
||||
if not success:
|
||||
return False, 404
|
||||
|
@ -301,9 +303,9 @@ class PeeringsRoute(Resource):
|
|||
|
||||
requested_peerings = peerings.get_peerings_by_asn(args["ASN"])
|
||||
if requested_peerings:
|
||||
return {"success": True, "asn": args["ASN"], "peerings": requested_peerings}, 200
|
||||
return {"success": True, "ASN": args["ASN"], "peerings": requested_peerings}, 200
|
||||
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()
|
||||
def post(self):
|
||||
|
@ -321,8 +323,8 @@ class PeeringsRoute(Resource):
|
|||
parser.add_argument('ipv6ll')
|
||||
parser.add_argument('ipv4')
|
||||
parser.add_argument('ipv6')
|
||||
parser.add_argument('bgp_mp')
|
||||
parser.add_argument('bgp_enh')
|
||||
parser.add_argument('bgp_mp', type = bool)
|
||||
parser.add_argument('bgp_enh', type = bool)
|
||||
args = parser.parse_args() # parse arguments to dictionary
|
||||
|
||||
if not peerings.exists(asn=args["ASN"], wg_key=args["wg_key"]):
|
||||
|
@ -353,8 +355,8 @@ class PeeringsRoute(Resource):
|
|||
parser.add_argument('ipv6ll', store_missing=False)
|
||||
parser.add_argument('ipv4', store_missing=False)
|
||||
parser.add_argument('ipv6', store_missing=False)
|
||||
parser.add_argument('bgp_mp', store_missing=False)
|
||||
parser.add_argument('bgp_enh', store_missing=False)
|
||||
parser.add_argument('bgp_mp', type=bool, store_missing=False)
|
||||
parser.add_argument('bgp_enh', type=bool, store_missing=False)
|
||||
args = parser.parse_args() # parse arguments to dictionary
|
||||
print(args)
|
||||
if peerings.exists(asn=args["ASN"], wg_key=args["wg_key"]):
|
||||
|
|
|
@ -9,11 +9,11 @@
|
|||
"production": true, //optional, default true;
|
||||
"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-commands": { // {MNT} will get replaced with the lowercase mnter without "-MNT"
|
||||
"enable": "systemctl enable wg-quick@dn42_{MNT}", //command to execute for enabling the wg-interface
|
||||
"up": "systemctl start wg-quick@dn42_{MNT}", //command to execute for starting the wg-interface
|
||||
"down": "systemctl stop wg-quick@dn42_{MNT}", //command to execute for stopping the wg-interface
|
||||
"disable": "systemctl disable wg-quick@dn42_{MNT}" //command to execute for disabling the wg-interface
|
||||
"wg-commands": { // {PEERING} will get replaced with the lowercase mnter without "-MNT" followed by the last four digits of the ASn
|
||||
"enable": "./wg-services.sh enable {PEERING}", //command to execute for enabling the wg-interface
|
||||
"up": "./wg-services.sh start {PEERING}", //command to execute for starting the wg-interface
|
||||
"down": "./wg-services.sh stop {PEERING}", //command to execute for stopping the wg-interface
|
||||
"disable": "./wg-services.sh disable {PEERING}" //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-reload": "birdc configure", // optional, default: "birdc configure"; command to reconfigure bird or other bgp daemon
|
||||
|
|
0
nodes/start.sh
Normal file → Executable file
0
nodes/start.sh
Normal file → Executable file
|
@ -1,7 +1,7 @@
|
|||
{% if peering["bgp_mp"] %}
|
||||
protocol bgp dn42_{{peering["MNT"][:-4].lower()}} from dnpeers {
|
||||
neighbor {{peering["ipv6ll"]}} as {{peering["ASN"]}};
|
||||
interface "dn42_{{peering['MNT'][:-4].lower()}}";
|
||||
{% if peering["bgp_mp"] == True %}
|
||||
protocol bgp dn42_{{peering["MNT"][:-4].lower()}}_{{peering["ASN"][-4:]}} from dnpeers {
|
||||
neighbor {{peering["ipv6ll"] or peering["ipv4"] or peering["ipv6"]}} as {{peering["ASN"]}};
|
||||
interface {% if peering['ASN'].__len__() >=6 %}"dn42_{{peering['ASN'][-6:]}}"{% else %}"dn42_{{peering['asn']}}"{% endif %};
|
||||
passive off;
|
||||
|
||||
ipv4 {
|
||||
|
@ -16,19 +16,21 @@ protocol bgp dn42_{{peering["MNT"][:-4].lower()}} from dnpeers {
|
|||
#import where dn42_import_filter(x,y,z);
|
||||
#export where dn42_export_filter(x,y,z);
|
||||
#}
|
||||
extended next hop {% if peering["bgp_enh"] %}on{%else%}off{%endif%};
|
||||
extended next hop off;
|
||||
};
|
||||
};
|
||||
{%else%}
|
||||
protocol bgp dn42_{{peering["MNT"][:-4].lower()}}_4 from dnpeers {
|
||||
{% elif peering["bgp_mp"] == False %}
|
||||
protocol bgp dn42_{{peering["MNT"][:-4].lower()}}_{{peering['ASN'][-4:]}}_4 from dnpeers {
|
||||
neighbor {{peering["ipv4"]}} as {{peering["ASN"]}};
|
||||
interface {% if peering['ASN'].__len__() >=6 %}"dn42_{{peering['ASN'][-6:]}}"{% else %}"dn42_{{peering['asn']}}"{% endif %};
|
||||
passive off;
|
||||
#import where dn42_import_filter(x,y,z);
|
||||
#export where dn42_export_filter(x,y,z);
|
||||
};
|
||||
|
||||
protocol bgp dn42_{{peering["MNT"][:-4].lower()}}_6 from dnpeers {
|
||||
neighbor {{peering["ipv6"]}} as {{peering["ASN"]}};
|
||||
protocol bgp dn42_{{peering["MNT"][:-4].lower()}}_{{peering['ASN'][-4:]}}_6 from dnpeers {
|
||||
neighbor {% peering["ipv6"] != None %}{{peering["ipv6"]}}{% else %}{{peering["ipv6ll"]}}{% endif %} as {{peering["ASN"]}};
|
||||
interface {% if peering['ASN'].__len__() >=6 %}"dn42_{{peering['ASN'][-6:]}}"{% else %}"dn42_{{peering['asn']}}"{% endif %};
|
||||
passive off;
|
||||
#import where dn42_import_filter(x,y,z);
|
||||
#export where dn42_export_filter(x,y,z);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
PostUp = wg set %i private-key /etc/wireguard/dn42.priv
|
||||
ListenPort = 2{{peering["ASN"][-4:]}}
|
||||
{% 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 fe63:5d40:47e5::130/128 peer {{peering["ipv6"]}}/128
|
||||
{%endif%}{% if peering["ipv6"] %}PostUp = /sbin/ip addr add dev %i fd63: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%}
|
||||
Table = off
|
||||
|
||||
|
|
0
nodes/wg-services.sh
Normal file → Executable file
0
nodes/wg-services.sh
Normal file → Executable file
|
@ -43,12 +43,16 @@ class AuthVerifyer ():
|
|||
user_data = json.loads(base64.b64decode(params))
|
||||
if (time.time() - user_data["time"]) > 60:
|
||||
return False, "Signature to old"
|
||||
elif user_data["domain"] != self.domain.replace("https://",""):
|
||||
return False, "invalid domain"
|
||||
except json.decoder.JSONDecodeError:
|
||||
# we shouldn't get here unless kioubit's service is misbehaving
|
||||
return False, "invalid JSON"
|
||||
except KeyError:
|
||||
return False, "value not found in JSON"
|
||||
logging.debug(user_data)
|
||||
# use mnt[0] as mnt
|
||||
user_data["mnt"] = user_data["mnt"][0]
|
||||
return True, user_data
|
||||
|
||||
|
||||
|
|
|
@ -1,14 +1,19 @@
|
|||
#! /usr/bin/env python3
|
||||
|
||||
# flask
|
||||
from flask import Flask, Response, redirect, render_template, request, session, abort
|
||||
import werkzeug.exceptions as werkzeug_exceptions
|
||||
# other utils
|
||||
import json
|
||||
import os
|
||||
import base64
|
||||
# potential errors on base64 decode: binascii.Error
|
||||
import binascii
|
||||
import logging
|
||||
import random
|
||||
from functools import wraps
|
||||
from ipaddress import ip_address, ip_network, IPv4Network, IPv6Network
|
||||
# self written modules
|
||||
import kioubit_verify
|
||||
from peering_manager import PeeringManager
|
||||
|
||||
|
@ -49,21 +54,21 @@ class Config (dict):
|
|||
except json.decoder.JSONDecodeError:
|
||||
raise SyntaxError(f"no valid JSON found in '{cf.name}'")
|
||||
|
||||
if not "flask-template-dir" in self._config:
|
||||
if "flask-template-dir" not in self._config:
|
||||
self._config["flask-template-dir"] = "../frontend"
|
||||
|
||||
if not "debug-mode" in self._config:
|
||||
if "debug-mode" not in self._config:
|
||||
self._config["debug-mode"] = False
|
||||
if not "base-dir" in self._config:
|
||||
if "base-dir" not in self._config:
|
||||
self._config["base-dir"] = "/"
|
||||
|
||||
if not "peerings-data" in self._config:
|
||||
if "peerings-data" not in self._config:
|
||||
self._config["peering-data"] = "./peerings"
|
||||
|
||||
if not "nodes" in self._config:
|
||||
if "nodes" not in self._config:
|
||||
self,_config = {}
|
||||
for node in self._config["nodes"]:
|
||||
if not "capacity" in self._config["nodes"][node]:
|
||||
if "capacity" not in self._config["nodes"][node]:
|
||||
self._config["nodes"][node]["capacity"] = -1
|
||||
|
||||
logging.info(self._config)
|
||||
|
@ -81,7 +86,8 @@ def check_peering_data(form):
|
|||
|
||||
# check if all required (and enabled) options are specified
|
||||
try:
|
||||
new_peering["peer-asn"] = session["user-data"]["asn"]
|
||||
# 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"] if "peer-asn" not in request.form or session["login"] != config["MNT"] else form["peer-asn"]
|
||||
new_peering["peer-wgkey"] = form["peer-wgkey"]
|
||||
if "peer-endpoint-enabled" in form and form["peer-endpoint-enabled"] == "on":
|
||||
new_peering["peer-endpoint"] = form["peer-endpoint"]
|
||||
|
@ -125,7 +131,7 @@ def check_peering_data(form):
|
|||
wg_key_invalid = True
|
||||
try:
|
||||
base64.b64decode(new_peering["peer-wgkey"])
|
||||
except:
|
||||
except binascii.Error:
|
||||
wg_key_invalid = True
|
||||
if wg_key_invalid:
|
||||
return False, "invalid wireguard Key"
|
||||
|
@ -134,7 +140,7 @@ def check_peering_data(form):
|
|||
if new_peering["peer-endpoint"]:
|
||||
if not new_peering["peer-endpoint"].split(":")[-1].isnumeric():
|
||||
return False, "no port number in endpoint"
|
||||
elif len(new_peering["peer-endpoint"].split(":")) < 2 and not "." in new_peering["peer-endpoint"]:
|
||||
elif len(new_peering["peer-endpoint"].split(":")) < 2 and "." not in new_peering["peer-endpoint"]:
|
||||
return False, "endpoint doesn't look like a ip address or fqdn"
|
||||
|
||||
# check if at least one ip is specified/enabled
|
||||
|
@ -148,13 +154,13 @@ def check_peering_data(form):
|
|||
try:
|
||||
if new_peering["peer-v6ll"]:
|
||||
ipv6ll = ip_address(new_peering["peer-v6ll"])
|
||||
if not ipv6ll.version == 6:
|
||||
if ipv6ll.version != 6:
|
||||
raise ValueError()
|
||||
if not ipv6ll.is_link_local:
|
||||
raise ValueError()
|
||||
if new_peering["peer-v4"]:
|
||||
ipv4 = ip_address(new_peering["peer-v4"])
|
||||
if not ipv4.version == 4:
|
||||
if ipv4.version != 4:
|
||||
raise ValueError()
|
||||
if ipv4.is_link_local:
|
||||
pass
|
||||
|
@ -163,6 +169,11 @@ def check_peering_data(form):
|
|||
raise ValueError()
|
||||
is_in_allowed = False
|
||||
if session["user-data"]["allowed4"]:
|
||||
if not isinstance(session["user-data"]["allowed4"],list):
|
||||
allowed4 = session["user-data"]["allowed4"]
|
||||
if ipv4 in ip_network(allowed4):
|
||||
is_in_allowed = True
|
||||
else:
|
||||
for allowed4 in session["user-data"]["allowed4"]:
|
||||
if ipv4 in ip_network(allowed4):
|
||||
is_in_allowed = True
|
||||
|
@ -172,7 +183,7 @@ def check_peering_data(form):
|
|||
raise ValueError()
|
||||
if new_peering["peer-v6"]:
|
||||
ipv6 = ip_address(new_peering["peer-v6"])
|
||||
if not ipv6.version == 6:
|
||||
if ipv6.version != 6:
|
||||
raise ValueError()
|
||||
if not ipv6.is_private:
|
||||
raise ValueError()
|
||||
|
@ -180,19 +191,25 @@ def check_peering_data(form):
|
|||
raise ValueError()
|
||||
is_in_allowed = False
|
||||
if session["user-data"]["allowed6"]:
|
||||
if not isinstance(session["user-data"]["allowed6"],list):
|
||||
allowed6 = session["user-data"]["allowed6"]
|
||||
if ipv6 in ip_network(allowed6):
|
||||
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:
|
||||
return False, "supplied ipv6 addr not in allowed ip range"
|
||||
except ValueError:
|
||||
except ValueError as e:
|
||||
print(e)
|
||||
return False, "invalid ip address(es) supplied"
|
||||
|
||||
# check bgp options
|
||||
try:
|
||||
if new_peering["bgp-mp"] == False and new_peering["bgp-enh"] == True:
|
||||
if new_peering["bgp-mp"] is False and new_peering["bgp-enh"] is True:
|
||||
return False, "extended next hop requires multiprotocol bgp"
|
||||
if new_peering["bgp-mp"] == False:
|
||||
if new_peering["bgp-mp"] is False:
|
||||
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"
|
||||
except ValueError:
|
||||
|
@ -204,7 +221,7 @@ def auth_required():
|
|||
def wrapper(f):
|
||||
@wraps(f)
|
||||
def decorated(*args, **kwargs):
|
||||
if not "login" in session:
|
||||
if "login" not in session:
|
||||
request_url = f"{config['base-dir']}{request.full_path}".replace("//", "/")
|
||||
return redirect(f"{config['base-dir']}login?return={request_url}")
|
||||
else:
|
||||
|
@ -254,7 +271,7 @@ def login():
|
|||
session["return_url"] = request.args["return"] if "return" in request.args else ""
|
||||
|
||||
return render_template("login.html", session=session, config=config, return_addr=session["return_url"])
|
||||
elif request.method == "POST" and config["debug-mode"]:
|
||||
elif request.method == "POST" and (config["debug-mode"] or session["login"] == config["MNT"]):
|
||||
try:
|
||||
print(request.form)
|
||||
if request.form["theanswer"] != "42":
|
||||
|
@ -282,7 +299,7 @@ def login():
|
|||
allowed6 = None
|
||||
session["user-data"] = {'asn': asn, 'allowed4': allowed4,
|
||||
'allowed6': allowed6, 'mnt': mnt, 'authtype': "debug"}
|
||||
session["login"] = mnt
|
||||
session["login"] = mnt if "login" not in session else session["login"]
|
||||
return redirect(session["return_url"])
|
||||
except ValueError:
|
||||
msg = "at least one of the values provided is wrong/invalid"
|
||||
|
@ -303,12 +320,14 @@ def peerings_delete():
|
|||
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":
|
||||
if 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"]):
|
||||
# mnt=None, if admin
|
||||
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")
|
||||
print(str(request))
|
||||
if not peerings.delete_peering(request.args["asn"], request.args["node"], mnt=session["login"]):
|
||||
# mnt=None, if admin
|
||||
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")
|
||||
session["msg"] = {"msg": "peer-del",
|
||||
"node": request.args["node"], "asn": request.args["asn"]}
|
||||
|
@ -321,9 +340,9 @@ def peerings_delete():
|
|||
def peerings_edit():
|
||||
print(session)
|
||||
if request.method == "GET":
|
||||
if not "node" in request.args or not request.args["node"]:
|
||||
if "node" not 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")
|
||||
mnt_peerings = peerings.get_peerings_by_mnt(session["login"])
|
||||
mnt_peerings = peerings.get_peerings_by_mnt(session["user-data"]["mnt"])
|
||||
# print(mnt_peerings)
|
||||
if "node" in request.args and request.args["node"] in config["nodes"]:
|
||||
selected_peering = None
|
||||
|
@ -339,14 +358,14 @@ def peerings_edit():
|
|||
elif request.method == "POST":
|
||||
print(request.args)
|
||||
print(request.form)
|
||||
if not "node" in request.args or not request.args["node"]:
|
||||
if "node" not 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")
|
||||
|
||||
peering_valid, peering_or_msg = check_peering_data(request.form)
|
||||
print(peering_valid)
|
||||
print(peering_or_msg)
|
||||
selected_peering = None
|
||||
mnt_peerings = peerings.get_peerings_by_mnt(session["login"])
|
||||
mnt_peerings = peerings.get_peerings_by_mnt(session["user-data"]["mnt"])
|
||||
for p in mnt_peerings:
|
||||
if p["node"] == request.args["node"] and p["ASN"] == request.args["asn"]:
|
||||
selected_peering = p
|
||||
|
@ -354,7 +373,7 @@ def peerings_edit():
|
|||
break
|
||||
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
|
||||
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"]):
|
||||
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"]):
|
||||
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")
|
||||
|
@ -373,15 +392,17 @@ def peerings_new():
|
|||
elif request.method == "POST":
|
||||
print(request.args)
|
||||
print(request.form)
|
||||
if not "node" in request.args or not request.args["node"]:
|
||||
if "node" not 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")
|
||||
|
||||
peering_valid, peering_or_msg = check_peering_data(request.form)
|
||||
|
||||
if not peering_valid:
|
||||
return render_template("peerings-new.html", session=session, config=config, peerings=peerings, msg=peering_or_msg), 400
|
||||
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
|
||||
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"])
|
||||
print(f"{success}, {code}")
|
||||
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")
|
||||
|
||||
|
@ -400,8 +421,14 @@ def peerings_view():
|
|||
elif request.method == "DELETE":
|
||||
return peerings_delete()
|
||||
else:
|
||||
# shouldn't get here
|
||||
abort(405)
|
||||
|
||||
@app.route("/hidden", methods=["GET", "POST", "DELETE"])
|
||||
@auth_required()
|
||||
def hidden_page():
|
||||
...
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
|
|
|
@ -15,15 +15,22 @@ class NodeCommunicator:
|
|||
self.__api_addr = config["api-con"]
|
||||
|
||||
def update(self, action: str, peering: dict):
|
||||
try:
|
||||
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":
|
||||
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":
|
||||
print(requests.delete(f"{self.__api_addr}peerings", json=peering))
|
||||
req = requests.delete(f"{self.__api_addr}peerings", json=peering, timeout=10)
|
||||
else:
|
||||
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:
|
||||
|
||||
|
@ -44,15 +51,22 @@ class PeeringManager:
|
|||
def __load_peerings(self):
|
||||
if not os.path.exists(self.__peering_file):
|
||||
with open(self.__peering_file, "x") as p:
|
||||
json.dump({"mnter": {}, "asn": {}}, p)
|
||||
json.dump({"mntner": {}, "asn": {}}, p)
|
||||
try:
|
||||
with open(self.__peering_file, "r") as p:
|
||||
self.peerings = json.load(p)
|
||||
except json.decoder.JSONDecodeError:
|
||||
with open(self.__peering_file, "w") as p:
|
||||
json.dump({"mnter": {}, "asn": {}}, p)
|
||||
with open(self.__peering_file, "r") as p:
|
||||
self.peerings = json.load(p)
|
||||
# migration due to typo in "mnter" key:
|
||||
if "mnter" in self.peerings:
|
||||
print("WARN: migrating peering data (Typo mnter)")
|
||||
self.peerings = {"mntner": self.peerings["mnter"], "asn": self.peerings["asn"]}
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
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 = {}
|
||||
# missing_peerings = False
|
||||
|
@ -78,37 +92,35 @@ class PeeringManager:
|
|||
new_peering: if mode=="update" the new peering to update to
|
||||
"""
|
||||
if peering["node"] in self._nodes:
|
||||
#thread = threading.Thread(target=
|
||||
self._nodes[peering["node"]].update(#), kwargs={
|
||||
action=action, peering = peering if not new_peering else new_peering#, })
|
||||
)
|
||||
#thread.start()
|
||||
#self._threads.append(thread)
|
||||
|
||||
return_code = self._nodes[peering["node"]].update(action=action, peering = peering if not new_peering else new_peering)
|
||||
if return_code in [200, 201]:
|
||||
return True, return_code
|
||||
else:
|
||||
return False
|
||||
return False, return_code
|
||||
else:
|
||||
return False, 404
|
||||
|
||||
def _update_amounts(self):
|
||||
__new = {}
|
||||
for asn in self.peerings["asn"]:
|
||||
for peering in self.peerings["asn"][asn]:
|
||||
if not peering["node"] in __new:
|
||||
if peering["node"] not in __new:
|
||||
__new[peering["node"]] = 0
|
||||
__new[peering["node"]] += 1
|
||||
self._amounts = __new
|
||||
|
||||
def amount_by_node(self, node_name: str):
|
||||
if self._amounts == None:
|
||||
if self._amounts is None:
|
||||
self._update_amounts()
|
||||
try:
|
||||
return self._amounts[node_name]
|
||||
except KeyError:
|
||||
return 0
|
||||
|
||||
def exists(self, asn, node, mnt=None, wg_key=None, endpoint=None, ipv6ll=None, ipv4=None, ipv6=None, bgp_mp=True, bgp_enh=True):
|
||||
def exists(self, asn, node, mnt=None, wg_key=None, endpoint=None, ipv6ll=None, ipv4=None, ipv6=None, bgp_mp=None, bgp_enh=None):
|
||||
"""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]):
|
||||
if mnt and not (mnt in self.peerings["mntner"] and asn in self.peerings["mntner"][mnt]):
|
||||
return False
|
||||
selected_peerings = self.peerings["asn"][asn]
|
||||
# check if the ASn even has peerings
|
||||
|
@ -126,27 +138,37 @@ class PeeringManager:
|
|||
# print(self.peerings)
|
||||
try:
|
||||
out = []
|
||||
for asn in self.peerings["mnter"][mnt]:
|
||||
# if admin is logged in: return all
|
||||
if mnt == self.__config["MNT"]:
|
||||
for mntner in self.peerings["mntner"]:
|
||||
for asn in self.peerings["mntner"][mntner]:
|
||||
try:
|
||||
for peering in self.peerings["asn"][asn]:
|
||||
out.append(peering)
|
||||
except KeyError as e:
|
||||
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
|
||||
except KeyError:
|
||||
return {}
|
||||
|
||||
def add_peering(self, asn, node, mnt, 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)-> tuple[bool,int]:
|
||||
# check if this MNT already has a/this asn
|
||||
try:
|
||||
if not asn in self.peerings["mnter"][mnt]:
|
||||
if asn not in self.peerings["mntner"][mnt]:
|
||||
# ... and add it if it hasn't
|
||||
self.peerings[mnt].append(asn)
|
||||
except KeyError:
|
||||
# ... and cerate it if it doesn't have any yet
|
||||
self.peerings["mnter"][mnt] = [asn]
|
||||
self.peerings["mntner"][mnt] = [asn]
|
||||
try:
|
||||
if not asn in self.peerings["asn"]:
|
||||
if asn not in self.peerings["asn"]:
|
||||
self.peerings["asn"][asn] = []
|
||||
except KeyError:
|
||||
self.peerings["asn"][asn] = []
|
||||
|
@ -154,54 +176,78 @@ class PeeringManager:
|
|||
# deny more than one peering per ASN to one node
|
||||
for peering in self.peerings["asn"][asn]:
|
||||
if peering["node"] == node:
|
||||
return False
|
||||
return False, 409
|
||||
|
||||
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}
|
||||
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._update_nodes("add", peering=peering)
|
||||
self._save_peerings()
|
||||
return True
|
||||
return True, code
|
||||
|
||||
def update_peering(self, asn, node, mnt, wg_key, endpoint=None, ipv6ll=None, ipv4=None, ipv6=None, bgp_mp=True, bgp_enh=True):
|
||||
# check if this MNT already has a/this asn
|
||||
try:
|
||||
if not asn in self.peerings["mnter"][mnt]:
|
||||
if asn not in self.peerings["mntner"][mnt]:
|
||||
# ... and add it if it hasn't
|
||||
self.peerings[mnt].append(asn)
|
||||
except KeyError:
|
||||
# ... and cerate it if it doesn't have any yet
|
||||
self.peerings["mnter"][mnt] = [asn]
|
||||
# ... and create it if it doesn't have any yet
|
||||
self.peerings["mntner"][mnt] = [asn]
|
||||
try:
|
||||
if not asn in self.peerings["asn"]:
|
||||
return False
|
||||
# there are no peerings for this asn -> can't edit nothing...
|
||||
if asn not in self.peerings["asn"]:
|
||||
return False, 404
|
||||
except KeyError:
|
||||
return False
|
||||
# this should only happen if "asn" not in self.peerings
|
||||
return False, 404
|
||||
|
||||
success = False
|
||||
found = False
|
||||
for pNr in range(len(self.peerings["asn"][asn])):
|
||||
if self.peerings["asn"][asn][pNr]["node"] == node:
|
||||
old_peering = self.peerings["asn"][asn][pNr]
|
||||
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}
|
||||
success = True
|
||||
found = True
|
||||
peering_number = pNr
|
||||
if not found:
|
||||
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:
|
||||
return False
|
||||
# revert updating peering
|
||||
self.peerings["asn"][asn][peering_number] = old_peering
|
||||
return False, code
|
||||
|
||||
self._save_peerings()
|
||||
self._update_nodes("update", old_peering, new_peering=new_peering)
|
||||
return True
|
||||
return True, code
|
||||
|
||||
def delete_peering(self, asn, node, mnt, wg_key=None):
|
||||
if not self.exists(asn, node, mnt=mnt, wg_key=wg_key):
|
||||
return False
|
||||
return False, 404
|
||||
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()
|
||||
self._update_nodes("delete", peering=p)
|
||||
return True
|
||||
success, code = self._update_nodes("delete", peering=p)
|
||||
print(f"DELETE: {asn} on {node}: {success}, {code}")
|
||||
return success, code
|
||||
# if nothing got found (should have been catched by self.exists)
|
||||
return False
|
||||
return False, 404
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<form action="https://dn42.g-load.eu/auth/">
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://dn42.g-load.eu/auth/assets/button-font/auth.css"
|
||||
href="{{config['base-dir']}}static/auth.css"
|
||||
/>
|
||||
<input
|
||||
type="hidden"
|
||||
|
@ -15,13 +15,20 @@
|
|||
/>
|
||||
<button
|
||||
type="submit"
|
||||
class="kioubit-btn kioubit-btn-primary kioubit-btn-dark kioubit-btn-main"
|
||||
class="kioubit-btn-dark"
|
||||
>
|
||||
<span class="icon-kioubit-auth"></span>Authenticate with Kioubit.dn42
|
||||
<object
|
||||
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>
|
||||
</form>
|
||||
|
||||
{% if config["debug-mode"] %}
|
||||
{% if config["debug-mode"] or ("login" in session and session["login"] == config["MNT"])%}
|
||||
<form action="" method="post" class="flex">
|
||||
<label for="debug"
|
||||
>Debug login, if you see this in Production contact {{config["MNT"]}}</label
|
||||
|
|
|
@ -155,7 +155,7 @@
|
|||
let enh_anabled = document.getElementById("bgp-extended-next-hop").checked;
|
||||
if (enh_anabled) {
|
||||
example_config_bird2_enh4.innerHTML = "on";
|
||||
example_config_bird2_enh6.innerHTML = "on";
|
||||
//example_config_bird2_enh6.innerHTML = "on";
|
||||
} else {
|
||||
example_config_bird2_enh4.innerHTML = "off";
|
||||
example_config_bird2_enh6.innerHTML = "off";
|
||||
|
@ -174,9 +174,9 @@
|
|||
|
||||
<div>
|
||||
{% for peering in mnt_peerings %}
|
||||
<a href="?node={{peering['node']}}&asn={{session['user-data']['asn']}}">
|
||||
<a href="?node={{peering['node']}}&asn={% if session['login'] != config['MNT'] %}{{session['user-data']['asn']}}{%else%}{{peering['asn']}}{%endif%}">
|
||||
<button {% if selected_peering %}{% if selected_peering == peering['node'] %}class="button-selected"{% endif %}{% endif %}>
|
||||
with<br>{{peering["node"]}}
|
||||
{% if session['login'] == config['MNT'] %}{{peering['ASN']}}<br>{%endif%}with<br>{{peering["node"]}}
|
||||
</button>
|
||||
{% if selected_node == peering['node'] %}
|
||||
{% set selected_peering = peering %}
|
||||
|
@ -190,7 +190,7 @@
|
|||
<tr>
|
||||
<td><label for="peer-asn">Your ASN</label></td>
|
||||
<td></td>
|
||||
<td><input type="text" name="peer-asn" id="peer-asn" disabled="disabled" value="{{session['user-data']['asn']}}"></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>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><h4>Wireguard</h4></td>
|
||||
|
@ -205,22 +205,22 @@
|
|||
<tr>
|
||||
<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="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>
|
||||
<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>
|
||||
</tr>
|
||||
<tr>
|
||||
<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()"{% 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()" 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="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>
|
||||
<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="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>
|
||||
<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>
|
||||
</tr>
|
||||
<tr>
|
||||
<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="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>
|
||||
<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>
|
||||
</tr>
|
||||
<tr>
|
||||
<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>;
|
||||
};
|
||||
ipv6 {
|
||||
extended next hop <span id="example-config-bird2-enh6">on</span>;
|
||||
extended next hop <span id="example-config-bird2-enh6">off</span>;
|
||||
};
|
||||
}
|
||||
</pre>
|
||||
|
|
|
@ -155,7 +155,7 @@
|
|||
let enh_anabled = document.getElementById("bgp-extended-next-hop").checked;
|
||||
if (enh_anabled) {
|
||||
example_config_bird2_enh4.innerHTML = "on";
|
||||
example_config_bird2_enh6.innerHTML = "on";
|
||||
//example_config_bird2_enh6.innerHTML = "on";
|
||||
} else {
|
||||
example_config_bird2_enh4.innerHTML = "off";
|
||||
example_config_bird2_enh6.innerHTML = "off";
|
||||
|
@ -187,7 +187,7 @@
|
|||
<tr>
|
||||
<td><label for="peer-asn">Your ASN</label></td>
|
||||
<td></td>
|
||||
<td><input type="text" name="peer-asn" id="peer-asn" disabled="disabled" value="{{session['user-data']['asn']}}"></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>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><h4>Wireguard</h4></td>
|
||||
|
@ -202,22 +202,22 @@
|
|||
<tr>
|
||||
<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="text" name="peer-endpoint" id="peer-endpoint" onchange="return update_from_endpoint()"></td>
|
||||
<td><input type="text" name="peer-endpoint" id="peer-endpoint" onchange="return update_from_endpoint()" placeholder="node.example.org:1234"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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="text" name="peer-v6ll" id="peer-v6ll" onchange="return update_from_v6ll()"></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>
|
||||
</tr>
|
||||
<tr>
|
||||
<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="text" name="peer-v4" id="peer-v4"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>
|
||||
</tr>
|
||||
<tr>
|
||||
<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="text" name="peer-v6" id="peer-v6"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>
|
||||
</tr>
|
||||
<tr>
|
||||
<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>;
|
||||
};
|
||||
ipv6 {
|
||||
extended next hop <span id="example-config-bird2-enh6">on</span>;
|
||||
extended next hop <span id="example-config-bird2-enh6">off</span>;
|
||||
};
|
||||
}
|
||||
</pre>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
>
|
||||
</div>
|
||||
<div class="flex flex-row">
|
||||
{% for peering in peerings.get_peerings_by_mnt(session["login"]) %}
|
||||
{% for peering in peerings.get_peerings_by_mnt(session["user-data"]["mnt"]) %}
|
||||
<div class="peering">
|
||||
<div>
|
||||
<div>Node: {{peering["node"]}}</div>
|
||||
|
|
1
web/frontend/static/auth.css
Normal file
1
web/frontend/static/auth.css
Normal file
|
@ -0,0 +1 @@
|
|||
.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%)}
|
3
web/frontend/static/auth.svg
Normal file
3
web/frontend/static/auth.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<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>
|
After Width: | Height: | Size: 1.1 KiB |
|
@ -1,3 +1,4 @@
|
|||
Flask
|
||||
waitress
|
||||
pyopenssl
|
||||
requests
|
0
web/start.sh
Normal file → Executable file
0
web/start.sh
Normal file → Executable file
Loading…
Add table
Reference in a new issue