xpeditis2.0/infra/prod/README.md
2026-09-14 20:02:12 +02:00

153 lines
7.5 KiB
Markdown

# infra/prod — Production Hetzner
Tout ce qui est nécessaire pour déployer et exploiter Xpeditis en production.
> **La procédure pas à pas est dans [`docs/mise-en-prod/`](../../docs/mise-en-prod/README.md).**
> Ce README décrit *ce que contient le dossier* ; la doc décrit *quoi faire, dans quel ordre*.
---
## Architecture cible
```
Internet
|
Cloudflare (DNS + WAF + anti-DDoS)
| seules les IP Cloudflare passent le firewall
v
┌─────────────────────────────────────────────┐
│ app-01 CPX41 8 vCPU / 16 Go fsn1 │
│ │
│ k3s (mono-nœud) │
│ Traefik ──┬── api.xpeditis.com → backend │
│ ├── app / www / apex → frontend│
│ └── grafana.xpeditis.com │
│ backend NestJS x2 (HPA 2→4) │
│ frontend Next.js x2 │
│ log-exporter │
│ monitoring : Loki, Promtail, Prometheus, │
│ Alertmanager, Grafana │
└──────────────────┬──────────────────────────┘
│ réseau privé 10.10.1.0/24
│ TLS obligatoire (hostssl)
v
┌─────────────────────────────────────────────┐
│ db-01 CPX31 4 vCPU / 8 Go fsn1 │
│ │
│ PostgreSQL 15 + WAL-G (Docker Compose) │
│ Redis 7 │
│ postgres-exporter │
│ volume dédié 50 Go │
└──────────────────┬──────────────────────────┘
│
┌───────────┴────────────┐
v v
Hetzner Object Storage Hetzner Storage Box
(documents + WAL-G) (dumps chiffrés age)
```
**Pourquoi la base hors de Kubernetes** — PostgreSQL en StatefulSet ajoute des
volumes persistants, un ordre de démarrage et des montées de version délicates,
sans aucun gain à cette échelle. Hors cluster, les sauvegardes, la restauration
à un instant T et les tests de restauration sont triviaux, et le nœud
applicatif reste entièrement reconstructible sans toucher aux données.
---
## Contenu du dossier
| Chemin | Rôle |
|---|---|
| `terraform/` | Serveurs, réseau privé, firewalls, volume, clé SSH. La seule source de vérité de l'infrastructure. |
| `scripts/00-bootstrap-common.sh` | Durcissement système commun (SSH, UFW, fail2ban, auditd, sysctl, mises à jour auto). |
| `scripts/01-setup-data-node.sh` | Installe db-01 : volume, Docker, certificat TLS Postgres, timers de sauvegarde. |
| `scripts/02-setup-k3s-server.sh` | Installe k3s durci (secrets chiffrés au repos, audit API, Traefik configuré). |
| `scripts/03-install-cluster-addons.sh` | cert-manager, namespaces, accès registre, ClusterIssuer. |
| `scripts/secrets-apply.sh` | Déchiffre SOPS → applique sur le cluster, sans passer par le disque. |
| `scripts/harden-seed-data.sh` | Secours et audit : neutralise les comptes de démonstration qui acceptent encore `Password123!` (hash d'origine intact) sur une base migrée avant l'ajout de la garde `APP_ENV`. Un compte dont le mot de passe a été changé est conservé. Le cas nominal est traité par les migrations. |
| `scripts/deploy.sh` | Déploiement complet : migrations → images → attente → tests → retour arrière. |
| `scripts/ssh-deploy-wrapper.sh` | Restreint la clé SSH de la CI à quatre commandes. Une clé volée ne donne pas un shell. |
| `scripts/deploy-monitoring.sh` | Pile d'observabilité. |
| `scripts/smoke-test.sh` | Vérifie ce qu'un utilisateur constate réellement, de l'extérieur. |
| `scripts/preflight-check.sh` | Contrôle go / no-go avant ouverture au public. |
| `scripts/refresh-cloudflare-ips.sh` | Met à jour les rangs IP Cloudflare (firewall + Traefik). |
| `data-node/` | Docker Compose, `postgresql.conf`, `pg_hba.conf`, `redis.conf`, sauvegardes WAL-G. |
| `k8s/base/` | Namespaces, quotas, ConfigMap, gabarit de secrets, déploiements, Ingress, middlewares, politiques réseau, certificats. |
| `k8s/cluster/` | ClusterIssuer Let's Encrypt (DNS-01 Cloudflare). |
| `k8s/monitoring/` | Loki, Promtail, Prometheus, Alertmanager, Grafana. |
| `env/` | Gabarits de variables d'environnement et liste des secrets GitHub. |
| `cloudflare/` | Enregistrements DNS et règles WAF à créer côté Cloudflare. |
| `Makefile` | Raccourcis d'exploitation (`make help`). |
---
## Démarrage rapide
```bash
cd infra/prod
make help
# Infrastructure
cp terraform/terraform.tfvars.example terraform/terraform.tfvars
$EDITOR terraform/terraform.tfvars
make tf-init && make tf-plan && make tf-apply
make tf-output # IP + enregistrements DNS à créer
# ... provisioning des serveurs : voir docs/mise-en-prod/03 et 04 ...
# Secrets
cp k8s/base/03-secrets.template.yaml /tmp/secrets.yaml
$EDITOR /tmp/secrets.yaml
sops -e /tmp/secrets.yaml > k8s/base/03-secrets.sops.yaml && shred -u /tmp/secrets.yaml
make secrets-apply
# Déploiement
make deploy TAG=prod-a1b2c3d
make deploy-monitoring
# Avant d'ouvrir au public
make preflight
```
---
## Règles de sécurité non négociables
1. **Aucun secret en clair dans Git.** Uniquement des fichiers `*.sops.yaml`
chiffrés avec age. `make secrets-check` refuse le contraire.
2. **La base de données n'est jamais joignable depuis Internet.** Réseau privé,
bind explicite sur l'IP privée, UFW, et `pg_hba` en `hostssl` seulement.
3. **Personne ne contourne Cloudflare.** Le firewall Hetzner n'accepte 80/443
que depuis les rangs Cloudflare.
4. **SSH et l'API Kubernetes ne sont ouverts qu'à vos IP d'administration.**
Jamais `0.0.0.0/0` — Terraform refuse cette valeur.
5. **Une sauvegarde non restaurée n'est pas une sauvegarde.** Le test de
restauration tourne toutes les semaines et doit être passé avant l'ouverture.
6. **Les migrations passent par un Job**, jamais en concurrence entre replicas.
7. **L'image frontend est reconstruite pour la production**, jamais promue
depuis la preprod : `NEXT_PUBLIC_API_URL` est figée au build.
8. **Aucun mot de passe d'administrateur n'est stocké nulle part.**
`SeedTestUsers` ne s'exécute plus en production, une migration de secours
neutralise ses comptes s'ils existent, et le premier administrateur est créé
sans mot de passe utilisable — vous définissez le vôtre via « mot de passe
oublié ».
---
## Coûts (référence : `Xpeditis_Previsions_Couts.xlsx`, feuille « Hetzner »)
| Poste | € HT / mois |
|---|---|
| app-01 CPX41 | 29 |
| db-01 CPX31 | 15 |
| Volume 50 Go | 2,40 |
| Sauvegardes Hetzner (20 %) | 8,80 |
| Storage Box BX11 (1 To) | 3,90 |
| Object Storage (documents + WAL-G, < 1 To) | ~6 |
| IPv4 supplémentaire | 1 |
| Cloudflare Free | 0 |
| **Total infrastructure** | **≈ 66 €** |
Les services tiers (Brevo, Stripe, Sentry, Pappers, assurance) s'ajoutent :
voir la feuille « Synthèse comparatif » du fichier de prévisions.