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

7.5 KiB

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/. 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

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.