v0.7.0 : instance unique, handshake ré-affiché, sudoers fiabilisé, À propos refaite
- Instance unique : un second lancement réveille l'instance active. - Autostart et reconnexion auto activés par défaut (nouvelle installation). - Page « À propos » refaite en 5 onglets (contenu séparé dans app_info.py). - Corrige le handshake/RX-TX qui ne s'affichaient plus (wg show sans élévation). - Corrige le diagnostic sudo trompeur (sondes qui ne matchaient jamais les règles). - Corrige make setup-sudoers bloqué hors session graphique (pkexec -> sudo), idempotent. - Corrige le README : Kill Switch inexistant retiré, ordre d'élévation réel documenté.
This commit is contained in:
@@ -280,6 +280,23 @@ def run_privileged(cmd: list[str], timeout: int = 60) -> tuple[int, str, str]:
|
||||
)
|
||||
|
||||
|
||||
def run_privileged_readonly(cmd: list[str], timeout: int = 10) -> tuple[int, str, str]:
|
||||
"""Comme `run_privileged`, mais pour un sondage périodique en lecture seule.
|
||||
|
||||
N'ouvre jamais de dialogue (askpass/pkexec/UAC) : un sondage lancé toutes
|
||||
les quelques secondes (état du tunnel, handshake) rendrait l'application
|
||||
inutilisable si chaque appel pouvait faire apparaître une invite. Ne
|
||||
fonctionne donc que si le process est déjà root/administrateur, ou si la
|
||||
commande est couverte par une règle sudo NOPASSWD (`make setup-sudoers`).
|
||||
Échoue silencieusement sinon, comme un manque de privilèges ordinaire.
|
||||
"""
|
||||
if has_root_privileges():
|
||||
return run_command(cmd, timeout)
|
||||
if is_windows():
|
||||
return -1, "", "Élévation requise"
|
||||
return run_command(["sudo", "-n"] + cmd, timeout, env=_c_locale_env())
|
||||
|
||||
|
||||
def _find_askpass() -> str:
|
||||
"""Cherche un programme askpass graphique disponible."""
|
||||
candidates = [
|
||||
@@ -302,13 +319,39 @@ def _find_askpass() -> str:
|
||||
|
||||
|
||||
# Commandes que WGSecure lance avec des privilèges, et ce qu'elles servent.
|
||||
_PRIVILEGED_COMMANDS = (
|
||||
(["wg-quick", "--help"], "monter / démonter le tunnel"),
|
||||
(["install", "--version"], "écrire la config dans /etc/wireguard"),
|
||||
(["ip", "-V"], "supprimer une interface orpheline"),
|
||||
(["resolvconf", "--version"], "retirer l'entrée DNS du tunnel"),
|
||||
(["resolvectl", "--version"], "purger le DNS systemd-resolved"),
|
||||
)
|
||||
#
|
||||
# `make setup-sudoers` installe des règles NOPASSWD restreintes à des motifs
|
||||
# d'arguments précis (ex. `ip link delete dev *`), jamais à la commande nue.
|
||||
# Les sondes doivent donc reproduire un appel réel qui matche ce motif :
|
||||
# tester avec `--version`/`-V` interroge un appel que sudoers n'autorise
|
||||
# jamais, et affiche « dialogue à chaque appel » même quand tout est
|
||||
# correctement configuré.
|
||||
_PRIVILEGE_PROBE_IFACE = "wgsecure-privcheck0"
|
||||
|
||||
|
||||
def _install_probe_cmd() -> list[str]:
|
||||
"""Reproduit le motif `install -D -m 600 -o root -g root * /etc/wireguard/*`.
|
||||
|
||||
Écrit un fichier vide et sans conséquence (`.wgsecure-privcheck`, jamais
|
||||
lu par wg-quick) : c'est le seul moyen fiable de tester ce motif, `sudo
|
||||
-n -l` ne le pouvant pas (cf. `_sudo_refused_auth`).
|
||||
"""
|
||||
dest = os.path.join(get_wg_config_dir(), ".wgsecure-privcheck")
|
||||
return ["install", "-D", "-m", "600", "-o", "root", "-g", "root",
|
||||
"/dev/null", dest]
|
||||
|
||||
|
||||
def _privileged_commands() -> tuple[tuple[list[str], str], ...]:
|
||||
return (
|
||||
(["wg-quick", "--help"], "monter / démonter le tunnel"),
|
||||
(_install_probe_cmd(), "écrire la config dans /etc/wireguard"),
|
||||
(["ip", "link", "delete", "dev", _PRIVILEGE_PROBE_IFACE],
|
||||
"supprimer une interface orpheline"),
|
||||
(["resolvconf", "-d", _PRIVILEGE_PROBE_IFACE],
|
||||
"retirer l'entrée DNS du tunnel"),
|
||||
(["resolvectl", "revert", _PRIVILEGE_PROBE_IFACE],
|
||||
"purger le DNS systemd-resolved"),
|
||||
)
|
||||
|
||||
|
||||
def privilege_report() -> list[tuple[str, bool, str]]:
|
||||
@@ -322,7 +365,7 @@ def privilege_report() -> list[tuple[str, bool, str]]:
|
||||
report: list[tuple[str, bool, str]] = []
|
||||
if is_windows() or has_root_privileges():
|
||||
return report
|
||||
for probe, role in _PRIVILEGED_COMMANDS:
|
||||
for probe, role in _privileged_commands():
|
||||
if not _sh.which(probe[0]):
|
||||
continue
|
||||
code, _, err = run_command(["sudo", "-n"] + probe, 8, env=_c_locale_env())
|
||||
|
||||
Reference in New Issue
Block a user