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é primaireLockID(String). Utilisée pour le verrouillage concurrent — empêche deuxterraform applysimultané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) :
terraform {
backend "s3" {
bucket = "nex-terraform-state-009001720821"
key = "staging/terraform.tfstate"
region = "eu-west-3"
dynamodb_table = "nex-terraform-locks"
encrypt = true
}
}Validation
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, RedisComposants 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
# 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 : availablePHASE 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-nodesdé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 desg-rdsetsg-redisdoivent autoriser à la foissg-eks-nodesET le SG cluster auto-généré par EKS.Après la création du cluster (Phase 7), récupérer le SG cluster :
bashaws eks describe-cluster --name nex-staging --region eu-west-3 \ --query "cluster.resourcesVpcConfig.clusterSecurityGroupId" --output textPuis ajouter ce SG en inbound sur
sg-rds(port 5432) etsg-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
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 5432PHASE 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 :
- Se connecte via kubectl port-forward (pas de bastion)
- Crée un utilisateur applicatif
nex_appavec un mot de passe depuis Doppler - Crée la base
nex_staging - Accorde les droits SELECT, INSERT, UPDATE, DELETE à
nex_app(pas de DROP, pas de ALTER schema — moindre privilège) - 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 versnex_appest prévue mais pas encore effectuée. Le scriptseed-database.shexiste dansinfrastructure/scripts/et doit être exécuté pour créer le user applicatif, puis les variables DopplerPOSTGRES_USERetPOSTGRES_PASSWORDdoivent ê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_adminne 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
# 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
# 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 : PONGPHASE 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
uploadscô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
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 à truePHASE 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-roleavec les policies EKS standard (AmazonEKSClusterPolicy) - Node role :
nex-staging-eks-node-roleavec 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
# Configurer kubectl
aws eks update-kubeconfig --name nex-staging --region eu-west-3IMPORTANT — 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-rdsetsg-redis. Voir l'encadré "Piège EKS" en Phase 3.
Validation
# Cluster actif
kubectl cluster-info
# Nœuds prêts
kubectl get nodes
# Attendu : 2 nœuds en status Ready
# Version K8s correcte
kubectl version --shortPHASE 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 Controllercert-manager— Gestion des certificatsexternal-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 podsnex-production: max 16 CPU, 32 Gi mémoire, 50 podsnex-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: trueetreal-ip-header: CF-Connecting-IPpour recevoir la vraie IP client derrière Cloudflare - Configurer
proxy-real-ip-cidravec 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, ClusterIssuerletsencrypt-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
SecretStoredoppler-storedansnex-stagingqui pointe vers Doppler (avec le token Doppler stocké dans un K8s Secretdoppler-tokencréé manuellement une seule fois) - Un
ExternalSecretqui synchronise toutes les variables Doppler (regexp.*) vers un Secret K8snex-secretsdans le namespacenex-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) :
kubectl annotate externalsecret nex-secrets -n nex-staging \
force-sync=$(date +%s) --overwrite8.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
# 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) :
| Type | Nom | Valeur | Proxy Cloudflare | Note |
|---|---|---|---|---|
| CNAME | api.staging | Hostname du NLB (phase 8.3) | Oui (orange) | API staging |
| CNAME | admin | Site Firebase Hosting | Non (gris) | CMMS Dashboard |
| CNAME | bo | Site Firebase Hosting | Non (gris) | Backoffice |
| TXT | @ | SPF record pour Resend | — | Délivrabilité email |
| TXT | resend._domainkey | DKIM record Resend | — | Signature email |
| TXT | _dmarc | DMARC policy | — | Anti-spoofing |
| CNAME | *.review | Hostname 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
# 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 FirebasePHASE 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.comIMPORTANT — Variables de connexion :
Les services NestJS utilisent les noms
POSTGRES_*(pasDATABASE_*) etREDIS_PASSWORD(pasREDIS_AUTH_TOKEN). Deux variables cruciales à ne pas oublier :
POSTGRES_SSL=true— RDS exige SSL. Sans ça, erreurno pg_hba.conf entry... no encryption.REDIS_TLS=true— ElastiCache atransit_encryption_enabled. Sans ça, erreurETIMEDOUTcar 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:3010Note : 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 namespacenex-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
# 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 DopplerPHASE 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 :
app.setGlobalPrefix('v1', { exclude: ['health'] });Dans app.controller.ts :
@Get('health')
health() {
return { status: 'ok' };
}Sans l'exclusion
{ exclude: ['health'] }, le endpoint devient/v1/healthau 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) :
ssl: process.env.POSTGRES_SSL === 'true' ? { rejectUnauthorized: false } : false,Dans data-source.ts (si le service en a un — utilisé par les migrations) :
ssl: process.env.POSTGRES_SSL === 'true' ? { rejectUnauthorized: false } : false,Piège : certains services (auth, orchestrator, notifications...) ont deux connexions TypeORM — une dans
app.module.tset une dansdata-source.ts. Si on oublie l'une des deux, le service crash avecno pg_hba.conf entry... no encryption.
11.3 TLS Redis
Les services utilisant Redis (auth, notifications via BullMQ) doivent activer TLS :
ioredis (auth) :
...(process.env.REDIS_TLS === 'true' && { tls: {} }),BullMQ (notifications) :
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
| Service | Port | TypeORM SSL | data-source.ts | Redis TLS | Health endpoint |
|---|---|---|---|---|---|
| auth | 3001 | app.module.ts | oui | oui (ioredis) | oui |
| ledger-wallets | 3002 | app.module.ts | oui | non | oui |
| customer-profiles-kyc | 3003 | app.module.ts | oui | non | oui |
| orchestrator | 3004 | app.module.ts | oui | non | oui |
| notifications | 3005 | app.module.ts | oui | oui (BullMQ) | oui |
| configuration | 3006 | app.module.ts | oui | non | oui |
| file-service | 3007 | app.module.ts | oui | non | oui |
| providers-gateway | 3008 | app.module.ts | oui | non | oui |
| risk-engine | 3009 | app.module.ts | oui | non | oui |
| service-catalog | 3010 | database.providers.ts | non | non | oui |
| logs-reporting | 3011 | non (pas de DB) | non | non | oui |
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_appcréé avec droits restreints (ounex_admintemporairement) - [ ] 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-secretssynchronisé dans nex-staging - [ ] Secret
gitlab-registrycréé 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, configstg) contient toutes les variables - [ ] Variables critiques :
POSTGRES_SSL=true,REDIS_TLS=true - [ ] Noms corrects :
POSTGRES_HOST(pasDATABASE_HOST),REDIS_PASSWORD(pasREDIS_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 /healthexclu 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.