#!/usr/bin/env bash # ============================================================================= # SSH Tunnel Management Helper # Gère l'ouverture/fermeture des tunnels SSH vers les containers Incus # ============================================================================= set -euo pipefail # Répertoire du script SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" BAO_ROOT="$(dirname "$SCRIPT_DIR")" ENV_FILE="$BAO_ROOT/config/.env" # Couleurs RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[0;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' NC='\033[0m' # No Color # Fonctions d'affichage log_info() { echo -e "${BLUE}ℹ${NC} $*" } log_success() { echo -e "${GREEN}✓${NC} $*" } log_error() { echo -e "${RED}✗${NC} $*" >&2 } log_warning() { echo -e "${YELLOW}⚠${NC} $*" } # Charge une variable depuis .env get_env_var() { local key="$1" grep "^${key}=" "$ENV_FILE" 2>/dev/null | cut -d'=' -f2- | tr -d ' "' } # Vérifie si un tunnel SSH est actif is_tunnel_active() { local port="$1" # Vérifier si le port est en écoute if command -v ss >/dev/null 2>&1; then ss -tuln | grep -q ":${port} " && return 0 elif command -v netstat >/dev/null 2>&1; then netstat -tuln | grep -q ":${port} " && return 0 elif command -v lsof >/dev/null 2>&1; then lsof -i ":${port}" -sTCP:LISTEN >/dev/null 2>&1 && return 0 fi return 1 } # Trouve le PID du processus SSH pour un tunnel get_tunnel_pid() { local port="$1" # Chercher le processus SSH qui utilise ce port local if command -v lsof >/dev/null 2>&1; then lsof -ti ":${port}" -sTCP:LISTEN 2>/dev/null | head -n1 else # Fallback avec ps et grep ps aux | grep "ssh.*:${port}:localhost:3306" | grep -v grep | awk '{print $2}' | head -n1 fi } # Ouvre un tunnel SSH open_tunnel() { local env="${1^^}" # Convertir en majuscules local ssh_host=$(get_env_var "${env}_SSH_HOST") local local_port=$(get_env_var "${env}_SSH_PORT_LOCAL") local enabled=$(get_env_var "${env}_ENABLED") if [[ "$enabled" != "true" ]]; then log_error "Environnement ${env} désactivé dans .env" return 1 fi if [[ -z "$ssh_host" || -z "$local_port" ]]; then log_error "Configuration ${env} incomplète dans .env" return 1 fi # Vérifier si le tunnel est déjà actif if is_tunnel_active "$local_port"; then log_warning "Tunnel ${env} déjà actif sur le port ${local_port}" return 0 fi log_info "Ouverture du tunnel SSH vers ${ssh_host} (port local ${local_port})..." # Créer le tunnel en arrière-plan # -N : Ne pas exécuter de commande distante # -f : Passer en arrière-plan # -o ExitOnForwardFailure=yes : Quitter si le tunnel échoue # -o ServerAliveInterval=60 : Garder la connexion active ssh -N -f \ -L "${local_port}:localhost:3306" \ -o ExitOnForwardFailure=yes \ -o ServerAliveInterval=60 \ -o ServerAliveCountMax=3 \ "$ssh_host" 2>/dev/null # Attendre que le tunnel soit actif local max_attempts=10 local attempt=0 while [[ $attempt -lt $max_attempts ]]; do if is_tunnel_active "$local_port"; then log_success "Tunnel ${env} actif sur le port ${local_port}" return 0 fi sleep 0.5 ((attempt++)) done log_error "Impossible d'ouvrir le tunnel ${env}" return 1 } # Ferme un tunnel SSH close_tunnel() { local env="${1^^}" local local_port=$(get_env_var "${env}_SSH_PORT_LOCAL") if [[ -z "$local_port" ]]; then log_error "Port local pour ${env} introuvable dans .env" return 1 fi if ! is_tunnel_active "$local_port"; then log_warning "Tunnel ${env} non actif sur le port ${local_port}" return 0 fi local pid=$(get_tunnel_pid "$local_port") if [[ -z "$pid" ]]; then log_error "Impossible de trouver le PID du tunnel ${env}" return 1 fi log_info "Fermeture du tunnel ${env} (PID ${pid})..." kill "$pid" 2>/dev/null sleep 0.5 if ! is_tunnel_active "$local_port"; then log_success "Tunnel ${env} fermé" return 0 else log_error "Impossible de fermer le tunnel ${env}" return 1 fi } # Affiche le statut des tunnels status_tunnels() { echo -e "\n${CYAN}╔════════════════════════════════════════╗${NC}" echo -e "${CYAN}║ État des tunnels SSH ║${NC}" echo -e "${CYAN}╚════════════════════════════════════════╝${NC}\n" for env in DEV REC PROD; do local enabled=$(get_env_var "${env}_ENABLED") local local_port=$(get_env_var "${env}_SSH_PORT_LOCAL") local ssh_host=$(get_env_var "${env}_SSH_HOST") if [[ "$enabled" != "true" ]]; then echo -e " ${env}: ${YELLOW}Désactivé${NC}" continue fi if is_tunnel_active "$local_port"; then local pid=$(get_tunnel_pid "$local_port") echo -e " ${env}: ${GREEN}✓ Actif${NC} (port ${local_port}, PID ${pid})" else echo -e " ${env}: ${RED}✗ Inactif${NC} (port ${local_port})" fi done echo "" } # Ferme tous les tunnels close_all_tunnels() { log_info "Fermeture de tous les tunnels..." for env in DEV REC PROD; do local enabled=$(get_env_var "${env}_ENABLED") if [[ "$enabled" == "true" ]]; then close_tunnel "$env" 2>/dev/null || true fi done log_success "Tous les tunnels ont été fermés" } # Usage usage() { cat < [environment] Commandes: open Ouvre un tunnel SSH vers l'environnement close Ferme le tunnel SSH status Affiche l'état de tous les tunnels close-all Ferme tous les tunnels actifs Environnements: DEV, REC, PROD Exemples: $(basename "$0") open dev $(basename "$0") close rec $(basename "$0") status $(basename "$0") close-all EOF } # Main main() { if [[ ! -f "$ENV_FILE" ]]; then log_error "Fichier .env introuvable: $ENV_FILE" log_info "Copiez config/.env.example vers config/.env" exit 1 fi if [[ $# -eq 0 ]]; then usage exit 0 fi local command="$1" case "$command" in open) if [[ $# -lt 2 ]]; then log_error "Environnement manquant" usage exit 1 fi open_tunnel "$2" ;; close) if [[ $# -lt 2 ]]; then log_error "Environnement manquant" usage exit 1 fi close_tunnel "$2" ;; status) status_tunnels ;; close-all) close_all_tunnels ;; *) log_error "Commande invalide: $command" usage exit 1 ;; esac } main "$@"