#!/usr/bin/env python3 import os import smtplib import subprocess import sys import tempfile import textwrap from email.mime.multipart import MIMEMultipart from email.mime.application import MIMEApplication from email.mime.text import MIMEText def _run_cmd(cmd): print("$ %s" % (cmd)) subprocess.run(cmd, shell=True, check=True) def check_env (member_id): """ Checks if wireguard is correctly installed, the script is run as root and the member id is correct. """ wgInstalled = os.system("wg") if os.geteuid() != 0: print("On a besoin des droits root pour créer un accès VPN.") print("Relancez la commande préfixée d'un sudo.") raise Exception("Got root?") if wgInstalled != 0: print("Wireguard ne semble pas être installé sur votre système.") print("Il faudrait installer wireguard-tools et wireguard-dkms avant\ d'utiliser ce script") raise Exception("Install wg") if member_id < 2 or member_id > 253: print("On est parti du principe que les IDs des membres commencent à 1.") print("Ah, aussi, pour le moment, on est aussi partis du principe que ça \ s'arrête à 253.") print("Si on a plus de 253 membres, premièrement, FÉLICITATIONS venant\ du Félix du passé. Par contre, il faut repenser l'adressage des \ IPs du VPN maintenant :( Ranson du succès j'immagine.") raise Exception("Wrong member_id") def gen_wg_keys (temp_dir): """ Generates both the private and the public wireguard key of the new member. """ pubkey_path = os.path.join(temp_dir, "pub.key") privkey_path = os.path.join(temp_dir, "priv.key") psk_path = os.path.join(temp_dir, "psk.key") _run_cmd("wg genkey | tee %s | wg pubkey > %s" % (privkey_path, pubkey_path)) _run_cmd("wg genpsk > %s" % (psk_path)) return (privkey_path, pubkey_path, psk_path) def is_duplicate_entry_wg_conf (member_id, config_file): """ Look for a potential wireguard duplicate entry. Note: wg config format is not really a init file because of the multiple init entries. Hence, we cannot use a proper parser to to the check. We'll only try to pattern match the ip addr. """ with open(config_file, "r") as wg_conf_file: return "10.0.0.{}".format(member_id) in wg_conf_file.read() def update_wg_config (member_id, config_file, pubkey_path, psk_path): """ Generate the wireguard peer entry for this new member. """ wg_new_peer = ''' [Peer] PublicKey = %PUBKEY% PresharedKey = %PSK% AllowedIPs = 10.0.0.{1}/24, fd00::{1}/64 '''.format(member_id) with open(config_file, "a") as wg_conf_file: wg_conf_file.write(wg_config) _run_cmd('sed -i "s/%PUBKEY%/$(cat %s)/" "%s"' % (pubkey_path, config_file)) _run_cmd('sed -i "s/%PSK%/$(cat %s)/" "%s"' % (psk_path, config_file)) class Email: """ Not really necessary, but I keep on forgetting most of an email arguments and I'm tired of debugging this class of error... Too bad we don't have any record type in this language. PS: I hate python. """ def __init__(self, username, passwd, from_addr, to_addr, server): self.username = username self.passwd = passwd self.from_addr = from_addr self.to_addr = to_addr self.server = server def send_mail(email, wgconfig_path): """ Send the private key by email. email: - username - passwd - from_addr - to_addr - server """ from_addr = 'bureau@baionet.fr' password = email.passwd msg = MIMEMultipart() msg['Subject'] = "Votre acces VPN Baionet" msg['Date'] = formatdate(localtime=True) msg['From'] = email.from_addr msg['To'] = [email.to_addr] body = textwrap.dedent(''' blahblah, cf le wiki blahblahblah ''') msg.attach([MIMEText(body), MIMEText(config)]) with open(wgconfig_path, "rb") as f: part = MIMEApplication( f.read(), Name=os.path.basename(f)) part['Content-Disposition'] = 'attachment; filename=%s' % basename(f) msg.attach(part) username = email.username server = smtplib.SMTP(email.server) server.ehlo() server.starttls() server.login(email.username, email.passwd) server.sendmail(email.from_addr, email.to_addr, msg.as_string()) server.quit() # Main Function # ============= # ************* # ============= # 1- Parse email/member id. # 2- Génère les clés wireguard. # 3- Crée/déploie la configuration wireguard. # 5- Envoie la clé à l'utilisateur (email/manuellement) if __name__ == '__main__': member_email = input("EMail du nouveau membre: ") try: member_id = int(input("Numéro d'adhérant du nouveau membre: ")) except Exception as e: print("ERREUR: Le numéro d'adhérant est en théorie un entier entre 1 et 253.") sys.exit(1) try: service_name = os.environ['SYSTEMD_SERVICE'] wg_config_dir = os.environ['WG_CONF_PATH'] check_env(member_id) except Exception as e: print("ERREUR: problème d'environnement: {}".format(e)) sys.exit(1) print("[+] Génération des clés wireguard") with tempfile.TemporaryDirectory() as temp_dir: print("[+] Modification de la configuration wireguard") try: (privkey_path, pubkey_path, psk_path) = gen_wg_keys(temp_dir) if not is_duplicate_entry_wg_conf(member_id, config_file): update_wg_config(member_id, config_file, pubkey_path, psk_path) else: print("Le membre {} semble déja avoir un compte VPN.".format(member_id)) print("Veuillez contacter la liste de diffusion technique\ si son compte necessite une ré-activation.") sys.exit(1) except Exception as e: print("ERREUR: Problème lors de la génération des clés wireguard.") print(e) sys.exit(1) print("[+] Chargement de la nouvelle interface réseau") try: _run_cmd("systemctl restart %s" % (service_name)) except Exception as e: print("ERREUR: Problème lors du redémarrage du service.") print(e) print("[+] Envoi de la clé privée au nouveau membre") try: if use_email: username = os.environ['SMTP_USERNAME'] passwd = os.environ['SMTP_PASSWD'] server = os.environ['SMTP_SERVER'] server = os.environ['SYSTEMD_SERVICE'] server = os.environ['SYSTEMD_SERVICE'] email = Email(username, passwd, "bureau@baionet.fr", member_email, server) send_email(email, privkey_path) else: print("Mode utilisateur avancé") print("=======================") print("À vous de vous débrouiller pour donner les clés/config à l'utilisateur") print("Clé privée: %s" % (privkey_path)) print("Clé pré-partagée (psk): %s" % (psk_path)) print("Clé publique (psk): %s" % (pubkey_path)) input("Appuyez sur entrée pour continuer (les clés privées seront détruites): ") except Exception as e: print("ERREUR: erreur lors de l'envoi de l'email.") print(e) print("Veuillez envoyer tout ce message d'erreur à la liste de diffusion technique") sys.exit(1) print("[+] Nettoyage des clés") _run_cmd("shred -u %s %s %s" % (privkey_path, pubkey_path, psk_path)) print("[+] COMPTE CRÉE AVEC SUCCÈS")