158 lines
5.0 KiB
Python
158 lines
5.0 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
WGSecure (WGS) — Interface graphique WireGuard avec MFA
|
|
Version 0.4.3
|
|
"""
|
|
import sys
|
|
import argparse
|
|
|
|
from PyQt6.QtWidgets import QApplication, QMessageBox, QInputDialog, QLineEdit, QSystemTrayIcon
|
|
from PyQt6.QtCore import Qt
|
|
|
|
from app.core.config import Config
|
|
from app.core import dns as dns_util
|
|
from app.core import log as conn_log
|
|
from app.core import shutdown as shutdown_guard
|
|
from app.ui.main_window import MainWindow
|
|
from app.ui.systray import SystemTray
|
|
from app.ui import icons
|
|
from app.utils.platform_utils import install_desktop_entry
|
|
|
|
|
|
def _install_taskbar_icon(app_icon) -> None:
|
|
"""Exporte l'icône en PNG 256 px et installe l'entrée .desktop."""
|
|
try:
|
|
import tempfile, os
|
|
tmp = os.path.join(tempfile.gettempdir(), "wgsecure_icon.png")
|
|
app_icon.pixmap(256, 256).save(tmp, "PNG")
|
|
install_desktop_entry(tmp)
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
def recover_dns_state(config: Config) -> None:
|
|
"""Répare la résolution DNS laissée détournée par une session précédente.
|
|
|
|
Si l'application (ou la machine) s'est arrêtée sans exécuter
|
|
`wg-quick down`, la configuration DNS du tunnel reste active alors que le
|
|
résolveur correspondant est injoignable : plus aucun nom ne se résout.
|
|
Le nettoyage n'a lieu que si un résidu est effectivement constaté.
|
|
"""
|
|
iface = config.wg.get("interface_name", "wgs0")
|
|
try:
|
|
repaired, msg = dns_util.repair_if_broken(iface, config.wg.get("dns", ""))
|
|
except Exception as e: # noqa: BLE001
|
|
repaired, msg = False, f"Vérification DNS impossible : {e}"
|
|
if msg:
|
|
conn_log.log_event(msg, "warning" if repaired else "error")
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(
|
|
prog="wgsecure",
|
|
description="WGSecure — WireGuard GUI avec MFA",
|
|
)
|
|
parser.add_argument(
|
|
"--admin",
|
|
action="store_true",
|
|
help="Lancer directement en mode Administrateur",
|
|
)
|
|
parser.add_argument(
|
|
"--no-tray",
|
|
action="store_true",
|
|
help="Désactiver l'icône systray",
|
|
)
|
|
return parser.parse_args()
|
|
|
|
|
|
def require_admin_auth(config: Config, app: QApplication) -> bool:
|
|
"""Vérifie le mot de passe admin si configuré. Retourne True si autorisé."""
|
|
if not config.has_admin_password():
|
|
return True
|
|
pw, ok = QInputDialog.getText(
|
|
None,
|
|
"Accès Administrateur",
|
|
"Mot de passe administrateur :",
|
|
QLineEdit.EchoMode.Password,
|
|
)
|
|
if not ok:
|
|
return False
|
|
if config.check_admin_password(pw):
|
|
return True
|
|
QMessageBox.warning(None, "WGSecure", "Mot de passe incorrect.")
|
|
return False
|
|
|
|
|
|
def main():
|
|
args = parse_args()
|
|
|
|
# Support DPI haute résolution
|
|
QApplication.setHighDpiScaleFactorRoundingPolicy(
|
|
Qt.HighDpiScaleFactorRoundingPolicy.PassThrough
|
|
)
|
|
app = QApplication(sys.argv)
|
|
app.setApplicationName("WGSecure")
|
|
app.setApplicationVersion("0.4.3")
|
|
app.setOrganizationName("WGS")
|
|
# Icône globale multi-tailles — barre des tâches + alt-tab
|
|
app_icon = icons.icon_app()
|
|
app.setWindowIcon(app_icon)
|
|
app.setDesktopFileName("wgsecure")
|
|
# Installe .desktop + PNG pour que GNOME/KDE affiche l'icône sur la taskbar
|
|
_install_taskbar_icon(app_icon)
|
|
# Empêche la fermeture quand toutes les fenêtres sont cachées (tray)
|
|
app.setQuitOnLastWindowClosed(False)
|
|
|
|
config = Config()
|
|
|
|
# Reste d'une session précédente mal terminée : DNS détourné vers un
|
|
# résolveur mort. À faire avant d'ouvrir la fenêtre pour rendre l'accès
|
|
# au réseau tout de suite.
|
|
recover_dns_state(config)
|
|
|
|
# Restauration de l'état réseau garantie sur toutes les sorties :
|
|
# Quitter, croix, SIGINT/SIGTERM, atexit, exception non rattrapée.
|
|
shutdown_guard.install(config, report=conn_log.log_event, app=app)
|
|
|
|
# Mode Admin direct (--admin)
|
|
if args.admin:
|
|
if not require_admin_auth(config, app):
|
|
sys.exit(0)
|
|
from app.ui.admin_window import AdminWindow
|
|
win = AdminWindow(config)
|
|
win.setWindowIcon(icons.icon_admin())
|
|
win.show()
|
|
sys.exit(app.exec())
|
|
|
|
# Mode User (défaut)
|
|
window = MainWindow(config)
|
|
|
|
# Systray
|
|
if not args.no_tray and QSystemTrayIcon_available():
|
|
tray = SystemTray(config, window)
|
|
tray.show()
|
|
# Démarrer réduit si déjà configuré
|
|
if config.configured:
|
|
window.show()
|
|
else:
|
|
window.show()
|
|
tray.showMessage(
|
|
"WGSecure",
|
|
"Bienvenue ! Ouvrez le panneau Administrateur pour configurer WireGuard.",
|
|
QSystemTrayIcon.MessageIcon.Information,
|
|
4000,
|
|
)
|
|
else:
|
|
window.show()
|
|
|
|
sys.exit(app.exec())
|
|
|
|
|
|
def QSystemTrayIcon_available() -> bool:
|
|
from PyQt6.QtWidgets import QSystemTrayIcon
|
|
return QSystemTrayIcon.isSystemTrayAvailable()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|