# 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 (`admin@xpeditis.com` / `Password123!`) sur une base migrée avant l'ajout de la garde `NODE_ENV`. 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.