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:
+52
-1
@@ -458,6 +458,36 @@ def _local_route_source(ip: str, port: int) -> str:
|
||||
return ""
|
||||
|
||||
|
||||
def _next_hop(ip: str) -> str:
|
||||
"""Passerelle réellement empruntée pour joindre `ip`, "" si lien direct.
|
||||
|
||||
Une plage « IPs autorisées » qui contient cette passerelle est fatale :
|
||||
`wg-quick` pose sa route avec une métrique de 0, elle bat la route de lien
|
||||
de l'interface physique, la passerelle devient joignable uniquement par le
|
||||
tunnel — et les paquets de handshake, qui doivent sortir *par* elle, ne
|
||||
quittent plus jamais la machine.
|
||||
"""
|
||||
if is_windows():
|
||||
code, out, _ = run_command(
|
||||
["powershell", "-NoProfile", "-Command",
|
||||
f"(Find-NetRoute -RemoteIPAddress {ip}).NextHop"], 10)
|
||||
if code != 0:
|
||||
return ""
|
||||
for line in out.splitlines():
|
||||
hop = line.strip()
|
||||
if hop and hop not in ("0.0.0.0", "::"):
|
||||
return hop
|
||||
return ""
|
||||
|
||||
code, out, _ = run_command(["ip", "route", "get", ip], 10)
|
||||
if code != 0:
|
||||
return ""
|
||||
parts = out.split()
|
||||
if "via" in parts:
|
||||
return parts[parts.index("via") + 1]
|
||||
return ""
|
||||
|
||||
|
||||
def _diag_config(cfg: Config) -> list[dict]:
|
||||
wg = cfg.wg
|
||||
steps: list[dict] = []
|
||||
@@ -569,7 +599,28 @@ def _diag_reachability(cfg: Config, timeout: int) -> tuple[list[dict], str]:
|
||||
except ValueError:
|
||||
local_addr = None
|
||||
clash = [str(n) for n in allowed if local_addr and local_addr in n]
|
||||
if clash:
|
||||
|
||||
# Cas mortel du lot : la passerelle par défaut tombe dans une plage
|
||||
# autorisée. `wg-quick` route la plage vers le tunnel avec une
|
||||
# métrique de 0, ce qui bat la route de lien de la carte physique ; la
|
||||
# passerelle n'est plus joignable que *par* le tunnel, alors que le
|
||||
# tunnel a besoin d'elle pour sortir. Le tunnel monte, l'interface
|
||||
# existe, et plus un seul paquet de handshake ne quitte la machine.
|
||||
hop = _next_hop(server_ip)
|
||||
try:
|
||||
hop_addr = ipaddress.ip_address(hop) if hop else None
|
||||
except ValueError:
|
||||
hop_addr = None
|
||||
hop_clash = [str(n) for n in allowed if hop_addr and hop_addr in n]
|
||||
if hop_clash:
|
||||
steps.append(_step(
|
||||
_FAIL, "Cohérence des « IPs autorisées »",
|
||||
f"La passerelle qui mène au serveur ({hop}) est incluse dans "
|
||||
f"{', '.join(hop_clash)} : dès que le tunnel monte, cette "
|
||||
f"plage est routée vers lui, la passerelle devient injoignable "
|
||||
f"et le handshake n'arrive jamais. Retirez "
|
||||
f"{', '.join(hop_clash)} des IPs autorisées."))
|
||||
elif clash:
|
||||
steps.append(_step(
|
||||
_WARN, "Cohérence des « IPs autorisées »",
|
||||
f"Votre réseau local ({src}) est couvert par {', '.join(clash)} : "
|
||||
|
||||
Reference in New Issue
Block a user