chore(release): v0.7.5

Corrige l'échec silencieux « Accès refusé » à la connexion Windows :
wireguard.exe /install|uninstalltunnelservice était lancé sans élévation
en misant sur son manifeste UAC natif, qui ne s'auto-déclenche jamais via
un appel process-à-process. L'appel tente maintenant sans élévation
(compatible avec les ACLs accordées via le bouton dédié), puis retente
avec une vraie élévation UAC si le premier essai échoue. Le diagnostic
d'élévation est mis à jour pour refléter les deux cas.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-04 14:24:56 +02:00
co-authored by Claude Sonnet 5
parent 3b869aeae8
commit 66faf5e661
7 changed files with 45 additions and 20 deletions
+23 -8
View File
@@ -213,6 +213,27 @@ def interface_exists(cfg: Config) -> bool:
return dns_util.interface_exists(cfg.wg.get("interface_name", "wgs0"))
def _run_wireguard_service_cmd(cmd: list[str], timeout: int) -> tuple[int, str, str]:
"""Lance `wireguard.exe /install…` ou `/uninstall…tunnelservice`.
Un utilisateur dont les ACLs du service ont été accordées (bouton
« Configurer les permissions » du panneau Admin) peut créer/supprimer ses
tunnels sans élévation : l'appel non privilégié est donc tenté en premier,
pour ne jamais déclencher d'UAC inutile dans ce cas.
Sans ACL accordée, cet appel échoue sur « Accès refusé » — le manifeste
UAC de wireguard.exe ne s'applique que lancé depuis l'explorateur ou via
ShellExecute, jamais via CreateProcess (`run_command`) — et échouait donc
silencieusement plutôt que de demander l'élévation. On retente alors via
`run_privileged`, seul à passer par ShellExecuteEx "runas" pour obtenir une
vraie invite UAC.
"""
code, out, err = run_command(cmd, timeout)
if code == 0:
return code, out, err
return run_privileged(cmd, timeout)
def force_cleanup(cfg: Config) -> list[str]:
"""Démonte tout résidu du tunnel : entrée DNS orpheline + interface morte.
@@ -229,10 +250,9 @@ def force_cleanup(cfg: Config) -> list[str]:
if is_windows():
# Le service du tunnel restaure routes et DNS en s'arrêtant.
# Sous Windows, wireguard.exe gère lui-même l'élévation via son manifeste UAC.
if dns_util.interface_exists(name):
cmd = [wireguard_exe(), "/uninstalltunnelservice", name]
run_command(cmd, timeout=60)
_run_wireguard_service_cmd(cmd, timeout=60)
# Nettoyer les règles split-DNS
dns_util.cleanup_split_dns(cfg)
actions += dns_util.cleanup(name)
@@ -305,13 +325,8 @@ def connect(cfg: Config) -> tuple[bool, str]:
return True, "Tunnel WireGuard déjà actif"
if dns_util.interface_exists(name):
force_cleanup(cfg)
# Sous Windows, wireguard.exe gère lui-même l'élévation via son manifeste UAC.
# Sous Linux, wg-quick nécessite sudo.
cmd = [wireguard_exe(), "/installtunnelservice", result]
if is_windows():
code, _, err = run_command(cmd, timeout=90)
else:
code, _, err = run_privileged(cmd, timeout=90)
code, _, err = _run_wireguard_service_cmd(cmd, timeout=90)
# /installtunnelservice enregistre le service et rend la main : le
# démarrage effectif du tunnel est asynchrone. Tester l'état
# immédiatement conclurait « échec » sur un tunnel qui monte.