diff --git a/.env.example b/.env.example index 5103808..1f6a36f 100644 --- a/.env.example +++ b/.env.example @@ -39,9 +39,14 @@ SEED_DEMO=false NODE_ENV=production PORT=3000 -# URL publique du site. Se règle normalement dans Backend › Réseaux sociaux ; -# cette variable ne sert que de repli (puis AUTH_URL en dernier recours). -NEXT_PUBLIC_SITE_URL= +# ---- VPS sans reverse proxy existant (voir README) ---- +# Caddy devant l'application : ports 80 et 443, certificat Let's Encrypt +# automatique pour SITE_DOMAIN, et réponse immédiate sur http://IP_DU_SERVEUR +# tant que le DNS n'est pas configuré. Renseignez SITE_DOMAIN même si le +# domaine ne pointe pas encore : la bascule en HTTPS se fera toute seule. +#COMPOSE_FILE=docker-compose.yml:docker-compose.caddy.yml +#SITE_DOMAIN=pleinr.example.fr +#CADDY_EMAIL=vous@example.fr # ---- Publication des promotions sur les réseaux sociaux ---- # La configuration se fait désormais dans Backend › Réseaux sociaux : on y colle diff --git a/Caddyfile b/Caddyfile new file mode 100644 index 0000000..0b7ba57 --- /dev/null +++ b/Caddyfile @@ -0,0 +1,29 @@ +# Reverse proxy Caddy pour Plein R (voir docker-compose.caddy.yml). +# +# Deux entrées, pour que l'installation soit utilisable AVANT que le DNS ne +# soit configuré, et bascule toute seule ensuite : +# +# 1. {$SITE_DOMAIN} — le nom de domaine. Caddy obtient et renouvelle seul le +# certificat Let's Encrypt et redirige HTTP vers HTTPS. Tant que le DNS ne +# pointe pas sur le serveur, la demande de certificat échoue et Caddy +# réessaie en arrière-plan, avec un délai croissant : rien à relancer, le +# site devient joignable en HTTPS peu après la mise en place du DNS. +# +# 2. http:// — fourre-tout HTTP, qui répond notamment sur http://IP_DU_SERVEUR. +# Le préfixe http:// désactive explicitement le HTTPS automatique pour ce +# bloc (aucun certificat n'existe pour une IP nue). Le bloc du domaine étant +# plus spécifique, il reste prioritaire, et la redirection HTTP vers HTTPS +# du domaine est insérée avant ce fourre-tout. +{ + email {$CADDY_EMAIL} +} + +{$SITE_DOMAIN} { + encode zstd gzip + reverse_proxy pleinr-app:3000 +} + +http:// { + encode zstd gzip + reverse_proxy pleinr-app:3000 +} diff --git a/README.md b/README.md index 790dcf9..bb3635c 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,57 @@ charge des données de démonstration. Ensuite : > **Postgres déjà existant ?** Supprimez le service `postgres` de `docker-compose.yml` > et pointez `DATABASE_URL` vers votre instance. +## Installation sur un VPS sans reverse proxy + +Le `docker-compose.yml` suppose un Nginx Proxy Manager déjà en place (réseau +externe `nginx_default`). Sur un serveur nu, `docker-compose.caddy.yml` ajoute +**Caddy** devant l'application, sans modifier le fichier de base. + +Caddy sert deux entrées à la fois (voir `Caddyfile`) : + +| Adresse | Sert | Certificat | +|---|---|---| +| `http://IP_DU_SERVEUR` | tout de suite | aucun (impossible sur une IP nue) | +| `https://votre-domaine.fr` | dès que le DNS pointe sur le serveur | Let's Encrypt, automatique | + +On installe donc **tout d'un coup**, on valide par l'IP, et la bascule en HTTPS +se fait ensuite d'elle-même : il n'y a **rien à relancer** au moment de +configurer le DNS. Tant que le domaine ne résout pas, Caddy réessaie la demande +de certificat en arrière-plan avec un délai croissant. + +1. Installer Docker : `curl -fsSL https://get.docker.com | sh` +2. Ouvrir les ports : `sudo ufw allow 80,443/tcp` +3. Cloner le dépôt, copier `.env.example` en `.env` et renseigner au minimum + `POSTGRES_PASSWORD`, `DATABASE_URL`, `SEED_ADMIN_EMAIL`, puis : + + ```dotenv + COMPOSE_FILE=docker-compose.yml:docker-compose.caddy.yml + SITE_DOMAIN=votre-domaine.fr + CADDY_EMAIL=vous@example.fr + ``` + + Renseigner `SITE_DOMAIN` **même si le DNS n'est pas encore en place** : c'est + ce qui rend la bascule automatique. Laisser `AUTH_URL` **vide** : Auth.js + déduit alors l'URL des en-têtes de la requête, et suit donc l'IP puis le + domaine sans reconfiguration. +4. Lancer : `docker compose up -d --build` +5. Relever le mot de passe administrateur, affiché une seule fois : + `docker logs pleinr-app 2>&1 | grep -i -A2 "mot de passe"` + +Aucun port applicatif n'est publié : tout passe par Caddy. Les certificats +vivent dans le volume `caddy_data` et se renouvellent seuls. Une fois le DNS +posé, vérifier la délivrance avec +`docker logs pleinr-caddy 2>&1 | grep -i "certificate obtained"`. + +> **Vérification par l'IP** : en HTTP sur une adresse IP, la directive +> `upgrade-insecure-requests` de la CSP (`src/middleware.ts`) peut empêcher le +> navigateur de charger styles et scripts. Pour un test fidèle avant le DNS, +> passer par un tunnel SSH — `ssh -L 8080:localhost:80 utilisateur@IP` — puis +> ouvrir `http://localhost:8080` : `localhost` est une origine de confiance, la +> directive ne s'y applique pas. + +Mise à jour : `git pull && docker compose up -d --build`. + ## Comptes de démonstration (seed) | E-mail | Mot de passe | Rôle | diff --git a/docker-compose.caddy.yml b/docker-compose.caddy.yml new file mode 100644 index 0000000..f90bd96 --- /dev/null +++ b/docker-compose.caddy.yml @@ -0,0 +1,50 @@ +# Plein R — variante « VPS sans reverse proxy existant ». +# +# Ajoute Caddy devant l'application : il écoute en 80 et 443, obtient et +# renouvelle tout seul le certificat Let's Encrypt pour SITE_DOMAIN, et +# redirige automatiquement HTTP vers HTTPS. +# +# Utilisation : dans .env, poser +# COMPOSE_FILE=docker-compose.yml:docker-compose.caddy.yml +# SITE_DOMAIN=pleinr.example.fr +# CADDY_EMAIL=vous@example.fr +# AUTH_URL=https://pleinr.example.fr +# puis `docker compose up -d --build` comme d'habitude. +# +# Pré-requis : le DNS du domaine pointe vers le VPS et les ports 80/443 sont +# ouverts dans le pare-feu. + +services: + caddy: + image: caddy:2-alpine + container_name: pleinr-caddy + restart: unless-stopped + depends_on: + - app + environment: + SITE_DOMAIN: ${SITE_DOMAIN:?SITE_DOMAIN manquant dans .env} + CADDY_EMAIL: ${CADDY_EMAIL:?CADDY_EMAIL manquant dans .env (alertes d expiration Let's Encrypt)} + ports: + - "80:80" + - "443:443" + - "443:443/udp" + volumes: + - ./Caddyfile:/etc/caddy/Caddyfile:ro + - caddy_data:/data + - caddy_config:/config + networks: + - proxy + + app: + # L'application n'est plus publiée directement sur l'hôte : tout passe + # par Caddy (sinon http://IP:8413 contournerait le HTTPS). + ports: !reset [] + +volumes: + caddy_data: + caddy_config: + +networks: + # Sans Nginx Proxy Manager, personne ne crée ce réseau : Compose s'en charge. + proxy: + external: false diff --git a/docker-compose.yml b/docker-compose.yml index a59f4f8..8464b06 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -93,5 +93,6 @@ networks: proxy: # The reverse proxy's existing network. Must already exist (created by the # NPM/nginx stack). Override the name with PROXY_NETWORK if it differs. + # Sans proxy existant : voir docker-compose.caddy.yml (README). external: true name: ${PROXY_NETWORK:-nginx_default}