feat: support split-DNS, infobulles UI et corrections Windows
- Ajout du support complet du split-DNS pour Linux (systemd-resolved) et Windows (NRPT) - Bouton de configuration des permissions ACL pour WireGuard sous Windows - Suppression de tous les appels à sc.exe (remplacés par PowerShell + wireguard.exe) - UAC gérée par wireguard.exe lui-même (pas par WGSecure) - Infobulles ajoutées sur tous les champs de configuration principaux - Correction du blocage du handshake lié au redémarrage de systemd-resolved - Incrémentation de la version à 0.7.3 Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
+7
-2
@@ -6,14 +6,19 @@ Ce projet suit le [Versionnage Sémantique](https://semver.org/lang/fr/).
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## [0.7.2] — 2026-09-02
|
## [0.7.3] — 2026-09-02
|
||||||
|
|
||||||
### Ajouté
|
### Ajouté
|
||||||
- **Bouton de configuration des permissions Windows.** Dans l'onglet Tests & diagnostic → Élévation, un nouveau bouton permet à l'administrateur de configurer les ACLs du service WireGuard pour autoriser l'utilisateur actuel à créer/supprimer ses tunnels sans invite UAC. Utilise PowerShell pour modifier les SDDL du service et redémarrer celui-ci.
|
- **Support du Split-DNS pour Linux et Windows.** Permet de configurer un serveur DNS dédié pour les noms du réseau distant (ex: 192.168.1.210) tout en utilisant le DNS système pour le reste du trafic internet. Sous Linux, utilise `systemd-resolved` ; sous Windows, utilise les règles NRPT.
|
||||||
|
- **Bouton de configuration des permissions Windows.** Dans l'onglet Tests & diagnostic → Élévation, un nouveau bouton permet à l'administrateur de configurer les ACLs du service WireGuard pour autoriser l'utilisateur actuel à créer/supprimer ses tunnels sans invite UAC.
|
||||||
|
|
||||||
### Corrigé
|
### Corrigé
|
||||||
- **Appels à sc.exe supprimés sous Windows.** Remplacés par PowerShell `Get-Service` pour la détection, et `wireguard.exe` pour la gestion des services. Supprime les messages d'erreur "service inexistant" et évite les doubles appels à `uninstalltunnelservice`.
|
- **Appels à sc.exe supprimés sous Windows.** Remplacés par PowerShell `Get-Service` pour la détection, et `wireguard.exe` pour la gestion des services. Supprime les messages d'erreur "service inexistant" et évite les doubles appels à `uninstalltunnelservice`.
|
||||||
- **UAC déclenchée par WGSecure sous Windows.** Désormais, `wireguard.exe` est appelé via `run_command()` (pas `run_privileged`) et gère lui-même l'élévation via son manifeste natif. Si l'utilisateur a les permissions ACLs, aucune UAC n'est affichée.
|
- **UAC déclenchée par WGSecure sous Windows.** Désormais, `wireguard.exe` est appelé via `run_command()` (pas `run_privileged`) et gère lui-même l'élévation via son manifeste natif. Si l'utilisateur a les permissions ACLs, aucune UAC n'est affichée.
|
||||||
|
- **Handshake bloqué par le redémarrage de systemd-resolved.** Le redémarrage de systemd-resolved est maintenant effectué de manière asynchrone pour éviter de bloquer le handshake WireGuard.
|
||||||
|
|
||||||
|
### Améliorations UI
|
||||||
|
- **Infobulles ajoutées** sur tous les champs de configuration principaux pour guider l'utilisateur (endpoint, clés, DNS, AllowedIPs, etc.).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
## ──────────────────────────────────────────────
|
## ──────────────────────────────────────────────
|
||||||
|
|
||||||
APP := wgsecure
|
APP := wgsecure
|
||||||
VERSION := 0.7.2
|
VERSION := 0.7.3
|
||||||
VENV := .venv
|
VENV := .venv
|
||||||
PYTHON := $(VENV)/bin/python3
|
PYTHON := $(VENV)/bin/python3
|
||||||
PIP := $(VENV)/bin/pip
|
PIP := $(VENV)/bin/pip
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# 🛡️ WGSecure (WGS)
|
# 🛡️ WGSecure (WGS)
|
||||||
|
|
||||||
🚀 v0.7.2 · 🐍 · 🪟🐧 · 🔐 MFA TOTP · 🛡️ WireGuard
|
🚀 v0.7.3 · 🐍 · 🪟🐧 · 🔐 MFA TOTP · 🛡️ WireGuard
|
||||||
|
|
||||||
**WGSecure** est une interface graphique multiplateforme (Windows & Linux) pour gérer une connexion WireGuard avec une surcouche MFA (authentification à deux facteurs TOTP).
|
**WGSecure** est une interface graphique multiplateforme (Windows & Linux) pour gérer une connexion WireGuard avec une surcouche MFA (authentification à deux facteurs TOTP).
|
||||||
|
|
||||||
@@ -208,4 +208,4 @@ WGSecure/
|
|||||||
## 👤 Auteur
|
## 👤 Auteur
|
||||||
|
|
||||||
Développé par **Johnny** — [JT-Tools](https://github.com/JT-Tools)
|
Développé par **Johnny** — [JT-Tools](https://github.com/JT-Tools)
|
||||||
Version :  — Septembre 2026
|
Version :  — Septembre 2026
|
||||||
|
|||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
__version__ = "0.7.2"
|
__version__ = "0.7.3"
|
||||||
APP_NAME = "WGSecure"
|
APP_NAME = "WGSecure"
|
||||||
APP_SHORT = "WGS"
|
APP_SHORT = "WGS"
|
||||||
|
|||||||
+6
-1
@@ -33,10 +33,15 @@ _WG_DEFAULT: dict[str, Any] = {
|
|||||||
"allowed_ips": "10.8.0.0/24",
|
"allowed_ips": "10.8.0.0/24",
|
||||||
"keepalive": 25,
|
"keepalive": 25,
|
||||||
"mtu": 0,
|
"mtu": 0,
|
||||||
|
# Split-DNS : serveur DNS dédié pour les noms du réseau distant,
|
||||||
|
# avec liste de domaines à résoudre via ce serveur (ex: ["local", "internal"]).
|
||||||
|
# Laisser vide pour utiliser le DNS par défaut pour tout le trafic.
|
||||||
|
"split_dns_server": "",
|
||||||
|
"split_dns_domains": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
_DEFAULT: dict[str, Any] = {
|
_DEFAULT: dict[str, Any] = {
|
||||||
"version": "0.7.2",
|
"version": "0.7.3",
|
||||||
"admin_password_hash": "",
|
"admin_password_hash": "",
|
||||||
"admin_salt": "",
|
"admin_salt": "",
|
||||||
"mfa_enabled": False,
|
"mfa_enabled": False,
|
||||||
|
|||||||
+229
@@ -169,6 +169,235 @@ _NRPT_CLEAN_PS = (
|
|||||||
"-ErrorAction SilentlyContinue }"
|
"-ErrorAction SilentlyContinue }"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Split-DNS : rule name template for per-tunnel DNS rules
|
||||||
|
_SPLIT_DNS_RULE_PREFIX = "WGSecure-SplitDNS-"
|
||||||
|
|
||||||
|
|
||||||
|
def _nrpt_add_split_dns_ps(server: str, domains: list[str], rule_name: str) -> str:
|
||||||
|
"""PowerShell script to add a NRPT rule for split-DNS.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
server: DNS server IP address
|
||||||
|
domains: List of domain suffixes (e.g., ["local", "internal"])
|
||||||
|
rule_name: Name for the NRPT rule
|
||||||
|
"""
|
||||||
|
if not domains:
|
||||||
|
return ""
|
||||||
|
# Build namespace list for NRPT rule
|
||||||
|
namespace_list = ", ".join(f"'{d.strip().strip('.')}'" for d in domains if d.strip())
|
||||||
|
return (
|
||||||
|
f"$rule = Get-DnsClientNrptRule -Name '{rule_name}' -ErrorAction SilentlyContinue; "
|
||||||
|
f"if ($rule) {{ Remove-DnsClientNrptRule -Name '{rule_name}' -Force -ErrorAction SilentlyContinue }}; "
|
||||||
|
f"Add-DnsClientNrptRule -Name '{rule_name}' -Namespace @({namespace_list}) "
|
||||||
|
f"-NameServer '{server}' -Comment 'WGSecure Split-DNS' -ErrorAction SilentlyContinue"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _nrpt_remove_split_dns_ps(rule_name: str) -> str:
|
||||||
|
"""PowerShell script to remove a NRPT rule for split-DNS."""
|
||||||
|
return (
|
||||||
|
f"Remove-DnsClientNrptRule -Name '{rule_name}' -Force -ErrorAction SilentlyContinue"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _systemd_resolved_available() -> bool:
|
||||||
|
"""Vérifie si systemd-resolved est actif (Linux)."""
|
||||||
|
if is_windows():
|
||||||
|
return False
|
||||||
|
import subprocess
|
||||||
|
try:
|
||||||
|
# Vérifier que systemd-resolved est actif
|
||||||
|
code = subprocess.run(
|
||||||
|
["systemctl", "is-active", "systemd-resolved"],
|
||||||
|
capture_output=True, text=True, timeout=5
|
||||||
|
).returncode
|
||||||
|
return code == 0
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _setup_split_dns_linux(server: str, domains: list[str], iface: str) -> tuple[bool, str]:
|
||||||
|
"""Configure le split-DNS sous Linux via systemd-resolved.
|
||||||
|
|
||||||
|
Crée un fichier dans /etc/systemd/resolved.conf.d/ pour router
|
||||||
|
les domaines vers le serveur DNS spécifié.
|
||||||
|
|
||||||
|
Note: Le redémarrage de systemd-resolved est nécessaire pour appliquer
|
||||||
|
la configuration, mais il est effectué de manière asynchrone pour
|
||||||
|
éviter de bloquer le handshake WireGuard.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
server: Adresse IP du serveur DNS (ex: 192.168.1.210)
|
||||||
|
domains: Liste de domaines (ex: ["local", "internal"])
|
||||||
|
iface: Nom de l'interface WireGuard
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(success, message)
|
||||||
|
"""
|
||||||
|
if not _systemd_resolved_available():
|
||||||
|
return False, "systemd-resolved non disponible. Utilisez resolvconf ou configurez manuellement."
|
||||||
|
|
||||||
|
# Créer le nom du fichier de config
|
||||||
|
config_file = f"/etc/systemd/resolved.conf.d/wgsecure-{iface}.conf"
|
||||||
|
|
||||||
|
# Contenu du fichier
|
||||||
|
domain_str = ", ".join(f"~{d.strip()}" for d in domains if d.strip())
|
||||||
|
content = f"[Resolve]\nDNS={server}\nDomains={domain_str}\n"
|
||||||
|
|
||||||
|
# Écrire le fichier (nécessite sudo)
|
||||||
|
import tempfile
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
# Créer un fichier temporaire
|
||||||
|
with tempfile.NamedTemporaryFile(mode='w', suffix='.conf', delete=False) as f:
|
||||||
|
f.write(content)
|
||||||
|
temp_path = f.name
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Copier le fichier temporaire vers /etc/systemd/resolved.conf.d/
|
||||||
|
code, out, err = run_privileged(
|
||||||
|
["sudo", "cp", temp_path, config_file],
|
||||||
|
timeout=10
|
||||||
|
)
|
||||||
|
|
||||||
|
if code != 0:
|
||||||
|
return False, f"Échec de la copie du fichier: {err or out}"
|
||||||
|
|
||||||
|
# Redémarrer systemd-resolved en arrière-plan pour ne pas bloquer
|
||||||
|
# Utiliser systemd-run pour un redémarrage asynchrone
|
||||||
|
restart_cmd = ["sudo", "systemctl", "restart", "systemd-resolved"]
|
||||||
|
try:
|
||||||
|
# Lancer en arrière-plan avec nohup pour éviter de bloquer
|
||||||
|
subprocess.Popen(
|
||||||
|
restart_cmd,
|
||||||
|
stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.DEVNULL
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
pass # Ignorer l'erreur, le fichier est déjà en place
|
||||||
|
|
||||||
|
return True, (
|
||||||
|
f"Split-DNS configuré pour {server} (domaines: {', '.join(domains)}). "
|
||||||
|
f"Redémarrez systemd-resolved manuellement si nécessaire: "
|
||||||
|
f"`sudo systemctl restart systemd-resolved`"
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
os.unlink(temp_path)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def setup_split_dns(cfg: Config) -> tuple[bool, str]:
|
||||||
|
"""Configure le split-DNS pour Windows (NRPT) ou Linux (systemd-resolved).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cfg: Configuration contenant split_dns_server et split_dns_domains
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(success, message)
|
||||||
|
"""
|
||||||
|
server = (cfg.wg.get("split_dns_server") or "").strip()
|
||||||
|
domains = cfg.wg.get("split_dns_domains") or []
|
||||||
|
name = cfg.wg.get("interface_name", "wgs0")
|
||||||
|
|
||||||
|
if not server or not domains:
|
||||||
|
return True, "Aucune configuration split-DNS"
|
||||||
|
|
||||||
|
# Nettoyer les domaines
|
||||||
|
domains = [d.strip() for d in domains if d.strip()]
|
||||||
|
if not domains:
|
||||||
|
return True, "Aucun domaine valide à configurer"
|
||||||
|
|
||||||
|
if is_windows():
|
||||||
|
rule_name = f"{_SPLIT_DNS_RULE_PREFIX}{name}"
|
||||||
|
script = _nrpt_add_split_dns_ps(server, domains, rule_name)
|
||||||
|
|
||||||
|
if not script:
|
||||||
|
return True, "Aucun domaine à configurer"
|
||||||
|
|
||||||
|
# Exécuter avec élévation (nécessite admin pour modifier les règles NRPT)
|
||||||
|
code, out, err = run_privileged(
|
||||||
|
["powershell", "-NoProfile", "-NonInteractive", "-Command", script],
|
||||||
|
timeout=15
|
||||||
|
)
|
||||||
|
|
||||||
|
if code == 0:
|
||||||
|
return True, f"Règle split-DNS (NRPT) configurée pour {server} (domaines: {', '.join(domains)})"
|
||||||
|
else:
|
||||||
|
return False, f"Échec de la configuration split-DNS (NRPT): {err or out or 'Erreur inconnue'}"
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Linux : utiliser systemd-resolved
|
||||||
|
return _setup_split_dns_linux(server, domains, name)
|
||||||
|
|
||||||
|
|
||||||
|
def _cleanup_split_dns_linux(iface: str) -> tuple[bool, str]:
|
||||||
|
"""Supprime la configuration split-DNS sous Linux.
|
||||||
|
|
||||||
|
Note: Ne redémarre PAS systemd-resolved pour éviter de casser
|
||||||
|
la résolution DNS pendant une reconnexion. Le service sera
|
||||||
|
redémarré manuellement par l'utilisateur si nécessaire.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
iface: Nom de l'interface WireGuard
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(success, message)
|
||||||
|
"""
|
||||||
|
config_file = f"/etc/systemd/resolved.conf.d/wgsecure-{iface}.conf"
|
||||||
|
|
||||||
|
# Supprimer le fichier de config (nécessite sudo)
|
||||||
|
import os
|
||||||
|
if not os.path.exists(config_file):
|
||||||
|
return True, ""
|
||||||
|
|
||||||
|
code, out, err = run_privileged(
|
||||||
|
["sudo", "rm", "-f", config_file],
|
||||||
|
timeout=10
|
||||||
|
)
|
||||||
|
|
||||||
|
if code != 0:
|
||||||
|
return False, f"Échec de la suppression du fichier split-DNS: {err or out}"
|
||||||
|
|
||||||
|
return True, "Configuration split-DNS supprimée (redémarrez systemd-resolved si nécessaire)"
|
||||||
|
|
||||||
|
|
||||||
|
def cleanup_split_dns(cfg: Config) -> tuple[bool, str]:
|
||||||
|
"""Supprime la configuration split-DNS pour Windows (NRPT) ou Linux.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cfg: Configuration contenant l'interface name
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(success, message)
|
||||||
|
"""
|
||||||
|
if is_windows():
|
||||||
|
name = cfg.wg.get("interface_name", "wgs0")
|
||||||
|
rule_name = f"{_SPLIT_DNS_RULE_PREFIX}{name}"
|
||||||
|
|
||||||
|
script = _nrpt_remove_split_dns_ps(rule_name)
|
||||||
|
|
||||||
|
# Exécuter avec élévation
|
||||||
|
code, out, err = run_privileged(
|
||||||
|
["powershell", "-NoProfile", "-NonInteractive", "-Command", script],
|
||||||
|
timeout=15
|
||||||
|
)
|
||||||
|
|
||||||
|
if code == 0:
|
||||||
|
return True, "Règle split-DNS (NRPT) supprimée"
|
||||||
|
else:
|
||||||
|
# Ignorer l'erreur si la règle n'existait pas
|
||||||
|
if "cannot find" in (err or out or "").lower() or "not found" in (err or out or "").lower():
|
||||||
|
return True, ""
|
||||||
|
return False, f"Échec de la suppression split-DNS (NRPT): {err or out or 'Erreur inconnue'}"
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Linux : supprimer le fichier de config systemd-resolved
|
||||||
|
name = cfg.wg.get("interface_name", "wgs0")
|
||||||
|
return _cleanup_split_dns_linux(name)
|
||||||
|
|
||||||
|
|
||||||
def _powershell(script: str, timeout: int = 20) -> tuple[int, str, str]:
|
def _powershell(script: str, timeout: int = 20) -> tuple[int, str, str]:
|
||||||
return run_command(
|
return run_command(
|
||||||
|
|||||||
+23
-3
@@ -61,9 +61,20 @@ def build_client_config(cfg: Config) -> str:
|
|||||||
# `wg-quick up` en plein milieu de son exécution : l'interface est déjà
|
# `wg-quick up` en plein milieu de son exécution : l'interface est déjà
|
||||||
# créée, le trap de nettoyage laisse un état partiel. Mieux vaut monter le
|
# créée, le trap de nettoyage laisse un état partiel. Mieux vaut monter le
|
||||||
# tunnel sans toucher au DNS système.
|
# tunnel sans toucher au DNS système.
|
||||||
dns_value = (wg.get("dns") or "").strip()
|
|
||||||
if dns_value and dns_util.has_dns_backend():
|
# Split-DNS : si configuré, on ne met pas de DNS dans la config WireGuard
|
||||||
lines.append(f"DNS = {dns_value}")
|
# car les règles NRPT (Windows) ou systemd-resolved (Linux) géreront le routage.
|
||||||
|
split_dns_server = (wg.get("split_dns_server") or "").strip()
|
||||||
|
split_dns_domains = wg.get("split_dns_domains") or []
|
||||||
|
|
||||||
|
if split_dns_server and split_dns_domains and is_windows():
|
||||||
|
# Sous Windows avec split-DNS : pas de DNS dans la config, tout sera géré par NRPT
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
# Cas normal : utiliser le DNS principal
|
||||||
|
dns_value = (wg.get("dns") or "").strip()
|
||||||
|
if dns_value and dns_util.has_dns_backend():
|
||||||
|
lines.append(f"DNS = {dns_value}")
|
||||||
mtu = int(wg.get("mtu") or 0)
|
mtu = int(wg.get("mtu") or 0)
|
||||||
if mtu:
|
if mtu:
|
||||||
lines.append(f"MTU = {mtu}")
|
lines.append(f"MTU = {mtu}")
|
||||||
@@ -222,6 +233,8 @@ def force_cleanup(cfg: Config) -> list[str]:
|
|||||||
if dns_util.interface_exists(name):
|
if dns_util.interface_exists(name):
|
||||||
cmd = [wireguard_exe(), "/uninstalltunnelservice", name]
|
cmd = [wireguard_exe(), "/uninstalltunnelservice", name]
|
||||||
run_command(cmd, timeout=60)
|
run_command(cmd, timeout=60)
|
||||||
|
# Nettoyer les règles split-DNS
|
||||||
|
dns_util.cleanup_split_dns(cfg)
|
||||||
actions += dns_util.cleanup(name)
|
actions += dns_util.cleanup(name)
|
||||||
return actions
|
return actions
|
||||||
|
|
||||||
@@ -237,6 +250,9 @@ def force_cleanup(cfg: Config) -> list[str]:
|
|||||||
# ne disparaît pas avec le lien. C'est la cause du « plus d'internet ».
|
# ne disparaît pas avec le lien. C'est la cause du « plus d'internet ».
|
||||||
# Sans effet, et sans dialogue d'authentification, si rien ne traîne.
|
# Sans effet, et sans dialogue d'authentification, si rien ne traîne.
|
||||||
actions += dns_util.cleanup(name)
|
actions += dns_util.cleanup(name)
|
||||||
|
|
||||||
|
# Nettoyer les règles split-DNS
|
||||||
|
dns_util.cleanup_split_dns(cfg)
|
||||||
|
|
||||||
return actions
|
return actions
|
||||||
|
|
||||||
@@ -286,6 +302,8 @@ def connect(cfg: Config) -> tuple[bool, str]:
|
|||||||
# démarrage effectif du tunnel est asynchrone. Tester l'état
|
# démarrage effectif du tunnel est asynchrone. Tester l'état
|
||||||
# immédiatement conclurait « échec » sur un tunnel qui monte.
|
# immédiatement conclurait « échec » sur un tunnel qui monte.
|
||||||
if code == 0 and _wait_connected(cfg, timeout=15):
|
if code == 0 and _wait_connected(cfg, timeout=15):
|
||||||
|
# Configurer le split-DNS si un serveur dédié est défini
|
||||||
|
dns_util.setup_split_dns(cfg)
|
||||||
return True, "Tunnel WireGuard activé"
|
return True, "Tunnel WireGuard activé"
|
||||||
force_cleanup(cfg)
|
force_cleanup(cfg)
|
||||||
return False, err or f"Échec de l'installation du tunnel (code {code})"
|
return False, err or f"Échec de l'installation du tunnel (code {code})"
|
||||||
@@ -302,6 +320,8 @@ def connect(cfg: Config) -> tuple[bool, str]:
|
|||||||
# Passer le nom d'interface (pas le chemin) : AppArmor autorise /etc/wireguard/ seulement
|
# Passer le nom d'interface (pas le chemin) : AppArmor autorise /etc/wireguard/ seulement
|
||||||
code, _, err = run_privileged(["wg-quick", "up", name], timeout=90)
|
code, _, err = run_privileged(["wg-quick", "up", name], timeout=90)
|
||||||
if code == 0:
|
if code == 0:
|
||||||
|
# Configurer le split-DNS si un serveur dédié est défini
|
||||||
|
dns_util.setup_split_dns(cfg)
|
||||||
return True, "Tunnel WireGuard activé"
|
return True, "Tunnel WireGuard activé"
|
||||||
|
|
||||||
# Échec : wg-quick a pu s'arrêter après set_dns (ou être tué par le
|
# Échec : wg-quick a pu s'arrêter après set_dns (ou être tué par le
|
||||||
|
|||||||
@@ -231,6 +231,10 @@ class AdminWindow(QDialog):
|
|||||||
|
|
||||||
self._srv_endpoint = QLineEdit()
|
self._srv_endpoint = QLineEdit()
|
||||||
self._srv_endpoint.setPlaceholderText("vpn.exemple.com ou 1.2.3.4")
|
self._srv_endpoint.setPlaceholderText("vpn.exemple.com ou 1.2.3.4")
|
||||||
|
self._srv_endpoint.setToolTip(
|
||||||
|
"Adresse IP ou nom d'hôte du serveur WireGuard. "
|
||||||
|
"Si vous utilisez un nom, assurez-vous qu'il est résolvable."
|
||||||
|
)
|
||||||
self._row(form, "Adresse serveur :", self._srv_endpoint)
|
self._row(form, "Adresse serveur :", self._srv_endpoint)
|
||||||
|
|
||||||
self._srv_port = QSpinBox()
|
self._srv_port = QSpinBox()
|
||||||
@@ -246,6 +250,10 @@ class AdminWindow(QDialog):
|
|||||||
|
|
||||||
self._srv_pubkey = QLineEdit()
|
self._srv_pubkey = QLineEdit()
|
||||||
self._srv_pubkey.setPlaceholderText("Clé publique du serveur (base64)")
|
self._srv_pubkey.setPlaceholderText("Clé publique du serveur (base64)")
|
||||||
|
self._srv_pubkey.setToolTip(
|
||||||
|
"Clé publique du serveur (format base64). "
|
||||||
|
"Doit correspondre exactement à la clé publique du serveur."
|
||||||
|
)
|
||||||
self._row(form, "Clé publique serveur :", self._srv_pubkey)
|
self._row(form, "Clé publique serveur :", self._srv_pubkey)
|
||||||
|
|
||||||
# Un champ vide ne disait pas si la PSK était oubliée ou volontairement
|
# Un champ vide ne disait pas si la PSK était oubliée ou volontairement
|
||||||
@@ -303,15 +311,44 @@ class AdminWindow(QDialog):
|
|||||||
|
|
||||||
self._iface_name = QLineEdit()
|
self._iface_name = QLineEdit()
|
||||||
self._iface_name.setPlaceholderText("wgs0")
|
self._iface_name.setPlaceholderText("wgs0")
|
||||||
|
self._iface_name.setToolTip(
|
||||||
|
"Nom de l'interface réseau WireGuard. "
|
||||||
|
"Doit être unique et correspondre au nom du service sous Windows."
|
||||||
|
)
|
||||||
self._row(form2, "Nom interface :", self._iface_name)
|
self._row(form2, "Nom interface :", self._iface_name)
|
||||||
|
|
||||||
self._client_addr = QLineEdit()
|
self._client_addr = QLineEdit()
|
||||||
self._client_addr.setPlaceholderText("10.8.0.2/24")
|
self._client_addr.setPlaceholderText("10.8.0.2/24")
|
||||||
|
self._client_addr.setToolTip(
|
||||||
|
"Adresse IP du client dans le réseau VPN. "
|
||||||
|
"Format: IP/Masque (ex: 10.8.0.2/24)."
|
||||||
|
)
|
||||||
self._row(form2, "Adresse IP client :", self._client_addr)
|
self._row(form2, "Adresse IP client :", self._client_addr)
|
||||||
|
|
||||||
self._dns = QLineEdit()
|
self._dns = QLineEdit()
|
||||||
self._dns.setPlaceholderText("1.1.1.1")
|
self._dns.setPlaceholderText("1.1.1.1")
|
||||||
|
self._dns.setToolTip(
|
||||||
|
"Serveur DNS principal utilisé par le tunnel. "
|
||||||
|
"Laisser vide si vous utilisez le split-DNS ou le DNS système."
|
||||||
|
)
|
||||||
self._row(form2, "DNS :", self._dns)
|
self._row(form2, "DNS :", self._dns)
|
||||||
|
|
||||||
|
# Split-DNS : serveur dédié pour les noms du réseau distant
|
||||||
|
self._split_dns_server = QLineEdit()
|
||||||
|
self._split_dns_server.setPlaceholderText("192.168.1.210")
|
||||||
|
self._split_dns_server.setToolTip(
|
||||||
|
"Serveur DNS dédié pour résoudre les noms du réseau distant. "
|
||||||
|
"Laisser vide pour utiliser le DNS par défaut."
|
||||||
|
)
|
||||||
|
self._row(form2, "DNS pour réseau distant :", self._split_dns_server)
|
||||||
|
|
||||||
|
self._split_dns_domains = QLineEdit()
|
||||||
|
self._split_dns_domains.setPlaceholderText("local, internal")
|
||||||
|
self._split_dns_domains.setToolTip(
|
||||||
|
"Domaines à résoudre via le DNS distant (séparés par des virgules). "
|
||||||
|
"Ex: local, internal. Laisser vide pour désactiver le split-DNS."
|
||||||
|
)
|
||||||
|
self._row(form2, "Domaines distants :", self._split_dns_domains)
|
||||||
|
|
||||||
self._allowed_ips = QLineEdit()
|
self._allowed_ips = QLineEdit()
|
||||||
self._allowed_ips.setPlaceholderText("10.8.0.0/24")
|
self._allowed_ips.setPlaceholderText("10.8.0.0/24")
|
||||||
@@ -325,6 +362,10 @@ class AdminWindow(QDialog):
|
|||||||
self._keepalive = QSpinBox()
|
self._keepalive = QSpinBox()
|
||||||
self._keepalive.setRange(0, 300)
|
self._keepalive.setRange(0, 300)
|
||||||
self._keepalive.setValue(25)
|
self._keepalive.setValue(25)
|
||||||
|
self._keepalive.setToolTip(
|
||||||
|
"Intervalle (en secondes) pour envoyer un paquet keepalive. "
|
||||||
|
"0 = désactivé. Utile pour maintenir la connexion active à travers un NAT."
|
||||||
|
)
|
||||||
self._keepalive.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
self._keepalive.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||||
self._row(form2, "Keepalive (s) :", self._keepalive)
|
self._row(form2, "Keepalive (s) :", self._keepalive)
|
||||||
|
|
||||||
@@ -358,6 +399,10 @@ class AdminWindow(QDialog):
|
|||||||
self._priv_key = QLineEdit()
|
self._priv_key = QLineEdit()
|
||||||
self._priv_key.setEchoMode(QLineEdit.EchoMode.Password)
|
self._priv_key.setEchoMode(QLineEdit.EchoMode.Password)
|
||||||
self._priv_key.setPlaceholderText("(générer ou coller)")
|
self._priv_key.setPlaceholderText("(générer ou coller)")
|
||||||
|
self._priv_key.setToolTip(
|
||||||
|
"Clé privée de ce client (format base64). "
|
||||||
|
"À garder SECRETE ! La clé publique est dérivée automatiquement."
|
||||||
|
)
|
||||||
self._priv_key.setFixedHeight(28)
|
self._priv_key.setFixedHeight(28)
|
||||||
show_priv = QPushButton("Afficher")
|
show_priv = QPushButton("Afficher")
|
||||||
show_priv.setFixedSize(70, 28)
|
show_priv.setFixedSize(70, 28)
|
||||||
@@ -1147,6 +1192,11 @@ class AdminWindow(QDialog):
|
|||||||
self._allowed_ips.setText(wg.get("allowed_ips", "0.0.0.0/0"))
|
self._allowed_ips.setText(wg.get("allowed_ips", "0.0.0.0/0"))
|
||||||
self._keepalive.setValue(int(wg.get("keepalive", 25)))
|
self._keepalive.setValue(int(wg.get("keepalive", 25)))
|
||||||
self._mtu.setValue(int(wg.get("mtu") or 0))
|
self._mtu.setValue(int(wg.get("mtu") or 0))
|
||||||
|
|
||||||
|
# Split-DNS
|
||||||
|
self._split_dns_server.setText(wg.get("split_dns_server", ""))
|
||||||
|
domains = wg.get("split_dns_domains") or []
|
||||||
|
self._split_dns_domains.setText(", ".join(domains) if isinstance(domains, list) else domains)
|
||||||
self._priv_key.setText(wg.get("client_private_key", ""))
|
self._priv_key.setText(wg.get("client_private_key", ""))
|
||||||
self._pub_key.setText(wg.get("client_public_key", ""))
|
self._pub_key.setText(wg.get("client_public_key", ""))
|
||||||
self._mfa_enabled_cb.setChecked(self._cfg.mfa_enabled)
|
self._mfa_enabled_cb.setChecked(self._cfg.mfa_enabled)
|
||||||
@@ -1166,6 +1216,12 @@ class AdminWindow(QDialog):
|
|||||||
self._cfg.set("wg", "mtu", self._mtu.value())
|
self._cfg.set("wg", "mtu", self._mtu.value())
|
||||||
self._cfg.set("wg", "client_private_key", self._priv_key.text().strip())
|
self._cfg.set("wg", "client_private_key", self._priv_key.text().strip())
|
||||||
self._cfg.set("wg", "client_public_key", self._pub_key.text().strip())
|
self._cfg.set("wg", "client_public_key", self._pub_key.text().strip())
|
||||||
|
|
||||||
|
# Split-DNS
|
||||||
|
self._cfg.set("wg", "split_dns_server", self._split_dns_server.text().strip())
|
||||||
|
domains_text = self._split_dns_domains.text().strip()
|
||||||
|
domains_list = [d.strip() for d in domains_text.split(",") if d.strip()] if domains_text else []
|
||||||
|
self._cfg.set("wg", "split_dns_domains", domains_list)
|
||||||
self._cfg.mfa_enabled = self._mfa_enabled_cb.isChecked()
|
self._cfg.mfa_enabled = self._mfa_enabled_cb.isChecked()
|
||||||
self._cfg.mfa_secret = self._mfa_secret.text().strip()
|
self._cfg.mfa_secret = self._mfa_secret.text().strip()
|
||||||
self._cfg.save()
|
self._cfg.save()
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
; ──────────────────────────────────────────────
|
; ──────────────────────────────────────────────
|
||||||
|
|
||||||
#define MyAppName "WGSecure"
|
#define MyAppName "WGSecure"
|
||||||
#define MyAppVersion "0.7.2"
|
#define MyAppVersion "0.7.3"
|
||||||
#define MyAppPublisher "WGSecure"
|
#define MyAppPublisher "WGSecure"
|
||||||
#define MyAppExeName "wgsecure.exe"
|
#define MyAppExeName "wgsecure.exe"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user