fix: DNS coupée après arrêt du tunnel, gel UI, dialogues sudo à répétition
- wg-quick down échoue silencieusement si l'interface a déjà disparu (crash, kill, veille) et ne retire donc jamais l'entrée DNS posée par wg-quick up : la résolution reste pointée sur un résolveur mort. Ajout d'un nettoyage DNS inconditionnel (app/core/dns.py) déclenché à la connexion, la déconnexion, la fermeture et au démarrage suivant. - is_connected() se basait sur `wg show`, qui rend un code 0 même en cas d'échec de permission pour un utilisateur non-root : l'app se croyait déconnectée en permanence et ne démontait donc jamais le tunnel à la fermeture. Détection réécrite via sysfs (sans privilèges). - Connexion/déconnexion et ping tournaient sur le thread Qt : gel de l'UI pendant les élévations de privilèges. Déportés dans des QThread (app/ui/worker.py). - Restauration réseau garantie sur toutes les sorties (croix, tray, SIGINT/SIGTERM, atexit, exception) via app/core/shutdown.py. - run_privileged() : la détection "sudo veut un mot de passe" ratait en session non-anglophone et sur les comptes ALL=(ALL) ALL ; réécrite pour se fier au message d'erreur réel plutôt qu'à `sudo -l`. - Écriture de la config WireGuard dans /etc/wireguard (root:root 0700) passe maintenant par install -D via la chaîne de privilèges complète au lieu d'un tee non autorisé par les sudoers. - Ajout d'un diagnostic des droits (make check-privileges, bouton Admin) et mise à jour de setup-sudoers pour couvrir toutes les commandes désormais utilisées.
This commit is contained in:
@@ -27,17 +27,29 @@ PI_OPTS := \
|
||||
--hidden-import cryptography.hazmat.primitives.asymmetric.x25519 \
|
||||
--collect-submodules PyQt6
|
||||
|
||||
.PHONY: all linux windows release install install-gnome uninstall-gnome run run-admin icon reset-password setup-sudoers clean clean-all help venv
|
||||
.PHONY: all linux windows release install install-gnome uninstall-gnome run run-admin icon reset-password setup-sudoers check-privileges clean clean-all help venv
|
||||
|
||||
# ── Cible par défaut ────────────────────────────────────────────────────────
|
||||
all: linux
|
||||
|
||||
# ── Environnement virtuel ────────────────────────────────────────────────────
|
||||
venv: $(VENV)/bin/python3
|
||||
|
||||
$(VENV)/bin/python3:
|
||||
python3 -m venv $(VENV)
|
||||
$(PIP) install --upgrade pip --quiet
|
||||
# Le venv est reconstruit si la version de Python du système a changé : un venv
|
||||
# créé pour python3.13 pointe vers .venv/lib/python3.13/site-packages, que
|
||||
# python3.14 n'ouvre pas. L'interpréteur démarrait alors sans aucune de ses
|
||||
# dépendances (« No module named PyQt6 ») sans que make ne le remarque.
|
||||
venv:
|
||||
@if [ -x $(PYTHON) ] && \
|
||||
[ "$$($(PYTHON) -c 'import sys;print("%d.%d"%sys.version_info[:2])' 2>/dev/null)" \
|
||||
= "$$(python3 -c 'import sys;print("%d.%d"%sys.version_info[:2])')" ] && \
|
||||
$(PYTHON) -c 'import PyQt6' >/dev/null 2>&1; then \
|
||||
echo " ✅ Environnement virtuel valide ($(VENV))"; \
|
||||
else \
|
||||
echo " ♻️ (Re)création de l'environnement virtuel $(VENV)…"; \
|
||||
rm -rf $(VENV); \
|
||||
python3 -m venv $(VENV); \
|
||||
$(PIP) install --upgrade pip --quiet; \
|
||||
$(PIP) install -r requirements.txt --quiet; \
|
||||
fi
|
||||
|
||||
# ── Génération de l'icône PNG (utilisée par PyInstaller) ────────────────────
|
||||
icon:
|
||||
@@ -147,23 +159,43 @@ install: venv
|
||||
$(PIP) install pyinstaller
|
||||
@echo " ✅ Dépendances installées dans $(VENV)"
|
||||
|
||||
# ── Diagnostic des droits ────────────────────────────────────────────────────
|
||||
check-privileges:
|
||||
@echo ""
|
||||
@echo " 🔐 Droits requis par WGSecure :"
|
||||
@$(PYTHON) -c "\
|
||||
import sys; sys.path.insert(0, '.'); \
|
||||
from app.utils.platform_utils import privilege_report; \
|
||||
rows = privilege_report(); \
|
||||
print(' (exécution en root : aucun droit à configurer)') if not rows else None; \
|
||||
[print(' ' + ('✅' if ok else '⚠️ ') + ' ' + name.ljust(12) + ('sans mot de passe' if ok else 'dialogue polkit à chaque appel').ljust(32) + '— ' + role) for name, ok, role in rows]; \
|
||||
print() if not rows else print(chr(10) + ' ⚠️ → lancez make setup-sudoers pour supprimer les dialogues.' if any(not ok for _, ok, _ in rows) else chr(10) + ' ✅ Tout est configuré.')"
|
||||
@echo ""
|
||||
|
||||
# ── Droits sudo wg-quick sans dialogue de mot de passe ───────────────────────
|
||||
setup-sudoers:
|
||||
@echo ""
|
||||
@echo " 🔧 Configuration sudoers pour wg-quick (sans mot de passe)…"
|
||||
@echo " 🔧 Configuration sudoers (wg-quick + nettoyage réseau, sans mot de passe)…"
|
||||
@$(PYTHON) -c "\
|
||||
import os, subprocess; \
|
||||
import os, subprocess, tempfile, shutil; \
|
||||
user = os.environ.get('USER', os.path.basename(os.path.expanduser('~'))); \
|
||||
rules = ( \
|
||||
user + ' ALL=(ALL) NOPASSWD: /usr/bin/wg-quick\n' \
|
||||
+ user + ' ALL=(ALL) NOPASSWD: /usr/bin/tee /etc/wireguard/*\n' \
|
||||
+ user + ' ALL=(ALL) NOPASSWD: /bin/chmod 600 /etc/wireguard/*\n' \
|
||||
+ user + ' ALL=(ALL) NOPASSWD: /usr/bin/chmod 600 /etc/wireguard/*\n' \
|
||||
); \
|
||||
tmp = '/tmp/wgsecure_sudoers_tmp'; \
|
||||
open(tmp, 'w').write(rules); \
|
||||
r = subprocess.run(['pkexec', 'bash', '-c', 'cp ' + tmp + ' /etc/sudoers.d/wgsecure && chmod 440 /etc/sudoers.d/wgsecure']); \
|
||||
w = lambda n: shutil.which(n) or ''; \
|
||||
cmds = [c for c in ( \
|
||||
w('wg-quick'), \
|
||||
w('wg'), \
|
||||
w('ip') + ' link delete dev *' if w('ip') else '', \
|
||||
w('resolvconf') + ' -d *' if w('resolvconf') else '', \
|
||||
w('resolvectl') + ' revert *' if w('resolvectl') else '', \
|
||||
w('install') + ' -D -m 600 -o root -g root * /etc/wireguard/*' if w('install') else '', \
|
||||
) if c]; \
|
||||
rules = ''.join(user + ' ALL=(ALL) NOPASSWD: ' + c + chr(10) for c in cmds); \
|
||||
fd, tmp = tempfile.mkstemp(prefix='wgsecure_sudoers_', dir=os.path.expanduser('~')); \
|
||||
os.write(fd, rules.encode()); os.close(fd); os.chmod(tmp, 0o600); \
|
||||
chk = subprocess.run(['visudo', '-cqf', tmp], capture_output=True); \
|
||||
ok = chk.returncode == 0; \
|
||||
r = subprocess.run(['pkexec', 'bash', '-c', 'install -m 440 -o root -g root ' + tmp + ' /etc/sudoers.d/wgsecure']) if ok else None; \
|
||||
os.unlink(tmp); \
|
||||
print(' ❌ Règles sudoers invalides, rien installé : ' + chk.stderr.decode().strip()) if not ok else \
|
||||
print(' ✅ /etc/sudoers.d/wgsecure configuré — plus de dialogue admin.' if r.returncode == 0 else ' ❌ Échec.')"
|
||||
@echo ""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user