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:
|
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 (#todo)
|
- add/edit/delete peerings
|
||||||
- communicate new/update peerings with nodes #todo
|
- communicate new/update peerings with nodes
|
||||||
2. the "nodes daemon" #todo
|
2. the "nodes daemon"
|
||||||
- reveives new/updated peering configs from the "server" #todo
|
- reveives new/updated peering configs from the "server"
|
||||||
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
|
@ -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=start.sh
|
ExecStart=</path/to/autopeering>/web/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=start.sh
|
ExecStart=</path/to/autopeering>/nodes/start.sh
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|
|
@ -130,52 +130,53 @@ 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['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_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)
|
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)
|
print(wg_up)
|
||||||
time.sleep(5)
|
time.sleep(3)
|
||||||
with open(f"{self.__config['bird-peers']}/dn42_{peering['MNT'][:-4].lower()}.conf", "w") as bgp_file:
|
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_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)
|
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['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_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)
|
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)
|
print(wg_up)
|
||||||
time.sleep(5)
|
time.sleep(3)
|
||||||
with open(f"{self.__config['bird-peers']}/dn42_{peering['MNT'][:-4].lower()}.conf", "w") as bgp_file:
|
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_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)
|
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()}.conf")
|
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("{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)
|
print(bgp_reload)
|
||||||
time.sleep(5)
|
time.sleep(3)
|
||||||
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)
|
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)
|
print(wg_disable)
|
||||||
|
|
||||||
return 200
|
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):
|
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,8 +239,10 @@ 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"], "wg_key": wg_key,
|
new_peering = self.peerings[asn][pNr] = {"MNT": MNT if MNT!=NotSpecified else old_peering["MNT"], "ASN": asn, "node": config["nodename"],
|
||||||
"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"]}
|
"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
|
success = True
|
||||||
if not success:
|
if not success:
|
||||||
return False, 404
|
return False, 404
|
||||||
|
@ -301,9 +303,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):
|
||||||
|
@ -321,8 +323,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')
|
parser.add_argument('bgp_mp', type = bool)
|
||||||
parser.add_argument('bgp_enh')
|
parser.add_argument('bgp_enh', type = bool)
|
||||||
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"]):
|
||||||
|
@ -353,8 +355,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', store_missing=False)
|
parser.add_argument('bgp_mp', type=bool, store_missing=False)
|
||||||
parser.add_argument('bgp_enh', store_missing=False)
|
parser.add_argument('bgp_enh', type=bool, 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"]):
|
||||||
|
|
|
@ -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": { // {MNT} will get replaced with the lowercase mnter without "-MNT"
|
"wg-commands": { // {PEERING} will get replaced with the lowercase mnter without "-MNT" followed by the last four digits of the ASn
|
||||||
"enable": "systemctl enable wg-quick@dn42_{MNT}", //command to execute for enabling the wg-interface
|
"enable": "./wg-services.sh enable {PEERING}", //command to execute for enabling the wg-interface
|
||||||
"up": "systemctl start wg-quick@dn42_{MNT}", //command to execute for starting the wg-interface
|
"up": "./wg-services.sh start {PEERING}", //command to execute for starting the wg-interface
|
||||||
"down": "systemctl stop wg-quick@dn42_{MNT}", //command to execute for stopping the wg-interface
|
"down": "./wg-services.sh stop {PEERING}", //command to execute for stopping the wg-interface
|
||||||
"disable": "systemctl disable wg-quick@dn42_{MNT}" //command to execute for disabling 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-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
Normal file → Executable file
0
nodes/start.sh
Normal file → Executable file
|
@ -1,7 +1,7 @@
|
||||||
{% if peering["bgp_mp"] %}
|
{% if peering["bgp_mp"] == True %}
|
||||||
protocol bgp dn42_{{peering["MNT"][:-4].lower()}} from dnpeers {
|
protocol bgp dn42_{{peering["MNT"][:-4].lower()}}_{{peering["ASN"][-4:]}} from dnpeers {
|
||||||
neighbor {{peering["ipv6ll"]}} as {{peering["ASN"]}};
|
neighbor {{peering["ipv6ll"] or peering["ipv4"] or peering["ipv6"]}} as {{peering["ASN"]}};
|
||||||
interface "dn42_{{peering['MNT'][:-4].lower()}}";
|
interface {% if peering['ASN'].__len__() >=6 %}"dn42_{{peering['ASN'][-6:]}}"{% else %}"dn42_{{peering['asn']}}"{% endif %};
|
||||||
passive off;
|
passive off;
|
||||||
|
|
||||||
ipv4 {
|
ipv4 {
|
||||||
|
@ -16,19 +16,21 @@ protocol bgp dn42_{{peering["MNT"][:-4].lower()}} from dnpeers {
|
||||||
#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 {% if peering["bgp_enh"] %}on{%else%}off{%endif%};
|
extended next hop off;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
{%else%}
|
{% elif peering["bgp_mp"] == False %}
|
||||||
protocol bgp dn42_{{peering["MNT"][:-4].lower()}}_4 from dnpeers {
|
protocol bgp dn42_{{peering["MNT"][:-4].lower()}}_{{peering['ASN'][-4:]}}_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()}}_6 from dnpeers {
|
protocol bgp dn42_{{peering["MNT"][:-4].lower()}}_{{peering['ASN'][-4:]}}_6 from dnpeers {
|
||||||
neighbor {{peering["ipv6"]}} as {{peering["ASN"]}};
|
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;
|
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);
|
||||||
|
|
|
@ -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 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%}
|
{%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
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))
|
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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
#! /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
|
||||||
|
|
||||||
|
@ -49,21 +54,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 not "flask-template-dir" in self._config:
|
if "flask-template-dir" not in self._config:
|
||||||
self._config["flask-template-dir"] = "../frontend"
|
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
|
self._config["debug-mode"] = False
|
||||||
if not "base-dir" in self._config:
|
if "base-dir" not in self._config:
|
||||||
self._config["base-dir"] = "/"
|
self._config["base-dir"] = "/"
|
||||||
|
|
||||||
if not "peerings-data" in self._config:
|
if "peerings-data" not in self._config:
|
||||||
self._config["peering-data"] = "./peerings"
|
self._config["peering-data"] = "./peerings"
|
||||||
|
|
||||||
if not "nodes" in self._config:
|
if "nodes" not in self._config:
|
||||||
self,_config = {}
|
self,_config = {}
|
||||||
for node in self._config["nodes"]:
|
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
|
self._config["nodes"][node]["capacity"] = -1
|
||||||
|
|
||||||
logging.info(self._config)
|
logging.info(self._config)
|
||||||
|
@ -81,7 +86,8 @@ 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:
|
||||||
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"]
|
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"]
|
||||||
|
@ -125,7 +131,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:
|
except binascii.Error:
|
||||||
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"
|
||||||
|
@ -134,7 +140,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
|
||||||
|
@ -148,13 +154,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 not ipv6ll.version == 6:
|
if 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 not ipv4.version == 4:
|
if ipv4.version != 4:
|
||||||
raise ValueError()
|
raise ValueError()
|
||||||
if ipv4.is_link_local:
|
if ipv4.is_link_local:
|
||||||
pass
|
pass
|
||||||
|
@ -163,16 +169,21 @@ 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"]:
|
||||||
for allowed4 in session["user-data"]["allowed4"]:
|
if not isinstance(session["user-data"]["allowed4"],list):
|
||||||
|
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 not ipv6.version == 6:
|
if ipv6.version != 6:
|
||||||
raise ValueError()
|
raise ValueError()
|
||||||
if not ipv6.is_private:
|
if not ipv6.is_private:
|
||||||
raise ValueError()
|
raise ValueError()
|
||||||
|
@ -180,19 +191,25 @@ 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"]:
|
||||||
for allowed6 in session["user-data"]["allowed6"]:
|
if not isinstance(session["user-data"]["allowed6"],list):
|
||||||
|
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:
|
except ValueError as e:
|
||||||
|
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"] == 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"
|
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"])):
|
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:
|
||||||
|
@ -204,7 +221,7 @@ def auth_required():
|
||||||
def wrapper(f):
|
def wrapper(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated(*args, **kwargs):
|
def decorated(*args, **kwargs):
|
||||||
if not "login" in session:
|
if "login" not 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:
|
||||||
|
@ -254,7 +271,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"]:
|
elif request.method == "POST" and (config["debug-mode"] or session["login"] == config["MNT"]):
|
||||||
try:
|
try:
|
||||||
print(request.form)
|
print(request.form)
|
||||||
if request.form["theanswer"] != "42":
|
if request.form["theanswer"] != "42":
|
||||||
|
@ -282,7 +299,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
|
session["login"] = mnt if "login" not in session else session["login"]
|
||||||
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"
|
||||||
|
@ -303,12 +320,14 @@ 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 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")
|
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")
|
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))
|
||||||
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")
|
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"]}
|
||||||
|
@ -321,9 +340,9 @@ def peerings_delete():
|
||||||
def peerings_edit():
|
def peerings_edit():
|
||||||
print(session)
|
print(session)
|
||||||
if request.method == "GET":
|
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")
|
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)
|
# 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
|
||||||
|
@ -339,14 +358,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 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")
|
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["login"])
|
mnt_peerings = peerings.get_peerings_by_mnt(session["user-data"]["mnt"])
|
||||||
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
|
||||||
|
@ -354,7 +373,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["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 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")
|
||||||
|
@ -373,15 +392,17 @@ def peerings_new():
|
||||||
elif request.method == "POST":
|
elif request.method == "POST":
|
||||||
print(request.args)
|
print(request.args)
|
||||||
print(request.form)
|
print(request.form)
|
||||||
if not "node" in request.args or not request.args["node"]:
|
if "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")
|
return render_template("peerings-new.html", session=session, config=config, peerings=peerings, msg="no node specified, please click one of the buttons above")
|
||||||
|
|
||||||
peering_valid, peering_or_msg = check_peering_data(request.form)
|
peering_valid, peering_or_msg = check_peering_data(request.form)
|
||||||
|
|
||||||
if not peering_valid:
|
if not peering_valid:
|
||||||
return render_template("peerings-new.html", session=session, config=config, peerings=peerings, msg=peering_or_msg), 400
|
return render_template("peerings-new.html", session=session, config=config, peerings=peerings, msg=peering_or_msg), 400
|
||||||
if not peerings.add_peering(session["user-data"]["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"]):
|
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"])
|
||||||
return render_template("peerings-new.html", session=session, config=config, peerings=peerings, msg="this ASN already has a peering with the requested node"), 400
|
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")
|
return redirect(f"{config['base-dir']}peerings")
|
||||||
|
|
||||||
|
@ -400,8 +421,14 @@ 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():
|
||||||
|
|
|
@ -15,14 +15,21 @@ 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):
|
||||||
if action == "add":
|
try:
|
||||||
print(requests.post(f"{self.__api_addr}peerings", json=peering))
|
if action == "add":
|
||||||
elif action == "update":
|
req = requests.post(f"{self.__api_addr}peerings", json=peering, timeout=10)
|
||||||
print(requests.put(f"{self.__api_addr}peerings", json=peering))
|
elif action == "update":
|
||||||
elif action == "delete":
|
req = requests.put(f"{self.__api_addr}peerings", json=peering, timeout=10)
|
||||||
print(requests.delete(f"{self.__api_addr}peerings", json=peering))
|
elif action == "delete":
|
||||||
else:
|
req = requests.delete(f"{self.__api_addr}peerings", json=peering, timeout=10)
|
||||||
return 400
|
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:
|
class PeeringManager:
|
||||||
|
@ -44,15 +51,22 @@ class PeeringManager:
|
||||||
def __load_peerings(self):
|
def __load_peerings(self):
|
||||||
if not os.path.exists(self.__peering_file):
|
if not os.path.exists(self.__peering_file):
|
||||||
with open(self.__peering_file, "x") as p:
|
with open(self.__peering_file, "x") as p:
|
||||||
json.dump({"mnter": {}, "asn": {}}, p)
|
json.dump({"mntner": {}, "asn": {}}, p)
|
||||||
try:
|
try:
|
||||||
with open(self.__peering_file, "r") as p:
|
with open(self.__peering_file, "r") as p:
|
||||||
self.peerings = json.load(p)
|
self.peerings = json.load(p)
|
||||||
except json.decoder.JSONDecodeError:
|
# migration due to typo in "mnter" key:
|
||||||
with open(self.__peering_file, "w") as p:
|
if "mnter" in self.peerings:
|
||||||
json.dump({"mnter": {}, "asn": {}}, p)
|
print("WARN: migrating peering data (Typo mnter)")
|
||||||
with open(self.__peering_file, "r") as p:
|
self.peerings = {"mntner": self.peerings["mnter"], "asn": self.peerings["asn"]}
|
||||||
self.peerings = json.load(p)
|
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 = {}
|
# self.peerings = {}
|
||||||
# missing_peerings = False
|
# missing_peerings = False
|
||||||
|
@ -78,37 +92,35 @@ 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:
|
||||||
#thread = threading.Thread(target=
|
return_code = self._nodes[peering["node"]].update(action=action, peering = peering if not new_peering else new_peering)
|
||||||
self._nodes[peering["node"]].update(#), kwargs={
|
if return_code in [200, 201]:
|
||||||
action=action, peering = peering if not new_peering else new_peering#, })
|
return True, return_code
|
||||||
)
|
else:
|
||||||
#thread.start()
|
return False, return_code
|
||||||
#self._threads.append(thread)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return False
|
return False, 404
|
||||||
|
|
||||||
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 not peering["node"] in __new:
|
if peering["node"] not 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 == None:
|
if self._amounts is 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=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"""
|
"""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["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
|
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
|
||||||
|
@ -126,27 +138,37 @@ class PeeringManager:
|
||||||
# print(self.peerings)
|
# print(self.peerings)
|
||||||
try:
|
try:
|
||||||
out = []
|
out = []
|
||||||
for asn in self.peerings["mnter"][mnt]:
|
# if admin is logged in: return all
|
||||||
try:
|
if mnt == self.__config["MNT"]:
|
||||||
for peering in self.peerings["asn"][asn]:
|
for mntner in self.peerings["mntner"]:
|
||||||
out.append(peering)
|
for asn in self.peerings["mntner"][mntner]:
|
||||||
except KeyError as e:
|
try:
|
||||||
pass
|
for peering in self.peerings["asn"][asn]:
|
||||||
|
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):
|
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
|
# check if this MNT already has a/this asn
|
||||||
try:
|
try:
|
||||||
if not asn in self.peerings["mnter"][mnt]:
|
if asn not in self.peerings["mntner"][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["mnter"][mnt] = [asn]
|
self.peerings["mntner"][mnt] = [asn]
|
||||||
try:
|
try:
|
||||||
if not asn in self.peerings["asn"]:
|
if asn not in self.peerings["asn"]:
|
||||||
self.peerings["asn"][asn] = []
|
self.peerings["asn"][asn] = []
|
||||||
except KeyError:
|
except KeyError:
|
||||||
self.peerings["asn"][asn] = []
|
self.peerings["asn"][asn] = []
|
||||||
|
@ -154,54 +176,78 @@ 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
|
return False, 409
|
||||||
|
|
||||||
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
|
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):
|
def update_peering(self, asn, node, mnt, wg_key, endpoint=None, ipv6ll=None, ipv4=None, ipv6=None, bgp_mp=True, bgp_enh=True):
|
||||||
# check if this MNT already has a/this asn
|
# check if this MNT already has a/this asn
|
||||||
try:
|
try:
|
||||||
if not asn in self.peerings["mnter"][mnt]:
|
if asn not in self.peerings["mntner"][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 create it if it doesn't have any yet
|
||||||
self.peerings["mnter"][mnt] = [asn]
|
self.peerings["mntner"][mnt] = [asn]
|
||||||
try:
|
try:
|
||||||
if not asn in self.peerings["asn"]:
|
# there are no peerings for this asn -> can't edit nothing...
|
||||||
return False
|
if asn not in self.peerings["asn"]:
|
||||||
|
return False, 404
|
||||||
except KeyError:
|
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])):
|
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}
|
||||||
success = True
|
found = True
|
||||||
if not success:
|
peering_number = pNr
|
||||||
return False
|
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:
|
||||||
|
# revert updating peering
|
||||||
|
self.peerings["asn"][asn][peering_number] = old_peering
|
||||||
|
return False, code
|
||||||
|
|
||||||
self._save_peerings()
|
self._save_peerings()
|
||||||
self._update_nodes("update", old_peering, new_peering=new_peering)
|
return True, code
|
||||||
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
|
return False, 404
|
||||||
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()
|
||||||
self._update_nodes("delete", peering=p)
|
success, code = self._update_nodes("delete", peering=p)
|
||||||
return True
|
print(f"DELETE: {asn} on {node}: {success}, {code}")
|
||||||
|
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
|
return False, 404
|
||||||
|
|
|
@ -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="https://dn42.g-load.eu/auth/assets/button-font/auth.css"
|
href="{{config['base-dir']}}static/auth.css"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
type="hidden"
|
type="hidden"
|
||||||
|
@ -15,13 +15,20 @@
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
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>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{% if config["debug-mode"] %}
|
{% if config["debug-mode"] or ("login" in session and session["login"] == config["MNT"])%}
|
||||||
<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
|
||||||
|
|
|
@ -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={{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 %}>
|
<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>
|
</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" 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>
|
||||||
<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()" {% 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>
|
||||||
<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()"{% 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>
|
<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()" {% 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>
|
||||||
<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()" {% 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>
|
||||||
<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">on</span>;
|
extended next hop <span id="example-config-bird2-enh6">off</span>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
</pre>
|
</pre>
|
||||||
|
|
|
@ -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" 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>
|
||||||
<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()"></td>
|
<td><input type="text" name="peer-endpoint" id="peer-endpoint" onchange="return update_from_endpoint()" placeholder="node.example.org:1234"></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()"></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>
|
||||||
<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()"></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>
|
||||||
<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()"></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>
|
||||||
<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">on</span>;
|
extended next hop <span id="example-config-bird2-enh6">off</span>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
</pre>
|
</pre>
|
||||||
|
|
|
@ -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["login"]) %}
|
{% for peering in peerings.get_peerings_by_mnt(session["user-data"]["mnt"]) %}
|
||||||
<div class="peering">
|
<div class="peering">
|
||||||
<div>
|
<div>
|
||||||
<div>Node: {{peering["node"]}}</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
|
Flask
|
||||||
waitress
|
waitress
|
||||||
pyopenssl
|
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