Skip to content

01 — INFRASTRUCTURE DE BASE ​

Objectif ​

À la fin de ce fichier, l'environnement staging dispose de :

  • Un VPC avec réseau segmenté (public / privé / données)
  • Un cluster Kubernetes (EKS) opérationnel avec des nœuds prêts
  • Une base PostgreSQL accessible depuis le cluster
  • Un cache Redis accessible depuis le cluster
  • Des buckets S3 pour les backups
  • Le DNS Cloudflare configuré et les certificats TLS prêts
  • Les secrets injectables via Doppler

Pré-requis : fichier 00_variables_et_stack.md lu par l'agent, tous les comptes créés, outils CLI installés.


PHASE 1 — TERRAFORM STATE BACKEND ​

Objectif ​

Créer le stockage distant du state Terraform avant toute autre ressource. Sans ça, le state reste en local et ne peut pas être partagé ni verrouillé.

Instructions pour l'agent ​

Créer un dossier infrastructure/terraform/bootstrap/ séparé des environnements. Ce bootstrap est exécuté une seule fois, manuellement.

Ressources à créer :

  • Un bucket S3 nex-terraform-state-<AWS_ACCOUNT_ID> (ex: nex-terraform-state-009001720821) : versioning activé, encryption AES-256, block public access total. Le suffixe avec l'account ID garantit l'unicité globale du nom.
  • Une table DynamoDB nex-terraform-locks : clé primaire LockID (String). Utilisée pour le verrouillage concurrent — empêche deux terraform apply simultanés.

