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 ResendDélivrabilité email
TXTresend._domainkeyDKIM record ResendSignature email
TXT_dmarcDMARC policyAnti-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