33800 Docs

← Retour
#!/bin/bash # ═══════════════════════════════════════════════════════════════════════════ # NGINX.SH - Génération et déploiement automatique de configs Nginx # ═══════════════════════════════════════════════════════════════════════════ # ─────────────────────────────────────────────────────────────────────────── # Configuration # ─────────────────────────────────────────────────────────────────────────── NGINX_HOST="${NGINX_HOST:-192.168.1.104}" NGINX_USER="${NGINX_USER:-gouroubleu}" NGINX_SITES_AVAILABLE="${NGINX_SITES_AVAILABLE:-/etc/nginx/sites-available}" NGINX_SITES_ENABLED="${NGINX_SITES_ENABLED:-/etc/nginx/sites-enabled}" # Mapping des certificats SSL par domaine declare -A SSL_PATHS=( ["33800.nowhere84.com"]="/etc/letsencrypt/live/33800.nowhere84.com" ["86000.nowhere84.com"]="/etc/letsencrypt/live/86000.nowhere84.com" ["nowhere84.com"]="/etc/letsencrypt/live/nowhere84.com" ) DEFAULT_SSL_PATH="/etc/letsencrypt/live/33800.nowhere84.com" # ─────────────────────────────────────────────────────────────────────────── # Trouver le certificat SSL approprié pour un domaine # ─────────────────────────────────────────────────────────────────────────── find_ssl_path() { local domain="$1" for base_domain in "${!SSL_PATHS[@]}"; do if [[ "$domain" == *"$base_domain" ]]; then echo "${SSL_PATHS[$base_domain]}" return fi done echo "$DEFAULT_SSL_PATH" } # ─────────────────────────────────────────────────────────────────────────── # Résoudre le domaine complet depuis la config # ─────────────────────────────────────────────────────────────────────────── resolve_domain() { local domain="${CFG_nginx_domain:-}" local name="${CFG_name}" if [[ -z "$domain" ]]; then # Pas de domaine configuré echo "" return fi # Si c'est déjà un FQDN if [[ "$domain" == *.nowhere84.com ]]; then echo "$domain" return fi # Sinon, construire le FQDN # Patterns: "api" -> "api.33800.nowhere84.com" # "api.33800" -> "api.33800.nowhere84.com" if [[ "$domain" == *.* ]]; then echo "${domain}.nowhere84.com" else echo "${domain}.33800.nowhere84.com" fi } # ─────────────────────────────────────────────────────────────────────────── # Générer la configuration Nginx # ─────────────────────────────────────────────────────────────────────────── generate_nginx_config() { local domain="$1" local port="${CFG_port:-3000}" local name="${CFG_name}" local websocket="${CFG_nginx_websocket:-false}" local private="${CFG_nginx_private:-true}" # Private par défaut = true local output_file="${PROJECT_DIR}/.nginx.conf" # Déterminer le backend local backend_host="$TARGET_HOST" [[ "$TARGET_LOCAL" == "true" ]] && backend_host="192.168.1.12" # Trouver le certificat SSL local ssl_path=$(find_ssl_path "$domain") local ssl_cert="${ssl_path}/fullchain.pem" local ssl_key="${ssl_path}/privkey.pem" local mode_info="PUBLIC" [[ "$private" == "true" ]] && mode_info="PRIVÉ (réseau local uniquement)" log_step "Génération config Nginx pour ${domain} [${mode_info}]..." >&2 cat > "$output_file" << EOF # ═══════════════════════════════════════════════════════════════════════════ # ${name} - Configuration Nginx # Auto-generated by smart-deploy.sh # Date: $(date '+%Y-%m-%d %H:%M:%S') # ═══════════════════════════════════════════════════════════════════════════ # Redirect HTTP to HTTPS server { listen 80; server_name ${domain}; return 301 https://\$server_name\$request_uri; } # HTTPS Server server { listen 443 ssl http2; server_name ${domain}; # SSL Configuration ssl_certificate ${ssl_cert}; ssl_certificate_key ${ssl_key}; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers off; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; EOF # Ajouter restriction IP si private (utilise le snippet centralisé) if [[ "$private" == "true" ]]; then # Lire les chemins publics (bypass) depuis le YAML local public_paths=() if [[ -n "${CONFIG_PATH:-}" ]]; then while IFS= read -r path; do [[ -n "$path" ]] && public_paths+=("$path") done < <(parse_yaml_list "$CONFIG_PATH" "nginx" "public_paths" 2>/dev/null) fi # Générer les locations publiques AVANT la restriction globale if [[ ${#public_paths[@]} -gt 0 ]]; then cat >> "$output_file" << 'EOF' # ── Chemins publics (bypass restrictions IP) ── EOF for pub_path in "${public_paths[@]}"; do cat >> "$output_file" << EOF location = ${pub_path} { # Public: bypass IP restrictions (ex: OAuth callback) allow all; proxy_pass http://${backend_host}:${port}; proxy_http_version 1.1; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; proxy_connect_timeout 60s; proxy_read_timeout 60s; client_max_body_size 1M; } EOF log_info " → Chemin public: ${pub_path}" >&2 done fi cat >> "$output_file" << 'EOF' # Restrictions IP - voir /etc/nginx/snippets/allowed-ips.conf include /etc/nginx/snippets/allowed-ips.conf; EOF fi cat >> "$output_file" << EOF # Security Headers add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; # Logging access_log /var/log/nginx/${name}.access.log; error_log /var/log/nginx/${name}.error.log; # Cache static assets location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { proxy_pass http://${backend_host}:${port}; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; expires 30d; add_header Cache-Control "public, immutable"; } # Main location location / { proxy_pass http://${backend_host}:${port}; proxy_http_version 1.1; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; EOF # Ajouter support WebSocket si configuré if [[ "$websocket" == "true" ]]; then cat >> "$output_file" << 'EOF' # WebSocket support proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_cache_bypass $http_upgrade; proxy_read_timeout 86400s; proxy_send_timeout 86400s; EOF fi cat >> "$output_file" << 'EOF' # Timeouts proxy_connect_timeout 60s; EOF # Only add read/send timeouts if not already set by WebSocket block if [[ "$websocket" != "true" ]]; then cat >> "$output_file" << 'EOF' proxy_read_timeout 60s; proxy_send_timeout 60s; EOF fi cat >> "$output_file" << 'EOF' # Body size client_max_body_size 100M; } } EOF log_success "Config Nginx générée: ${output_file}" >&2 echo "$output_file" } # ─────────────────────────────────────────────────────────────────────────── # Déployer la configuration sur le serveur Nginx # ─────────────────────────────────────────────────────────────────────────── deploy_nginx_config() { local config_file="$1" local domain="$2" local config_name="${domain}.conf" log_step "Déploiement config Nginx vers ${NGINX_HOST}..." # Test connexion SSH if ! ssh -o ConnectTimeout=5 -o BatchMode=yes "${NGINX_USER}@${NGINX_HOST}" "echo 'OK'" > /dev/null 2>&1; then log_error "Impossible de se connecter à ${NGINX_HOST}" return 1 fi # Backup si le fichier existe local remote_file="${NGINX_SITES_AVAILABLE}/${config_name}" if ssh "${NGINX_USER}@${NGINX_HOST}" "[ -f '${remote_file}' ]" 2>/dev/null; then log_info "Backup de l'ancienne config..." ssh "${NGINX_USER}@${NGINX_HOST}" "sudo cp '${remote_file}' '${remote_file}.backup.$(date +%Y%m%d%H%M%S)'" fi # Copier le fichier scp -q "$config_file" "${NGINX_USER}@${NGINX_HOST}:/tmp/${config_name}" ssh "${NGINX_USER}@${NGINX_HOST}" "sudo mv /tmp/${config_name} ${remote_file}" # Créer le symlink si nécessaire local symlink="${NGINX_SITES_ENABLED}/${config_name}" if ! ssh "${NGINX_USER}@${NGINX_HOST}" "[ -L '${symlink}' ]" 2>/dev/null; then log_info "Création du symlink..." ssh "${NGINX_USER}@${NGINX_HOST}" "sudo ln -sf '${remote_file}' '${symlink}'" fi # Test de la configuration log_step "Test de la configuration Nginx..." if ! ssh "${NGINX_USER}@${NGINX_HOST}" "sudo nginx -t" 2>&1; then log_error "Configuration Nginx invalide!" return 1 fi # Reload Nginx log_step "Rechargement de Nginx..." ssh "${NGINX_USER}@${NGINX_HOST}" "sudo systemctl reload nginx" log_success "Config Nginx déployée: https://${domain}" return 0 } # ─────────────────────────────────────────────────────────────────────────── # Fonction principale : génère et déploie si domain configuré # ─────────────────────────────────────────────────────────────────────────── generate_and_deploy_nginx_config() { # Résoudre le domaine local domain=$(resolve_domain) if [[ -z "$domain" ]]; then log_info "Pas de domaine configuré, skip Nginx" return 0 fi # Vérifier si nginx est activé if [[ "${CFG_nginx_enabled:-true}" == "false" ]]; then log_info "Nginx désactivé dans la config, skip" return 0 fi log_info "Configuration Nginx pour: ${domain}" # Vérifier si la config existe déjà sur nginx (symlink ou fichier) local config_name="${domain}.conf" local remote_file="${NGINX_SITES_ENABLED}/${config_name}" if ssh -o ConnectTimeout=5 -o BatchMode=yes "${NGINX_USER}@${NGINX_HOST}" "[ -e '${remote_file}' ]" 2>/dev/null; then log_info "Config Nginx existe déjà: ${config_name}, skip" return 0 fi # Générer la config local config_file=$(generate_nginx_config "$domain") # Déployer if deploy_nginx_config "$config_file" "$domain"; then log_success "Nginx configuré: https://${domain}" else log_warning "Échec déploiement Nginx (le service fonctionne peut-être sans)" fi } # ─────────────────────────────────────────────────────────────────────────── # Switch upstream backend (blue-green) # Met à jour le port dans l'upstream block et reload nginx # ─────────────────────────────────────────────────────────────────────────── switch_nginx_upstream() { local service_name="$1" local new_port="$2" local domain="$3" local config_name="${domain}.conf" log_step "Switch upstream ${service_name} → port ${new_port}..." # Déterminer le backend host local backend_host="$TARGET_HOST" [[ "$TARGET_LOCAL" == "true" ]] && backend_host="192.168.1.12" # Vérifier que la config existe if ! ssh -o ConnectTimeout=5 -o BatchMode=yes "${NGINX_USER}@${NGINX_HOST}" \ "[ -f '${NGINX_SITES_AVAILABLE}/${config_name}' ]" 2>/dev/null; then log_warning "Config Nginx ${config_name} non trouvée, génération complète..." # Générer la config complète avec upstream local config_file=$(generate_nginx_config_upstream "$domain" "$new_port") deploy_nginx_config "$config_file" "$domain" return $? fi # Vérifier si la config utilise déjà un upstream block local has_upstream has_upstream=$(ssh "${NGINX_USER}@${NGINX_HOST}" \ "grep -c 'upstream ${service_name}-backend' ${NGINX_SITES_AVAILABLE}/${config_name} 2>/dev/null") || has_upstream=0 if [[ "$has_upstream" -gt 0 ]]; then # Config avec upstream : juste changer le port dans le server line log_info "Upstream existant, update port..." ssh "${NGINX_USER}@${NGINX_HOST}" \ "sudo sed -i 's|server ${backend_host}:[0-9]*;|server ${backend_host}:${new_port};|' \ ${NGINX_SITES_AVAILABLE}/${config_name}" else # Config sans upstream (ancien format proxy_pass direct) : migrer vers upstream log_info "Migration vers upstream block..." local config_file=$(generate_nginx_config_upstream "$domain" "$new_port") deploy_nginx_config "$config_file" "$domain" return $? fi # Test et reload log_step "Test et reload Nginx..." if ! ssh "${NGINX_USER}@${NGINX_HOST}" "sudo nginx -t" 2>&1; then log_error "Configuration Nginx invalide après switch!" return 1 fi ssh "${NGINX_USER}@${NGINX_HOST}" "sudo systemctl reload nginx" log_success "Nginx switched: ${service_name} → port ${new_port}" return 0 } # ─────────────────────────────────────────────────────────────────────────── # Rollback upstream (retour au port précédent) # ─────────────────────────────────────────────────────────────────────────── rollback_nginx_upstream() { local service_name="$1" local old_port="$2" local domain="$3" log_step "Rollback Nginx: ${service_name} → port ${old_port}..." switch_nginx_upstream "$service_name" "$old_port" "$domain" } # ─────────────────────────────────────────────────────────────────────────── # Générer config Nginx avec upstream block (pour blue-green) # ─────────────────────────────────────────────────────────────────────────── generate_nginx_config_upstream() { local domain="$1" local port="${2:-${CFG_port:-3000}}" local name="${CFG_name}" local websocket="${CFG_nginx_websocket:-false}" local private="${CFG_nginx_private:-true}" local output_file="${PROJECT_DIR}/.nginx.conf" # Déterminer le backend local backend_host="$TARGET_HOST" [[ "$TARGET_LOCAL" == "true" ]] && backend_host="192.168.1.12" # Trouver le certificat SSL local ssl_path=$(find_ssl_path "$domain") local ssl_cert="${ssl_path}/fullchain.pem" local ssl_key="${ssl_path}/privkey.pem" local mode_info="PUBLIC" [[ "$private" == "true" ]] && mode_info="PRIVÉ (réseau local uniquement)" log_step "Génération config Nginx (upstream) pour ${domain} [${mode_info}]..." >&2 cat > "$output_file" << EOF # ═══════════════════════════════════════════════════════════════════════════ # ${name} - Configuration Nginx (blue-green upstream) # Auto-generated by smart-deploy.sh # Date: $(date '+%Y-%m-%d %H:%M:%S') # ═══════════════════════════════════════════════════════════════════════════ # Upstream backend (port updated by blue-green switch) upstream ${name}-backend { server ${backend_host}:${port}; } # Redirect HTTP to HTTPS server { listen 80; server_name ${domain}; return 301 https://\$server_name\$request_uri; } # HTTPS Server server { listen 443 ssl http2; server_name ${domain}; # SSL Configuration ssl_certificate ${ssl_cert}; ssl_certificate_key ${ssl_key}; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers off; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; EOF # Ajouter restriction IP si private if [[ "$private" == "true" ]]; then local public_paths=() if [[ -n "${CONFIG_PATH:-}" ]]; then while IFS= read -r path; do [[ -n "$path" ]] && public_paths+=("$path") done < <(parse_yaml_list "$CONFIG_PATH" "nginx" "public_paths" 2>/dev/null) fi if [[ ${#public_paths[@]} -gt 0 ]]; then cat >> "$output_file" << 'EOF' # ── Chemins publics (bypass restrictions IP) ── EOF for pub_path in "${public_paths[@]}"; do cat >> "$output_file" << EOF location = ${pub_path} { allow all; proxy_pass http://${name}-backend; proxy_http_version 1.1; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; proxy_connect_timeout 60s; proxy_read_timeout 60s; client_max_body_size 1M; } EOF log_info " → Chemin public: ${pub_path}" >&2 done fi cat >> "$output_file" << 'EOF' # Restrictions IP - voir /etc/nginx/snippets/allowed-ips.conf include /etc/nginx/snippets/allowed-ips.conf; EOF fi cat >> "$output_file" << EOF # Security Headers add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; # Logging access_log /var/log/nginx/${name}.access.log; error_log /var/log/nginx/${name}.error.log; # Cache static assets location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { proxy_pass http://${name}-backend; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; expires 30d; add_header Cache-Control "public, immutable"; } # Main location location / { proxy_pass http://${name}-backend; proxy_http_version 1.1; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; EOF if [[ "$websocket" == "true" ]]; then cat >> "$output_file" << 'EOF' # WebSocket support proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_cache_bypass $http_upgrade; proxy_read_timeout 86400s; proxy_send_timeout 86400s; EOF fi cat >> "$output_file" << 'EOF' # Timeouts proxy_connect_timeout 60s; EOF # Only add read/send timeouts if not already set by WebSocket block if [[ "$websocket" != "true" ]]; then cat >> "$output_file" << 'EOF' proxy_read_timeout 60s; proxy_send_timeout 60s; EOF fi cat >> "$output_file" << 'EOF' # Body size client_max_body_size 100M; } } EOF log_success "Config Nginx (upstream) générée: ${output_file}" >&2 echo "$output_file" } # ─────────────────────────────────────────────────────────────────────────── # Désactiver une config Nginx # ─────────────────────────────────────────────────────────────────────────── disable_nginx_config() { local domain="$1" local config_name="${domain}.conf" log_step "Désactivation config Nginx: ${domain}..." ssh "${NGINX_USER}@${NGINX_HOST}" "sudo rm -f '${NGINX_SITES_ENABLED}/${config_name}'" ssh "${NGINX_USER}@${NGINX_HOST}" "sudo nginx -t && sudo systemctl reload nginx" log_success "Config Nginx désactivée" } # ─────────────────────────────────────────────────────────────────────────── # Lister les configs Nginx actives # ─────────────────────────────────────────────────────────────────────────── list_nginx_configs() { log_step "Configs Nginx actives sur ${NGINX_HOST}..." ssh "${NGINX_USER}@${NGINX_HOST}" "ls -la ${NGINX_SITES_ENABLED}/*.conf 2>/dev/null" | \ awk '{print $NF}' | xargs -I{} basename {} .conf }