feat: option clé pré-partagée et refonte des onglets du panneau admin
La présence d'une clé pré-partagée se devinait au fait que le champ soit rempli. Un serveur qui n'en utilise pas — le cas par défaut de WireGuard — déclenchait donc un avertissement à chaque diagnostic, et une clé oubliée dans le champ finissait écrite dans la configuration alors que le serveur n'en attendait aucune, ce qui casse le handshake aussi sûrement qu'une clé manquante. Une case à cocher tranche désormais, et le diagnostic distingue les quatre états au lieu d'un avertissement unique. Le drapeau vaut None par défaut et non False : _merge() recopie le défaut dans toute configuration antérieure, et False aurait désactivé la PSK d'un tunnel qui fonctionnait. Refonte des onglets, qui passaient de sept à cinq : - WireGuard et Clés se chevauchaient — la clé publique du serveur se saisissait dans l'un pendant que l'assistant qui la produit vivait dans l'autre, et les quatre façons de sortir une configuration étaient réparties entre les deux. Fusionnés en sous-onglets Tunnel, Clés, Profils & fichiers. - Les trois tests s'empilaient sur une page unique où le rapport de diagnostic se retrouvait comprimé. Chacun a son sous-onglet. - MFA et Sécurité répondent à la même question et sont regroupés. - La section des droits parlait de sudo et de setup-sudoers sous Windows, où rien de tout cela n'existe, et concluait « privilèges root » sur un rapport simplement vide. Elle vérifie maintenant l'élévation UAC et WireGuard. Chaque onglet s'ouvre sur un encart expliquant ce qu'on y décide, et les couleurs — vingt-quatre valeurs en dur, cinq bandeaux sans rapport de teinte, des blocs de résultat au fond incohérent — passent par app/ui/theme.py.
This commit is contained in:
+11
-1
@@ -15,6 +15,16 @@ _WG_DEFAULT: dict[str, Any] = {
|
||||
"server_endpoint": "",
|
||||
"server_port": 51820,
|
||||
"server_public_key": "",
|
||||
# Drapeau explicite : une PSK vide ne veut pas dire « oubliée ». Sans lui,
|
||||
# le diagnostic criait au champ manquant sur tout serveur qui n'en utilise
|
||||
# pas — c'est-à-dire la configuration WireGuard par défaut.
|
||||
#
|
||||
# Le défaut est `None`, pas `False` : `_merge()` recopie le défaut dans
|
||||
# toute configuration antérieure au drapeau, et `False` aurait désactivé
|
||||
# la clé pré-partagée d'un tunnel qui fonctionnait. `None` signifie
|
||||
# « jamais renseigné » et laisse `uses_preshared_key()` déduire l'état de
|
||||
# la présence de la clé.
|
||||
"use_preshared_key": None,
|
||||
"preshared_key": "",
|
||||
"client_private_key": "",
|
||||
"client_public_key": "",
|
||||
@@ -26,7 +36,7 @@ _WG_DEFAULT: dict[str, Any] = {
|
||||
}
|
||||
|
||||
_DEFAULT: dict[str, Any] = {
|
||||
"version": "0.4.6",
|
||||
"version": "0.5.0",
|
||||
"admin_password_hash": "",
|
||||
"admin_salt": "",
|
||||
"mfa_enabled": False,
|
||||
|
||||
+61
-13
@@ -35,6 +35,19 @@ def generate_preshared_key() -> str:
|
||||
return base64.b64encode(os.urandom(32)).decode()
|
||||
|
||||
|
||||
def uses_preshared_key(wg: dict) -> bool:
|
||||
"""La configuration met-elle une clé pré-partagée en jeu ?
|
||||
|
||||
Le drapeau `use_preshared_key` fait foi. Une configuration importée avant
|
||||
son introduction n'a que la clé : une PSK non vide vaut alors activation,
|
||||
sinon un import de .conf perdrait sa PSK au premier enregistrement.
|
||||
"""
|
||||
if wg.get("use_preshared_key"):
|
||||
return True
|
||||
return bool(wg.get("use_preshared_key") is None
|
||||
and (wg.get("preshared_key") or "").strip())
|
||||
|
||||
|
||||
def build_client_config(cfg: Config) -> str:
|
||||
wg = cfg.wg
|
||||
lines = [
|
||||
@@ -62,8 +75,12 @@ def build_client_config(cfg: Config) -> str:
|
||||
# existe, l'application affiche « Connecté » — et le handshake n'aboutit
|
||||
# jamais. La PSK était générée par l'assistant puis perdue, ni écrite dans
|
||||
# la config client ni relue à l'import d'un .conf.
|
||||
#
|
||||
# Symétriquement, écrire une PSK que le serveur n'attend pas casse le
|
||||
# handshake tout aussi silencieusement : le drapeau tranche, la présence
|
||||
# d'un reliquat dans le champ ne suffit pas.
|
||||
psk = (wg.get("preshared_key") or "").strip()
|
||||
if psk:
|
||||
if psk and uses_preshared_key(wg):
|
||||
lines.append(f"PresharedKey = {psk}")
|
||||
lines += [
|
||||
f"AllowedIPs = {wg['allowed_ips']}",
|
||||
@@ -529,19 +546,36 @@ def _diag_config(cfg: Config) -> list[dict]:
|
||||
steps.append(_step(_OK, "Paire de clés du client",
|
||||
f"Clé publique déclarée au serveur : {derived[:20]}…"))
|
||||
|
||||
# L'ancien message avertissait sur toute PSK vide : sur un serveur qui
|
||||
# n'en utilise pas — le cas par défaut de WireGuard — le diagnostic
|
||||
# pointait un coupable inexistant à chaque exécution.
|
||||
psk = (wg.get("preshared_key") or "").strip()
|
||||
if psk:
|
||||
if not uses_preshared_key(wg):
|
||||
if psk:
|
||||
steps.append(_step(
|
||||
_WARN, "Clé pré-partagée",
|
||||
"Désactivée, mais une clé subsiste dans le champ : elle n'est "
|
||||
"pas écrite dans la configuration. Cochez l'option si le "
|
||||
"serveur l'attend."))
|
||||
else:
|
||||
steps.append(_step(
|
||||
_OK, "Clé pré-partagée",
|
||||
"Non utilisée — le serveur ne doit pas en déclarer une pour "
|
||||
"ce pair"))
|
||||
elif not psk:
|
||||
steps.append(_step(
|
||||
_FAIL, "Clé pré-partagée",
|
||||
"Option activée mais champ vide : le pair déclare une PSK au "
|
||||
"serveur sans en fournir. Renseignez-la, ou décochez l'option si "
|
||||
"le serveur n'en utilise pas."))
|
||||
else:
|
||||
try:
|
||||
valid = len(base64.b64decode(psk)) == 32
|
||||
except Exception:
|
||||
valid = False
|
||||
steps.append(_step(_OK if valid else _FAIL, "Clé pré-partagée",
|
||||
"Présente (doit être identique côté serveur)" if valid
|
||||
"Activée (doit être identique côté serveur)" if valid
|
||||
else "Format invalide : 32 octets en base64 attendus"))
|
||||
else:
|
||||
steps.append(_step(_WARN, "Clé pré-partagée",
|
||||
"Absente — à renseigner si le serveur en attend une, "
|
||||
"sinon le handshake n'aboutira jamais"))
|
||||
return steps
|
||||
|
||||
|
||||
@@ -685,12 +719,19 @@ def _diag_tunnel(cfg: Config) -> list[dict]:
|
||||
+ ("administrateur" if is_windows() else "root")))
|
||||
return steps
|
||||
if age < 0:
|
||||
if uses_preshared_key(wg):
|
||||
psk_cause = ("clé pré-partagée différente de celle du serveur (ou "
|
||||
"absente côté serveur)")
|
||||
else:
|
||||
psk_cause = ("clé pré-partagée attendue par le serveur alors que "
|
||||
"l'option est décochée ici")
|
||||
steps.append(_step(
|
||||
_FAIL, "Handshake",
|
||||
"Jamais négocié. Le tunnel est monté mais le serveur ne répond "
|
||||
"pas à nos paquets. Causes usuelles : clé publique du client "
|
||||
"absente côté serveur, clé publique du serveur erronée, clé "
|
||||
"pré-partagée manquante ou différente, port UDP bloqué."))
|
||||
f"absente côté serveur, clé publique du serveur erronée, "
|
||||
f"{psk_cause}, port UDP du serveur différent de celui configuré, "
|
||||
"port UDP bloqué en chemin."))
|
||||
return steps
|
||||
if age > 180:
|
||||
steps.append(_step(_WARN, "Handshake",
|
||||
@@ -811,8 +852,10 @@ def parse_conf_file(path: str) -> dict | None:
|
||||
result["server_public_key"] = val
|
||||
elif key == "PresharedKey":
|
||||
# Ignorée jusqu'ici : le .conf importé perdait sa PSK
|
||||
# et le tunnel ne négociait plus rien.
|
||||
# et le tunnel ne négociait plus rien. La présence de
|
||||
# la directive vaut activation de l'option.
|
||||
result["preshared_key"] = val
|
||||
result["use_preshared_key"] = True
|
||||
elif key == "Endpoint" and ":" in val:
|
||||
host, port = val.rsplit(":", 1)
|
||||
result["server_endpoint"] = host.strip("[]")
|
||||
@@ -964,14 +1007,19 @@ def generate_server_config(
|
||||
server_address: str = "10.8.0.1/24",
|
||||
client_address: str = "10.8.0.2/24",
|
||||
client_allowed_ips: str = "10.8.0.2/32",
|
||||
use_psk: bool = True,
|
||||
) -> dict:
|
||||
"""
|
||||
Génère une paire complète (server_conf, client_conf) avec de nouvelles clés.
|
||||
Retourne un dict avec server_priv/pub, client_priv/pub, server_conf, client_conf.
|
||||
|
||||
`use_psk=False` produit les deux configurations sans clé pré-partagée :
|
||||
l'assistant en imposait une des deux côtés, sans moyen de s'en passer.
|
||||
"""
|
||||
srv_priv, srv_pub = generate_keypair()
|
||||
cli_priv, cli_pub = generate_keypair()
|
||||
psk = generate_preshared_key()
|
||||
psk = generate_preshared_key() if use_psk else ""
|
||||
psk_line = [f"PresharedKey = {psk}"] if use_psk else []
|
||||
|
||||
server_conf = "\n".join([
|
||||
"[Interface]",
|
||||
@@ -984,7 +1032,7 @@ def generate_server_config(
|
||||
"# === Peer client ===",
|
||||
"[Peer]",
|
||||
f"PublicKey = {cli_pub}",
|
||||
f"PresharedKey = {psk}",
|
||||
*psk_line,
|
||||
f"AllowedIPs = {client_allowed_ips}",
|
||||
])
|
||||
|
||||
@@ -996,7 +1044,7 @@ def generate_server_config(
|
||||
"",
|
||||
"[Peer]",
|
||||
f"PublicKey = {srv_pub}",
|
||||
f"PresharedKey = {psk}",
|
||||
*psk_line,
|
||||
"AllowedIPs = 10.8.0.0/24",
|
||||
"Endpoint = <SERVER_IP>:" + str(server_port),
|
||||
"PersistentKeepalive = 25",
|
||||
|
||||
Reference in New Issue
Block a user