Files
WGSecure/Makefile
T
tuxgyver 5c53cf2500 chore(release): v0.7.4
Split-DNS repose désormais sur resolvectl par interface (Linux) et une
règle NRPT complète (Windows), avec diagnostic de résolution distante,
verrouillage des champs après import d'un .conf, et consolidation des
couleurs de l'UI dans theme.py.
2026-09-04 08:57:52 +02:00

312 lines
16 KiB
Makefile

## ──────────────────────────────────────────────
## WGSecure (WGS) — Makefile
## Compilation Linux & Windows via PyInstaller
## ──────────────────────────────────────────────
APP := wgsecure
VERSION := 0.7.4
VENV := .venv
PYTHON := $(VENV)/bin/python3
PIP := $(VENV)/bin/pip
SRC := main.py
DIST := dist
BUILD := build
ICON := /tmp/wgsecure_icon_build.png
# Options PyInstaller communes aux deux plateformes
PI_OPTS := \
--noconfirm \
--clean \
--name $(APP) \
--hidden-import pyotp \
--hidden-import qrcode \
--hidden-import qrcode.image.pil \
--hidden-import PIL \
--hidden-import PIL.Image \
--hidden-import cryptography \
--hidden-import cryptography.hazmat.primitives.asymmetric.x25519 \
--collect-submodules PyQt6
.PHONY: all linux windows installer-deps release version changelog 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 ────────────────────────────────────────────────────
# 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:
@$(PYTHON) -c "\
import sys; sys.path.insert(0,'.');\
from PyQt6.QtWidgets import QApplication; app=QApplication(sys.argv);\
from app.ui.icons import icon_app;\
pix=icon_app().pixmap(256,256); pix.save('$(ICON)','PNG');\
print(' Icône générée →', '$(ICON)')" 2>/dev/null
@test -f $(ICON) || (echo " Génération icône échouée"; exit 1)
# ── Binaire Linux ────────────────────────────────────────────────────────────
linux: install icon
@echo ""
@echo " 🐧 Compilation Linux (PyInstaller onefile)…"
@echo ""
$(PYTHON) -m PyInstaller $(PI_OPTS) \
--onefile \
--icon=$(ICON) \
--add-data "CHANGELOG.md:." \
$(SRC)
@echo ""
@echo " ✅ Binaire Linux → $(DIST)/$(APP)"
@echo ""
# ── Binaire Windows (via Wine + Python Windows) ──────────────────────────────
# Le hook PyQt6 de pyinstaller-hooks-contrib introspecte QLibraryInfo pour
# localiser Qt6/plugins/platforms au moment du build : sous Wine cette
# introspection échoue silencieusement, donc qwindows.dll n'est jamais copié
# et l'exe plante au lancement avec « no Qt platform plugin could be
# initialized ». On force sa copie via --add-data.
WINE_QT_PLUGINS := $(HOME)/.wine/drive_c/users/$(USER)/AppData/Local/Programs/Python/Python311/Lib/site-packages/PyQt6/Qt6/plugins
windows: icon
@echo ""
@echo " 🪟 Compilation Windows (Wine + PyInstaller)…"
@echo ""
@which wine > /dev/null 2>&1 \
|| (echo " ❌ Wine non installé → sudo apt install wine"; exit 1)
@test -f "$(WINE_QT_PLUGINS)/platforms/qwindows.dll" \
|| (echo " ❌ qwindows.dll introuvable dans $(WINE_QT_PLUGINS)/platforms"; \
echo " → Installez PyQt6 dans Wine avec : make setup-wine"; exit 1)
@wine $(WINE_PYTHON) -m PyInstaller $(PI_OPTS) \
--onefile \
--windowed \
--icon=$(ICON) \
--add-data "$(WINE_QT_PLUGINS)/platforms;PyQt6/Qt6/plugins/platforms" \
--add-data "$(WINE_QT_PLUGINS)/styles;PyQt6/Qt6/plugins/styles" \
--add-data "CHANGELOG.md;." \
$(SRC) \
|| (echo ""; \
echo " ❌ Échec : Python Windows introuvable dans Wine."; \
echo " → Installez-le avec : make setup-wine"; \
echo ""; exit 1)
@echo ""
@echo " ✅ Binaire Windows → $(DIST)/$(APP).exe"
@echo ""
# ── Prérequis de l'installeur Windows (wgsecure.iss) ─────────────────────────
# WireGuard n'est pas empaqueté avec WGSecure (c'est wg.exe et le pilote
# tunnel qui manquent, pas une simple DLL) : wgsecure.iss l'installe donc en
# silencieux à partir de ce binaire, téléchargé ici une bonne fois pour
# toutes plutôt que d'imposer une étape manuelle avant chaque compilation.
WIREGUARD_URL := https://download.wireguard.com/windows-client/wireguard-installer.exe
VCREDIST_URL := https://aka.ms/vs/17/release/vc_redist.x64.exe
installer/wireguard-installer.exe:
@echo " ⬇️ Téléchargement de l'installeur WireGuard…"
@curl -L --fail -o $@ $(WIREGUARD_URL)
installer/VC_redist.x64.exe:
@echo " ⬇️ Téléchargement du Visual C++ Redistributable…"
@curl -L --fail -o $@ $(VCREDIST_URL)
installer-deps: installer/wireguard-installer.exe installer/VC_redist.x64.exe
@echo " ✅ Prérequis de l'installeur prêts dans installer/"
# ── Installation de Python dans Wine ─────────────────────────────────────────
WINE_PY_VER := 3.11.9
WINE_PY_URL := https://www.python.org/ftp/python/$(WINE_PY_VER)/python-$(WINE_PY_VER)-amd64.exe
WINE_PY_EXE := /tmp/python-$(WINE_PY_VER)-amd64.exe
WINE_PYTHON := $(HOME)/.wine/drive_c/users/$(USER)/AppData/Local/Programs/Python/Python311/python.exe
setup-wine:
@echo " 📦 Installation Python $(WINE_PY_VER) Windows dans Wine…"
@which wine > /dev/null 2>&1 \
|| (echo " ❌ wine non installé → sudo apt install wine"; exit 1)
@test -f $(WINE_PY_EXE) \
|| (echo " ⬇️ Téléchargement Python Windows…" \
&& curl -L -o $(WINE_PY_EXE) $(WINE_PY_URL))
@echo " 🔧 Installation silencieuse…"
wine $(WINE_PY_EXE) /quiet InstallAllUsers=0 PrependPath=1
@echo " 📦 Installation des dépendances…"
wine $(WINE_PYTHON) -m pip install --upgrade pip
wine $(WINE_PYTHON) -m pip install -r requirements.txt
wine $(WINE_PYTHON) -m pip install pyinstaller
@echo " ✅ Python Windows prêt dans Wine"
# ── Release (Linux renommé avec version) ─────────────────────────────────────
release: clean linux
@mv $(DIST)/$(APP) $(DIST)/$(APP)-$(VERSION)-linux-x86_64
@echo " 📦 Release → $(DIST)/$(APP)-$(VERSION)-linux-x86_64"
# ── Version courante ──────────────────────────────────────────────────────────
version:
@echo "$(VERSION)"
# ── Dernière entrée du changelog ─────────────────────────────────────────────
changelog:
@awk '/^## \[/{if(n++)exit} n' CHANGELOG.md
# ── Installation GNOME (utilisateur local, sans sudo) ────────────────────────
INSTALL_BIN := $(HOME)/.local/bin
INSTALL_ICON := $(HOME)/.local/share/icons/hicolor/256x256/apps
INSTALL_APPS := $(HOME)/.local/share/applications
install-gnome: linux
@echo ""
@echo " 🐧 Installation de WGSecure pour GNOME…"
@mkdir -p $(INSTALL_BIN) $(INSTALL_ICON) $(INSTALL_APPS)
@cp $(DIST)/$(APP) $(INSTALL_BIN)/$(APP)
@chmod +x $(INSTALL_BIN)/$(APP)
@echo " ✅ Binaire → $(INSTALL_BIN)/$(APP)"
@cp $(ICON) $(INSTALL_ICON)/$(APP).png
@echo " ✅ Icône → $(INSTALL_ICON)/$(APP).png"
@printf '[Desktop Entry]\nType=Application\nName=WGSecure\nComment=WireGuard GUI avec MFA TOTP\nExec=%s\nIcon=%s\nCategories=Network;Security;\nStartupWMClass=wgsecure\nTerminal=false\n' \
"$(INSTALL_BIN)/$(APP)" "$(APP)" \
> $(INSTALL_APPS)/$(APP).desktop
@echo " ✅ Lanceur → $(INSTALL_APPS)/$(APP).desktop"
@gtk-update-icon-cache -f -t $(HOME)/.local/share/icons/hicolor 2>/dev/null || true
@update-desktop-database $(INSTALL_APPS) 2>/dev/null || true
@echo ""
@echo " 🎉 Installation terminée. WGSecure est disponible dans le menu GNOME."
@echo ""
uninstall-gnome:
@echo " 🗑️ Désinstallation de WGSecure…"
@rm -f $(INSTALL_BIN)/$(APP)
@rm -f $(INSTALL_ICON)/$(APP).png
@rm -f $(INSTALL_APPS)/$(APP).desktop
@gtk-update-icon-cache -f -t $(HOME)/.local/share/icons/hicolor 2>/dev/null || true
@update-desktop-database $(INSTALL_APPS) 2>/dev/null || true
@echo " ✅ WGSecure désinstallé"
# ── Dépendances Python ────────────────────────────────────────────────────────
install: venv
$(PIP) install -r requirements.txt
$(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 ───────────────────────
# `pkexec` (utilisé jusqu'ici) exige un agent d'authentification polkit
# graphique : absent d'un terminal simple (SSH, tmux, session sans agent), il
# reste bloqué en attente d'une réponse qui n'arrive jamais. `sudo` n'a pas ce
# problème : lancé depuis un terminal, il y invite directement le mot de
# passe, avec ou sans polkit. La cible est idempotente : elle recalcule les
# règles à chaque lancement et n'invite pour un mot de passe que si le
# contenu déjà installé diffère (donc jamais lors d'une relance sans
# changement).
setup-sudoers:
@echo ""
@echo " 🔧 Configuration sudoers (wg-quick + nettoyage réseau, sans mot de passe)…"
@$(PYTHON) -c "\
import os, subprocess, tempfile, shutil; \
user = os.environ.get('SUDO_USER') or os.environ.get('USER') or os.path.basename(os.path.expanduser('~')); \
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('resolvectl') + ' dns *' if w('resolvectl') else '', \
w('resolvectl') + ' domain *' 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); \
dest = '/etc/sudoers.d/wgsecure'; \
current = subprocess.run(['sudo', '-n', 'cat', dest], capture_output=True, text=True); \
already_ok = current.returncode == 0 and current.stdout == rules; \
print(' ✅ Déjà configuré pour ' + user + ' — rien à faire.') if already_ok else None; \
import sys; sys.exit(0) if already_ok else None; \
fd, tmp = tempfile.mkstemp(prefix='wgsecure_sudoers_'); \
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(['sudo', 'install', '-m', '440', '-o', 'root', '-g', 'root', tmp, dest]) if ok else None; \
os.unlink(tmp); \
print(' ❌ Règles sudoers invalides, rien installé : ' + chk.stderr.decode().strip()) if not ok else \
print(' ✅ ' + dest + ' configuré pour ' + user + ' — plus de dialogue admin.' if r.returncode == 0 else ' ❌ Échec.')"
@echo ""
# ── Réinitialisation du mot de passe administrateur ──────────────────────────
reset-password:
@echo ""
@echo " ⚠️ Réinitialisation du mot de passe administrateur WGSecure…"
@$(PYTHON) -c "\
import sys; sys.path.insert(0,'.');\
from app.core.config import Config;\
cfg = Config();\
cfg.set_admin_password('');\
print(' ✅ Mot de passe supprimé — accès admin sans restriction au prochain démarrage.')"
@echo ""
# ── Lancer l'application ─────────────────────────────────────────────────────
run: venv
DISPLAY=:0 $(PYTHON) $(SRC)
run-admin: venv
DISPLAY=:0 $(PYTHON) $(SRC) --admin
# ── Nettoyage ────────────────────────────────────────────────────────────────
clean:
@echo " 🧹 Nettoyage…"
@rm -rf $(BUILD) $(DIST) *.spec
@find . -name "__pycache__" -type d -exec rm -rf {} + 2>/dev/null; true
@find . -name "*.pyc" -delete 2>/dev/null; true
@echo " Artefacts supprimés"
clean-all: clean
@rm -rf $(VENV)
@echo " Venv supprimé"
# ── Aide ─────────────────────────────────────────────────────────────────────
help:
@echo ""
@echo " ╔══════════════════════════════════════════════╗"
@echo " 🛡️ WGSecure v$(VERSION) Makefile "
@echo " ╠══════════════════════════════════════════════╣"
@echo " make install Installe les dépendances "
@echo " make linux Binaire Linux (onefile) "
@echo " make windows Binaire Windows (Wine) "
@echo " make installer-deps Prérequis wgsecure.iss "
@echo " make release Linux + nommage release "
@echo " make version Affiche la version "
@echo " make changelog Dernière entrée changelog║"
@echo " make setup-wine Python Windows dans Wine "
@echo " make install-gnome Installe dans GNOME "
@echo " make uninstall-gnome Désinstalle de GNOME "
@echo " make setup-sudoers wg-quick sans sudo "
@echo " make reset-password Supprime le MDP admin "
@echo " make run Lance l'application "
@echo " make run-admin Lance en mode admin "
@echo " make clean Supprime les artefacts "
@echo " ╚══════════════════════════════════════════════╝"
@echo ""