Les binaires ne peuvent pas venir du dépôt Gitea : il est privé, et un jeton
embarqué dans une application distribuée est un jeton donné à tous ses
utilisateurs. Ils viennent donc du serveur WGSecure, où le contrôle d'accès
existe déjà — l'API n'est joignable que par le tunnel, et on y parvient parce
que sa clé WireGuard est déclarée. Aucun secret à embarquer.
Le serveur expose GET /api/version, qui rend la version publiée et l'empreinte
de chaque artefact, et /api/version/download/{clé} qui les sert. Le manifeste
est déposé par `make publish-updates`, avec les condensats calculés une fois à
la publication : les recalculer à chaque appel bloquerait l'API sur 125 Mo.
`ANNOUNCE=` permet d'annoncer les artefacts sous un autre numéro, pour exercer
la chaîne sans compiler une seconde version.
Rien ne s'installe sans un clic. La vérification est automatique — au montage
du tunnel puis toutes les demi-heures, car ne la faire qu'au montage laissait
un serveur momentanément injoignable annuler toute proposition pour la session
— mais le téléchargement attend le bouton. Cette application monte un VPN, et
une version défaillante qui se propagerait seule couperait l'accès d'un parc
entier sans que personne ne l'ait demandé.
L'artefact est vérifié contre son empreinte SHA-256 avant d'être mis en place,
et le fichier temporaire est détruit à la moindre anomalie. Le canal est déjà
authentifié par WireGuard ; le condensat couvre ce qu'il ne couvre pas — un
téléchargement tronqué, un disque plein, un artefact mal publié.
Sous Linux, le binaire est remplacé par renommage, sur le même système de
fichiers que sa destination. Un déplacement depuis /tmp se rabattait sur une
copie, donc sur une écriture dans l'exécutable en cours, que le noyau refuse
(ETXTBSY). Un renommage ne touche qu'une entrée de répertoire : l'ancien inode
reste vivant pour le processus, qui continue jusqu'à sa fermeture — et
l'application propose désormais de redémarrer plutôt que de le laisser deviner.
Sous Windows, l'installation passe par le service, qui tourne en LocalSystem et
écrit dans Program Files sans invite d'élévation. La commande `apply_update` ne
prend aucun paramètre : le service lit le manifeste, télécharge et vérifie
lui-même. Lui passer un fichier déjà téléchargé aurait donné à tout compte du
poste — le pipe est ouvert aux utilisateurs interactifs — le moyen de faire
exécuter ce qu'il veut avec les privilèges du système.
L'installeur orchestre le remplacement, ce qu'il ne faisait pas : les deux
exécutables à remplacer tournent au moment de la mise à jour, et Windows
verrouille l'image d'un processus vivant. Il ferme donc l'application par le
gestionnaire de redémarrage, arrête les tunnels et le service avant la copie —
en attendant la libération réelle du fichier, `sc stop` rendant la main avant
la fin de l'arrêt — et garde `restartreplace` en secours. Le service, lui,
lance l'installeur détaché et ne l'attend pas : il lui demande de se remplacer
lui-même.
Durcissement du service au passage. Le premier appelant devient propriétaire et
son SID est retenu ; ensuite seuls ce compte et les administrateurs sont
servis. Le pipe étant ouvert à tout utilisateur interactif, n'importe quel
compte du poste pouvait jusqu'ici couper le tunnel d'un autre ou poser une
règle NRPT valable pour toute la machine. Le contenu d'un `.conf` est également
vérifié — seul son chemin l'était — et les directives exécutables y sont
refusées.
Corrige enfin un défaut de compilation : Linux et Windows partageaient le
répertoire de travail de PyInstaller, que `--clean` vide au démarrage. Lancées
à la suite, les deux cibles effaçaient mutuellement leurs fichiers
intermédiaires, produisant un .exe gonflé de 44 Mo et un binaire Linux tronqué
dont l'archive ne se décompressait plus.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
🛡️ WGSecure (WGS)
🚀 v0.10.0 · 🐍 · 🪟🐧 · 🔐 Accès validé par le serveur · 🛡️ WireGuard
WGSecure est une interface graphique multiplateforme (Windows & Linux) pour gérer une connexion WireGuard dont l'accès aux ressources distantes est ouvert par un code à 6 chiffres validé par le serveur.
✨ Fonctionnalités
| # | Fonctionnalité | Détail |
|---|---|---|
| 🖥️ | Interface systray | Icône bouclier dans la barre de notification, connexion en un clic |
| 👤 | Mode User (défaut) | Connexion / déconnexion WireGuard, statut RX/TX en temps réel |
| 📊 | Graphique bande passante | Courbes RX/TX en direct sur les 30 dernières secondes |
| 📋 | Journal d'événements | Horodatage de chaque connexion, déconnexion, erreur |
| 🕘 | Historique des sessions | Durée, données transférées, serveur par session |
| ⚙️ | Mode Admin | Configuration complète, protégé par mot de passe |
| 🔑 | Génération de clés | Paire Curve25519 native (sans dépendance au binaire wg) |
| 👤 | Multi-profils | Sauvegarde / bascule entre profils, import/export .conf |
| 🛡️ | Configuration WireGuard | Serveur, port UDP, DNS, IP client, keepalive |
| 🔐 | Code d'accès serveur | Le tunnel monte en quarantaine ; le serveur ouvre l'accès après validation du code |
| 🔍 | Test DNS leak | Vérifie que le DNS passe bien par le tunnel |
| 🏓 | Ping en temps réel | Badge de latence dans l'interface principale |
| 🔄 | Auto-reconnexion | Surveillance et reconnexion automatique si le tunnel tombe — activée par défaut (15 s) |
| 📡 | Test de connexion | Handshake WireGuard réel, sans monter le tunnel, avec détection du filtrage sur port source |
| 🚀 | Démarrage automatique | Lancement au démarrage du système — activé par défaut |
| 1️⃣ | Instance unique | Un second lancement réveille l'instance déjà active plutôt que d'en ouvrir une seconde |
| 🪟🐧 | Cross-platform | Windows 10+ et Linux (wg-quick) |
📋 Prérequis
🐧 Linux
sudo apt install wireguard-tools # Debian/Ubuntu
sudo dnf install wireguard-tools # Fedora
🪟 Windows
- WireGuard for Windows installé
🐍 Python
Python >= 3.11
📦 Installation
git clone <repo>
cd WGSecure
pip install -r requirements.txt
🚀 Utilisation
Mode User (par défaut)
python main.py
# ou via Makefile :
make run
⚙️ Mode Administrateur (configuration)
python main.py --admin
# ou via Makefile :
make run-admin
Sans systray
python main.py --no-tray
🔧 Makefile
| Commande | Description |
|---|---|
make install |
Installe les dépendances Python |
make linux |
Compile un binaire Linux autonome (PyInstaller) |
make windows |
Compile un binaire Windows via Wine |
make release |
Binaire Linux renommé avec la version |
make install-gnome |
Installe dans le menu GNOME (~/.local/) |
make uninstall-gnome |
Désinstalle du menu GNOME |
make reset-password |
Supprime le mot de passe administrateur |
make run |
Lance l'application |
make run-admin |
Lance en mode administrateur |
make clean |
Supprime les artefacts de build |
🗺️ Workflow recommandé
- 🚀 Lancer
python main.py --admin - 🛡️ Onglet WireGuard : renseigner l'adresse, le port et la clé publique du serveur
- 🔑 Onglet Clés : générer une paire de clés → copier la clé publique sur le serveur
- 🔐 Onglet WireGuard : renseigner l'identifiant VPN connu du serveur (le QR code TOTP est délivré par le serveur, pas par ce client)
- 🔒 Onglet Sécurité : définir un mot de passe pour l'accès admin
- ✅ Enregistrer → quitter → lancer
python main.pyen mode user
🐧 Élévation de privilèges Linux
wg-quick, la lecture de l'état du tunnel (wg show) et le nettoyage réseau
(interface orpheline, DNS) nécessitent les droits root. WGSecure tente dans
l'ordre :
- sudo -n — silencieux, fonctionne si une règle NOPASSWD est en place (voir ci-dessous)
- sudo -A avec programme askpass graphique (
ssh-askpass, etc.) - pkexec (polkit) — dialogue graphique GNOME/KDE ; nécessite un agent d'authentification actif, absent d'un simple terminal (SSH, tmux)
Le sondage périodique de l'état du tunnel (toutes les 3 s) ne tente que l'étape 1 : sans règle NOPASSWD, aucun dialogue n'apparaît, le handshake et les compteurs RX/TX restent simplement vides.
Supprimer la demande de mot de passe (recommandé)
make setup-sudoers
Invite le mot de passe une fois dans le terminal (via sudo, plus fiable que
pkexec hors session graphique), puis installe une règle NOPASSWD limitée à
wg-quick, wg, et au nettoyage d'une interface/DNS orpheline — jamais un
accès root généralisé. Idempotent : une relance ne redemande le mot de passe
que si le contenu a changé. make check-privileges affiche l'état courant.
Pour le faire à la main :
sudo visudo -f /etc/sudoers.d/wgsecure
<utilisateur> ALL=(ALL) NOPASSWD: /usr/bin/wg-quick
<utilisateur> ALL=(ALL) NOPASSWD: /usr/bin/wg
<utilisateur> ALL=(ALL) NOPASSWD: /usr/sbin/ip link delete dev *
<utilisateur> ALL=(ALL) NOPASSWD: /usr/sbin/resolvconf -d *
<utilisateur> ALL=(ALL) NOPASSWD: /usr/bin/resolvectl revert *
<utilisateur> ALL=(ALL) NOPASSWD: /usr/bin/resolvectl dns *
<utilisateur> ALL=(ALL) NOPASSWD: /usr/bin/resolvectl domain *
<utilisateur> ALL=(ALL) NOPASSWD: /usr/bin/install -D -m 600 -o root -g root * /etc/wireguard/*
Réinitialiser le mot de passe admin oublié
make reset-password
🏗️ Architecture
WGSecure/
├── main.py # 🚀 Point d'entrée
├── requirements.txt
├── Makefile
├── app/
│ ├── __init__.py # Version
│ ├── app_info.py # ℹ️ Contenu de la page « À propos »
│ ├── core/
│ │ ├── config.py # 💾 Configuration JSON persistante + bcrypt
│ │ ├── wireguard.py # 🛡️ Clés, tunnel, statut, DNS leak
│ │ ├── wg_handshake.py # 🤝 Sonde de handshake réel, sans monter le tunnel
│ │ ├── single_instance.py # 1️⃣ Garde une seule instance active
│ │ ├── history.py # 🕘 Historique des sessions (sessions.json)
│ │ ├── log.py # 📋 Journal d'événements (events.json)
│ │ └── vpn_session.py # 🔐 Validation du code auprès du serveur
│ ├── ui/
│ │ ├── icons.py # 🎨 Icônes bouclier générées par QPainter
│ │ ├── main_window.py # 👤 Fenêtre principale (mode user)
│ │ ├── admin_window.py # ⚙️ Panneau admin (onglets, thème sombre)
│ │ ├── bw_graph.py # 📊 Widget graphique bande passante
│ │ ├── history_dialog.py # 🕘 Dialogue historique des sessions
│ │ ├── access_code_dialog.py # 🔐 Saisie du code d'accès
│ │ └── systray.py # 🖥️ Icône systray + menu contextuel
│ └── utils/
│ └── platform_utils.py # 🔧 Détection OS, élévation de privilèges
🔒 Sécurité
| Élément | Protection |
|---|---|
| 🔑 Mot de passe admin | bcrypt (rounds=12) + sel intégré — migration transparente depuis SHA-256 |
| 🗝️ Clé privée WireGuard | ~/.wgsecure/config.json (Linux) / %APPDATA%\WGSecure\ (Windows) |
| 🔐 Secret TOTP | Jamais sur le poste client — détenu par le serveur, qui seul valide le code et ouvre l'accès réseau |
📄 Fichier .conf WireGuard |
Permissions 0600 (lecture propriétaire uniquement) |
⚠️ Recommandation : chiffrez votre disque pour protéger le fichier de configuration.
👤 Auteur
Développé par Johnny — JT-Tools
Version : — Septembre 2026