From 58ca3fa9ae3d1f65b8729e6bb4cf6f86c4aab9d6 Mon Sep 17 00:00:00 2001 From: Franziska Kunsmann Date: Sat, 21 Nov 2020 15:38:38 +0100 Subject: [PATCH] bundles/wireguard: add netdev and network files, add iptables rules --- bundles/wireguard/files/wg0.netdev | 24 +++++++++ bundles/wireguard/files/wg0.network | 8 +++ bundles/wireguard/items.py | 26 ++++++++++ bundles/wireguard/metadata.py | 76 +++++++++++++++++++++++++++++ libs/keys.py | 5 +- nodes/home/router.py | 17 +++++-- nodes/ovh/icinga2.py | 9 +++- nodes/ovh/wireguard.py | 11 ++++- 8 files changed, 168 insertions(+), 8 deletions(-) create mode 100644 bundles/wireguard/files/wg0.netdev create mode 100644 bundles/wireguard/files/wg0.network create mode 100644 bundles/wireguard/items.py create mode 100644 bundles/wireguard/metadata.py diff --git a/bundles/wireguard/files/wg0.netdev b/bundles/wireguard/files/wg0.netdev new file mode 100644 index 0000000..dcee9f9 --- /dev/null +++ b/bundles/wireguard/files/wg0.netdev @@ -0,0 +1,24 @@ +[NetDev] +Name=wg0 +Kind=wireguard +Description=WireGuard server + +[WireGuard] +PrivateKey=${privatekey} +ListenPort=51820 + +% for peer, config in sorted(peers.items()): +# Peer ${peer} +[WireGuardPeer] +PublicKey=${config['pubkey']} +% if len(peers) == 1: # FIXME +AllowedIPs=${network} +% else: +AllowedIPs=${','.join(sorted(config['ips']))} +% endif +PresharedKey=${config['psk']} +% if 'endpoint' in config: +Endpoint=${config['endpoint']} +% endif + +% endfor diff --git a/bundles/wireguard/files/wg0.network b/bundles/wireguard/files/wg0.network new file mode 100644 index 0000000..b9d37a1 --- /dev/null +++ b/bundles/wireguard/files/wg0.network @@ -0,0 +1,8 @@ +[Match] +Name=wg0 + +[Network] +Address=${my_ip} + +[Route] +Destination=${network} diff --git a/bundles/wireguard/items.py b/bundles/wireguard/items.py new file mode 100644 index 0000000..9ef7df2 --- /dev/null +++ b/bundles/wireguard/items.py @@ -0,0 +1,26 @@ +assert node.has_bundle('systemd-networkd') + +files = { + '/etc/systemd/network/99-wg0.netdev': { + 'source': 'wg0.netdev', + 'content_type': 'mako', + 'context': node.metadata['wireguard'], + 'needs': { + 'pkg_apt:wireguard', + }, + 'triggers': { + 'svc_systemd:systemd-networkd:restart', + }, + }, + '/etc/systemd/network/99-wg0.network': { + 'source': 'wg0.network', + 'content_type': 'mako', + 'context': node.metadata['wireguard'], + 'needs': { + 'pkg_apt:wireguard', + }, + 'triggers': { + 'svc_systemd:systemd-networkd:restart', + }, + }, +} diff --git a/bundles/wireguard/metadata.py b/bundles/wireguard/metadata.py new file mode 100644 index 0000000..8c08bd0 --- /dev/null +++ b/bundles/wireguard/metadata.py @@ -0,0 +1,76 @@ +defaults = { + 'apt': { + 'packages': { + 'wireguard': {}, + }, + 'repos': { + 'backports': { + 'install_gpg_key': False, # default debian signing key + 'items': [ + 'deb http://deb.debian.org/debian {os_release}-backports main', + ], + }, + }, + }, + 'iptables': { + 'bundle_rules': { + 'wireguard': [ + 'iptables_both -A INPUT -p udp --dport 51820 -j ACCEPT', + 'iptables_both -A FORWARD -i wg0 -j ACCEPT', + ], + }, + }, + 'wireguard': { + 'privatekey': repo.libs.keys.gen_privkey(repo, f'{node.name} wireguard privatekey'), + }, +} + + +@metadata_reactor +def get_wireguard_network_from_server(metadata): + # FIXME This will break if more than one node sets 'wireguard/network' + for rnode in repo.nodes: + if not rnode.has_bundle('wireguard'): + continue + + if node.name in rnode.metadata.get('wireguard/peers', {}).keys(): + network = rnode.metadata.get('wireguard/network', None) + + if network: + return { + 'wireguard': { + 'network': network, + }, + } + + return {} + + +@metadata_reactor +def get_my_wireguard_peers(metadata): + peers = {} + + for rnode in repo.nodes: + if not rnode.has_bundle('wireguard'): + continue + + if node.name in rnode.metadata.get('wireguard/peers', {}).keys(): + peers[rnode.name] = { + 'pubkey': repo.libs.keys.get_pubkey_from_privkey(repo, f'{node.name} wireguard {rnode.name}', rnode.metadata.get('wireguard/privatekey')), + 'psk': rnode.metadata.get('wireguard/psk', metadata.get('wireguard/psk', None)), + } + + if not rnode.metadata.get(f'wireguard/peers/{node.name}/do_not_initiate_a_connection_from_your_side', False): + peers[rnode.name]['endpoint'] = f'{rnode.hostname}:51820' + + peers[rnode.name]['ips'] = rnode.metadata.get('wireguard/subnets', set()) + + your_ip = rnode.metadata.get('wireguard/my_ip', None) + if your_ip: + peers[rnode.name]['ips'].add(your_ip) + + return { + 'wireguard': { + 'peers': peers, + }, + } diff --git a/libs/keys.py b/libs/keys.py index 0995458..1565fee 100644 --- a/libs/keys.py +++ b/libs/keys.py @@ -1,11 +1,12 @@ +import base64 from nacl.public import PrivateKey from nacl.encoding import Base64Encoder from bundlewrap.utils import Fault -def gen_privkey(identifier): +def gen_privkey(repo, identifier): return repo.vault.random_bytes_as_base64_for(identifier) -def get_pubkey_from_privkey(identifier, privkey): +def get_pubkey_from_privkey(repo, identifier, privkey): # FIXME this assumes the privkey is always a base64 encoded string def derive_pubkey(): pub_key = PrivateKey(base64.b64decode(str(privkey))).public_key diff --git a/nodes/home/router.py b/nodes/home/router.py index e4894bf..dd9af8f 100644 --- a/nodes/home/router.py +++ b/nodes/home/router.py @@ -11,6 +11,7 @@ nodes['home.router'] = { 'dhcpd', 'vnstat', 'wide-dhcp6c', + 'wireguard', }, 'groups': { 'debian-buster', @@ -41,9 +42,6 @@ nodes['home.router'] = { # day. 'restart_pppd': '23 2 * * * root systemctl restart pppoe', }, - 'icinga_options': { - 'hostname': 'franzi-home.kunbox.net', - }, 'iptables': { 'custom_rules': [ # This is a router. Allow forwarding traffic for all internal networks. @@ -133,5 +131,18 @@ nodes['home.router'] = { 'enp1s0.42': '1', }, }, + 'wireguard': { + # TODO autogenerate? + 'my_ip': '172.19.137.2/32', + 'subnets': { + '172.19.138.0/24', + '172.19.139.0/24', + }, + 'peers': { + 'ovh.wireguard': { + 'do_not_initiate_a_connection_from_your_side': True, + }, + }, + }, }, } diff --git a/nodes/ovh/icinga2.py b/nodes/ovh/icinga2.py index 6e604a1..d24971b 100644 --- a/nodes/ovh/icinga2.py +++ b/nodes/ovh/icinga2.py @@ -3,6 +3,7 @@ nodes['ovh.icinga2'] = { 'icinga2', 'php', 'postgresql', + 'wireguard', 'zfs', }, 'groups': { @@ -14,7 +15,7 @@ nodes['ovh.icinga2'] = { 'eth0': { 'ips': { '51.195.44.8', - '2001:41d0:701:1100::2618/64' + '2001:41d0:701:1100::2618/128' }, 'gateway4': '51.195.44.1', 'gateway6': '2001:41d0:701:1100::1' @@ -53,6 +54,12 @@ nodes['ovh.icinga2'] = { 'xml', }, }, + 'wireguard': { + 'my_ip': '172.19.137.3/32', + 'peers': { + 'ovh.wireguard': {}, + }, + }, 'zfs': { 'pools': { 'tank': { diff --git a/nodes/ovh/wireguard.py b/nodes/ovh/wireguard.py index 8d75063..f0ef3ec 100644 --- a/nodes/ovh/wireguard.py +++ b/nodes/ovh/wireguard.py @@ -1,5 +1,7 @@ nodes['ovh.wireguard'] = { - 'bundles': set(), + 'bundles': { + 'wireguard', + }, 'groups': { 'debian-buster', }, @@ -8,7 +10,7 @@ nodes['ovh.wireguard'] = { 'eth0': { 'ips': { '51.195.47.180', - '2001:41d0:701:1100::20da/64' + '2001:41d0:701:1100::20da/128' }, 'gateway4': '51.195.44.1', 'gateway6': '2001:41d0:701:1100::1' @@ -21,5 +23,10 @@ nodes['ovh.wireguard'] = { 'cpu': 1, 'ram': 2, }, + 'wireguard': { + 'network': '172.19.136.0/22', + 'my_ip': '172.19.137.1/32', + 'psk': vault.random_bytes_as_base64_for('ovh.icinga2 wireguard psk'), + }, }, }