#!/usr/bin/env python3 """launch_vpn.py — auth = static+TOTP, конкатенация без разделителя. Параметры берутся из общего vpn_config.json (см. launch_vpn_gui.py); секреты — из keyring.""" import subprocess, pyotp, keyring, tempfile, json, os, base64 CONFIG_PATH = os.path.join( os.path.dirname(os.path.abspath(__file__)), "vpn_config.json") DEFAULTS = { "username": "boyko_vn", "ovpn_config": r"C:\Users\User\app\vpn\ovpn-config-ano.ovpn", "openvpn_bin": r"C:\Program Files\OpenVPN\bin\openvpn.exe", "keyring_service": "openvpn-totp", } def load_config(): cfg = dict(DEFAULTS) try: with open(CONFIG_PATH, "r", encoding="utf-8") as f: data = json.load(f) if isinstance(data, dict): cfg.update(data) except (OSError, ValueError): pass return cfg def main(): cfg = load_config() service = cfg.get("keyring_service", "openvpn-totp") secret = keyring.get_password(service, "secret") # static — необязателен: если его нет в keyring, пароль = только TOTP-код. static = keyring.get_password(service, "static") or "" code = pyotp.TOTP(secret).now() if cfg.get("static_challenge"): password = (f"SCRV1:{base64.b64encode(static.encode()).decode()}" f":{base64.b64encode(code.encode()).decode()}") else: password = f"{static}{code}" fd, path = tempfile.mkstemp(suffix=".txt") try: with os.fdopen(fd, "w", newline="\n") as f: f.write(f"{cfg['username']}\n{password}\n") subprocess.run([cfg["openvpn_bin"], "--config", cfg["ovpn_config"], "--auth-user-pass", path]) finally: os.remove(path) if __name__ == "__main__": main()