From 8a94149411d904ea6d1bd01c8638393c95f54d2d Mon Sep 17 00:00:00 2001 From: Astrid Yu Date: Fri, 5 Apr 2024 17:50:53 -0700 Subject: [PATCH] make asmodeus conf scripted --- .gitignore | 2 + netconf/asmodeus.txt | 192 ----------------------------------- netconf/asmodeus/__init__.py | 0 netconf/asmodeus/__main__.py | 72 +++++++++++++ netconf/asmodeus/dn42.py | 127 +++++++++++++++++++++++ netconf/asmodeus/firewall.py | 16 +++ netconf/asmodeus/utils.py | 68 +++++++++++++ 7 files changed, 285 insertions(+), 192 deletions(-) delete mode 100644 netconf/asmodeus.txt create mode 100644 netconf/asmodeus/__init__.py create mode 100755 netconf/asmodeus/__main__.py create mode 100644 netconf/asmodeus/dn42.py create mode 100644 netconf/asmodeus/firewall.py create mode 100644 netconf/asmodeus/utils.py diff --git a/.gitignore b/.gitignore index 023a5291..5822961d 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ result # Watchman cookies (no more "Watchman would like to access your Documents folder") .watchman-cookie-* + +__pycache__ \ No newline at end of file diff --git a/netconf/asmodeus.txt b/netconf/asmodeus.txt deleted file mode 100644 index 37a41d4d..00000000 --- a/netconf/asmodeus.txt +++ /dev/null @@ -1,192 +0,0 @@ -container { - name gortr { - command "-cache https://dn42.burble.com/roa/dn42_roa_46.json -verify=false -checktime=false -bind :8082" - image cloudflare/gortr - network rpki { - address 172.16.2.10 - } - restart on-failure - } - network rpki { - prefix 172.16.2.0/24 - } -} -interfaces { - ethernet eth0 { - address dhcp - hw-id 52:54:00:40:30:53 - } - loopback lo { - } - wireguard wg4242422717 { - address fe80::1846/64 - address fd00:ca7:b015::7e57/64 - description "peering tunnel to whojk" - peer whojk { - address 141.148.191.208 - allowed-ips ::/0 - allowed-ips 0.0.0.0/0 - port 24210 - public-key **************** - } - private-key **************** - } -} -nat { - source { - rule 10 { - outbound-interface { - name eth0 - } - source { - address 172.16.2.0/24 - } - translation { - address masquerade - } - } - } -} -policy { - route-map dn42-neighbors { - rule 2 { - action permit - match { - } - } - } -} -protocols { - bgp { - address-family { - ipv4-unicast { - network 172.23.7.176/28 { - } - } - ipv6-unicast { - network fd00:ca7:b015::/48 { - } - } - } - neighbor fe80::2717 { - address-family { - ipv4-unicast { - route-map { - export dn42-neighbors - import dn42-neighbors - } - soft-reconfiguration { - inbound - } - } - ipv6-unicast { - route-map { - export dn42-neighbors - import dn42-neighbors - } - soft-reconfiguration { - inbound - } - } - } - description "whojk dn42" - interface { - source-interface wg4242422717 - v6only { - } - } - peer-group dn42 - remote-as 4242422717 - update-source wg4242422717 - } - parameters { - router-id 172.23.7.177 - } - peer-group dn42 { - address-family { - ipv4-unicast { - } - ipv6-unicast { - } - } - capability { - extended-nexthop - } - } - system-as 4242421846 - } - rpki { - cache 172.16.2.10 { - port 8082 - preference 1 - } - } - static { - route 0.0.0.0/0 { - next-hop 192.168.122.1 { - } - } - } -} -service { - ntp { - allow-client { - address 0.0.0.0/0 - address ::/0 - } - server time1.vyos.net { - } - server time2.vyos.net { - } - server time3.vyos.net { - } - } - ssh { - port 22 - } -} -system { - config-management { - commit-revisions 10000 - } - conntrack { - modules { - ftp - h323 - nfs - pptp - sip - sqlnet - tftp - } - } - console { - device ttyS0 { - speed 115200 - } - } - host-name asmodeus - login { - user vyos { - authentication { - encrypted-password **************** - public-keys chungus { - key **************** - type ssh-ed25519 - } - } - } - } - name-server 8.8.8.8 - name-server 8.8.4.4 - syslog { - global { - facility all { - level info - } - facility local7 { - level debug - } - } - } -} diff --git a/netconf/asmodeus/__init__.py b/netconf/asmodeus/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/netconf/asmodeus/__main__.py b/netconf/asmodeus/__main__.py new file mode 100755 index 00000000..dc1669e0 --- /dev/null +++ b/netconf/asmodeus/__main__.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 + +import os +from typing import List, NamedTuple, Optional, Tuple + +from .firewall import make_firewall + +from . import dn42 + + +def basic_setup() -> List[str]: + return [ + "set interfaces ethernet eth0 address 'dhcp'", + "set interfaces ethernet eth0 hw-id '52:54:00:40:30:53'", + "set interfaces loopback lo", + "set system console device ttyS0 speed '115200'", + "set system host-name 'asmodeus'", + "set system login user vyos authentication public-keys chungus key 'AAAAC3NzaC1lZDI1NTE5AAAAIEl4yuE1X4IqjBqt/enMyZFZKJQLxeq34BTCNqey59aZ'", + "set system login user vyos authentication public-keys chungus type 'ssh-ed25519'", + "set system name-server '8.8.8.8'", + "set system name-server '8.8.4.4'", + "set system syslog global facility all level 'info'", + "set system syslog global facility local7 level 'debug'", + "set protocols static route 0.0.0.0/0 next-hop 192.168.122.1", + "set service ntp allow-client address '0.0.0.0/0'", + "set service ntp allow-client address '::/0'", + "set service ntp server time1.vyos.net", + "set service ntp server time2.vyos.net", + "set service ntp server time3.vyos.net", + "set service ssh port '22'", + "set system config-management commit-revisions '10000'", + "set system conntrack modules ftp", + "set system conntrack modules h323", + "set system conntrack modules nfs", + "set system conntrack modules pptp", + "set system conntrack modules sip", + "set system conntrack modules sqlnet", + "set system conntrack modules tftp", + ] + + +def main(): + cmds = [] + wg_privkey = os.environ["WG_PRIVKEY"] + + cmds += basic_setup() + cmds += dn42.dn42_rpki() + cmds += dn42.bgp_setup_configs() + cmds += dn42.dn42_bgp_group() + cmds += dn42.dn42_route_collector() + cmds += dn42.dn42_wireguard_ll_peer( + name="whojk", + our_ll_address_cidr="fe80::1846/64", + peer_ll_address="fe80::2717", + peer_endpoint=("141.148.191.208", 24210), + our_private_key=wg_privkey, + peer_asn=4242422717, + peer_pubkey="SpnH/BlVNDx5QiMxHhuF4i8hKr5qWMxnPYky6Mp4fEA=", + ) + cmds += make_firewall() + + # extra commands + cmds += [ + "set interfaces wireguard wg4242422717 address fd00:ca7:b015:7e57::7e57/64" + ] + + for c in cmds: + print(c) + + +if __name__ == "__main__": + main() diff --git a/netconf/asmodeus/dn42.py b/netconf/asmodeus/dn42.py new file mode 100644 index 00000000..edf4d734 --- /dev/null +++ b/netconf/asmodeus/dn42.py @@ -0,0 +1,127 @@ +from typing import List, Optional, Tuple + +from .utils import WireguardTunnel, LinkLocalBgpPeer, WireguardPeer + +ROUTE_MAP_DN42_ROA = "dn42-roa" + + +def bgp_setup_configs() -> List[str]: + return [ + "set protocols bgp parameters router-id '172.23.7.177'", + "set protocols bgp system-as '4242421846'", + "set protocols bgp address-family ipv4-unicast network 172.23.7.176/28", + "set protocols bgp address-family ipv6-unicast network fd00:ca7:b015::/48", + ] + + +def dn42_bgp_group() -> List[str]: + cmds = [ + "delete protocols bgp peer-group dn42", + "set protocols bgp peer-group dn42 capability extended-nexthop", + ] + + for af in ["ipv4-unicast", "ipv6-unicast"]: + cmds += [ + f"set protocols bgp peer-group dn42 address-family {af} route-map export {ROUTE_MAP_DN42_ROA}", + f"set protocols bgp peer-group dn42 address-family {af} route-map import {ROUTE_MAP_DN42_ROA}", + f"set protocols bgp peer-group dn42 address-family {af} soft-reconfiguration inbound", + ] + + return cmds + + +def dn42_wireguard_ll_peer( + *, + name: str, + our_ll_address_cidr: str, + our_private_key: str, + our_endpoint_port: Optional[int] = None, + peer_ll_address: str, + peer_asn: int, + peer_pubkey: int, + peer_endpoint: Optional[Tuple[str, int]] = None, +) -> List[str]: + ifname = f"wg{peer_asn}" + + tunnel = WireguardTunnel( + ifname=ifname, + our_address_cidr=our_ll_address_cidr, + our_private_key=our_private_key, + our_endpoint_port=our_endpoint_port, + description=f"'dn42 peering tunnel for {name} (AS{peer_asn})'", + peers=[ + WireguardPeer( + name=name, + endpoint=peer_endpoint, + public_key=peer_pubkey, + ) + ], + ) + + bgp = LinkLocalBgpPeer( + ifname=ifname, + description=f"'dn42 peer {name} (AS{peer_asn})'", + peer_address=peer_ll_address, + peer_asn=peer_asn, + peer_group="dn42", + ) + + return tunnel.render() + bgp.render() + + +def dn42_route_collector(): + addr = "fd42:4242:2601:ac12::1" + return [ + f"delete policy route-map Deny-All", + f"set policy route-map Deny-All rule 1 action deny", + f"delete protocols bgp neighbor {addr}", + f"set protocols bgp neighbor {addr} address-family ipv4-unicast route-map import 'Deny-All'", + f"set protocols bgp neighbor {addr} address-family ipv6-unicast route-map import 'Deny-All'", + f"set protocols bgp neighbor {addr} description 'https://lg.collector.dn42'", + f"set protocols bgp neighbor {addr} ebgp-multihop '10'", + f"set protocols bgp neighbor {addr} remote-as '4242422602'", + ] + + +def dn42_rpki(nat_rulenum: int = 10): + container_addr = "172.16.2.10" + subnet = "172.16.2.0/24" + port = 8082 + + cmds = [] + + # use the gortr container + cmds += [ + "delete container name gortr", + "delete container network rpki", + "set container name gortr image 'cloudflare/gortr'", + "set container name gortr restart 'on-failure'", + f"set container name gortr command '-cache https://dn42.burble.com/roa/dn42_roa_46.json -verify=false -checktime=false -bind :{port}'", + f"set container name gortr network rpki address '{container_addr}'", + f"set container network rpki prefix '{subnet}'", + ] + + # NAT the container network + cmds += [ + f"delete nat source rule {nat_rulenum}" + f"set nat source rule {nat_rulenum} outbound-interface name 'eth0'", + f"set nat source rule {nat_rulenum} translation address 'masquerade'", + f"set nat source rule {nat_rulenum} source address '{subnet}'", + ] + + # Point at rpki + cmds += [ + f"set protocols rpki cache {container_addr} port {port}", + f"set protocols rpki cache {container_addr} preference 1", + ] + + cmds += [ + f"set policy route-map {ROUTE_MAP_DN42_ROA} rule 10 action 'permit'", + f"set policy route-map {ROUTE_MAP_DN42_ROA} rule 10 match rpki 'valid'", + f"set policy route-map {ROUTE_MAP_DN42_ROA} rule 20 action 'permit'", + f"set policy route-map {ROUTE_MAP_DN42_ROA} rule 20 match rpki 'notfound'", + f"set policy route-map {ROUTE_MAP_DN42_ROA} rule 30 action 'deny'", + f"set policy route-map {ROUTE_MAP_DN42_ROA} rule 30 match rpki 'invalid'", + ] + + return cmds diff --git a/netconf/asmodeus/firewall.py b/netconf/asmodeus/firewall.py new file mode 100644 index 00000000..28e750c5 --- /dev/null +++ b/netconf/asmodeus/firewall.py @@ -0,0 +1,16 @@ +from typing import List + + +def make_firewall() -> List[str]: + return [ + "delete firewall", + "set firewall global-options state-policy established action 'accept'", + "set firewall global-options state-policy related action 'accept'", + "set firewall global-options state-policy invalid action 'accept'", + "set firewall group ipv6-network-group dn42-allowed-transit-v6 network 'fd00::/8'", + "set firewall group network-group dn42-allowed-transit-v4 network '10.0.0.0/8'", + "set firewall group network-group dn42-allowed-transit-v4 network '172.20.0.0/14'", + "set firewall group network-group dn42-allowed-transit-v4 network '172.31.0.0/16'", + "set firewall group ipv6-network-group ifd3f-dn42-v6 network 'fd00:ca7:b015::/48'", + "set firewall group network-group ifd3f-dn42-v4 network '172.23.7.176/28'", + ] diff --git a/netconf/asmodeus/utils.py b/netconf/asmodeus/utils.py new file mode 100644 index 00000000..844d7cbf --- /dev/null +++ b/netconf/asmodeus/utils.py @@ -0,0 +1,68 @@ +from typing import List, NamedTuple, Optional, Tuple + + +class WireguardPeer(NamedTuple): + name: str + public_key: str + endpoint: Optional[Tuple[str, int]] + + def render(self, ifname: str) -> List[str]: + cmds = [ + f"set interfaces wireguard {ifname} peer {self.name} public-key {self.public_key}", + f"set interfaces wireguard {ifname} peer {self.name} allowed-ips '::/0'", + f"set interfaces wireguard {ifname} peer {self.name} allowed-ips '0.0.0.0/0'", + ] + + match self.endpoint: + case (addr, port): + cmds += [ + f"set interfaces wireguard {ifname} peer {self.name} port {port}", + f"set interfaces wireguard {ifname} peer {self.name} address {addr}", + ] + + return cmds + + +class WireguardTunnel(NamedTuple): + ifname: str + our_address_cidr: str + our_private_key: str + description: str + peers: List[WireguardPeer] + our_endpoint_port: Optional[int] = None + + def render(self) -> List[str]: + cmds = [ + f"delete interfaces wireguard {self.ifname}", + f"set interfaces wireguard {self.ifname} address {self.our_address_cidr}", + f"set interfaces wireguard {self.ifname} description {self.description}", + f"set interfaces wireguard {self.ifname} private-key {self.our_private_key}", + ] + for p in self.peers: + cmds += p.render(self.ifname) + + if self.our_endpoint_port is not None: + cmds.append( + f"set interfaces wireguard {self.ifname} port {self.our_endpoint_port}" + ) + + return cmds + + +class LinkLocalBgpPeer(NamedTuple): + ifname: str + description: str + peer_address: str + peer_asn: int + peer_group: str + + def render(self) -> List[str]: + return [ + f"delete protocols bgp neighbor {self.peer_address}", + f"set protocols bgp neighbor {self.peer_address} description {self.description}", + f"set protocols bgp neighbor {self.peer_address} interface source-interface {self.ifname}", + f"set protocols bgp neighbor {self.peer_address} interface v6only", + f"set protocols bgp neighbor {self.peer_address} peer-group {self.peer_group}", + f"set protocols bgp neighbor {self.peer_address} remote-as {self.peer_asn}", + f"set protocols bgp neighbor {self.peer_address} update-source {self.ifname}", + ]