[READ-ONLY] Mirror of https://github.com/thoda-dev/scripts. custom scripts
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225#!/bin/bash
# ------------------------------# Script réutilisable Docker + Nginx + Certbot Cloudflare# Arguments :# $1 = Domaine (ex: exemple.com)# $2 = Port de l'app principale (ex: 3000)# $3 = Email pour SSL (ex: email@example.com)# $4+ = (optionnel) sous-domaines supplémentaires, au format sous-domaine:port## Exemples :# set_new_nginx_docker_app.sh exemple.com 3000 moi@exemple.com# set_new_nginx_docker_app.sh exemple.com 3000 moi@exemple.com docs:3001 api:3002## Variables d'environnement :# MAX_BODY_SIZE taille max du corps des requêtes (défaut: 10m)# ------------------------------
# Un provisionnement qui continue après une erreur laisse un serveur à moitié configuré,# plus difficile à diagnostiquer qu'un arrêt net.set -euo pipefail
if [ "$#" -lt 3 ]; then echo "Usage: $0 <domain> <app_port> <email> [sous-domaine:port ...]" exit 1fi
DOMAIN="$1"APP_PORT="$2"EMAIL="$3"shift 3EXTRA_APPS=("$@")MAX_BODY_SIZE="${MAX_BODY_SIZE:-10m}"CLOUDFLARE_CREDENTIALS="$HOME/.secrets/certbot/cloudflare.ini"
echo "⚙️ Configuration pour :"echo "Domaine: $DOMAIN"echo "Port principal: $APP_PORT"echo "Email SSL: $EMAIL"if [ ${#EXTRA_APPS[@]} -gt 0 ]; then echo "Sous-domaines: ${EXTRA_APPS[*]+"${EXTRA_APPS[*]}"}"fi
if [ ! -f "$CLOUDFLARE_CREDENTIALS" ]; then echo "❌ Identifiants Cloudflare absents : $CLOUDFLARE_CREDENTIALS" echo " Crée le fichier avec dns_cloudflare_api_token = <token>, puis chmod 600." exit 1fi
# ------------------------------# Fonction pour installer un paquet si absent# ------------------------------install_if_missing() { if ! command -v "$1" &> /dev/null; then echo "Installation de $1..." sudo apt install -y "$2" else echo "$1 déjà installé, skipping..." fi}
# ------------------------------# Mise à jour système# ------------------------------sudo apt update && sudo apt upgrade -y
install_if_missing curl curlinstall_if_missing git git
# ------------------------------# Docker# ------------------------------# Le script officiel plutôt que docker.io d'apt : celui-ci installe une version souvent ancienne et# le paquet docker-compose d'Ubuntu est la v1 Python, abandonnée, incapable de lire un fichier# compose moderne. get.docker.com pose docker-ce et le plugin compose v2 ensemble.if ! command -v docker &> /dev/null; then echo "Installation de Docker..." curl -fsSL https://get.docker.com | sudo sh # Sans ça, docker n'est utilisable qu'en sudo. Prend effet à la prochaine session. sudo usermod -aG docker "$USER" echo "ℹ️ $USER ajouté au groupe docker — reconnecte-toi pour que ça prenne effet."else echo "Docker déjà installé, skipping..."fi
if ! docker compose version &> /dev/null; then echo "Installation du plugin compose v2..." sudo apt install -y docker-compose-pluginfi
# ------------------------------# Nginx# ------------------------------install_if_missing nginx nginxsudo systemctl enable nginxsudo systemctl start nginx
# ------------------------------# Bloc de proxy partagé par tous les vhosts# ------------------------------proxy_block() { local port="$1" cat <<BLOCK # nginx plafonne le corps des requêtes à 1m par défaut. Une app qui accepte des uploads répond # alors un 413 nginx avant même d'être atteinte, avec un message que personne ne comprend. client_max_body_size $MAX_BODY_SIZE;
location / { proxy_pass http://127.0.0.1:$port; proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; # Sans lui, une app derrière ce proxy croit être servie en HTTP et peut refuser de poser # ses cookies en Secure. proxy_set_header X-Forwarded-Proto \$scheme;
proxy_cache_bypass \$http_upgrade; proxy_redirect off;
# Pour les requêtes de streaming proxy_buffering off; proxy_cache off; proxy_request_buffering off; proxy_read_timeout 3600s; proxy_send_timeout 3600s; }BLOCK}
# ------------------------------# Nginx en HTTP seul, le temps d'obtenir le certificat# ------------------------------NGINX_CONF="/etc/nginx/sites-available/$DOMAIN"sudo tee "$NGINX_CONF" > /dev/null <<EOFserver { listen 80; server_name $DOMAIN *.$DOMAIN;
$(proxy_block "$APP_PORT")}EOF
sudo ln -sf "$NGINX_CONF" /etc/nginx/sites-enabled/sudo nginx -t && sudo systemctl reload nginx
# ------------------------------# Certbot et plugin Cloudflare# ------------------------------install_if_missing certbot certbotif ! dpkg -s python3-certbot-dns-cloudflare &> /dev/null; then sudo apt install -y python3-certbot-dns-cloudflarefi
# Wildcard via DNS-01 : aucun port à exposer, et un seul certificat pour tous les sous-domaines.# Attention, un wildcard ne couvre qu'un niveau : *.exemple.com vaut pour a.exemple.com mais pas# pour b.a.exemple.com.if [ ! -d "/etc/letsencrypt/live/$DOMAIN" ]; then sudo certbot certonly \ --dns-cloudflare \ --dns-cloudflare-credentials "$CLOUDFLARE_CREDENTIALS" \ -d "$DOMAIN" -d "*.$DOMAIN" \ -m "$EMAIL" \ --agree-tos \ --non-interactiveelse echo "Certificat SSL déjà existant pour $DOMAIN, skipping..."fi
# ------------------------------# Nginx en HTTPS# ------------------------------# Les vhosts explicites viennent avant le wildcard : nginx retient le server_name exact en priorité,# donc chaque sous-domaine listé part vers son propre port et le reste tombe sur l'app principale.{ echo "server {" echo " listen 80;" echo " server_name $DOMAIN *.$DOMAIN;" echo " return 301 https://\$host\$request_uri;" echo "}"
for app in ${EXTRA_APPS[@]+"${EXTRA_APPS[@]}"}; do sub="${app%%:*}" port="${app##*:}" if [ -z "$sub" ] || [ -z "$port" ] || [ "$sub" = "$app" ]; then echo "❌ Format invalide : '$app' (attendu sous-domaine:port)" >&2 exit 1 fi echo "" echo "server {" echo " listen 443 ssl;" echo " http2 on;" echo " server_name $sub.$DOMAIN;" echo "" echo " ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem;" echo " ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem;" echo "" proxy_block "$port" echo "}" done
echo "" echo "server {" echo " listen 443 ssl;" echo " http2 on;" echo " server_name $DOMAIN *.$DOMAIN;" echo "" echo " ssl_certificate /etc/letsencrypt/live/$DOMAIN/fullchain.pem;" echo " ssl_certificate_key /etc/letsencrypt/live/$DOMAIN/privkey.pem;" echo "" proxy_block "$APP_PORT" echo "}"} | sudo tee "$NGINX_CONF" > /dev/null
sudo nginx -t && sudo systemctl reload nginx
echo "✅ Installation terminée !"echo " $DOMAIN et *.$DOMAIN → port $APP_PORT"for app in ${EXTRA_APPS[@]+"${EXTRA_APPS[@]}"}; do echo " ${app%%:*}.$DOMAIN → port ${app##*:}"done