# Livre blanc P&P
## Architecture technique, cybersécurité et plan de déploiement

**Version : 1.2 — Septembre 2026**
*(révision de la v1.1 : Docker repositionné comme standard d'exécution et non outil de versioning ; architecture initiale simplifiée ; durcissement PostgreSQL RLS ; correction du modèle de sécurité des webhooks Mollie ; clarification des secrets Docker/Compose ; sauvegardes et observabilité renforcées ; gates de production précisés)*

---

# Résumé exécutif

Plan&Play (P&P) est une plateforme SaaS multi-clubs qui héberge des données de mineurs (joueurs) et traite des paiements via Mollie. Ce document définit l'architecture cible, la chaîne de sécurité et le calendrier de déploiement.

**Principe directeur :** on ne sécurise pas la production en expérimentant dessus. On construit et on casse ailleurs, puis on reproduit ce qui a été validé.

**Les cinq risques prioritaires pour P&P, dans l'ordre :**

1. Fuite de données entre clubs (isolation multi-tenant) — risque n°1 compte tenu du modèle SaaS multi-clubs.
2. Non-conformité RGPD sur des données de mineurs — risque juridique et réputationnel, pas seulement technique.
3. Compromission ou rejeu des webhooks de paiement Mollie — risque financier direct.
4. Perte de données sans possibilité de restauration vérifiée.
5. Modification non tracée en production (accès direct, secret en clair, agent IA sur-privilégié).

Le reste du document détaille l'architecture et le plan en 14 semaines qui adressent ces cinq risques.

## Décisions structurantes de la v1.2

1. **Git reste la source de vérité pour le versioning.** Docker n'est pas utilisé pour versionner le code : il sert à empaqueter et reproduire l'environnement d'exécution.
2. **Docker est recommandé, pas dogmatique.** Si P&P sait garantir le même niveau de reproductibilité avec une autre méthode maîtrisée, Docker peut être remplacé. Le critère est la reproductibilité, pas l'outil.
3. **Démarrage à trois VPS, pas cinq.** Pour une petite équipe : un hôte NON-PROD (sandbox + préproduction isolées), un PROD-APP, un PROD-DATA. L'observabilité devient un quatrième hôte lorsque le pilote est stabilisé ou plus tôt si la criticité l'exige.
4. **La préproduction mutualisée avec la sandbox n'est pas une préproduction d'infrastructure.** Elle permet les tests applicatifs, mais pas de valider les changements noyau, firewall ou hôte ; ceux-ci doivent être validés sur un hôte réellement équivalent avant la production.
5. **Aucun outil de sécurité n'est une preuve de sécurité.** Les scanners créent des signaux ; les gates, revues, tests d'isolation, sauvegardes et exercices de restauration constituent le dispositif de maîtrise.

---

# 1. Objectif

P&P doit être conçu comme une plateforme SaaS capable d'héberger les données de plusieurs clubs, membres, responsables, entraîneurs et administrateurs.

La priorité n'est pas seulement de « faire fonctionner » P&P, mais de construire une plateforme isolée, reproductible, auditable, sauvegardée, surveillée, testable, récupérable après incident, résistante aux erreurs humaines, et capable de détecter les mauvaises pratiques de développement avant qu'elles arrivent en production.

---

# 2. Principes d'architecture

P&P doit disposer au minimum de trois environnements distincts : **sandbox → préproduction → production**.

Le passage entre environnements ne doit jamais être manuel (copie de fichiers). Il passe par : Git → CI → tests → scans sécurité → artefact d'exécution → registry/dépôt → déploiement. **Dans l'architecture proposée, l'artefact d'exécution est une image Docker. Git assure le versioning ; Docker assure la reproductibilité de l'environnement d'exécution.**

**Principe fondamental — un seul artefact :** le même artefact Docker est testé en préproduction puis déployé en production, sans recompilation.

```text
planplay-api:2026.09.17-6f24a81
```

est testée en préproduction. Lorsqu'elle est validée, c'est exactement cette même image qui part en production.

---

# 3. Architecture générale

```text
                         INTERNET
                            |
                       DNS P&P
                            |
                    Reverse Proxy HTTPS (Caddy)
                            |
               +------------+------------+
               |                         |
            FRONTEND                   API
               |                         |
               +------------+------------+
                            |
                         BACKEND
                            |
                 +----------+---------+
                 |                    |
              PostgreSQL            Redis
                 |
            sauvegardes chiffrées
                 |
         stockage hors serveur
```

Infrastructure de sécurité associée :

```text
Git Repository
       |
CI/CD SECURITY PIPELINE
  tests unitaires, tests intégration, Semgrep, Gitleaks, Trivy,
  audit dépendances, SBOM, build Docker
       |
PREPRODUCTION
  OWASP ZAP, tests fonctionnels, tests manuels
       |
APPROBATION HUMAINE
       |
PRODUCTION
```

---

# 4. Architecture des serveurs

Ne pas installer toute la plateforme sur le VPS P&P actuel : cela crée une cible unique (voir section 5).

## Architecture initiale recommandée — 3 VPS

### Serveur 1 — P&P-NONPROD

Héberge **deux stacks strictement distinctes** :

- `pnp-sandbox` : développement, formation, tests destructifs ;
- `pnp-preprod` : validation applicative d'une release candidate.

Chaque stack possède ses propres réseaux Docker, volumes, base PostgreSQL, Redis, noms DNS et secrets. Aucun volume ou secret n'est partagé entre sandbox et préproduction.

Contenu : Docker Engine, Docker Compose, Caddy, deux stacks frontend/backend, PostgreSQL sandbox, PostgreSQL préproduction, Redis distincts, Mailpit pour sandbox, outils de test et scanners.

Taille de départ indicative : **4 à 8 vCPU, 8 à 16 GB RAM, 100 GB SSD**, à confirmer par mesure réelle. Prévoir des limites CPU/RAM par service pour empêcher un test sandbox d'affamer la préproduction.

**Limite importante :** une préproduction hébergée sur le même noyau et le même firewall que la sandbox n'est pas suffisante pour valider des changements d'infrastructure hôte. Avant toute modification de Docker, firewall, kernel, stockage ou réseau de production, reproduire le test sur un hôte temporaire équivalent ou séparer P&P-PREPROD sur son propre VPS.

### Serveur 2 — P&P-PROD-APP

Exposé à Internet uniquement via 80/443. Héberge Caddy, frontend, backend/API et workers. Ne contient pas PostgreSQL de production.

### Serveur 3 — P&P-PROD-DATA

Jamais accessible directement depuis Internet. Héberge PostgreSQL, Redis de production et les mécanismes locaux nécessaires à la sauvegarde. Seul PROD-APP et les flux d'administration explicitement autorisés peuvent y accéder.

```text
PROD APP  10.20.0.10  ->  PROD DATA 10.20.0.20:5432   ALLOW
PROD APP  10.20.0.10  ->  PROD DATA 10.20.0.20:6379   ALLOW
tout le reste                                           DENY
```

Redis contenant des sessions ou données applicatives reste sur PROD-DATA.

## Serveur 4 — OBSERVABILITÉ / SECURITY — à ajouter

À créer dès que le pilote est stabilisé, ou plus tôt si le niveau de service l'exige. Il collecte métriques, logs centralisés, audit, alertes et uptime (par exemple Grafana, Prometheus, Loki, Uptime Kuma, Alertmanager).

**Ne pas considérer P&P-NONPROD comme destination définitive des logs de sécurité de production.** Un incident sur un serveur de test ne doit pas supprimer ou altérer les traces de production.

## Pourquoi cette simplification ?

Le but est d'éviter que l'équipe apprenne simultanément cinq hôtes, plusieurs stacks et plusieurs outils nouveaux. L'isolation critique est conservée là où elle compte immédiatement : **production applicative séparée des données**, et **production séparée du non-production**. L'observabilité est ensuite extraite vers son propre hôte.

---

# 5. Pourquoi ne pas tout mettre sur un seul VPS

Une compromission de l'application donnerait alors accès à l'application, la base de données, les logs, les sauvegardes et le monitoring en même temps : cible unique extrêmement intéressante. L'architecture proposée réduit le **blast radius** — la quantité de systèmes compromis quand un composant tombe.

---

# 6. Système d'exploitation

Ubuntu Server 24.04 LTS sur tous les VPS P&P, pour une base homogène.

## 6.1 Pourquoi Docker alors que P&P possède déjà Git ?

Git et Docker répondent à deux problèmes différents :

```text
Git     = versionner le code, la configuration et l'historique des changements
Docker  = figer et reproduire l'environnement d'exécution
```

Le bénéfice attendu n'est donc pas le versioning. Il est d'éviter qu'une même révision Git tourne avec des versions de runtime ou des bibliothèques différentes en sandbox, préproduction et production.

Docker reste **un choix d'implémentation**, pas une exigence métier. Si l'équipe maîtrise mieux une alternative qui garantit :

- la même version de runtime partout ;
- des dépendances reproductibles ;
- des déploiements immuables ;
- un rollback fiable ;
- une configuration versionnée ;
- des environnements reconstruisibles automatiquement ;

alors cette alternative peut remplacer Docker. La décision doit être fondée sur la capacité opérationnelle de l'équipe, pas sur une préférence de technologie.

---

# 7. Configuration initiale d'un serveur

À reproduire sur chaque VPS.

```bash
sudo apt update
sudo apt upgrade -y
sudo apt autoremove -y

sudo apt install -y \
  curl wget git ca-certificates gnupg jq unzip fail2ban
```

## Compte d'administration

Ne pas administrer quotidiennement avec root.

```bash
sudo adduser pnpadmin
sudo usermod -aG sudo pnpadmin

sudo mkdir -p /home/pnpadmin/.ssh
sudo nano /home/pnpadmin/.ssh/authorized_keys   # coller la clé publique

sudo chmod 700 /home/pnpadmin/.ssh
sudo chmod 600 /home/pnpadmin/.ssh/authorized_keys
sudo chown -R pnpadmin:pnpadmin /home/pnpadmin/.ssh
```

## SSH

```bash
sudo nano /etc/ssh/sshd_config.d/99-pnp-security.conf
```

```text
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
X11Forwarding no
AllowUsers pnpadmin
```

```bash
sudo sshd -t
sudo systemctl restart ssh
```

**Important :** garder la session SSH actuelle ouverte et tester une deuxième connexion avant de fermer la première.

## Firewall

Ne jamais considérer Docker + UFW comme sûr par défaut : Docker manipule directement iptables et peut publier un port en contournant les règles UFW.

Principe : seuls 80/443 sont publics, tout passe par Caddy, le reste reste sur le réseau Docker interne. Les applications ne doivent jamais publier directement 3000, 3001, 5432, 6379 sur Internet.

```yaml
# Mauvais
ports:
  - "5432:5432"

# Production recommandée
expose:
  - "5432"
```

**Correction v1.1 — la solution manquait en v1.0 :** UFW seul ne suffit pas car les règles `iptables` ajoutées par Docker sont insérées avant les règles UFW. Deux options, à documenter et tester en sandbox avant la préproduction :

- Restreindre les publications de port à la boucle locale : `127.0.0.1:5432:5432` plutôt que `5432:5432`, pour tout service qui doit rester interne mais garder un mapping de port.
- Ou insérer les règles de filtrage dans la chaîne `DOCKER-USER` (chaîne lue avant le NAT de Docker), qui est le point d'ancrage documenté par Docker lui-même pour appliquer un firewall par-dessus les conteneurs.

---

# 8. Installation Docker

```bash
sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
  -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

sudo tee /etc/apt/sources.list.d/docker.sources >/dev/null <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF

sudo apt update
sudo apt install -y \
  docker-ce docker-ce-cli containerd.io \
  docker-buildx-plugin docker-compose-plugin

sudo systemctl status docker
sudo docker run hello-world
```

## Organisation des fichiers

```bash
sudo mkdir -p /srv/planplay/{compose,data,logs,backups}
sudo chown -R pnpadmin:pnpadmin /srv/planplay
```

## Réseau Docker

```bash
docker network create pnp_internal
```

Ne jamais publier PostgreSQL publiquement.

### Important — accès Docker

L'ajout d'un utilisateur au groupe `docker` lui donne pratiquement des capacités équivalentes à root sur l'hôte. Pour les comptes humains d'administration, conserver `sudo docker ...` par défaut tant qu'un besoin opérationnel contraire n'est pas démontré. Ne pas distribuer l'appartenance au groupe `docker` comme une simple commodité.

---

# 9. PostgreSQL et Redis

```yaml
services:
  postgres:
    image: postgres:16
    restart: unless-stopped
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - backend
    secrets:
      - postgres_password

  redis:
    image: redis:7-alpine
    restart: unless-stopped
    command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
    volumes:
      - ./secrets/redis.conf:/usr/local/etc/redis/redis.conf:ro
    networks:
      - backend

secrets:
  postgres_password:
    file: ./secrets/postgres_password

volumes:
  postgres_data:

networks:
  backend:
    internal: true
```

Aucun `ports:` pour PostgreSQL ou Redis en production ; les deux restent sur le réseau interne.

**Correction v1.1 — contradiction corrigée :** la v1.0 passait le mot de passe Redis via `command: --requirepass ${REDIS_PASSWORD}`, ce qui l'expose en clair dans `docker inspect` et dans la sortie de `ps` sur l'hôte — exactement ce que la section sur les secrets interdit par ailleurs. Le mot de passe doit passer par un fichier `redis.conf` monté en lecture seule (`requirepass <mot de passe>` dans le fichier), jamais en argument de commande ou en variable d'environnement visible.

---

# 10. Caddy

Seul service public du serveur applicatif, sur 80/443.

```text
app.planandplay.example {
    reverse_proxy frontend:3000
}

api.planandplay.example {
    reverse_proxy backend:8000
}
```

---

# 11. Gestion des secrets

Interdiction absolue de tout secret en clair dans le code ou dans Git (`const password = "..."`, `DATABASE_PASSWORD=xxx` commité).

Les secrets proviennent idéalement d'un gestionnaire dédié (Infisical ou Hashicorp Vault) ou sont injectés au déploiement.

**Clarification v1.2 :** un `secret:` Docker Compose monté depuis un fichier hôte améliore l'exposition à l'intérieur du conteneur, mais **ne chiffre pas magiquement le secret sur l'hôte**. Le fichier source doit lui-même être protégé (`root:root`, permissions minimales), exclu de Git, sauvegardé de façon maîtrisée, et remplacé à terme par un gestionnaire de secrets lorsque l'équipe est prête.

Éviter également de placer des secrets en argument de ligne de commande ou dans des variables qui apparaissent facilement dans les outils d'inspection.

**Procédure en cas de fuite :** un secret détecté par Gitleaks dans un commit poussé, même sur une branche privée ou un dépôt privé, doit être considéré comme compromis dès l'instant où il a quitté la machine du développeur. La réponse correcte est la **rotation immédiate** du secret concerné ; la réécriture de l'historique Git (`git filter-repo`, force-push) est une mesure d'hygiène complémentaire, pas une remédiation suffisante à elle seule.

---

# 12. RGPD et conformité (section ajoutée en v1.1)

P&P traite des données de mineurs (joueurs) et des données de paiement, en Europe. Cette section était absente de la v1.0 ; elle est nécessaire avant l'ouverture en production, pas seulement à terme.

**Registre des traitements.** Documenter, par catégorie de donnée (identité, contact, données de paiement, données de mineur), la finalité, la base légale, la durée de conservation et les destinataires.

**Base légale spécifique aux mineurs.** Le consentement pour un mineur passe généralement par le titulaire de l'autorité parentale ; documenter le flux de consentement (inscription par un parent, lien joueur-représentant légal) dans `SECURITY.md`.

**Durées de conservation applicatives**, distinctes des durées de rétention des sauvegardes (section 20) : par exemple, anonymisation des données d'un membre X mois après la fin de son adhésion à un club, sauf obligation légale de conservation plus longue (comptabilité, paiements).

**Droits des personnes.** Implémentation technique — pas seulement une procédure manuelle — des droits d'accès, de rectification et d'effacement : un export des données d'un membre, et une suppression ou anonymisation qui respecte l'intégrité référentielle (ne pas casser l'historique comptable d'un club en supprimant un membre ayant payé).

**Sous-traitants.** Contrat de traitement des données (DPA) avec l'hébergeur des VPS et avec Mollie ; liste à jour dans le registre des traitements.

**Notification de violation.** Procédure documentée dans `INCIDENT_RESPONSE.md` pour notifier une violation de données sous 72 heures à l'autorité compétente lorsque le risque pour les personnes le requiert.

Ce point est à traiter en **P0**, en parallèle de l'inventaire (Phase 0), et non en P3 comme le suggérait implicitement la priorisation initiale — un club qui audite P&P avant de signer un contrat posera ces questions dès le départ.

---

# 13. Scanners de sécurité

## Gitleaks

Détecte API keys, mots de passe, tokens, clés privées, credentials avant leur arrivée dans Git.

```bash
docker run --rm \
  -v "$(pwd):/repo" \
  ghcr.io/gitleaks/gitleaks:latest \
  detect --source=/repo
```

Une détection fait échouer la CI. En cas de détection réelle, voir la procédure de rotation (section 11).

## Trivy

Analyse images Docker, packages OS, dépendances, configurations, vulnérabilités connues.

```bash
sudo apt-get install -y wget gnupg
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key \
  | gpg --dearmor \
  | sudo tee /usr/share/keyrings/trivy.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb generic main" \
  | sudo tee /etc/apt/sources.list.d/trivy.list
sudo apt-get update
sudo apt-get install -y trivy

trivy fs .
trivy image planplay-api:latest
```

**Correction v1.1 — gate CI réaliste :** un gate `--severity HIGH,CRITICAL --exit-code 1` sans nuance bloque la CI en permanence, car les images de base contiennent quasi toujours des CVE connues sans correctif disponible encore publié. Un gate qui échoue systématiquement finit par être désactivé ou contourné par l'équipe, ce qui annule l'intérêt du scanner. Gate recommandé :

```bash
trivy image \
  --severity HIGH,CRITICAL \
  --ignore-unfixed \
  --exit-code 1 \
  planplay-api:latest
```

Compléter par un fichier `.trivyignore` pour les exceptions ponctuelles, chaque ligne commentée avec une justification et une **date de revue** :

```text
# CVE-2026-XXXXX — pas de correctif upstream, revue prévue 2026-11-01
CVE-2026-XXXXX
```

Revoir ce fichier mensuellement ; une entrée sans date de revue expirée devient un blocage.

## Semgrep

Analyse le code source lui-même (SQL injection, XSS, command injection, mauvaises validations, authentification incorrecte, cryptographie incorrecte) — complémentaire à Trivy, qui regarde les composants tiers plutôt que le code écrit par l'équipe.

## Audit des dépendances

```bash
npm audit        # Node
pip-audit         # Python
composer audit    # PHP
```

Chaque dépendance verrouillée par `package-lock.json`, `poetry.lock` ou `composer.lock` selon la technologie.

## SBOM

Chaque release génère une Software Bill of Materials, pour savoir exactement quelle librairie, quelle version, quelle image étaient présentes dans P&P à un instant donné.

```bash
trivy image --format cyclonedx --output sbom.json planplay-api:release
```

## OWASP ZAP

Attaque l'application en fonctionnement (headers manquants, XSS, cookies mal configurés, endpoints exposés). Lancé contre sandbox et préproduction uniquement — jamais de test agressif automatique contre la production.

---

# 14. Pipelines CI/CD

## Pipeline de sécurité

```text
git push → lint → unit tests → Gitleaks → Semgrep →
dependency audit → Docker build → Trivy → SBOM → registry
```

Si une étape critique échoue : STOP, aucun déploiement.

## Pipeline de déploiement

```text
developer → Pull Request → code review → CI security → merge →
SANDBOX → tests → PREPRODUCTION → DAST/ZAP →
validation humaine → PRODUCTION
```

## Aucun accès direct à la production

Les développeurs ne corrigent pas une fonctionnalité par `ssh prod`, édition de fichier, ou `docker compose restart`. Le chemin normal est code → Git → Pull Request → tests → CI → déploiement. SSH en production reste un accès exceptionnel d'administration.

## Protection Git

Branches `main`, `develop`, `feature/*`, `hotfix/*`. `main` protégée : `git push --force` interdit ; Pull Request + tests verts + scans verts + review exigés avant merge.

---

# 15. Agent d'analyse du code (AppSec)

Un agent IA peut être extrêmement utile, mais **il n'est pas l'autorité finale**.

```text
Git repository
      |
      v
Security Agent
      |
      +-- résultats Semgrep
      +-- résultats Trivy
      +-- résultats Gitleaks
      +-- résultats de tests
      +-- diff de la PR
      |
      v
Rapport
```

L'agent peut expliquer pourquoi une vulnérabilité existe, quel serait le scénario d'exploitation, quelle partie du code est concernée, quelle correction serait appropriée, et s'il s'agit d'un faux positif.

Il n'a **jamais** accès direct : aux secrets de production, au mot de passe PostgreSQL de production, à un shell root en production, ni au droit de déployer automatiquement.

Rôle exact de l'agent :

```text
READ  repository, résultats CI, vulnérabilités, dépendances

WRITE  rapports de sécurité, commentaires de PR, patchs proposés

NO     base de données de production
       secrets de production
       déploiement automatique en production
```

Cette séparation est fondamentale : findings → patch proposé → Pull Request → tests → validation humaine → merge.

---

# 16. Isolation multi-tenant et RBAC

P&P est destinée à plusieurs clubs. L'attaque la plus dangereuse est qu'un membre du Club A accède aux informations du Club B.

Tous les objets doivent être associés explicitement à un tenant (`club_id`), et le backend doit contrôler systématiquement : utilisateur → appartenance → club → permission → ressource. Ne jamais considérer un identifiant reçu du frontend comme preuve d'autorisation.

**Exemple.** Pour `GET /clubs/15/members/1234`, le backend vérifie que l'utilisateur est authentifié **et** appartient au club 15 **et** que le membre 1234 appartient au club 15 **et** que l'utilisateur possède la permission `READ_MEMBER` — pas seulement que le membre 1234 existe.

**Défense en profondeur avec Row Level Security (RLS).** Le contrôle applicatif seul (une clause `WHERE club_id = ...` oubliée) constitue un point de défaillance unique sur le risque n°1 de la plateforme. PostgreSQL fournit une seconde ligne de défense :

```sql
ALTER TABLE members ENABLE ROW LEVEL SECURITY;
ALTER TABLE members FORCE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation ON members
  USING (club_id = current_setting('app.current_club_id', true)::int)
  WITH CHECK (club_id = current_setting('app.current_club_id', true)::int);
```

### Conditions indispensables

- Le **rôle applicatif ne doit pas être propriétaire des tables**.
- Le rôle applicatif ne doit jamais avoir l'attribut `BYPASSRLS`.
- Utiliser un rôle séparé de migration/DDL, non utilisé par l'application.
- Positionner le tenant **dans la transaction**, avec `SET LOCAL`, après authentification et avant toute requête tenantée.
- Avec un pool de connexions, tester explicitement qu'aucun contexte de tenant ne fuit d'une requête vers la suivante.
- Les opérations superadmin ou cross-tenant doivent passer par un chemin distinct, audité et volontaire, pas par une désactivation opportuniste de RLS.

Exemple conceptuel :

```sql
BEGIN;
SET LOCAL app.current_club_id = '15';
-- requêtes de la transaction
COMMIT;
```

Même avec RLS, conserver les contrôles applicatifs et les tests `Club A cannot read Club B`. RLS est une défense en profondeur, pas un substitut au modèle d'autorisation.

Test permanent et automatique à conserver en CI (section 22, Phase 5) :

```text
Club A cannot read Club B member
```

## RBAC

Rôles : SUPERADMIN P&P, CLUB_ADMIN, COMMITTEE, COACH, TEAM_MANAGER, MEMBER, PARENT, PLAYER. Permissions indépendantes des rôles (`MEMBER_READ`, `MEMBER_WRITE`, `PAYMENT_READ`, `PAYMENT_WRITE`, `TEAM_READ`, `TEAM_MANAGE`, `DOCUMENT_READ`, `DOCUMENT_WRITE`). Éviter `if admin = true` dispersé dans le code.

## Audit log

Les actions importantes produisent une trace non modifiable par un administrateur de club :

```text
2026-09-17T10:34:17  user:784  club:NSRC  action:MEMBER_ROLE_CHANGE
target:member 421  before:PLAYER  after:COACH  ip:xxx  request_id:xxx
```

## Logs applicatifs

Logs structurés (JSON, avec `request_id`). Ne jamais enregistrer mot de passe, token complet, numéro de carte, secret API, cookie de session.

---

# 17. Sécurité des paiements Mollie

Le traitement des paiements doit distinguer **les webhooks classiques Mollie** et les mécanismes **Next-gen Webhooks**.

## Webhooks classiques

Pour le flux classique, Mollie envoie notamment l'identifiant de la ressource modifiée. P&P ne doit pas faire confiance à un statut transmis par l'appel entrant : il utilise l'identifiant pour **relire l'état courant auprès de l'API Mollie avec ses propres credentials**, puis décide quoi faire.

Flux :

```text
POST webhook Mollie
      |
      v
extraire payment_id
      |
      v
retrouver ce payment_id dans P&P
      |
      v
GET payment auprès de l'API Mollie
      |
      v
vérifier mode / montant / devise / profil / metadata attendus
      |
      v
appliquer une transition idempotente
```

## Next-gen Webhooks

Mollie propose également des webhooks de nouvelle génération et des **signed webhooks** pour les scénarios où des payloads sont livrés directement. Si P&P adopte ce mécanisme, implémenter la vérification de signature **exactement selon la documentation Mollie du mode retenu**, avec gestion de rotation des secrets/clés.

Ne pas écrire dans l'architecture « vérification de signature obligatoire » sans préciser le type de webhook effectivement utilisé.

## Idempotence

Un webhook peut être livré plusieurs fois. Les transitions de paiement doivent être idempotentes :

- identifiant Mollie unique côté P&P ;
- état courant vérifié avant transition ;
- aucune cotisation créditée deux fois ;
- aucune notification utilisateur envoyée deux fois à cause d'un retry.

## Corrélation métier

Avant de considérer un paiement comme valide, contrôler au minimum :

- que l'identifiant Mollie correspond à un paiement créé par P&P ;
- que le mode `test/live` correspond à l'environnement ;
- que le montant et la devise correspondent à la cotisation attendue ;
- que le club/compte connecté attendu est celui du paiement ;
- que les métadonnées métier sont cohérentes.

## Séparation des clés

Les credentials Mollie test et production sont strictement séparés. Aucun secret de production n'est présent en sandbox ou préproduction.

## Rapprochement

Un job périodique compare les paiements P&P et Mollie pour détecter les webhooks manqués, statuts divergents et paiements orphelins.

## Logging

Journaliser l'ID d'événement/paiement, la transition appliquée, le `request_id` et le résultat du traitement. Ne jamais journaliser une clé API ou des données de carte.


---

# 18. Sécurité des comptes P&P

Minimum : Argon2id pour les mots de passe, MFA pour les administrateurs, rate limiting, vérification d'email, protection anti brute-force, sessions invalidables, rotation des tokens, tokens de réinitialisation courts, journalisation des connexions.

MFA obligatoire pour les superadmins P&P, les administrateurs infrastructure, CI/CD et base de données ; fortement recommandé pour les administrateurs de club.

## Headers HTTP

Minimum : `Strict-Transport-Security`, `Content-Security-Policy`, `X-Content-Type-Options`, `Referrer-Policy`, `Permissions-Policy`, adaptés au frontend P&P.

---

# 19. Monitoring

Minimum surveillé : CPU, RAM, disque, base de données, statuts HTTP, latence, santé des conteneurs, expiration de certificat, succès des sauvegardes.

Alertes : disque > 80 %, RAM > 85 %, CPU élevée durablement, taux de HTTP 500 élevé, base inaccessible, sauvegarde échouée, certificat proche de l'expiration, service down.

---

# 20. Sauvegardes et restauration

Un snapshot VPS seul n'est pas suffisant. Minimum : sauvegarde PostgreSQL quotidienne, sauvegarde des fichiers, copie hors VPS, snapshot avant les opérations importantes.

```bash
# Exemple si pg_dump est installé sur l'hôte :
pg_dump -Fc -h <adresse-privee-db> -U planplay planplay \
  > planplay_$(date +%Y%m%d_%H%M).dump

# Si PostgreSQL tourne dans un conteneur, documenter explicitement
# le nom du service et la méthode de dump/restore utilisée.
```

Le runbook de sauvegarde doit être exécutable sans improvisation et inclure : chiffrement, transfert hors VPS, contrôle de taille, contrôle d'intégrité, alerte en cas d'échec et procédure de restauration.

Chiffrer les sauvegardes, puis les envoyer vers un stockage distinct du VPS.

**Ajout v1.1 — RPO/RTO explicites.** Une sauvegarde quotidienne implique, en l'état, jusqu'à 24 heures de transactions potentiellement perdues en cas d'incident juste avant le prochain dump (RPO = 24 h). Sur une plateforme qui encaisse des paiements, ce chiffre doit être une décision assumée et documentée dans `BACKUP.md`, pas une conséquence implicite du script. Si 24 h de perte n'est pas acceptable, activer l'archivage continu des WAL PostgreSQL (`archive_mode = on` + expédition des segments vers le stockage distant), qui ramène le RPO à quelques minutes. Documenter également le RTO cible (temps de restauration) pour que l'équipe sache ce qu'elle vise avant l'incident, pas pendant.

## Rétention

7 sauvegardes quotidiennes, 4 hebdomadaires, 12 mensuelles — à ajuster selon les exigences légales et métiers, et à recroiser avec les durées de conservation RGPD de la section 12 (une sauvegarde ne doit pas devenir un moyen de contourner un effacement RGPD).

## Test de restauration

Une sauvegarde jamais restaurée n'est pas une sauvegarde vérifiée. Tous les mois : backup production → environnement temporaire → restore → test applicatif, pour prouver que la restauration fonctionne réellement.

---

# 21. Migrations de base de données et rollback (section ajoutée en v1.1)

La v1.0 demandait un rollback réellement testé (Phase 10) sans donner la méthode : avec une base PostgreSQL partagée entre l'ancienne et la nouvelle version applicative, une migration destructive (colonne supprimée, colonne renommée, contrainte NOT NULL ajoutée sans valeur par défaut) rend le rollback applicatif impossible, même si l'ancienne image Docker est toujours disponible.

**Pattern expand/contract.** Toute migration qui modifie la forme des données se fait en au moins deux releases distinctes :

1. **Expand** — ajouter la nouvelle colonne/table sans toucher à l'ancienne ; déployer une version du code qui écrit dans les deux ; laisser tourner le temps de valider.
2. **Contract** — une fois la nouvelle version stable en production, déployer une release ultérieure qui bascule totalement sur le nouveau schéma, puis supprime l'ancienne colonne.

Avec ce pattern, revenir de l'étape « expand » à la version précédente ne casse rien : l'ancien code ignore simplement la nouvelle colonne. C'est ce qui rend un rollback réellement exécutable et pas seulement documenté.

## Mise à jour des serveurs

Jamais `apt upgrade` directement et spontanément en production. Processus : sandbox (update + test) → préproduction (update + test) → snapshot de production → production.

## Déploiement Blue/Green à terme

Lorsque P&P devient critique : BLUE (version en production) / GREEN (nouvelle version) testée séparément, puis bascule de Caddy de BLUE vers GREEN, avec rollback GREEN → BLUE en quelques secondes.

---

# 22. Environnements et domaines

```text
SANDBOX      sandbox.planandplay.xxx / api.sandbox.planandplay.xxx
PREPROD      staging.planandplay.xxx / api.staging.planandplay.xxx
PRODUCTION   app.planandplay.xxx / api.planandplay.xxx
```

Ne jamais différencier production et sandbox par une simple variable cachée.

---

# 23. Données de test

La sandbox ne doit jamais recevoir un dump complet de la base réelle contenant des données personnelles de membres (`pg_dump production > sandbox.sql` est interdit). Utiliser des données synthétiques ou anonymisées — cette règle rejoint directement les obligations RGPD de la section 12.

---

# 24. Plan de déploiement — 14 semaines

Il serait irréaliste de demander à une équipe découvrant une partie de ces outils de tout mettre en production en deux semaines. 14 semaines, avec une période volontairement importante d'apprentissage et d'erreurs.

| Semaine | Action | Environnement |
|---|---|---|
| 1 | Audit, inventaire, **début du registre RGPD** | existant |
| 2-3 | Formation Git/Linux/Docker/CI-CD | formation |
| 3-4 | Construction sandbox (+ préprod si mutualisée initialement) | sandbox |
| 5 | Gitleaks / Trivy / Semgrep, baseline sécurité | sandbox |
| 6 | Pipeline CI/CD | sandbox |
| 7 | Tests automatisés, **tests isolation multi-tenant + RLS** | sandbox |
| 8 | OWASP ZAP | sandbox |
| 9 | Validation préproduction ; décider si un VPS PREPROD dédié est nécessaire avant tests d'infrastructure | preprod |
| 10 | Simulations d'incidents, restauration, **rollback via expand/contract** | preprod |
| 11 | Infrastructure production (PROD-APP + PROD-DATA), réseau privé et sauvegardes | prod |
| 12 | Répétition générale, dry-run complet avec rollback réel | preprod + prod |
| 13 | Audit final, **revue RGPD et Mollie** | tous |
| 14 | Pilote en production (quelques clubs) | prod |

## Détail des phases (inchangé sur le fond, v1.0)

**Phase 0 — Cartographie.** Ne rien modifier en production. Inventorier code, serveurs, DNS, base de données, API, services externes (Mollie, emails), secrets, administrateurs, ports, conteneurs Docker, sauvegardes. Livrable : `P&P-INFRA-INVENTORY.md`. **Ajout v1.1 : démarrer en parallèle le registre des traitements RGPD (section 12).**

**Phase 1 — Formation.** Git, branches, Pull Requests, Docker, Docker Compose, Linux, SSH, logs, secrets, principes CI/CD. Objectif : que plusieurs personnes comprennent le système, pas une seule.

**Phase 2 — Construction sandbox.** Ubuntu, Docker, Caddy, PostgreSQL, Redis, P&P. Puis volontairement : arrêter un conteneur, le supprimer, restaurer, casser une variable d'environnement, tester un rollback. L'équipe apprend à diagnostiquer.

**Phase 3 — Outils sécurité.** Gitleaks, Trivy (avec `--ignore-unfixed`, voir section 13), Semgrep, audit de dépendances. Créer une baseline de sécurité. On accepte initialement de découvrir beaucoup d'alertes ; l'objectif est de comprendre les alertes, pas d'obtenir immédiatement 0 vulnérabilité.

**Phase 4 — CI/CD.** Pipeline push → tests → sécurité → build → registry → sandbox. Interdiction de déployer automatiquement en production à ce stade.

**Phase 5 — Tests applicatifs.** Tests unitaires, intégration, authentification, RBAC, **isolation multi-tenant et policies RLS**. Le test `Club A cannot read Club B member` devient automatique et permanent.

**Phase 6 — OWASP ZAP.** Contre la sandbox. Corriger progressivement headers de sécurité, cookies, endpoints exposés, problèmes d'authentification, XSS, injections.

**Phase 7 — Préproduction.** Déployer la stack P&P-PREPROD par procédure automatisée et à partir du même artefact que celui candidat à la production. Si sandbox et préproduction partagent encore P&P-NONPROD, les garder strictement isolées et créer un VPS PREPROD temporaire ou permanent avant tout test portant sur l'hôte, le réseau, Docker Engine, le firewall ou le stockage.

**Phase 8 — Simulations.** Incidents volontaires : conteneur backend détruit, version défectueuse, migration DB échouée, serveur indisponible, secret accidentellement commité, backup à restaurer. **Ajout v1.1 : ajouter un exercice « rollback après migration expand/contract » et un exercice « webhook Mollie dupliqué ».**

**Phase 9 — Architecture production.** Créer P&P-PROD-APP et P&P-PROD-DATA. Installer l'infrastructure sans encore migrer les utilisateurs.

**Phase 10 — Dry run.** Répétition complète : release → CI → preprod → tests → backup → déploiement production → rollback. Le rollback doit être réellement exécuté, pas seulement documenté — et doit fonctionner parce que la migration a suivi le pattern expand/contract.

**Phase 11 — Audit final.** Vérifier ports, firewall, SSH, secrets, Docker, TLS, RBAC, MFA, sauvegardes, monitoring, logs, CI/CD, vulnérabilités, dépendances, OWASP. **Ajout v1.1 : vérifier également le registre RGPD, le DPA hébergeur, et la vérification de signature des webhooks Mollie.** Bloquer tout point critique.

**Phase 12 — Go live.** Déploiement progressif. Éviter de migrer tous les clubs immédiatement ; commencer avec quelques clubs pilotes, observer erreurs, latence, CPU, base de données, demandes de support, événements de sécurité, puis augmenter progressivement.

---

# 25. Conditions obligatoires avant GO LIVE

- [ ] backup automatique, avec RPO/RTO documentés
- [ ] restauration testée
- [ ] monitoring
- [ ] HTTPS
- [ ] MFA administrateurs
- [ ] secrets hors Git (y compris Redis — voir section 9)
- [ ] Gitleaks en CI, avec procédure de rotation en cas de détection
- [ ] SAST (Semgrep)
- [ ] dependency scanning
- [ ] container scanning (Trivy avec `--ignore-unfixed` et `.trivyignore` daté)
- [ ] tests unitaires
- [ ] tests isolation multi-tenant, **avec policies RLS actives**
- [ ] logs d'audit
- [ ] firewall (règles `DOCKER-USER` ou publication en `127.0.0.1`, pas UFW seul)
- [ ] rollback documenté **et exécutable** (migrations en expand/contract)
- [ ] rollback réellement testé
- [ ] préproduction fonctionnelle
- [ ] **registre des traitements RGPD à jour** *(ajout v1.1)*
- [ ] **DPA signé avec l'hébergeur** *(ajout v1.1)*
- [ ] **mécanisme Mollie conforme au type de webhook utilisé** : relecture API pour webhooks classiques ; vérification de signature si signed/Next-gen webhooks

---

# 26. Ce qu'il ne faut PAS faire

1. Installer directement les nouveaux outils de sécurité sur la production actuelle.
2. Donner à l'agent IA un accès root permanent.
3. Donner aux développeurs le mot de passe PostgreSQL de production.
4. Utiliser les données réelles des clubs en sandbox.
5. Mettre PostgreSQL directement sur Internet.
6. Faire confiance uniquement au firewall UFW alors que Docker publie des ports.
7. Corriger directement le code sur le serveur.
8. Faire les mises à jour directement en production.
9. Considérer un snapshot comme l'unique sauvegarde.
10. Considérer qu'un scan « 0 vulnérabilité » signifie que l'application est sûre.
11. Faire confiance à un webhook Mollie sans appliquer le mécanisme de vérification prévu par le flux réellement utilisé (relecture API pour le flux classique ; signature si applicable au flux Next-gen retenu).
12. *(ajout v1.1)* Traiter le RGPD comme un sujet de fin de projet plutôt que comme une condition de mise en production.

---

# 27. Architecture finale

```text
                         INTERNET
                            |
                       DNS / HTTPS
                            |
                          CADDY
                            |
                  P&P-PROD-APP VPS
                  +---------+---------+
                  |                   |
              FRONTEND               API
                                      |
                                   WORKERS
                                      |
                       private network only
                                      |
                              P&P-PROD-DATA
                              +-------------+
                              | PostgreSQL  |
                              | Redis       |
                              +-------------+
                                      |
                               encrypted backup
                                      |
                              external storage


DEV → Git → Pull Request → CI/CD
  [tests, Gitleaks, Semgrep, dependency scan, Docker build, Trivy, SBOM]
       |
    SANDBOX → PREPROD → ZAP → Human approval → PRODUCTION


             OBSERVABILITY VPS (phase suivante)
        +-----------+-----------+
        |           |           |
    Grafana       Loki      Alerting
```

---

# 28. Organisation humaine

Trois rôles à distinguer, même si une même personne les cumule temporairement :

- **Développement** — code, tests, Pull Requests.
- **Infrastructure** — serveurs, Docker, réseau, sauvegardes, déploiement.
- **Sécurité** — gestion des vulnérabilités, politique de sécurité, sécurité CI, réponse à incident, audit (RGPD inclus).

Un développeur ne devrait pas pouvoir simultanément écrire une modification, l'approuver, neutraliser les tests, et déployer en production sans aucun second regard.

---

# 29. Documentation obligatoire

Dans `/docs` du dépôt Git :

```text
ARCHITECTURE.md
DEPLOYMENT.md
BACKUP.md
RESTORE.md
INCIDENT_RESPONSE.md
SECURITY.md
SECRETS.md
ONBOARDING.md
OFFBOARDING.md
DATABASE.md
RUNBOOK_PRODUCTION.md
RGPD.md            (ajout v1.1 — registre des traitements, DPA, procédure de notification)
PAYMENTS.md         (ajout v1.1 — vérification webhooks, idempotence, rapprochement Mollie)
```

Ainsi, l'infrastructure P&P ne dépend pas de la mémoire d'une seule personne.

---

# 30. Priorités

**P0 — immédiatement**
inventaire, Git protégé, secrets, sauvegardes vérifiables, P&P-NONPROD, **registre RGPD**, cartographie Mollie

**P1**
CI/CD, Gitleaks, Trivy, Semgrep, tests automatisés, **tests isolation + RLS**

**P2**
préproduction, ZAP, RBAC, audit logs, monitoring, **flux webhooks Mollie vérifié et idempotent**

**P3**
base de données séparée, observabilité séparée, blue/green, agent de sécurité, SBOM complet

---

# 31. Notes de vérification technique de la v1.2

Les points suivants ont été revérifiés avant cette révision :

- Docker supporte officiellement Ubuntu 24.04 LTS ; sa documentation avertit explicitement que les ports publiés par Docker peuvent contourner UFW et recommande d'appliquer les règles appropriées via la chaîne `DOCKER-USER`.
- PostgreSQL précise que le propriétaire d'une table contourne normalement les policies RLS, d'où la séparation entre rôle propriétaire/migration et rôle applicatif, ainsi que l'usage de `FORCE ROW LEVEL SECURITY` lorsque pertinent.
- Mollie documente, pour les webhooks classiques, le flux consistant à recevoir l'ID de la ressource puis à récupérer son état actuel via l'API. Mollie propose également des Next-gen Webhooks et des signed webhooks : le mécanisme de validation dépend donc du flux choisi.
- Le RGPD, article 33, prévoit une notification à l'autorité compétente dans les meilleurs délais et, lorsque cela est possible, au plus tard 72 heures après connaissance d'une violation susceptible d'engendrer un risque pour les droits et libertés.

Références officielles à conserver dans la documentation de mise en œuvre :

- https://docs.docker.com/engine/install/ubuntu/
- https://www.postgresql.org/docs/current/ddl-rowsecurity.html
- https://docs.mollie.com/reference/webhooks
- https://docs.mollie.com/reference/webhooks-new
- https://eur-lex.europa.eu/eli/reg/2016/679/oj

---

# 32. Audit critique — décisions à confirmer avant exécution

La v1.2 corrige des points techniques mais certaines décisions restent à prendre avec des informations que ce document ne possède pas encore :

1. **Stack réelle P&P** : langage backend, framework frontend, méthode actuelle de déploiement, versions, gestionnaire de paquets.
2. **Git interne** : produit utilisé, capacité CI/CD, registry d'images disponible, sauvegarde du serveur Git, MFA et protection de branches.
3. **Charge** : nombre de clubs, utilisateurs actifs, pics, volume de fichiers et volume PostgreSQL. Les tailles de VPS restent des hypothèses tant que ces mesures ne sont pas connues.
4. **Redis** : vérifier qu'il est réellement nécessaire. S'il ne sert qu'à une fonction évitable à court terme, ne pas ajouter un composant uniquement parce qu'il apparaît dans une architecture type.
5. **Mollie** : figer le modèle exact (Connect, compte des clubs, ownership des paiements, webhook classique ou Next-gen) avant de coder la couche paiement.
6. **Données mineurs / Suisse / UE** : faire valider le rôle juridique exact de P&P et des clubs (responsable, sous-traitant, responsabilités respectives) ; le document technique ne remplace pas cette qualification juridique.
7. **RPO/RTO** : les objectifs doivent être décidés par le métier. La technique de sauvegarde en découle.
8. **Docker** : lancer un petit pilote opérationnel avant d'en faire un standard irréversible. Si l'équipe est objectivement plus fiable avec un autre modèle de déploiement reproductible, conserver cette alternative.

---

# 33. Conclusion

L'architecture cible de P&P ne doit pas chercher à accumuler le plus possible d'outils de cybersécurité. Elle doit surtout rendre les erreurs difficiles à propager, protéger les deux actifs les plus sensibles de la plateforme — l'isolation entre clubs et les données de mineurs — par plusieurs lignes de défense indépendantes, et rendre le rollback réellement exécutable plutôt que seulement documenté.

L'objectif est de transformer « quelqu'un fait quelque chose sur le serveur » en :

```text
modification documentée → Git → review → tests → analyse sécurité →
sandbox → préproduction → validation → production → monitoring →
possibilité de rollback réel
```

C'est cette chaîne, davantage qu'un scanner particulier, qui constitue la principale protection technique de Plan&Play.
