153 lines
7.5 KiB
Markdown
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.
|