#!/usr/bin/env python3 from __future__ import annotations import argparse import os import subprocess import sys from dataclasses import dataclass, field from typing import Final PUB_FILENAME_TEMPLATE: Final[str] = "pub_{}.key" PRI_FILENAME_TEMPLATE: Final[str] = "pri_{}.key" CONF_FILENAME_TEMPLATE: Final[str] = "wg_{}.conf" PUB_KEYS: Final[dict[str, str]] = { "he": "aE94vplTV3Uq0sx2wwoTM1+KQhK4MNjyVhQuOruI3TM=", "dr": "WnMaHABgoM64A4nRFOs+Vs62gMMXGV/ZYkRoWAXJ3DQ=", "br": "8W8hUyinttW1C1xhCOfdZu0Obvus5v2tjDyA8yhESTw=", } NAMES: Final[tuple[str, str, str]] = ("dr", "br", "he") SUBNET: Final[dict[str, int]] = { "dr": 8, # für Brew, 2 ist für Fassreiniger, 5 für Füller "br": 9, # für Brew, 3 ist für Fassreiniger, 6 für Füller "he": 7, # für Brew, 1 ist für Fassreiniger, 4 für Füller } ALLOWED_IPS = { "dr": "10.8.2.1/32", "br": "10.8.10.1/32", "he": "10.8.0.1/32", } ENDPOINT: Final[dict[str, str]] = { "dr": "dreherhome.us.to:21337", "br": "wg-dreher.brunotic.at:51820", "he": "135.181.29.147:51820", } CONF_TEMPLATE: Final[str] = """[Interface] Address=10.8.{}.{}/32 PrivateKey={} [Peer] PublicKey={} AllowedIPs={} Endpoint={} PersistentKeepalive=25 """ VALID_COMMANDS: Final[tuple[str, ...]] = ( "start", "stop", "restart", "enable", "disable", "status") @dataclass class Args: id: int | None = None cwd: str | None = None commands: list[str] = field(default_factory=list) def create_config(who: str, host_id: int): pub_filename = PUB_FILENAME_TEMPLATE.format(who) pri_filename = PRI_FILENAME_TEMPLATE.format(who) conf_filename = CONF_FILENAME_TEMPLATE.format(who) pri_proc = subprocess.run(("wg", "genkey"), capture_output=True, text=True) pri_key = pri_proc.stdout if pri_proc.returncode: if pri_key: print(pri_key) if pri_proc.stderr: print(pri_proc.stderr, file=sys.stderr) sys.exit("Beim Ausführen von 'wg genkey lief etwas schief'. " "Programmabbruch ...") if not pri_key: sys.exit( "'wg genkey' hat keinen Schlüssel erzeugt. Programmabbruch ...") pub_proc = subprocess.run( ("wg", "pubkey"), capture_output=True, text=True, input=pri_key) pub_key = pub_proc.stdout if pub_proc.returncode: if pub_key: print(pub_key) if pub_proc.stderr: print(pub_proc.stderr, file=sys.stderr) sys.exit("Beim Ausführen von 'wg genkey lief etwas schief'. " "Programmabbruch ...") if not pub_key: sys.exit( "'wg genkey' hat keinen Schlüssel erzeugt. Programmabbruch ...") conf = CONF_TEMPLATE.format(SUBNET[who], host_id, pri_key, PUB_KEYS[who], ALLOWED_IPS[who], ENDPOINT[who]) with open(conf_filename, "w") as f: f.write(conf) with open(pri_filename, "w") as f: f.write(pri_key) with open(pub_filename, "w") as f: f.write(pub_key) print(f"{pub_filename}:\n{pub_key}") if __name__ == "__main__": parser = argparse.ArgumentParser( description="wg-config für Dreher Brauanlage") parser.add_argument("commands", nargs="*", help=f"systemctl [ {' | '.join(VALID_COMMANDS)} ] (wird auf jede " "config angewendet)") parser.add_argument("--id", type=int, help="Die ID der Maschine - also " "die Nummer, mit der der Hostname endet") parser.add_argument("--cwd", type=str, help="Das Verzeichnis, in dem die " "Dateien abgelegt werden sollen. Als 'root' ist das standardmäßig " "/etc/wireguard, ansonsten das aktuelle Verzeichnis") args = Args() parser.parse_args(namespace=args) if args.cwd: os.chdir(args.cwd) elif (os.getuid() == 0): os.chdir("/etc/wireguard") if args.commands: failed: list[str] = [] for command in args.commands: if command not in VALID_COMMANDS: failed.append(command) if failed: sys.exit(f"Folgende Kommandos sind ungültig: {', '.join(failed)}. " f"Zulässig sind nur: {', '.join(VALID_COMMANDS)}") if args.id is not None: if args.id > 255: sys.exit("--id kann höchstens 255 sein") elif args.id <= 0: sys.exit("--id kann nicht kleiner als 1 sein") for name in NAMES: create_config(name, args.id) print(f"Erfolgreich {len(NAMES)} configs in {os.getcwd()} erstellt") for command in args.commands: for name in NAMES: conf = CONF_FILENAME_TEMPLATE.format(name) quick = f"wg-quick@{conf.rstrip('.conf')}" subprocess.run(("systemctl", command, quick)) if not args.id and not args.commands: parser.print_help()