commit 1a441b8ce971935fb267784dfd958156a2712292 Author: clerie Date: Wed Mar 16 21:42:58 2022 +0100 Init repository diff --git a/README.md b/README.md new file mode 100644 index 0000000..e29083e --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# DN42 Peering Automation + +This script automatically creates and configures Wireguard tunnels and creates +a Bird2 configuration for use with automatic deployment of DN42 peerings. diff --git a/nopeers.json b/nopeers.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/nopeers.json @@ -0,0 +1 @@ +[] diff --git a/peers.json b/peers.json new file mode 100644 index 0000000..c67cfbf --- /dev/null +++ b/peers.json @@ -0,0 +1,62 @@ +[ + { + "peering-id": 0, + "peeing-name": "himalia", + "local-ip": "fe80::42:2", + "local-privatekey": "WEojmIAGAjOI8i40hhFA/HRclsIAAO3U6qScTr+OuXU=", + "local-endpoint-port": 52900, + "remote-as": 4242420197, + "remote-ip": "fe80::42:42:1", + "remote-publickey": "WEojmIAGAjOI8i40hhFA/HRclsIAAO3U6qScTr+OuXU=", + "remote-endpoint-addr": "127.2.2.2", + "remote-endpoint-port": 52575 + }, + { + "peering-id": 1, + "peering-name": "", + "local-ip": "fd80::43:43:1", + "local-privatekey": "WEojmIAGAjOI8i40hhFA/HRclsIAAO3U6qScTr+OuXU=", + "local-endpoint-port": 52901, + "remote-as": 4242420565, + "remote-ip": "fe80::43:1", + "remote-publickey": "WEojmIAGAjOI8i40hhFA/HRclsIAAO3U6qScTr+OuXU=", + "remote-endpoint-addr": "127.0.0.1", + "remote-endpoint-port": 52575 + }, + { + "peering-id": 2, + "peering-name": "north", + "local-ip": "fe80::1", + "local-privatekey": "WEojmIAGAjOI8i40hhFA/HRclsIAAO3U6qScTr+OuXU=", + "local-endpoint-port": 52902, + "remote-as": 4242421271, + "remote-ip": "fe80::2", + "remote-publickey": "WEojmIAGAjOI8i40hhFA/HRclsIAAO3U6qScTr+OuXU=", + "remote-endpoint-addr": "127.0.0.1", + "remote-endpoint-port": 52575 + }, + { + "peering-id": 3, + "peering-name": "south", + "local-ip": "fe80::1", + "local-privatekey": "WEojmIAGAjOI8i40hhFA/HRclsIAAO3U6qScTr+OuXU=", + "local-endpoint-port": 52903, + "remote-as": 4242421271, + "remote-ip": "fe80::2", + "remote-publickey": "WEojmIAGAjOI8i40hhFA/HRclsIAAO3U6qScTr+OuXU=", + "remote-endpoint-addr": "127.0.0.1", + "remote-endpoint-port": 52575 + }, + { + "peering-id": 4, + "peering-name": "", + "local-ip": "fe80::a14e", + "local-privatekey": "WEojmIAGAjOI8i40hhFA/HRclsIAAO3U6qScTr+OuXU=", + "local-endpoint-port": 52904, + "remote-as": 4242421302, + "remote-ip": "fe80::a14d", + "remote-publickey": "WEojmIAGAjOI8i40hhFA/HRclsIAAO3U6qScTr+OuXU=", + "remote-endpoint-addr": "127.0.0.1", + "remote-endpoint-port": 52575 + } +] diff --git a/provision-config.py b/provision-config.py new file mode 100755 index 0000000..e1aba14 --- /dev/null +++ b/provision-config.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 + +import json +from pathlib import Path +import subprocess +import sys + +upstream_config_file = Path(sys.argv[1]).resolve() +bird_config_file = Path(sys.argv[2]).resolve() + +print("Loading upstream config from {}".format(upstream_config_file)) +print("Deploying bird peering config file to {}".format(bird_config_file)) +print("\n") +upstream_config = json.loads(upstream_config_file.read_text()) + +# collect tunnel interface names +tunnel_interface_names = [] + +# collect bird 2 peering directives +bird_config = "" + +for peering in upstream_config: + tunnel_interface_name = "peer{id}as{asn}".format( + id=peering["peering-id"], + asn=str(peering["remote-as"])[-4:], + ) + print("Configuring {}".format(tunnel_interface_name)) + + tunnel_interface_names.append(tunnel_interface_name) + + tunnel_config = """[Interface] +PrivateKey = {local_privatekey} +ListenPort = {local_endpoint_port} + +[Peer] +PublicKey = {remote_publickey} +Endpoint = {remote_endpoint_addr}:{remote_endpoint_port} +AllowedIPs = fd00::/8, fe80::/10""".format( + local_privatekey=peering["local-privatekey"], + local_endpoint_port=peering["local-endpoint-port"], + remote_publickey=peering["remote-publickey"], + remote_endpoint_addr=peering["remote-endpoint-addr"], + remote_endpoint_port=peering["remote-endpoint-port"], + ) + + tunnel_config_file = Path() / "{}.conf".format(tunnel_interface_name) + tunnel_config_file.write_text(tunnel_config) + + + r = subprocess.run(["ip", "address", "show", "dev", tunnel_interface_name], capture_output=True) + if r.returncode != 0: + subprocess.run(["ip", "link", "add", "dev", tunnel_interface_name, "type", "wireguard"], check=True) + subprocess.run(["ip", "link", "set", "up", "dev", tunnel_interface_name], check=True) + + subprocess.run(["wg", "syncconf", tunnel_interface_name, tunnel_config_file], check=True) + subprocess.run(["ip", "address", "change", "{}/128".format(peering["local-ip"]), "peer", "{}/128".format(peering["remote-ip"]), "dev", tunnel_interface_name], check=True) + + tunnel_config_file.unlink(missing_ok=True) + + bird_config += """ +protocol bgp {tunnel_interface_name} from bgp_dn42 {{ + neighbor {remote_ip}%{tunnel_interface_name} as {remote_as}; +}} +""".format( + tunnel_interface_name=tunnel_interface_name, + remote_ip=peering["remote-ip"], + remote_as=peering["remote-as"], +) + +# Writing generated bird config +bird_config_file.write_text(bird_config) + +# remove all peer interfaces that are not supported +r = subprocess.run(["ip", "--json", "link", "show"], check=True, capture_output=True,text=True) +interfaces = json.loads(r.stdout) +for interface in interfaces: + if interface["ifname"].startswith("peer") and interface["ifname"] not in tunnel_interface_names: + print("Removing {}".format(interface["ifname"])) + subprocess.run(["ip", "link", "delete", interface["ifname"]], check=True)