diff wireguard/wireguard.py @ 326:5b88b38f2471

huge reorg, reog toplevel functions in preparation of a ui with nice task lists
author drewp@bigasterisk.com
date Mon, 20 Jan 2025 21:55:08 -0800
parents wireguard.py@65e28d2e0cd8
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wireguard/wireguard.py	Mon Jan 20 21:55:08 2025 -0800
@@ -0,0 +1,85 @@
+import subprocess
+
+from pyinfra.context import host
+from pyinfra.facts.files import FindInFile
+from pyinfra.operations import files, systemd
+
+import wireguard.wireguard_pubkey as wireguard_pubkey
+
+# other options:
+#   https://www.reddit.com/r/WireGuard/comments/fkr240/shortest_path_between_peers/
+#   https://github.com/k4yt3x/wireguard-mesh-configurator
+#   https://github.com/mawalu/wireguard-private-networking
+#
+
+
+def peer_block(hostname, allowed_ips, endpoint=None, keepalive=None):
+    # allowed_ips should be determined mostly from host.data.wireguard_address
+
+    public_key = wireguard_pubkey.pubkey[hostname]
+    out = f'''\
+
+[Peer]
+# {hostname}
+PublicKey = {public_key}
+AllowedIPs = {allowed_ips}
+'''
+    if endpoint is not None:
+        out += f'Endpoint = {endpoint}\n'
+    if keepalive is not None:
+        out += f'PersistentKeepalive = {keepalive}\n'
+    return out
+
+
+def get_priv_key(wireguard_interface) -> str:
+    priv_key_lines = host.get_fact(FindInFile, path=f'/etc/wireguard/{wireguard_interface}.conf', pattern=r'PrivateKey.*')
+    if not priv_key_lines:
+        priv_key = subprocess.check_output(['wg', 'genkey']).strip().decode('ascii')
+    else:
+        priv_key = priv_key_lines[0].split(' = ')[1]
+    return priv_key
+
+
+def compute_pub_key(priv_key: str) -> str:
+    pub_key = subprocess.check_output(['wg', 'pubkey'], input=priv_key.encode('ascii')).strip().decode('ascii')
+    # todo: if this was new, it should be added to a file of pubkeys that
+    # peer_block can refer to. meanwhile, edit the template.
+    return pub_key
+
+
+def wireguard():
+    for wireguard_interface in ['wg0', 'bogasterisk']:
+        if wireguard_interface == 'bogasterisk' and host.name != 'prime':
+            continue
+
+        # note- this is specific to the wg0 setup. Other conf files don't use it.
+        wireguard_ip = host.host_data.get('wireguard_address')
+        if wireguard_interface == 'wg0' and wireguard_ip is None:
+            continue
+
+        # new pi may fail with 'Unable to access interface: Protocol not supported'. reboot fixes.
+
+        priv_key = get_priv_key(wireguard_interface)
+
+        # unused since I still hand-maintain wireguard_pubkey.py :(
+        # pub_key = compute_pub_key(priv_key)
+
+        files.template(
+            src=f'wireguard/templates/{wireguard_interface}.conf.j2',
+            dest=f'/etc/wireguard/{wireguard_interface}.conf',
+            mode='600',
+            wireguard_ip=wireguard_ip,
+            priv_key=priv_key,
+            peer_block=peer_block,
+        )
+        svc = f'wg-quick@{wireguard_interface}.service'
+
+        files.template(src='wireguard/templates/wg.service.j2',
+                       dest=f'/etc/systemd/system/{svc}',
+                       wireguard_interface=wireguard_interface)
+        systemd.service(service=svc, daemon_reload=True, restarted=True, enabled=True)
+
+
+operations = [
+    wireguard,
+]