RoKbA.
Espace de téléchargement
LES FICHIERS, À PORTÉE DE CLIC

Le partage.
Version RoKbA.

Parcourez les fichiers disponibles.
Un clic, et c’est à vous.

2Fichiers · 0 dossier(s)
25,0 KoDans ce dossier
← Dossier précédent

install-wg-easy.sh

#!/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
Bibliothèque de fichiers2 disponible(s)

↓ Téléchargement directVos fichiers. Votre rythme.