docs : corrige le déploiement prod (Docker) et documente les variables d'env mail
- README : section Variables d'environnement (ENCRYPTION_KEY, LOCK_DSN) + section Déploiement passée au flow Docker (deploy.sh) - mail-cron-setup : sépare dev (make, php-lesstime-fpm) et prod (lesstime-app, docker compose exec), cron prod réel - infra/prod/.env.example : ajoute ENCRYPTION_KEY et LOCK_DSN (manquaient, requis pour la sync mail) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
43
README.md
43
README.md
@@ -45,6 +45,10 @@ make install
|
|||||||
|
|
||||||
L'application est accessible sur **http://localhost:8082**.
|
L'application est accessible sur **http://localhost:8082**.
|
||||||
|
|
||||||
|
Les valeurs par défaut du `.env` committé suffisent pour démarrer en local. Pour la prod
|
||||||
|
(et pour activer la messagerie), surcharger les variables sensibles dans `.env.local` —
|
||||||
|
voir « Variables d'environnement » ci-dessous.
|
||||||
|
|
||||||
### Comptes de test (fixtures)
|
### Comptes de test (fixtures)
|
||||||
|
|
||||||
| Utilisateur | Mot de passe | Rôle | Détails |
|
| Utilisateur | Mot de passe | Rôle | Détails |
|
||||||
@@ -56,6 +60,25 @@ L'application est accessible sur **http://localhost:8082**.
|
|||||||
| `client-liot` | `client` | ROLE_CLIENT | Client LIOT (projet SIRH) |
|
| `client-liot` | `client` | ROLE_CLIENT | Client LIOT (projet SIRH) |
|
||||||
| `client-acme` | `client` | ROLE_CLIENT | Client ACME (projet CRM) |
|
| `client-acme` | `client` | ROLE_CLIENT | Client ACME (projet CRM) |
|
||||||
|
|
||||||
|
## Variables d'environnement
|
||||||
|
|
||||||
|
Les variables sont définies dans `.env` (committé, valeurs par défaut pour le dev) et
|
||||||
|
peuvent être surchargées dans `.env.local` (jamais committé). En prod, elles vont dans le
|
||||||
|
`.env` du serveur (`/var/www/lesstime/.env`, voir `infra/prod/.env.example`).
|
||||||
|
|
||||||
|
| Variable | Rôle | Défaut dev | À fixer en prod |
|
||||||
|
|----------|------|-----------|-----------------|
|
||||||
|
| `APP_SECRET` | Secret Symfony | placeholder | ✅ (hex 32) |
|
||||||
|
| `JWT_PASSPHRASE` | Passphrase des clés JWT | placeholder | ✅ |
|
||||||
|
| `DATABASE_URL` | Connexion PostgreSQL | container `db` | ✅ (`host.docker.internal`) |
|
||||||
|
| `CORS_ALLOW_ORIGIN` | Origines CORS autorisées | localhost | ✅ (domaine prod) |
|
||||||
|
| **`ENCRYPTION_KEY`** | **Clé hex 32 bytes chiffrant les credentials IMAP/SMTP (feature mail)** | placeholder | ✅ — doit rester **stable**, sinon les credentials mail stockés deviennent illisibles |
|
||||||
|
| **`LOCK_DSN`** | **Store de verrous Symfony pour la sync mail (anti-chevauchement)** | `flock` | `flock` suffit |
|
||||||
|
|
||||||
|
> **Messagerie** : `ENCRYPTION_KEY` et `LOCK_DSN` sont introduites par l'intégration mail.
|
||||||
|
> Détails de config et cron de synchronisation : `docs/mail-integration.md` et `docs/mail-cron-setup.md`.
|
||||||
|
> Générer une clé : `php -r "echo bin2hex(random_bytes(32));"`.
|
||||||
|
|
||||||
## Commandes
|
## Commandes
|
||||||
|
|
||||||
### Docker
|
### Docker
|
||||||
@@ -218,13 +241,19 @@ docker exec -u www-data php-lesstime-fpm php bin/console app:generate-api-token
|
|||||||
|
|
||||||
## Déploiement
|
## Déploiement
|
||||||
|
|
||||||
1. Déployer le code sur le serveur
|
La prod tourne en **Docker** : l'image est buildée par la CI Gitea sur push de tag `v*`
|
||||||
2. `composer install --no-dev --optimize-autoloader`
|
(`gitea.malio.fr/malio-dev/lesstime:<tag>`), puis déployée par le script `deploy.sh` sur
|
||||||
3. `php bin/console doctrine:migrations:migrate --no-interaction`
|
le serveur (dossier `/var/www/lesstime`, container `lesstime-app`).
|
||||||
4. `php bin/console cache:clear --env=prod`
|
|
||||||
5. `cd frontend && npm install && npm run build:dist`
|
```bash
|
||||||
6. `docker restart nginx-lesstime`
|
# Sur le serveur, depuis /var/www/lesstime
|
||||||
7. Ouvrir le port 8082 sur le firewall (LAN uniquement)
|
sudo ./deploy.sh # déploie la dernière image (latest)
|
||||||
|
sudo ./deploy.sh v0.4.2 # déploie une version précise
|
||||||
|
```
|
||||||
|
|
||||||
|
Le script active la maintenance, pull l'image, redémarre le container, lance les migrations
|
||||||
|
et vide le cache. Guide complet (première installation, BDD, Nginx, JWT, rollback) :
|
||||||
|
**`doc/deployment-docker.md`**.
|
||||||
|
|
||||||
## Licence
|
## Licence
|
||||||
|
|
||||||
|
|||||||
@@ -3,44 +3,24 @@
|
|||||||
## Vue d'ensemble
|
## Vue d'ensemble
|
||||||
|
|
||||||
La synchronisation IMAP est déclenchée par un cron OS toutes les 10 minutes.
|
La synchronisation IMAP est déclenchée par un cron OS toutes les 10 minutes.
|
||||||
Elle appelle la commande Symfony `app:mail:sync` qui s'exécute dans le container PHP.
|
Elle appelle la commande Symfony `app:mail:sync` qui s'exécute **dans le container PHP**.
|
||||||
|
|
||||||
Un Symfony Lock (`mail.sync`, TTL 10 min, store `flock` via `LOCK_DSN=flock`) empêche
|
Un Symfony Lock (`mail.sync`, TTL 10 min, store `flock` via `LOCK_DSN=flock`) empêche
|
||||||
les runs de se chevaucher si une sync prend plus de 10 min.
|
les runs de se chevaucher si une sync prend plus de 10 min.
|
||||||
|
|
||||||
|
> **Dev vs prod** — en dev le container s'appelle `php-lesstime-fpm` et on passe par `make`.
|
||||||
|
> En **production** le container s'appelle `lesstime-app` (service `app` du `docker-compose.yml`
|
||||||
|
> dans `/var/www/lesstime`), il n'y a **pas de `make`** : tout passe par `docker compose` / `docker exec`.
|
||||||
|
|
||||||
## Prérequis
|
## Prérequis
|
||||||
|
|
||||||
- Container `php-lesstime-fpm` démarré (`make start`)
|
- `MailConfiguration.enabled = true` (configurable depuis l'admin — onglet « Mail »)
|
||||||
- `MailConfiguration.enabled = true` (configurable depuis l'admin — Phase 7)
|
- `ENCRYPTION_KEY` (clé hex 32 bytes) défini dans l'environnement :
|
||||||
- `ENCRYPTION_KEY` défini dans `infra/dev/.env.docker.local` (ou production env)
|
- **dev** : `infra/dev/.env.docker.local`
|
||||||
|
- **prod** : `/var/www/lesstime/.env`
|
||||||
## Installation du cron
|
- Container démarré :
|
||||||
|
- **dev** : `make start` (container `php-lesstime-fpm`)
|
||||||
Sur la **machine hôte** (pas dans le container) :
|
- **prod** : déployé via `sudo ./deploy.sh` (container `lesstime-app`)
|
||||||
|
|
||||||
```bash
|
|
||||||
crontab -e
|
|
||||||
```
|
|
||||||
|
|
||||||
Ajouter la ligne suivante (adapter le chemin) :
|
|
||||||
|
|
||||||
```cron
|
|
||||||
*/10 * * * * cd /home/r-dev/malio-dev/Lesstime && make mail-sync >> /var/log/lesstime-mail-sync.log 2>&1
|
|
||||||
```
|
|
||||||
|
|
||||||
Ou directement via `docker exec` (sans dépendance à `make`) :
|
|
||||||
|
|
||||||
```cron
|
|
||||||
*/10 * * * * docker exec php-lesstime-fpm php bin/console app:mail:sync >> /var/log/lesstime-mail-sync.log 2>&1
|
|
||||||
```
|
|
||||||
|
|
||||||
### Avec un utilisateur système dédié
|
|
||||||
|
|
||||||
Si le cron est configuré pour un utilisateur système spécifique (ex: `www-data` ou `deploy`) :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo crontab -u deploy -e
|
|
||||||
```
|
|
||||||
|
|
||||||
## Variables d'environnement nécessaires
|
## Variables d'environnement nécessaires
|
||||||
|
|
||||||
@@ -49,63 +29,100 @@ sudo crontab -u deploy -e
|
|||||||
| `ENCRYPTION_KEY` | Clé hex 32 bytes pour déchiffrer le password IMAP | `$(php -r "echo bin2hex(random_bytes(32));")` |
|
| `ENCRYPTION_KEY` | Clé hex 32 bytes pour déchiffrer le password IMAP | `$(php -r "echo bin2hex(random_bytes(32));")` |
|
||||||
| `LOCK_DSN` | DSN du store de verrous Symfony | `flock` (défaut, fichier local) |
|
| `LOCK_DSN` | DSN du store de verrous Symfony | `flock` (défaut, fichier local) |
|
||||||
|
|
||||||
La clé doit être la même que celle utilisée pour chiffrer le password lors de la configuration.
|
La clé `ENCRYPTION_KEY` doit être **identique** à celle utilisée pour chiffrer le password
|
||||||
|
lors de la configuration depuis l'admin. Si elle change, les credentials stockés deviennent illisibles.
|
||||||
|
|
||||||
## Checklist setup production
|
---
|
||||||
|
|
||||||
1. [ ] Définir `ENCRYPTION_KEY` dans les variables d'environnement production
|
## Dev
|
||||||
2. [ ] Créer le compte mail dédié (ex: `lesstime@votre-domaine.fr`) chez OVH
|
|
||||||
3. [ ] Accéder à `/admin` → onglet "Mail" → renseigner les credentials IMAP/SMTP
|
|
||||||
4. [ ] Cliquer "Tester la connexion" → vérifier le succès
|
|
||||||
5. [ ] Cocher "Activer la synchronisation" → Enregistrer
|
|
||||||
6. [ ] Installer le cron OS (voir section "Installation du cron")
|
|
||||||
7. [ ] Vérifier les logs après la première sync : `make logs-dev` (chercher `mail.sync`)
|
|
||||||
|
|
||||||
## Commandes utiles
|
### Lancer une sync à la main
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Sync complète (toutes les boîtes)
|
make mail-sync # sync complète (toutes les boîtes)
|
||||||
make mail-sync
|
make mail-sync FOLDER=INBOX # un seul dossier (doit déjà exister en base)
|
||||||
|
make mail-sync DRYRUN=1 # simulation (dry-run, pas d'écriture BDD)
|
||||||
|
```
|
||||||
|
|
||||||
# Sync d'un seul dossier (le dossier doit déjà exister en base)
|
Ou directement dans le container :
|
||||||
make mail-sync FOLDER=INBOX
|
|
||||||
|
|
||||||
# Simulation (dry-run, pas d'écriture BDD)
|
```bash
|
||||||
make mail-sync DRYRUN=1
|
|
||||||
|
|
||||||
# Directement dans le container
|
|
||||||
docker exec php-lesstime-fpm php bin/console app:mail:sync
|
docker exec php-lesstime-fpm php bin/console app:mail:sync
|
||||||
docker exec php-lesstime-fpm php bin/console app:mail:sync --folder=INBOX
|
docker exec php-lesstime-fpm php bin/console app:mail:sync --folder=INBOX
|
||||||
docker exec php-lesstime-fpm php bin/console app:mail:sync --dry-run
|
docker exec php-lesstime-fpm php bin/console app:mail:sync --dry-run
|
||||||
```
|
```
|
||||||
|
|
||||||
## Logs
|
### Logs (dev)
|
||||||
|
|
||||||
Les logs Symfony sont dans `var/log/dev.log` (ou `prod.log` en production).
|
|
||||||
Suivre les logs en temps réel :
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make logs-dev
|
make logs-dev # tail -f var/log/dev.log
|
||||||
```
|
```
|
||||||
|
|
||||||
Les messages loggés par `MailSyncService` sont préfixés `mail.sync`.
|
Les messages loggés par `MailSyncService` sont préfixés `mail.sync`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Production
|
||||||
|
|
||||||
|
En prod, l'app tourne dans le container `lesstime-app` déployé par `sudo ./deploy.sh`
|
||||||
|
(dossier `/var/www/lesstime`). La commande s'exécute en tant que `www-data` (uid 33),
|
||||||
|
comme les migrations lancées par `deploy.sh`.
|
||||||
|
|
||||||
|
### Lancer une sync à la main
|
||||||
|
|
||||||
|
Depuis `/var/www/lesstime` :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo docker compose exec -T -u www-data app php bin/console app:mail:sync
|
||||||
|
sudo docker compose exec -T -u www-data app php bin/console app:mail:sync --folder=INBOX
|
||||||
|
sudo docker compose exec -T -u www-data app php bin/console app:mail:sync --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
|
### Installer le cron
|
||||||
|
|
||||||
|
Sur la **machine hôte** (pas dans le container). Comme `docker` requiert `sudo` en prod,
|
||||||
|
installer le cron sous root :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo crontab -e
|
||||||
|
```
|
||||||
|
|
||||||
|
Ajouter :
|
||||||
|
|
||||||
|
```cron
|
||||||
|
*/10 * * * * cd /var/www/lesstime && docker compose exec -T -u www-data app php bin/console app:mail:sync >> /var/log/lesstime-mail-sync.log 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
> Le crontab de root exécute déjà les commandes en root → pas de `sudo` à l'intérieur de la ligne cron.
|
||||||
|
> La commande est **idempotente** (UIDs uniques en base) : la relancer ne duplique pas les données.
|
||||||
|
|
||||||
|
### Logs (prod)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /var/www/lesstime
|
||||||
|
docker compose logs -f --tail=100 app # logs container
|
||||||
|
docker compose exec app cat var/log/prod.log # log Symfony (volume lesstime_logs)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Checklist setup production
|
||||||
|
|
||||||
|
1. [ ] Définir `ENCRYPTION_KEY` (hex 32 bytes) et `LOCK_DSN=flock` dans `/var/www/lesstime/.env`
|
||||||
|
2. [ ] Créer le compte mail dédié (ex: `lesstime@votre-domaine.fr`) chez OVH
|
||||||
|
3. [ ] Accéder à `/admin` → onglet « Mail » → renseigner les credentials IMAP/SMTP
|
||||||
|
4. [ ] Cliquer « Tester la connexion » → vérifier le succès
|
||||||
|
5. [ ] Cocher « Activer la synchronisation » → Enregistrer
|
||||||
|
6. [ ] Lancer une sync manuelle pour valider (commande ci-dessus)
|
||||||
|
7. [ ] Installer le cron OS (voir « Installer le cron »)
|
||||||
|
8. [ ] Vérifier les logs après la première sync (`docker compose logs -f app`, chercher `mail.sync`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Sécurité
|
## Sécurité
|
||||||
|
|
||||||
- Le password IMAP est **toujours stocké chiffré** (libsodium secretbox)
|
- Le password IMAP est **toujours stocké chiffré** (libsodium secretbox)
|
||||||
- Les corps de mails, passwords et pièces jointes ne sont **jamais loggés**
|
- Les corps de mails, passwords et pièces jointes ne sont **jamais loggés**
|
||||||
- Le lock `flock` évite les runs parallèles (fichier dans `/tmp/sf.mail.sync.<hash>.lock`)
|
- Le lock `flock` évite les runs parallèles (fichier dans `/tmp/sf.mail.sync.<hash>.lock`)
|
||||||
|
|
||||||
## Rappels sécurité
|
|
||||||
|
|
||||||
- La page `/mail` et tous les endpoints `/api/mail/*` sont refusés aux `ROLE_CLIENT` exclusifs
|
- La page `/mail` et tous les endpoints `/api/mail/*` sont refusés aux `ROLE_CLIENT` exclusifs
|
||||||
- Le sidebar "Messagerie" est masqué pour les utilisateurs ROLE_CLIENT sans ROLE_USER
|
- Le sidebar « Messagerie » est masqué pour les utilisateurs `ROLE_CLIENT` sans `ROLE_USER`
|
||||||
- Le password IMAP est chiffré via libsodium secretbox avant stockage (jamais en clair en base)
|
- Les corps de mails sont sanitisés via DOMPurify avant affichage (`frontend/utils/sanitizeMailHtml.ts`)
|
||||||
- Les corps de mails sont sanitisés via DOMPurify avant affichage (voir `frontend/utils/sanitizeMailHtml.ts`)
|
- Les pixels de tracking distants sont remplacés par un placeholder
|
||||||
- Les pixels tracking distants sont remplacés par un placeholder
|
|
||||||
- Aucun body mail, password ou contenu de pièce jointe n'est loggé
|
|
||||||
|
|
||||||
## Production
|
|
||||||
|
|
||||||
En production, préférer un cron système ou un job scheduler (Kubernetes CronJob, ECS Scheduled Task, etc.).
|
|
||||||
La commande est idempotente : relancer plusieurs fois ne duplique pas les données (UIDs uniques en base).
|
|
||||||
|
|||||||
@@ -15,6 +15,13 @@ JWT_COOKIE_SAMESITE=lax
|
|||||||
JWT_TOKEN_TTL=86400
|
JWT_TOKEN_TTL=86400
|
||||||
JWT_COOKIE_TTL=86400
|
JWT_COOKIE_TTL=86400
|
||||||
|
|
||||||
|
# Mail (intégration IMAP/SMTP)
|
||||||
|
# Clé hex 32 bytes chiffrant les credentials mail stockés en base.
|
||||||
|
# Générer : php -r "echo bin2hex(random_bytes(32));" — doit rester STABLE.
|
||||||
|
ENCRYPTION_KEY=change-me
|
||||||
|
# Store de verrous Symfony pour la sync mail (anti-chevauchement du cron).
|
||||||
|
LOCK_DSN=flock
|
||||||
|
|
||||||
# CORS
|
# CORS
|
||||||
CORS_ALLOW_ORIGIN='^https?://project\.malio-dev\.fr$'
|
CORS_ALLOW_ORIGIN='^https?://project\.malio-dev\.fr$'
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user