Recommandations :

  • Ce module ne doit PAS être géré par un backend distant (problème de l'œuf et de la poule). State local uniquement pour le bootstrap.
  • Tagger les deux ressources avec les tags projet.
  • Activer la suppression protection sur le bucket (prevent_destroy).

Configuration backend (dans infrastructure/terraform/staging/main.tf) :

hcl
terraform {
  backend "s3" {
    bucket         = "nex-terraform-state-009001720821"
    key            = "staging/terraform.tfstate"
    region         = "eu-west-3"
    dynamodb_table = "nex-terraform-locks"
    encrypt        = true
  }
}

Validation ​

bash
aws s3 ls | grep nex-terraform
aws dynamodb describe-table --table-name nex-terraform-locks --query "Table.TableStatus"

PHASE 2 — VPC & RÉSEAU ​

Objectif ​

Créer le réseau isolé dans lequel toute l'infrastructure vivra. C'est la fondation — une erreur ici impacte tout le reste.

Instructions pour l'agent ​

Créer le module infrastructure/terraform/modules/vpc/.

Architecture réseau :

VPC 10.0.0.0/16
│
├── Subnets publics (2 AZ)
│   ├── 10.0.1.0/24  (eu-west-3a) — ALB, NAT Gateway
│   └── 10.0.2.0/24  (eu-west-3b) — ALB, NAT Gateway
│
├── Subnets privés (2 AZ)
│   ├── 10.0.10.0/24 (eu-west-3a) — Pods EKS
│   └── 10.0.20.0/24 (eu-west-3b) — Pods EKS
│
└── Subnets données (2 AZ)
    ├── 10.0.100.0/24 (eu-west-3a) — RDS, Redis
    └── 10.0.200.0/24 (eu-west-3b) — RDS, Redis

Composants réseau :

  • Internet Gateway attaché au VPC.
  • NAT Gateway : 1 seul en staging (dans public-a pour économiser). 2 en production (un par AZ pour la haute dispo). Elastic IP associée.
  • Route tables : une publique (0.0.0.0/0 → IGW), une privée (0.0.0.0/0 → NAT), une data (0.0.0.0/0 → NAT).
  • DNS hostnames et DNS support activés sur le VPC.

Tags Kubernetes obligatoires sur les subnets — sans ça, EKS ne pourra pas créer les load balancers :

  • Subnets publics : kubernetes.io/role/elb = 1
  • Subnets privés : kubernetes.io/role/internal-elb = 1
  • Tous : kubernetes.io/cluster/nex-staging = shared

Recommandations :

  • 3 couches de subnets (public/privé/data) au lieu de 2. La couche data isole physiquement les bases de données des pods applicatifs. Même si un pod est compromis et qu'il a accès au réseau privé, il ne peut pas scanner le réseau data sauf si le Security Group l'autorise explicitement.
  • Le NAT Gateway coûte ~35 €/mois. En staging un seul suffit. Si la zone A tombe, les pods en zone B perdent leur accès internet sortant — acceptable en staging.

Validation ​

bash
# VPC existe
aws ec2 describe-vpcs --filters "Name=tag:Project,Values=nex" --query "Vpcs[0].VpcId" --region eu-west-3

# 6 subnets
aws ec2 describe-subnets --filters "Name=tag:Project,Values=nex" --query "length(Subnets)" --region eu-west-3
# Attendu : 6

# NAT Gateway actif
aws ec2 describe-nat-gateways --filter "Name=tag:Project,Values=nex" --query "NatGateways[0].State" --region eu-west-3
# Attendu : available

PHASE 3 — SECURITY GROUPS ​

Objectif ​

Définir la matrice de communication réseau. Le principe : tout est fermé par défaut, on ouvre uniquement ce qui est nécessaire.

Instructions pour l'agent ​

Créer dans infrastructure/terraform/modules/vpc/security_groups.tf un Security Group par composant :

sg-alb — Load Balancer (point d'entrée) :

  • Inbound : 443 depuis 0.0.0.0/0 (trafic HTTPS via Cloudflare)
  • Inbound : 80 depuis 0.0.0.0/0 (redirect HTTPS)
  • Outbound : tout vers les subnets privés

sg-eks-nodes — Nœuds Kubernetes :

  • Inbound : tout depuis sg-alb (le LB route vers les pods)
  • Inbound : tout depuis sg-eks-nodes (communication inter-pods)
  • Inbound : 443 depuis le CIDR VPC (API server ↔ kubelets)
  • Outbound : 5432 vers sg-rds
  • Outbound : 6379 vers sg-redis
  • Outbound : 443 vers 0.0.0.0/0 (pull images, appels API externes : Firebase, Resend)

sg-rds — PostgreSQL :

  • Inbound : 5432 depuis sg-eks-nodes uniquement
  • Outbound : aucun

sg-redis — Redis :

  • Inbound : 6379 depuis sg-eks-nodes uniquement
  • Outbound : aucun

ATTENTION — Piège EKS avec les Security Groups :

EKS crée automatiquement un Security Group cluster (différent de sg-eks-nodes défini dans Terraform). Ce SG auto-généré est celui réellement attaché aux nœuds du node group managé. Par conséquent, les règles inbound de sg-rds et sg-redis doivent autoriser à la fois sg-eks-nodes ET le SG cluster auto-généré par EKS.

Après la création du cluster (Phase 7), récupérer le SG cluster :

bash
aws eks describe-cluster --name nex-staging --region eu-west-3 \
  --query "cluster.resourcesVpcConfig.clusterSecurityGroupId" --output text

Puis ajouter ce SG en inbound sur sg-rds (port 5432) et sg-redis (port 6379). Sans ça, les pods ne pourront pas joindre RDS ni Redis (ETIMEDOUT / no pg_hba.conf entry... no encryption).

Recommandations :

  • Ne jamais ouvrir les SG RDS ou Redis à 0.0.0.0/0, même temporairement pour debug. Utiliser kubectl port-forward à la place.
  • En production on pourra restreindre le 443 inbound de sg-alb aux seules IPs Cloudflare (plages publiées par Cloudflare). En staging on laisse ouvert pour simplifier.
  • Chaque SG doit avoir une description claire et des tags.

Validation ​

bash
aws ec2 describe-security-groups --filters "Name=tag:Project,Values=nex" \
  --query "SecurityGroups[].GroupName" --region eu-west-3
# Attendu : sg-alb, sg-eks-nodes, sg-rds, sg-redis

# Vérifier que le SG cluster EKS est dans les inbound de sg-rds
aws ec2 describe-security-groups --group-ids <sg-rds-id> --region eu-west-3 \
  --query "SecurityGroups[0].IpPermissions"
# Attendu : le SG cluster EKS apparaît en source sur port 5432

PHASE 4 — RDS POSTGRESQL ​

Objectif ​

Base de données relationnelle pour les 10 services qui en ont besoin (tous sauf logs-reporting).

Instructions pour l'agent ​

Créer le module infrastructure/terraform/modules/rds/.

Instance RDS :

  • Engine : PostgreSQL 16.13
  • Instance class : db.t3.small (staging)
  • Stockage : 20 Go gp3, auto-scaling jusqu'à 100 Go
  • Multi-AZ : non en staging, oui en production
  • Subnet group : subnets data_a et data_b
  • Security group : sg-rds
  • Backup : automatique, rétention 30 jours, fenêtre 03:00-04:00 UTC
  • Maintenance : dimanche 04:00-05:00 UTC
  • Encryption at rest : activé (KMS par défaut)
  • Public accessible : non
  • Deletion protection : activé
  • Performance Insights : activé (7 jours de rétention, gratuit sur db.t3.small)
  • Master username : nex_admin
  • Master password : généré par Terraform (random_password), stocké dans Doppler

Parameter Group PostgreSQL 16 (nex-staging-postgres16) :

  • log_statement = 'all' (staging seulement — pour le debug). En production : 'ddl'
  • log_min_duration_statement = 1000 (log les requêtes lentes > 1s)
  • shared_preload_libraries = 'pg_stat_statements' (monitoring des requêtes)
  • max_connections = 200 (11 services × ~10 connexions + marge)

Post-création — initialisation :

L'agent doit générer un script infrastructure/scripts/seed-database.sh qui :

  1. Se connecte via kubectl port-forward (pas de bastion)
  2. Crée un utilisateur applicatif nex_app avec un mot de passe depuis Doppler
  3. Crée la base nex_staging
  4. Accorde les droits SELECT, INSERT, UPDATE, DELETE à nex_app (pas de DROP, pas de ALTER schema — moindre privilège)
  5. Configure les default privileges pour les futures tables

Note : En l'état actuel du déploiement, les services se connectent avec nex_admin (le master user). La migration vers nex_app est prévue mais pas encore effectuée. Le script seed-database.sh existe dans infrastructure/scripts/ et doit être exécuté pour créer le user applicatif, puis les variables Doppler POSTGRES_USER et POSTGRES_PASSWORD doivent être mises à jour.

Recommandations :

  • Une seule instance RDS pour tous les services en staging. Chaque service utilise son propre schema dans la même base pour l'isolation logique (ex: auth, orchestrator, notification, ledger_wallets, etc.).
  • Le master user nex_admin ne doit jamais être utilisé par les applications en production. Uniquement pour l'administration et les migrations.
  • Activer Performance Insights (gratuit sur db.t3.small avec 7 jours de rétention) pour monitorer les requêtes lentes.

Validation ​

bash
# Instance disponible
aws rds describe-db-instances --db-instance-identifier nex-staging-postgres \
  --query "DBInstances[0].DBInstanceStatus" --region eu-west-3
# Attendu : available

# Encryption activée
aws rds describe-db-instances --db-instance-identifier nex-staging-postgres \
  --query "DBInstances[0].StorageEncrypted" --region eu-west-3
# Attendu : True

# Connexion depuis un pod (après EKS déployé) — nécessite SSL
kubectl run pg-test --rm -it --image=postgres:16-alpine -n nex-staging -- \
  psql "host=<RDS_ENDPOINT> user=nex_admin dbname=nex_staging sslmode=require" -c "SELECT version();"

PHASE 5 — ELASTICACHE REDIS ​

Objectif ​

Cache pour auth (sessions, tokens), notifications (BullMQ queues) et service-catalog (cache réponses). Aussi utilisable comme messaging léger via Redis Streams si besoin.

Instructions pour l'agent ​

Créer le module infrastructure/terraform/modules/redis/.

Replication Group :

  • Engine : Redis 7.1
  • Node type : cache.t3.micro (staging)
  • Nombre de nœuds : 1 en staging, 2 en production (Multi-AZ)
  • Automatic failover : désactivé en staging, activé en prod
  • Transit encryption (TLS) : activé
  • At-rest encryption : activé
  • Auth token : généré (64 chars, pas de caractères spéciaux), stocké dans Doppler
  • Subnet group : subnets data_a et data_b
  • Security group : sg-redis
  • Maintenance : dimanche 05:00-06:00 UTC
  • Snapshot window : 02:00-03:00 UTC
  • Snapshot retention : 7 jours

Recommandations :

  • Même si seuls auth, notifications (BullMQ) et service-catalog utilisent Redis aujourd'hui, d'autres services pourraient en avoir besoin (rate limiting, cache de config, queues). L'instance est partagée — utiliser des prefixes de clés par service (auth:session:xxx, catalog:cache:xxx).
  • Le TLS est important : les tokens de session transitent par Redis. Sans TLS un attaquant sur le réseau pourrait les intercepter.

Validation ​

bash
# Instance disponible
aws elasticache describe-replication-groups --replication-group-id nex-staging-redis \
  --query "ReplicationGroups[0].Status" --region eu-west-3
# Attendu : available

# Connexion depuis un pod (après EKS déployé) — nécessite TLS + auth token
kubectl run redis-test --rm -it --image=redis:7-alpine -n nex-staging -- \
  redis-cli -h <REDIS_ENDPOINT> -p 6379 --tls -a <AUTH_TOKEN> PING
# Attendu : PONG

PHASE 6 — S3 BUCKETS ​

Objectif ​

Stockage pour les backups de base de données et l'archivage réglementaire. Les fichiers utilisateurs restent sur Firebase Storage (géré par file-service).

Instructions pour l'agent ​

Créer le module infrastructure/terraform/modules/s3/.

Bucket nex-staging-backups :

  • Encryption : AES-256
  • Versioning : activé
  • Block public access : tout bloqué (4 flags à true)
  • Lifecycle rules :
    • Objets > 30 jours → transition vers S3 Glacier
    • Objets > 365 jours → transition vers S3 Glacier Deep Archive
    • Versions non-courantes > 90 jours → supprimées

Bucket nex-terraform-state-<ACCOUNT_ID> (déjà créé en phase 1 — vérifier la config).

Recommandations :

  • Pas de bucket uploads côté AWS — les fichiers utilisateurs sont sur Firebase Storage. Ça évite de dupliquer les coûts et la complexité.
  • En production, activer la réplication cross-région du bucket backups vers eu-west-1 (Irlande). En staging ce n'est pas nécessaire.
  • Créer un rôle IAM dédié pour les backups RDS qui écrivent dans ce bucket (IRSA — IAM Roles for Service Accounts).

Validation ​

bash
aws s3 ls | grep nex-staging-backups
# Le bucket existe

aws s3api get-bucket-encryption --bucket nex-staging-backups --region eu-west-3
# AES256

aws s3api get-public-access-block --bucket nex-staging-backups --region eu-west-3
# Tout à true

PHASE 7 — CLUSTER EKS ​

Objectif ​

Le cluster Kubernetes où tournent les 11 microservices. C'est la pièce centrale.

Instructions pour l'agent ​

Créer le module infrastructure/terraform/modules/eks/.

Cluster EKS :

  • Version : Kubernetes 1.29
  • Subnets : private_a et private_b (les pods ne sont jamais dans un subnet public)
  • Endpoint public access : activé (pour kubectl depuis les machines des devs). En production, restreindre aux IPs connues.
  • Endpoint private access : activé
  • Logging : activer api, audit, authenticator vers CloudWatch (les logs d'audit K8s sont utiles même si le monitoring principal est Grafana)
  • Secrets encryption : activé avec KMS (clé alias/nex-staging-eks-secrets)
  • OIDC provider : activé (obligatoire pour IRSA — permet aux pods d'assumer des rôles IAM)

Node Group managé :

  • Instance type : t3.medium (2 vCPU, 4 Go RAM)
  • Scaling : min 2, max 5, desired 2
  • Subnets : private_a et private_b (répartis sur 2 AZ)
  • AMI type : AL2_x86_64 (Amazon Linux 2 optimisé EKS)
  • Disk : 30 Go gp3
  • Security group : sg-eks-nodes
  • Labels : environment=staging, project=nex

IAM Roles :

  • Cluster role : nex-staging-eks-cluster-role avec les policies EKS standard (AmazonEKSClusterPolicy)
  • Node role : nex-staging-eks-node-role avec AmazonEKSWorkerNodePolicy, AmazonEKS_CNI_Policy, AmazonEC2ContainerRegistryReadOnly
  • OIDC provider : pour IRSA (les service accounts K8s pourront assumer des rôles IAM spécifiques)

Recommandations :

  • t3.medium (4 Go RAM) permet d'héberger ~6-8 pods selon les resource requests. Avec 2 nœuds ça fait ~14 pods. Tu as 11 services + les pods système (ingress, cert-manager, external-secrets, monitoring). Si les nœuds saturent, l'autoscaler montera à 3 puis 4 nœuds automatiquement.
  • En production, passer à t3.large (8 Go RAM) ou augmenter le min à 3 nœuds pour la haute dispo.
  • Le provisionnement EKS prend 15-20 minutes. L'agent doit attendre que le cluster soit ACTIVE avant de continuer.

Post-création ​

bash
# Configurer kubectl
aws eks update-kubeconfig --name nex-staging --region eu-west-3

IMPORTANT — Après la création du cluster, retourner en Phase 3 pour ajouter le SG cluster auto-généré par EKS aux inbound rules de sg-rds et sg-redis. Voir l'encadré "Piège EKS" en Phase 3.

Validation ​

bash
# Cluster actif
kubectl cluster-info

# Nœuds prêts
kubectl get nodes
# Attendu : 2 nœuds en status Ready

# Version K8s correcte
kubectl version --short

PHASE 8 — COMPOSANTS KUBERNETES DE BASE ​

Objectif ​

Installer les briques fondamentales dans le cluster avant de déployer les services.

Instructions pour l'agent ​

8.1 Namespaces ​

Créer infrastructure/kubernetes/base/namespaces.yaml :

  • nex-staging — les 11 microservices (environnement permanent)
  • nex-production — les 11 microservices (environnement permanent, overlay production)
  • nex-monitoring — Grafana Agent, Prometheus (fichier 04)
  • ingress-nginx — Ingress Controller
  • cert-manager — Gestion des certificats
  • external-secrets — Synchronisation Doppler → K8s

Namespaces review (dynamiques, créés par le pipeline CI) :

  • Pattern : nex-review-{branch} (ex: nex-review-nex-42-payment-flow)
  • Créés automatiquement par le job CI lors du déploiement d'une review
  • Supprimés automatiquement après 48h sans commit (CronJob review-env-cleanup.sh)
  • Utilisent le même cluster que staging avec des ResourceQuotas réduits

8.2 Resource Quotas ​

Créer infrastructure/kubernetes/base/resource-quotas.yaml :

  • nex-staging : max 8 CPU, 16 Gi mémoire, 30 pods
  • nex-production : max 16 CPU, 32 Gi mémoire, 50 pods
  • nex-monitoring : max 2 CPU, 4 Gi mémoire, 10 pods

Quotas review (appliqués dynamiquement par le pipeline CI à chaque nex-review-{branch}) :

  • max 4 CPU, 8 Gi mémoire, 15 pods
  • Toujours 1 replica par service en review

Ces quotas empêchent un service buggé de consommer toutes les ressources du cluster.

8.3 Ingress Controller (NGINX) ​

Installer via Helm ingress-nginx/ingress-nginx dans le namespace ingress-nginx.

Fichier Helm values : infrastructure/kubernetes/helm-values/ingress-nginx.yaml

Configuration critique :

  • Service type : LoadBalancer (crée automatiquement un NLB AWS)
  • Annotation aws-load-balancer-scheme: internet-facing
  • Annotation aws-load-balancer-cross-zone-load-balancing-enabled: true
  • Annotation aws-load-balancer-type: nlb (Network Load Balancer)
  • Activer les métriques Prometheus
  • Configurer use-forwarded-headers: true et real-ip-header: CF-Connecting-IP pour recevoir la vraie IP client derrière Cloudflare
  • Configurer proxy-real-ip-cidr avec les plages IP Cloudflare (l'agent doit récupérer la liste à jour depuis https://www.cloudflare.com/ips/)
  • Resources : requests 100m CPU / 128Mi, limits 200m CPU / 256Mi

Recommandation : après installation, récupérer le hostname du NLB créé par AWS. Ce hostname sera utilisé dans le DNS Cloudflare.

Ingress routes (dans infrastructure/kubernetes/services/<service>/ingress.yaml ou overlay staging) :

  • Host : staging.paywithnex.com
  • TLS via cert-manager (secret nex-api-tls, ClusterIssuer letsencrypt-prod)
  • Routes avec regex rewrite :
    • /auth(/|$)(.*) → service auth (port 3001)
    • /configuration(/|$)(.*) → service configuration (port 3006)
    • /api(/|$)(.*) → service orchestrator (port 3004)
  • SSL redirect activé, proxy body size 10m

8.4 cert-manager ​

Installer via Helm jetstack/cert-manager dans le namespace cert-manager avec installCRDs: true.

Fichier Helm values : infrastructure/kubernetes/helm-values/cert-manager.yaml

Créer deux ClusterIssuers Let's Encrypt :

  • letsencrypt-prod : serveur ACME production (https://acme-v02.api.letsencrypt.org/directory)
  • letsencrypt-staging : serveur ACME staging (https://acme-staging-v02.api.letsencrypt.org/directory)

Les deux utilisent le solver HTTP01 via l'Ingress NGINX. Email de contact : tech@paywithnex.com.

Recommandation : utiliser le staging issuer tant que le DNS n'est pas finalisé (rate limits plus souples).

8.5 External Secrets Operator ​

Installer via Helm external-secrets/external-secrets dans le namespace external-secrets.

Fichier Helm values : infrastructure/kubernetes/helm-values/external-secrets.yaml

Créer :

  • Un SecretStore doppler-store dans nex-staging qui pointe vers Doppler (avec le token Doppler stocké dans un K8s Secret doppler-token créé manuellement une seule fois)
  • Un ExternalSecret qui synchronise toutes les variables Doppler (regexp .*) vers un Secret K8s nex-secrets dans le namespace nex-staging
  • Refresh interval : 1h (les secrets se resynchronisent toutes les heures)

Les deployments des microservices référenceront ce secret via envFrom.secretRef.

Forcer la re-sync (après modification de secrets dans Doppler) :

bash
kubectl annotate externalsecret nex-secrets -n nex-staging \
  force-sync=$(date +%s) --overwrite

8.6 Secret GitLab Registry ​

Créer un secret gitlab-registry de type docker-registry dans nex-staging avec les credentials d'un Deploy Token GitLab (read_registry). Ce secret permet aux pods de pull les images depuis registry.gitlab.com/nxpay/nex/*.

Recommandation : créer le Deploy Token dans GitLab (Settings → Repository → Deploy Tokens) avec le scope read_registry uniquement. Ne pas utiliser un Personal Access Token.

Validation ​

bash
# Namespaces
kubectl get ns | grep -E "nex-|ingress|cert-manager|external-secrets"
# Attendu : 6 namespaces

# Ingress Controller avec un LoadBalancer
kubectl get svc -n ingress-nginx ingress-nginx-controller -o jsonpath='{.status.loadBalancer.ingress[0].hostname}'
# Attendu : un hostname AWS (xxxxx.elb.eu-west-3.amazonaws.com)

# cert-manager
kubectl get clusterissuer
# Attendu : letsencrypt-prod et letsencrypt-staging en Ready=True

# External Secrets
kubectl get secretstores -n nex-staging
# Attendu : doppler-store en Ready=True

kubectl get externalsecrets -n nex-staging
# Attendu : nex-secrets en status SecretSynced

# Secrets disponibles pour les pods
kubectl get secret nex-secrets -n nex-staging -o jsonpath='{.data}' | jq 'keys'
# Attendu : liste des clés (POSTGRES_HOST, REDIS_HOST, etc.)

PHASE 9 — DNS CLOUDFLARE & CERTIFICATS ​

Objectif ​

Rendre les services accessibles via les domaines paywithnex.com avec TLS.

Instructions pour l'agent ​

9.1 Configuration DNS ​

Dans Cloudflare (via Terraform provider Cloudflare ou manuellement) :

TypeNomValeurProxy CloudflareNote
CNAMEapi.stagingHostname du NLB (phase 8.3)Oui (orange)API staging
CNAMEadminSite Firebase HostingNon (gris)CMMS Dashboard
CNAMEboSite Firebase HostingNon (gris)Backoffice
TXT@SPF record pour Resend—Délivrabilité email
TXTresend._domainkeyDKIM record Resend—Signature email
TXT_dmarcDMARC policy—Anti-spoofing
CNAME*.reviewHostname du NLB (phase 8.3)Oui (orange)Review envs éphémères

Les enregistrements pour la production (api.paywithnex.com) seront ajoutés en semaine 2.

Note review : Le wildcard *.review.paywithnex.com pointe vers le même NLB que staging. L'Ingress Controller route vers le bon namespace nex-review-{branch} grâce au host header.

9.2 Configuration Cloudflare ​

  • SSL/TLS mode : Full (strict) — Cloudflare ↔ origin avec certificat valide (cert-manager)
  • Always Use HTTPS : activé
  • Minimum TLS Version : 1.2
  • HSTS : activé, max-age 6 mois
  • Brotli : activé
  • HTTP/2 : activé

9.3 Firebase Hosting ​

L'agent doit configurer le fichier infrastructure/firebase/firebase.json pour le hosting des 2 dashboards Nuxt.js :

  • Target admin → CMMS Dashboard (admin.paywithnex.com)
  • Target backoffice → Backoffice (bo.paywithnex.com)

Connecter les custom domains dans la console Firebase Hosting et ajouter les enregistrements DNS de vérification dans Cloudflare.

Recommandation : le proxy Cloudflare (orange) doit être désactivé pour les domaines Firebase Hosting. Firebase gère son propre SSL et CDN. Mettre Cloudflare en proxy devant Firebase crée des conflits de certificats.

Validation ​

bash
# DNS résout
dig staging.paywithnex.com +short
# Attendu : IP Cloudflare (le proxy est actif)

dig admin.paywithnex.com +short
# Attendu : IP Firebase Hosting

# TLS fonctionne
curl -I https://staging.paywithnex.com
# Attendu : HTTP/2 200 ou 404 (pas encore de routes) avec headers Cloudflare

# Firebase Hosting répond
curl -I https://admin.paywithnex.com
# Attendu : HTTP/2 200 avec headers Firebase

PHASE 10 — DOPPLER : INJECTION DES SECRETS ​

Objectif ​

Remplir Doppler avec toutes les valeurs réelles issues des outputs Terraform (endpoints RDS, Redis, etc.) et des services externes (Firebase, Sentry, Resend).

Instructions pour l'agent ​

Après les phases 4 à 9, tous les endpoints sont connus. L'agent doit générer un script qui récupère les outputs Terraform et met à jour Doppler.

Secrets à configurer dans le projet Doppler paywithnex, config stg :

# --- Base de données ---
POSTGRES_HOST=<output terraform: rds_endpoint (sans le port)>
POSTGRES_PORT=5432
POSTGRES_DB=nex_staging
POSTGRES_USER=nex_admin
POSTGRES_PASSWORD=<output terraform: rds_password>
POSTGRES_SSL=true

# --- Schemas par service ---
AUTH_SCHEMA=auth
ORCHESTRATOR_SCHEMA=orchestrator
NOTIFICATIONS_SCHEMA=notification
LEDGER_WALLETS_SCHEMA=ledger_wallets
# (chaque service a sa variable <SERVICE>_SCHEMA)

# --- Redis ---
REDIS_HOST=<output terraform: redis_endpoint>
REDIS_PORT=6379
REDIS_PASSWORD=<output terraform: redis_auth_token>
REDIS_TLS=true

# --- JWT ---
JWT_SECRET=<généré, 64 chars aléatoires>
JWT_EXPIRATION=3600

# --- Firebase ---
FIREBASE_PROJECT_ID=nex-app
FIREBASE_SERVER_KEY=<depuis Firebase console>
FIREBASE_STORAGE_BUCKET=nex-app.appspot.com

# --- Sentry ---
SENTRY_DSN=<depuis Sentry dashboard, un DSN par service ou un global>

# --- Resend ---
RESEND_API_KEY=<depuis Resend dashboard>

# --- App ---
NODE_ENV=staging
LOG_LEVEL=debug
API_BASE_URL=https://staging.paywithnex.com

IMPORTANT — Variables de connexion :

Les services NestJS utilisent les noms POSTGRES_* (pas DATABASE_*) et REDIS_PASSWORD (pas REDIS_AUTH_TOKEN). Deux variables cruciales à ne pas oublier :

  • POSTGRES_SSL=true — RDS exige SSL. Sans ça, erreur no pg_hba.conf entry... no encryption.
  • REDIS_TLS=true — ElastiCache a transit_encryption_enabled. Sans ça, erreur ETIMEDOUT car ioredis essaie une connexion non-TLS.

URLs de communication inter-services (utilisées par l'orchestrator) :

RISK_ENGINE_SERVICE_URL=http://risk-engine:3009
LEDGER_WALLET_SERVICE_URL=http://ledger-wallets:3002
AUTH_SERVICE_URL=http://auth:3001
CUSTOMER_SERVICE_URL=http://customer-profiles-kyc:3003
CONFIGURATION_SERVICE_URL=http://configuration:3006
NOTIFICATIONS_SERVICE_URL=http://notifications:3005
SERVICE_CATALOG_URL=http://service-catalog:3010

Note : Les services communiquent via le DNS interne Kubernetes (<service-name>.<namespace>.svc.cluster.local). Les noms courts (http://auth:3001) fonctionnent car tous les services sont dans le même namespace nex-staging.

Recommandations :

  • Le script doit être idempotent : relancer le script ne crée pas de doublons.
  • Les mots de passe générés (POSTGRES_PASSWORD, JWT_SECRET, REDIS_PASSWORD) ne doivent être générés qu'une fois. Si ils existent déjà dans Doppler, ne pas les écraser.
  • Après la mise à jour de Doppler, forcer un refresh de l'ExternalSecret K8s pour que les pods récupèrent les nouvelles valeurs.

Validation ​

bash
# Vérifier le nombre de secrets
doppler secrets --project paywithnex --config stg --only-names | wc -l
# Attendu : >= 25

# Vérifier que les endpoints sont corrects
doppler secrets get POSTGRES_HOST --project paywithnex --config stg --plain
# Attendu : nex-staging-postgres.xxxxx.eu-west-3.rds.amazonaws.com

# Vérifier les variables critiques
doppler secrets get POSTGRES_SSL --project paywithnex --config stg --plain
# Attendu : true

doppler secrets get REDIS_TLS --project paywithnex --config stg --plain
# Attendu : true

# Vérifier la synchro K8s
kubectl get secret nex-secrets -n nex-staging -o jsonpath='{.data.POSTGRES_HOST}' | base64 -d
# Attendu : même valeur que Doppler

PHASE 11 — CONFIGURATION NESTJS DES SERVICES ​

Objectif ​

S'assurer que chaque service NestJS est correctement configuré pour fonctionner sur EKS avec les managed services AWS (RDS + SSL, ElastiCache + TLS, health checks K8s).

Pré-requis par service ​

Chaque service NestJS doit avoir les 3 éléments suivants :

11.1 Health endpoint ​

Chaque service doit exposer un endpoint GET /health exclu du prefix global pour que les probes K8s fonctionnent.

Dans main.ts :

typescript
app.setGlobalPrefix('v1', { exclude: ['health'] });

Dans app.controller.ts :

typescript
@Get('health')
health() {
  return { status: 'ok' };
}

Sans l'exclusion { exclude: ['health'] }, le endpoint devient /v1/health au lieu de /health, et les readiness/liveness probes K8s renvoient 404.

11.2 SSL PostgreSQL ​

Chaque service utilisant TypeORM doit configurer SSL dans tous les endroits où une connexion est créée :

Dans app.module.ts (TypeORM.forRoot) :

typescript
ssl: process.env.POSTGRES_SSL === 'true' ? { rejectUnauthorized: false } : false,

Dans data-source.ts (si le service en a un — utilisé par les migrations) :

typescript
ssl: process.env.POSTGRES_SSL === 'true' ? { rejectUnauthorized: false } : false,

Piège : certains services (auth, orchestrator, notifications...) ont deux connexions TypeORM — une dans app.module.ts et une dans data-source.ts. Si on oublie l'une des deux, le service crash avec no pg_hba.conf entry... no encryption.

11.3 TLS Redis ​

Les services utilisant Redis (auth, notifications via BullMQ) doivent activer TLS :

ioredis (auth) :

typescript
...(process.env.REDIS_TLS === 'true' && { tls: {} }),

BullMQ (notifications) :

typescript
connection: {
  host: process.env.REDIS_HOST,
  port: parseInt(process.env.REDIS_PORT || '6379'),
  password: process.env.REDIS_PASSWORD,
  ...(process.env.REDIS_TLS === 'true' && { tls: {} }),
},

Récapitulatif par service ​

ServicePortTypeORM SSLdata-source.tsRedis TLSHealth endpoint
auth3001app.module.tsouioui (ioredis)oui
ledger-wallets3002app.module.tsouinonoui
customer-profiles-kyc3003app.module.tsouinonoui
orchestrator3004app.module.tsouinonoui
notifications3005app.module.tsouioui (BullMQ)oui
configuration3006app.module.tsouinonoui
file-service3007app.module.tsouinonoui
providers-gateway3008app.module.tsouinonoui
risk-engine3009app.module.tsouinonoui
service-catalog3010database.providers.tsnonnonoui
logs-reporting3011non (pas de DB)nonnonoui

CHECKLIST GLOBALE — FIN DU FICHIER 01 ​

Toutes ces vérifications doivent passer avant d'entamer le fichier 02_cicd_et_services.md.

Infrastructure AWS ​

  • [ ] VPC créé avec CIDR 10.0.0.0/16 dans eu-west-3
  • [ ] 6 subnets sur 2 AZ (public, privé, data)
  • [ ] NAT Gateway actif
  • [ ] 4 Security Groups (alb, eks-nodes, rds, redis)
  • [ ] SG cluster EKS ajouté aux inbound de sg-rds et sg-redis
  • [ ] RDS PostgreSQL 16.13 disponible, encrypted, backups 30j, Performance Insights activé
  • [ ] User applicatif nex_app créé avec droits restreints (ou nex_admin temporairement)
  • [ ] Redis disponible, TLS activé, auth token configuré
  • [ ] Bucket S3 backups créé, encrypted, lifecycle configuré
  • [ ] Terraform state stocké dans S3 avec lock DynamoDB

Kubernetes ​

  • [ ] Cluster EKS actif avec 2 nœuds Ready
  • [ ] 6 namespaces permanents créés (nex-staging, nex-production, nex-monitoring, ingress-nginx, cert-manager, external-secrets)
  • [ ] Ingress NGINX Controller avec NLB (hostname récupéré)
  • [ ] cert-manager avec ClusterIssuers Let's Encrypt (prod + staging) Ready
  • [ ] External Secrets Operator connecté à Doppler
  • [ ] Secret nex-secrets synchronisé dans nex-staging
  • [ ] Secret gitlab-registry créé pour le pull d'images
  • [ ] Resource Quotas appliqués (staging + production + template review)
  • [ ] Overlay Kustomize review prêt (infrastructure/kubernetes/overlays/review/)

DNS & Certificats ​

  • [ ] staging.paywithnex.com pointe vers le NLB via Cloudflare (proxy activé)
  • [ ] *.review.paywithnex.com pointe vers le NLB via Cloudflare (proxy activé)
  • [ ] admin.paywithnex.com pointe vers Firebase Hosting
  • [ ] bo.paywithnex.com pointe vers Firebase Hosting
  • [ ] SSL/TLS Full Strict configuré dans Cloudflare
  • [ ] HSTS activé
  • [ ] Records SPF, DKIM, DMARC pour Resend

Secrets ​

  • [ ] Doppler (projet paywithnex, config stg) contient toutes les variables
  • [ ] Variables critiques : POSTGRES_SSL=true, REDIS_TLS=true
  • [ ] Noms corrects : POSTGRES_HOST (pas DATABASE_HOST), REDIS_PASSWORD (pas REDIS_AUTH_TOKEN)
  • [ ] URLs inter-services configurées (RISK_ENGINE_SERVICE_URL, etc.)
  • [ ] Les endpoints Terraform sont synchronisés dans Doppler
  • [ ] Les secrets sont accessibles depuis les pods K8s

Services NestJS ​

  • [ ] Tous les services ont un endpoint GET /health exclu du prefix global
  • [ ] Tous les services avec TypeORM ont SSL configuré (app.module.ts ET data-source.ts)
  • [ ] Les services Redis (auth, notifications) ont TLS activé
  • [ ] Les images Docker sont buildées en linux/amd64 et pushées sur GitLab Registry

Connectivité ​

  • [ ] Un pod dans nex-staging peut joindre RDS sur le port 5432 (avec SSL)
  • [ ] Un pod dans nex-staging peut joindre Redis sur le port 6379 (avec TLS)
  • [ ] curl sur https://staging.paywithnex.com retourne une réponse (même 404)
  • [ ] curl sur https://test.review.paywithnex.com résout vers le NLB (wildcard DNS OK)
  • [ ] Les dashboards Firebase Hosting répondent sur leurs domaines

Si un point est rouge → corriger avant de passer au fichier 02.


Prochaine étape : 02_cicd_et_services.md — Pipeline GitLab CI + déploiement des microservices.

NxPay — Plateforme fintech CEMAC