Parcourez les fichiers disponibles.Un clic, et c’est à vous.
#!/usr/bin/env bash # # install-wg-easy.sh # Installe / désinstalle / met à jour WireGuard + WG-Easy (v15, Docker) # sur Debian 13 (Trixie). Compatible VPS KVM (machine virtuelle) et LXC (conteneur). # Interface web en HTTP sur IP:PORT, pare-feu UFW. # # Usage : sudo bash install-wg-easy.sh [install|uninstall|update] # (sans argument : menu interactif) # # -E : le trap ERR s'applique aussi dans les fonctions set -Eeuo pipefail # -------------------------------------------------------------------------- # Constantes # -------------------------------------------------------------------------- SCRIPT_VERSION="1.3" INSTALL_DIR="/opt/wg-easy" INSTALL_CONF="$INSTALL_DIR/install.conf" WG_EASY_IMAGE="ghcr.io/wg-easy/wg-easy:15" SYSCTL_FILE="/etc/sysctl.d/99-wg-easy.conf" MODULES_FILE="/etc/modules-load.d/wg-easy.conf" # -------------------------------------------------------------------------- # Affichage # -------------------------------------------------------------------------- C_RESET="\e[0m"; C_RED="\e[31m"; C_GREEN="\e[32m"; C_YELLOW="\e[33m"; C_BLUE="\e[34m"; C_BOLD="\e[1m" info() { echo -e "${C_BLUE}[INFO]${C_RESET} $*"; } ok() { echo -e "${C_GREEN}[OK]${C_RESET} $*"; } warn() { echo -e "${C_YELLOW}[ATTENTION]${C_RESET} $*"; } die() { echo -e "${C_RED}[ERREUR]${C_RESET} $*" >&2; exit 1; } trap 'die "Échec à la ligne $LINENO. Opération interrompue."' ERR # Lecture depuis le terminal, même si le script est lancé via "curl | bash" ask() { # ask "Question" "défaut" -> renvoie la réponse dans $REPLY local prompt="$1" default="${2:-}" if [[ -n "$default" ]]; then read -r -p "$(echo -e "${C_BOLD}${prompt}${C_RESET} [${default}] : ")" REPLY < /dev/tty REPLY="${REPLY:-$default}" else read -r -p "$(echo -e "${C_BOLD}${prompt}${C_RESET} : ")" REPLY < /dev/tty fi } ask_yes_no() { # ask_yes_no "Question" "o|n" -> code retour 0 si oui local prompt="$1" default="${2:-o}" ask "$prompt (o/n)" "$default" [[ "${REPLY,,}" =~ ^(o|oui|y|yes)$ ]] } valid_port() { [[ "$1" =~ ^[0-9]+$ ]] && (( $1 >= 1 && $1 <= 65535 )) } # Règles de mot de passe de wg-easy v15 : 12 caractères min, majuscule, # minuscule, chiffre et caractère spécial. valid_password() { local p="$1" (( ${#p} >= 12 )) || return 1 [[ "$p" =~ [A-Z] ]] || return 1 [[ "$p" =~ [a-z] ]] || return 1 [[ "$p" =~ [0-9] ]] || return 1 [[ "$p" =~ [^A-Za-z0-9] ]] || return 1 # L'apostrophe casserait le fichier .env de Docker Compose [[ "$p" != *"'"* ]] || return 1 } show_banner() { if [[ -t 1 ]]; then clear || true; fi echo -e "${C_BLUE}${C_BOLD}" cat <<'EOF' _ _ ____ | | _ _ | | __ ___ | _ \ ___ __ __ | | | | | || |/ // __|| | | | / _ \\ \ / / | |___ | |_| || < \__ \| |_| || __/ \ V / |_____| \__,_||_|\_\|___/|____/ \___| \_/ EOF echo -e "${C_RESET}" echo " WireGuard + WG-Easy Installer" echo " Version ${SCRIPT_VERSION}" echo } # -------------------------------------------------------------------------- # Détection de l'environnement # -------------------------------------------------------------------------- detect_env() { KERNEL="$(uname -r)" VIRT="$(systemd-detect-virt 2>/dev/null || true)" VIRT="${VIRT:-inconnu}" if systemd-detect-virt --container -q 2>/dev/null; then IS_CONTAINER=true else IS_CONTAINER=false fi case "$VIRT" in lxc|lxc-libvirt) VIRT_LABEL="LXC (conteneur)" ;; openvz) VIRT_LABEL="OpenVZ (conteneur)" ;; kvm|qemu) VIRT_LABEL="KVM (machine virtuelle)" ;; none) VIRT_LABEL="Serveur physique (dédié)" ;; *) if $IS_CONTAINER; then VIRT_LABEL="Conteneur ($VIRT)" else VIRT_LABEL="Machine virtuelle ($VIRT)" fi ;; esac } is_installed() { [[ -f "$INSTALL_DIR/docker-compose.yml" ]] } show_status() { echo -e " Environnement détecté : ${C_BOLD}${VIRT_LABEL}${C_RESET}" echo " Noyau : ${KERNEL}" echo " Système : ${PRETTY_NAME:-inconnu}" if is_installed; then echo -e " WG-Easy : ${C_GREEN}installé${C_RESET} ($INSTALL_DIR)" else echo -e " WG-Easy : ${C_YELLOW}non installé${C_RESET}" fi echo } load_install_conf() { # Valeurs par défaut si le fichier n'existe pas (installation par une ancienne version) WG_HOST=""; WG_PORT="51820"; WEB_PORT="51821"; SSH_PORT="22"; UFW_CONFIGURED=false if [[ -f "$INSTALL_CONF" ]]; then # shellcheck disable=SC1090 . "$INSTALL_CONF" elif [[ -f "$INSTALL_DIR/docker-compose.yml" ]]; then WEB_PORT="$(awk -F= '/- PORT=/{print $2; exit}' "$INSTALL_DIR/docker-compose.yml")" WG_PORT="$(awk -F= '/- INIT_PORT=/{print $2; exit}' "$INSTALL_DIR/docker-compose.yml")" WEB_PORT="${WEB_PORT:-51821}"; WG_PORT="${WG_PORT:-51820}" fi } wait_for_web() { info "Attente du démarrage de l'interface web..." for _ in $(seq 1 60); do if curl -fsS -o /dev/null --max-time 2 "http://127.0.0.1:${WEB_PORT}"; then return 0 fi sleep 2 done warn "L'interface web ne répond pas. Derniers logs :" (cd "$INSTALL_DIR" && docker compose logs --tail 30 wg-easy) || true die "Vérifie les logs avec : cd $INSTALL_DIR && docker compose logs -f" } # ========================================================================== # INSTALLATION # ========================================================================== do_install() { if is_installed; then warn "WG-Easy est déjà installé dans $INSTALL_DIR." die "Utilise « Mettre à jour », ou « Désinstaller » puis « Installer » pour repartir de zéro." fi if $IS_CONTAINER; then warn "Conteneur détecté. Pour que Docker et WireGuard fonctionnent :" warn " - les options « nesting » et « keyctl » doivent être activées" warn " (Proxmox : Options > Features, ou : pct set <ID> --features nesting=1,keyctl=1)" warn " - le module WireGuard doit être chargé sur la machine hôte." ask_yes_no "Continuer ?" "o" || exit 0 fi # ---------------------------------------------------------------- Paramètres info "Détection de l'IP publique..." local detected_ip detected_ip="$(curl -4 -fsS --max-time 5 https://api.ipify.org 2>/dev/null \ || curl -4 -fsS --max-time 5 https://ifconfig.me 2>/dev/null \ || hostname -I | awk '{print $1}')" ask "IP publique ou nom d'hôte du VPS (utilisé par les clients VPN)" "$detected_ip" WG_HOST="$REPLY" [[ -n "$WG_HOST" ]] || die "L'IP/hôte ne peut pas être vide." while true; do ask "Port UDP de WireGuard" "51820" valid_port "$REPLY" && { WG_PORT="$REPLY"; break; } warn "Port invalide." done while true; do ask "Port TCP de l'interface web" "51821" if valid_port "$REPLY" && [[ "$REPLY" != "$WG_PORT" ]]; then WEB_PORT="$REPLY"; break fi warn "Port invalide (ou identique au port WireGuard)." done ask "DNS pour les clients VPN (séparés par des virgules)" "1.1.1.1,1.0.0.1" local wg_dns="$REPLY" ask "Nom d'utilisateur admin de l'interface web" "admin" local admin_user="$REPLY" [[ -n "$admin_user" ]] || die "Le nom d'utilisateur ne peut pas être vide." echo "Le mot de passe doit contenir au moins 12 caractères, une majuscule, une minuscule, un chiffre et un caractère spécial (pas d'apostrophe ')." local admin_pass admin_pass2 while true; do read -r -s -p "$(echo -e "${C_BOLD}Mot de passe admin${C_RESET} : ")" admin_pass < /dev/tty; echo if ! valid_password "$admin_pass"; then warn "Mot de passe trop faible, réessaie." continue fi read -r -s -p "$(echo -e "${C_BOLD}Confirmation${C_RESET} : ")" admin_pass2 < /dev/tty; echo [[ "$admin_pass" == "$admin_pass2" ]] && break warn "Les mots de passe ne correspondent pas." done # Port SSH actuel (pour ne pas se bloquer avec UFW) SSH_PORT="$(sshd -T 2>/dev/null | awk '/^port /{print $2; exit}' || true)" ask "Port SSH à autoriser dans le pare-feu" "${SSH_PORT:-22}" SSH_PORT="$REPLY" valid_port "$SSH_PORT" || die "Port SSH invalide." # IPv6 disponible ? local ipv6_enabled=false if [[ -e /proc/sys/net/ipv6/conf/all/disable_ipv6 ]] \ && [[ "$(cat /proc/sys/net/ipv6/conf/all/disable_ipv6)" == "0" ]]; then ipv6_enabled=true fi echo echo -e "${C_BOLD}Récapitulatif :${C_RESET}" echo " Environnement : $VIRT_LABEL" echo " Hôte public : $WG_HOST" echo " Port WireGuard : $WG_PORT/udp" echo " Interface web : http://$WG_HOST:$WEB_PORT" echo " DNS clients : $wg_dns" echo " Admin : $admin_user" echo " Port SSH (UFW) : $SSH_PORT/tcp" echo " IPv6 : $ipv6_enabled" echo " Dossier : $INSTALL_DIR" echo ask_yes_no "Lancer l'installation ?" "o" || exit 0 # ---------------------------------------------------------------- 1. Paquets export DEBIAN_FRONTEND=noninteractive info "Mise à jour du système..." apt-get update -y apt-get upgrade -y info "Installation des paquets de base, WireGuard et UFW..." apt-get install -y ca-certificates curl gnupg ufw kmod iproute2 wireguard wireguard-tools ok "Paquets installés." # ---------------------------------------------------------------- 2. Noyau if $IS_CONTAINER; then # Un conteneur partage le noyau de l'hôte : modprobe y est impossible info "Conteneur : chargement des modules ignoré (géré par la machine hôte)." else info "Chargement des modules noyau..." local m err : > "$MODULES_FILE" for m in wireguard iptable_nat iptable_filter ip6table_nat ip6table_filter; do if err="$(modprobe "$m" 2>&1)"; then echo "$m" >> "$MODULES_FILE" else warn "Module $m non chargé : ${err:-erreur inconnue}" fi done fi # Test réel : création d'une interface WireGuard temporaire if ip link add wgtest0 type wireguard 2>/dev/null; then ip link del wgtest0 ok "WireGuard est disponible dans le noyau." elif $IS_CONTAINER; then die "WireGuard n'est pas disponible dans ce conteneur (${VIRT}). Il faut charger le module sur la machine hôte (Proxmox : modprobe wireguard), ou le demander à l'hébergeur. Sinon, il faut un VPS KVM." elif [[ ! -d "/lib/modules/$KERNEL" ]]; then die "Le noyau a été mis à jour pendant l'installation : les modules du noyau actuel ($KERNEL) ne sont plus présents. Redémarre le VPS (reboot) puis relance le script." else die "WireGuard n'est pas disponible dans ce noyau ($KERNEL). Vérifie que le paquet linux-image-amd64 (ou -cloud-amd64) est installé, redémarre, puis relance." fi info "Activation du forwarding IP..." cat > "$SYSCTL_FILE" <<EOF net.ipv4.ip_forward=1 net.ipv4.conf.all.src_valid_mark=1 EOF if $ipv6_enabled; then cat >> "$SYSCTL_FILE" <<EOF net.ipv6.conf.all.forwarding=1 net.ipv6.conf.default.forwarding=1 EOF fi # On n'applique que notre fichier : "sysctl --system" échoue dans un conteneur sysctl -p "$SYSCTL_FILE" >/dev/null 2>&1 || true if [[ "$(cat /proc/sys/net/ipv4/ip_forward 2>/dev/null)" == "1" ]]; then ok "Forwarding IP activé." else warn "Impossible d'activer net.ipv4.ip_forward ici (Docker s'en chargera normalement)." fi # ---------------------------------------------------------------- 3. Docker if command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then ok "Docker et Docker Compose sont déjà installés." else info "Installation de Docker depuis le dépôt officiel..." local pkg for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do apt-get remove -y "$pkg" >/dev/null 2>&1 || true done install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc chmod a+r /etc/apt/keyrings/docker.asc echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian ${VERSION_CODENAME} stable" \ > /etc/apt/sources.list.d/docker.list apt-get update -y apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin ok "Docker installé : $(docker --version)" fi if ! systemctl enable --now docker >/dev/null 2>&1 || ! docker info >/dev/null 2>&1; then if $IS_CONTAINER; then die "Docker ne démarre pas dans ce conteneur ${VIRT}. Sur l'hôte Proxmox, active « nesting » et « keyctl » pour ce conteneur (Options > Features, ou : pct set <ID> --features nesting=1,keyctl=1), redémarre le conteneur puis relance le script." fi die "Docker ne démarre pas. Voir : journalctl -u docker --no-pager | tail -n 30" fi ok "Docker fonctionne." # ---------------------------------------------------------------- 4. UFW info "Configuration du pare-feu UFW..." UFW_CONFIGURED=false if setup_ufw 2>/tmp/ufw-error.log; then UFW_CONFIGURED=true ok "UFW actif (SSH $SSH_PORT/tcp, WireGuard $WG_PORT/udp, web $WEB_PORT/tcp)." elif $IS_CONTAINER; then ufw --force disable >/dev/null 2>&1 || true warn "UFW ne fonctionne pas dans ce conteneur (voir /tmp/ufw-error.log), il a été désactivé." warn "Utilise plutôt le pare-feu de Proxmox / de l'hébergeur pour filtrer les ports." else die "Échec de la configuration UFW (voir /tmp/ufw-error.log)." fi # ---------------------------------------------------------------- 5. WG-Easy info "Création de la configuration WG-Easy dans $INSTALL_DIR..." mkdir -p "$INSTALL_DIR" chmod 700 "$INSTALL_DIR" # Les identifiants initiaux sont dans un .env protégé (vidé après le 1er démarrage) umask 077 cat > "$INSTALL_DIR/.env" <<EOF INIT_USERNAME=${admin_user} INIT_PASSWORD='${admin_pass}' EOF umask 022 # Dans un conteneur : pas d'accès aux modules ni à CAP_SYS_MODULE # (Docker refuserait de démarrer wg-easy). local extra_volumes="" extra_caps="" if ! $IS_CONTAINER; then extra_volumes=' - /lib/modules:/lib/modules:ro' extra_caps=' - SYS_MODULE' fi local ipv6_sysctls ipv6_net if $ipv6_enabled; then ipv6_sysctls=' - net.ipv6.conf.all.disable_ipv6=0 - net.ipv6.conf.all.forwarding=1 - net.ipv6.conf.default.forwarding=1' ipv6_net=' enable_ipv6: true ipam: driver: default config: - subnet: 10.42.42.0/24 - subnet: fdcc:ad94:bacf:61a3::/64' else ipv6_sysctls='' ipv6_net=' ipam: driver: default config: - subnet: 10.42.42.0/24' fi cat > "$INSTALL_DIR/docker-compose.yml" <<EOF services: wg-easy: image: ${WG_EASY_IMAGE} container_name: wg-easy restart: unless-stopped env_file: - .env environment: - PORT=${WEB_PORT} - INSECURE=true - INIT_ENABLED=true - INIT_HOST=${WG_HOST} - INIT_PORT=${WG_PORT} - INIT_DNS=${wg_dns} volumes: - etc_wireguard:/etc/wireguard ${extra_volumes} ports: - "${WG_PORT}:${WG_PORT}/udp" - "${WEB_PORT}:${WEB_PORT}/tcp" cap_add: - NET_ADMIN ${extra_caps} sysctls: - net.ipv4.ip_forward=1 - net.ipv4.conf.all.src_valid_mark=1 ${ipv6_sysctls} networks: wg: ipv4_address: 10.42.42.42 networks: wg: driver: bridge ${ipv6_net} volumes: etc_wireguard: EOF ok "docker-compose.yml créé." # Paramètres mémorisés pour la mise à jour et la désinstallation { printf 'WG_HOST=%q\n' "$WG_HOST" printf 'WG_PORT=%q\n' "$WG_PORT" printf 'WEB_PORT=%q\n' "$WEB_PORT" printf 'SSH_PORT=%q\n' "$SSH_PORT" printf 'UFW_CONFIGURED=%q\n' "$UFW_CONFIGURED" printf 'INSTALLED_WITH=%q\n' "$SCRIPT_VERSION" } > "$INSTALL_CONF" chmod 600 "$INSTALL_CONF" info "Téléchargement de l'image et démarrage de WG-Easy..." cd "$INSTALL_DIR" docker compose pull docker compose up -d wait_for_web # Le compte admin est créé : on retire le mot de passe du disque. # (Les variables INIT_* sont ignorées une fois la base initialisée.) : > "$INSTALL_DIR/.env" unset admin_pass admin_pass2 echo echo -e "${C_GREEN}${C_BOLD}=== Installation terminée ===${C_RESET}" echo echo -e " Interface web : ${C_BOLD}http://${WG_HOST}:${WEB_PORT}${C_RESET}" echo -e " Utilisateur : ${C_BOLD}${admin_user}${C_RESET}" echo -e " WireGuard : ${WG_HOST}:${WG_PORT}/udp" echo echo " Commandes utiles (dans $INSTALL_DIR) :" echo " docker compose logs -f # voir les logs" echo " docker compose restart # redémarrer" echo " Pour mettre à jour ou désinstaller : relance ce script." echo warn "L'interface web est en HTTP (non chiffré) : évite de t'y connecter depuis un réseau public non fiable." warn "Note : Docker publie ses ports directement via iptables, UFW ne peut donc pas bloquer le port ${WEB_PORT}." warn "Pour le fermer, remplace \"${WEB_PORT}:${WEB_PORT}/tcp\" par \"127.0.0.1:${WEB_PORT}:${WEB_PORT}/tcp\" dans" warn "docker-compose.yml, puis utilise un tunnel SSH : ssh -L ${WEB_PORT}:localhost:${WEB_PORT} root@${WG_HOST}" echo } # Commandes chaînées par && : "set -e" est ignoré dans une fonction appelée par un if setup_ufw() { ufw default deny incoming >/dev/null && ufw default allow outgoing >/dev/null && ufw allow "${SSH_PORT}/tcp" comment 'SSH' >/dev/null && ufw allow "${WG_PORT}/udp" comment 'WireGuard' >/dev/null && ufw allow "${WEB_PORT}/tcp" comment 'WG-Easy web' >/dev/null && # Autorise le trafic routé des clients VPN vers Internet sed -i 's/^DEFAULT_FORWARD_POLICY=.*/DEFAULT_FORWARD_POLICY="ACCEPT"/' /etc/default/ufw && ufw --force enable >/dev/null && ufw reload >/dev/null } # ========================================================================== # MISE À JOUR # ========================================================================== do_update() { is_installed || die "WG-Easy n'est pas installé : choisis « Installer »." command -v docker >/dev/null 2>&1 || die "Docker est introuvable." load_install_conf if ask_yes_no "Mettre aussi à jour le système (apt upgrade) ?" "o"; then export DEBIAN_FRONTEND=noninteractive info "Mise à jour du système..." apt-get update -y apt-get upgrade -y ok "Système à jour." if ! $IS_CONTAINER && [[ ! -d "/lib/modules/$KERNEL" ]]; then warn "Le noyau a été mis à jour : un redémarrage (reboot) sera nécessaire." fi fi cd "$INSTALL_DIR" local old_id new_id old_id="$(docker image inspect --format '{{.Id}}' "$WG_EASY_IMAGE" 2>/dev/null || true)" info "Téléchargement de la dernière image WG-Easy ($WG_EASY_IMAGE)..." docker compose pull new_id="$(docker image inspect --format '{{.Id}}' "$WG_EASY_IMAGE" 2>/dev/null || true)" if [[ -n "$old_id" && "$old_id" == "$new_id" ]] && docker ps --format '{{.Names}}' | grep -qx wg-easy; then ok "WG-Easy est déjà à jour." return 0 fi info "Redémarrage de WG-Easy avec la nouvelle image..." docker compose up -d wait_for_web docker image prune -f >/dev/null || true echo echo -e "${C_GREEN}${C_BOLD}=== Mise à jour terminée ===${C_RESET}" echo -e " Interface web : ${C_BOLD}http://${WG_HOST:-<IP>}:${WEB_PORT}${C_RESET}" echo " Les clients VPN et la configuration sont conservés." echo } # ========================================================================== # DÉSINSTALLATION # ========================================================================== do_uninstall() { local container_exists=false if command -v docker >/dev/null 2>&1 && docker ps -a --format '{{.Names}}' 2>/dev/null | grep -qx wg-easy; then container_exists=true fi if ! is_installed && ! $container_exists && [[ ! -d "$INSTALL_DIR" ]]; then die "WG-Easy ne semble pas installé (rien dans $INSTALL_DIR)." fi load_install_conf warn "La désinstallation supprime WG-Easy, TOUS les clients VPN et leur configuration." ask_yes_no "Confirmer la désinstallation ?" "n" || exit 0 local remove_docker=false remove_wg=false ask_yes_no "Supprimer aussi Docker ? (supprime TOUS les conteneurs Docker du serveur)" "n" && remove_docker=true ask_yes_no "Supprimer aussi les paquets WireGuard (wireguard, wireguard-tools) ?" "n" && remove_wg=true # ------------------------------------------------ Conteneur, volume, image if is_installed; then info "Arrêt et suppression de WG-Easy..." (cd "$INSTALL_DIR" && docker compose down -v --rmi all) || warn "docker compose down a échoué, suppression manuelle..." fi if command -v docker >/dev/null 2>&1; then docker rm -f wg-easy >/dev/null 2>&1 || true docker volume ls -q | grep -E '(^|_)etc_wireguard$' | xargs -r docker volume rm >/dev/null 2>&1 || true docker image rm "$WG_EASY_IMAGE" >/dev/null 2>&1 || true fi ok "Conteneur WG-Easy supprimé." # ------------------------------------------------ Pare-feu if command -v ufw >/dev/null 2>&1 && ufw status 2>/dev/null | grep -q "Status: active"; then info "Suppression des règles UFW de WG-Easy (la règle SSH est conservée)..." ufw delete allow "${WG_PORT}/udp" >/dev/null 2>&1 || true ufw delete allow "${WEB_PORT}/tcp" >/dev/null 2>&1 || true if [[ "$UFW_CONFIGURED" == "true" ]]; then sed -i 's/^DEFAULT_FORWARD_POLICY=.*/DEFAULT_FORWARD_POLICY="DROP"/' /etc/default/ufw ufw reload >/dev/null 2>&1 || true fi ok "Règles UFW supprimées." fi # ------------------------------------------------ Fichiers système rm -f "$SYSCTL_FILE" "$MODULES_FILE" rm -rf "$INSTALL_DIR" ok "Fichiers de configuration supprimés." # ------------------------------------------------ Paquets optionnels export DEBIAN_FRONTEND=noninteractive if $remove_docker; then info "Suppression de Docker..." systemctl stop docker docker.socket containerd >/dev/null 2>&1 || true apt-get purge -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin \ docker-compose-plugin docker-ce-rootless-extras >/dev/null 2>&1 || true rm -rf /var/lib/docker /var/lib/containerd rm -f /etc/apt/sources.list.d/docker.list /etc/apt/keyrings/docker.asc ok "Docker supprimé." fi if $remove_wg; then info "Suppression des paquets WireGuard..." apt-get purge -y wireguard wireguard-tools >/dev/null 2>&1 || true ok "WireGuard supprimé." fi if $remove_docker || $remove_wg; then apt-get autoremove -y >/dev/null 2>&1 || true fi echo echo -e "${C_GREEN}${C_BOLD}=== Désinstallation terminée ===${C_RESET}" echo } # ========================================================================== # PROGRAMME PRINCIPAL # ========================================================================== show_banner [[ $EUID -eq 0 ]] || die "Ce script doit être lancé en root (sudo bash $0)." [[ -r /etc/os-release ]] || die "Impossible de lire /etc/os-release." # shellcheck disable=SC1091 . /etc/os-release detect_env show_status [[ "${ID:-}" == "debian" ]] || die "Ce script est prévu pour Debian (détecté : ${ID:-inconnu})." if [[ "${VERSION_CODENAME:-}" != "trixie" ]]; then warn "Debian ${VERSION_CODENAME:-inconnue} détectée (le script est prévu pour Debian 13 Trixie)." ask_yes_no "Continuer quand même ?" "n" || exit 1 fi ACTION="${1:-}" if [[ -z "$ACTION" ]]; then echo -e "${C_BOLD}Que veux-tu faire ?${C_RESET}" echo " 1) Installer" echo " 2) Désinstaller" echo " 3) Mettre à jour" echo " 0) Quitter" echo while true; do ask "Choix" "" case "$REPLY" in 1) ACTION="install"; break ;; 2) ACTION="uninstall"; break ;; 3) ACTION="update"; break ;; 0|q|Q) exit 0 ;; *) warn "Choix invalide (1, 2, 3 ou 0)." ;; esac done echo fi case "$ACTION" in install) do_install ;; uninstall) do_uninstall ;; update) do_update ;; *) die "Action inconnue : $ACTION (utilise install, uninstall ou update)." ;; esac trap - ERR