Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BAUeCFpDkRD6tU5wGsc1C
6.1 KiB
Configuration Cloudflare — production
Cloudflare est la première ligne de défense : DNS, anti-DDoS, WAF, cache. Le firewall Hetzner n'accepte 80/443 que depuis les rangs Cloudflare, donc si le proxy est désactivé sur un enregistrement, ce domaine devient inaccessible. C'est voulu : personne ne doit pouvoir taper l'IP d'origine.
Le plan Free suffit en phase 1 (≈ 100 utilisateurs). Le plan Pro (20 €/mois, prévu au budget à partir de 1 000 utilisateurs) ajoute le WAF managé et l'analyse de bots.
1. Enregistrements DNS
Toutes les valeurs <IP_APP_01> viennent de make tf-output.
| Type | Nom | Contenu | Proxy | TTL |
|---|---|---|---|---|
| A | xpeditis.com |
<IP_APP_01> |
Proxifié | Auto |
| A | www |
<IP_APP_01> |
Proxifié | Auto |
| A | app |
<IP_APP_01> |
Proxifié | Auto |
| A | api |
<IP_APP_01> |
Proxifié | Auto |
| A | grafana |
<IP_APP_01> |
Proxifié | Auto |
| AAAA | idem si IPv6 activée | <IPv6_APP_01> |
Proxifié | Auto |
Aucun enregistrement ne pointe vers db-01. Son IP publique ne sert qu'au SSH d'administration et ne doit apparaître nulle part en DNS.
Messagerie (obligatoire pour que les e-mails arrivent)
| Type | Nom | Contenu |
|---|---|---|
| TXT | xpeditis.com |
v=spf1 include:spf.brevo.com -all |
| TXT | mail._domainkey |
clé DKIM fournie par Brevo |
| TXT | _dmarc |
v=DMARC1; p=quarantine; rua=mailto:dmarc@xpeditis.com; pct=100 |
Sans SPF/DKIM/DMARC, les confirmations de réservation et les liens magiques
transporteurs partent en spam — un défaut de production silencieux et coûteux.
Commencez à p=quarantine, passez à p=reject après deux semaines de rapports
propres.
2. Réglages SSL/TLS
| Réglage | Valeur | Pourquoi |
|---|---|---|
| Mode de chiffrement | Full (strict) | Cloudflare vérifie le certificat Let's Encrypt du serveur. En « Flexible », le trafic Cloudflare → origine serait en clair. |
| Always Use HTTPS | Activé | |
| Minimum TLS Version | 1.2 | TLS 1.0/1.1 sont cassés et refusés par la plupart des référentiels de conformité. |
| TLS 1.3 | Activé | |
| Automatic HTTPS Rewrites | Activé | |
| HSTS | Activé après vérification | Voir l'avertissement ci-dessous. |
HSTS est irréversible pendant un an. Ne l'activez qu'une fois certain que tous les sous-domaines servent bien du HTTPS valide. Traefik pose déjà l'en-tête (
k8s/base/08-traefik-middlewares.yaml) ; l'activer aussi côté Cloudflare ajoute la protection avant même que la requête n'atteigne le serveur.
3. Règles WAF
3.1 Bloquer l'accès direct aux chemins d'administration
(http.host eq "api.xpeditis.com" and starts_with(http.request.uri.path, "/api/docs"))
→ Action : Block
La documentation Swagger est déjà désactivée en production par main.ts, mais
une règle de bordure protège même en cas d'erreur de configuration.
3.2 Limiter les tentatives de connexion
Rate limiting (Security → WAF → Rate limiting rules) :
| Champ | Valeur |
|---|---|
| Expression | starts_with(http.request.uri.path, "/api/v1/auth/login") |
| Requêtes | 10 |
| Période | 1 minute |
| Par | Adresse IP |
| Action | Block, 10 minutes |
Cette limite s'ajoute à celle de Traefik (rate-limit-auth) et à celle de
NestJS (CustomThrottlerGuard). Trois couches indépendantes : une erreur de
configuration sur l'une ne laisse pas la porte ouverte.
3.3 Protéger le webhook Stripe
(http.host eq "api.xpeditis.com" and starts_with(http.request.uri.path, "/api/v1/subscriptions/webhook") and not ip.src in {IP_STRIPE})
→ Action : Block
Les rangs d'IP Stripe sont publiés sur
https://stripe.com/files/ips/ips_webhooks.txt. La signature du webhook est
déjà vérifiée côté applicatif ; ceci évite simplement que du bruit atteigne
l'application.
3.4 Filtrage géographique — à décider consciemment
Xpeditis sert des transitaires européens. Bloquer tout sauf l'Europe réduit massivement le bruit, mais coupe aussi les transitaires asiatiques légitimes (les connecteurs transporteurs partent du serveur, ils ne sont pas concernés).
Recommandation : ne pas bloquer par pays au lancement. Activer plutôt « Bot Fight Mode » (gratuit) et surveiller les journaux un mois avant de décider.
4. Cache
| Chemin | Règle |
|---|---|
api.xpeditis.com/* |
Bypass cache — une réponse d'API mise en cache servirait les données d'un utilisateur à un autre. |
app.xpeditis.com/_next/static/* |
Cache Everything, Edge TTL 1 an (contenu au nom versionné). |
app.xpeditis.com/* |
Standard (Next.js gère ses propres en-têtes). |
La règle de bypass sur l'API est critique. Un cache mal placé sur une route authentifiée est une fuite de données, pas un problème de performance.
5. Jeton API pour cert-manager
My Profile → API Tokens → Create Token → Edit zone DNS
| Permission | Portée |
|---|---|
| Zone / DNS / Edit | Zone xpeditis.com uniquement |
| Zone / Zone / Read | Zone xpeditis.com uniquement |
N'utilisez jamais la clé globale du compte : elle permettrait à cert-manager — ou à quiconque lirait le Secret — de rediriger tout votre trafic.
Le jeton alimente le Secret cert-manager/cloudflare-api-token
(k8s/base/03-secrets.template.yaml).
6. Accès à Grafana
Deux options, au choix :
- Liste d'IP (par défaut) — middleware Traefik
admin-ip-allowlist, à renseigner dansk8s/base/08-traefik-middlewares.yaml. - Cloudflare Access (Zero Trust, gratuit jusqu'à 50 utilisateurs) —
authentification par e-mail à usage unique devant
grafana.xpeditis.com. Préférable si votre IP est dynamique.
7. Vérification
# Le proxy est-il bien actif ? (l'IP retournée doit être une IP Cloudflare)
dig +short app.xpeditis.com
# L'origine est-elle injoignable en direct ?
curl -sS --max-time 5 --connect-to app.xpeditis.com:443:<IP_APP_01>:443 \
https://app.xpeditis.com/ # doit échouer par timeout
# SPF / DKIM / DMARC
dig +short TXT xpeditis.com
dig +short TXT _dmarc.xpeditis.com