fix: handshake bloqué par la passerelle dans les IPs autorisées

Quand la passerelle qui mène au serveur appartient à une plage des « IPs
autorisées », wg-quick pose sa route vers le tunnel avec une métrique de 0.
Elle bat la route de lien de la carte réseau : la passerelle n'est plus
joignable que par le tunnel, alors que le tunnel a besoin d'elle pour sortir.
Le tunnel montait, l'interface existait, et plus aucun paquet de handshake ne
quittait la machine.

Le diagnostic détecte désormais ce cas via _next_hop() (ip route get sous
Linux, Find-NetRoute sous Windows) et le classe en échec, au lieu du simple
avertissement sur l'imprimante et le NAS qui n'en disait pas la gravité.

Corrige aussi le rapport de test de connexion : un QLabel que la mise en page
comprimait à quelques lignes tronquées, remplacé par une zone de texte
défilante ; la fenêtre d'administration, figée à 700x600, devient
redimensionnable.
This commit is contained in:
tuxgyver
2026-09-01 13:35:56 +02:00
parent 9d785c00cc
commit f6c3622e1d
2 changed files with 116 additions and 28 deletions
+64 -27
View File
@@ -66,6 +66,16 @@ _TAB_CSS = """
"""
_RESULT_CSS = (
"QTextEdit { border: 1px solid rgba(255,255,255,0.12); border-radius: 6px;"
" padding: 8px; font-size: 12px; }"
"QScrollBar:vertical { background: transparent; width: 8px; border: none; }"
"QScrollBar::handle:vertical { background: #2e4057; border-radius: 4px; }"
"QScrollBar::add-line:vertical,"
"QScrollBar::sub-line:vertical { height: 0; }"
)
class AdminWindow(QDialog):
def __init__(self, config: Config, parent=None):
super().__init__(parent)
@@ -76,15 +86,24 @@ class AdminWindow(QDialog):
| Qt.WindowType.WindowTitleHint
| Qt.WindowType.WindowCloseButtonHint
)
self.setFixedSize(700, 600)
# Taille figée auparavant : le rapport de diagnostic, long d'une
# dizaine d'étapes détaillées, était comprimé en une bande illisible.
self.setMinimumSize(720, 620)
self.resize(880, 740)
self._build_ui()
self._load_values()
# ------------------------------------------------------------------ #
# Helper : page sombre avec bandeau coloré
# ------------------------------------------------------------------ #
def _dark_page(self, banner_text: str, banner_color: str):
"""Retourne (page_widget, body_layout) : fond sombre + bandeau."""
def _dark_page(self, banner_text: str, banner_color: str,
scroll: bool = False):
"""Retourne (page_widget, body_layout) : fond sombre + bandeau.
`scroll=True` place le corps dans une zone défilante : indispensable
pour les pages dont le contenu grandit à l'usage (rapport de
diagnostic), sinon les widgets se compriment jusqu'à l'illisible.
"""
w = QWidget()
w.setStyleSheet(_TAB_CSS)
outer = QVBoxLayout(w)
@@ -103,7 +122,25 @@ class AdminWindow(QDialog):
body_layout = QVBoxLayout(body)
body_layout.setContentsMargins(14, 14, 14, 14)
body_layout.setSpacing(10)
outer.addWidget(body)
if scroll:
from PyQt6.QtWidgets import QScrollArea
body.setStyleSheet("background: #1c2833;")
area = QScrollArea()
area.setWidgetResizable(True)
area.setStyleSheet(
"QScrollArea { border: none; background: #1c2833; }"
"QScrollBar:vertical { background: #1c2833; width: 8px;"
" border: none; }"
"QScrollBar::handle:vertical { background: #2e4057;"
" border-radius: 4px; }"
"QScrollBar::add-line:vertical,"
"QScrollBar::sub-line:vertical { height: 0; }"
)
area.setWidget(body)
outer.addWidget(area)
else:
outer.addWidget(body)
return w, body_layout
# ------------------------------------------------------------------ #
@@ -456,7 +493,8 @@ class AdminWindow(QDialog):
# Onglet Test connexion
# ------------------------------------------------------------------ #
def _tab_test(self) -> QWidget:
w, lay = self._dark_page("📡 Test de connectivité réseau", "#1a4a7a")
w, lay = self._dark_page("📡 Test de connectivité réseau", "#1a4a7a",
scroll=True)
# Test UDP serveur
grp1 = QGroupBox("Diagnostic de la chaîne de connexion")
@@ -474,20 +512,22 @@ class AdminWindow(QDialog):
)
btn_test.clicked.connect(self._run_test)
g1.addWidget(btn_test)
self._test_result = QLabel("")
self._test_result.setWordWrap(True)
self._test_result.setTextFormat(Qt.TextFormat.RichText)
self._test_result.setAlignment(
Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop
# QLabel auparavant : le rapport fait une dizaine d'étapes avec un
# détail par étape, et un QLabel se laisse écraser par le layout —
# le texte était rogné à quelques lignes illisibles. Une vue en
# lecture seule porte sa propre barre de défilement : le rapport reste
# entier quelle que soit la taille de la fenêtre.
self._test_result = QTextEdit()
self._test_result.setReadOnly(True)
self._test_result.setMinimumHeight(320)
self._test_result.setSizePolicy(
QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding
)
self._test_result.setTextInteractionFlags(
Qt.TextInteractionFlag.TextSelectableByMouse
)
self._test_result.setStyleSheet(
"color: white; padding: 8px; border-radius: 6px; font-size: 12px;"
)
g1.addWidget(self._test_result)
lay.addWidget(grp1)
self._test_result.setStyleSheet(_RESULT_CSS + "color: white;")
g1.addWidget(self._test_result, 1)
# Facteur d'étirement : l'espace vertical libre revient au rapport de
# diagnostic plutôt qu'au ressort de fin de page.
lay.addWidget(grp1, 1)
# Test DNS leak
grp2 = QGroupBox("🔍 Test de fuite DNS")
@@ -528,7 +568,6 @@ class AdminWindow(QDialog):
g3.addWidget(self._priv_result)
lay.addWidget(grp3)
lay.addStretch()
return w
def _refresh_profile_list(self):
@@ -705,7 +744,7 @@ class AdminWindow(QDialog):
name_lbl.setStyleSheet("color: white;")
layout.addWidget(name_lbl)
version_lbl = QLabel("Version 0.4.5")
version_lbl = QLabel("Version 0.4.6")
version_lbl.setAlignment(Qt.AlignmentFlag.AlignCenter)
version_lbl.setStyleSheet("color: rgba(255,255,255,0.7); font-size: 13px;")
layout.addWidget(version_lbl)
@@ -905,14 +944,14 @@ class AdminWindow(QDialog):
def _run_test(self):
from PyQt6.QtWidgets import QApplication
self._save_values()
self._test_result.setText("Diagnostic en cours…")
self._test_result.setStyleSheet(
"color: rgba(255,255,255,0.6); padding: 12px; border-radius: 6px; font-size: 13px;"
self._test_result.setHtml(
'<span style="color:rgba(255,255,255,0.6);">Diagnostic en cours…</span>'
)
self._test_result.setStyleSheet(_RESULT_CSS + "color: rgba(255,255,255,0.6);")
QApplication.processEvents()
steps = wg_core.diagnose_connection(self._cfg)
self._test_result.setText(self._format_diagnostic(steps))
self._test_result.setHtml(self._format_diagnostic(steps))
states = [s["state"] for s in steps]
if "fail" in states:
@@ -921,9 +960,7 @@ class AdminWindow(QDialog):
css = "background: rgba(241,196,15,0.15); color: #f7dc6f;"
else:
css = "background: rgba(39,174,96,0.20); color: #a9dfbf;"
self._test_result.setStyleSheet(
"padding: 12px; border-radius: 6px; font-size: 12px; " + css
)
self._test_result.setStyleSheet(_RESULT_CSS + "QTextEdit { " + css + " }")
def _format_diagnostic(self, steps: list[dict]) -> str:
"""Met en forme les étapes ; la première en échec porte le verdict."""