xpeditis2.0/docs/deployment/hetzner/10-ingress-tls-cloudflare.md
2026-03-26 18:08:28 +01:00

6.3 KiB

10 — Ingress, TLS et Cloudflare


Architecture TLS

Deux approches possibles, que vous pouvez combiner :

Option 1 — TLS Cloudflare uniquement (plus simple)
  Browser → Cloudflare (TLS terminé) → HTTP vers Hetzner LB → Pods

Option 2 — TLS de bout en bout (plus sécurisé)
  Browser → Cloudflare → HTTPS vers Hetzner LB → cert-manager TLS → Pods

Recommandation : Option 2 avec Cloudflare en "Full (strict)" mode

Configuration Cloudflare

1. Ajouter les entrées DNS

Dans votre dashboard Cloudflare → Votre domaine → DNS → Records :

Type    Name    Content                 Proxy   TTL
A       api     <IP_LB_HETZNER>        ✅ ON   Auto
A       app     <IP_LB_HETZNER>        ✅ ON   Auto
A       @       <IP_LB_HETZNER>        ✅ ON   Auto

Pour obtenir l'IP du Load Balancer Hetzner :

hcloud load-balancer list
# ID     NAME         TYPE   LOCATION   PUBLIC NET            PRIVATE NET
# 12345  xpeditis-lb  lb11   fsn1       1.2.3.4 / 2001::...  10.0.0.2

2. SSL/TLS Mode

Cloudflare → Votre domaine → SSL/TLS → Overview :

  • Sélectionnez Full (strict) ← obligatoire si cert-manager gère les certicats côté Hetzner

3. Page Rules / Transform Rules

Cloudflare → Votre domaine → Rules → Page Rules :

Rule 1 : Force HTTPS
  If URL matches: http://api.xpeditis.com/*
  Then: Always Use HTTPS

Rule 2 : Force HTTPS frontend
  If URL matches: http://app.xpeditis.com/*
  Then: Always Use HTTPS

4. WAF Rules (optionnel mais recommandé)

Cloudflare → Security → WAF → Managed Rules :

  • Activer Cloudflare Managed Ruleset (gratuit)
  • Activer Cloudflare OWASP Core Ruleset (gratuit)

Custom Rules pour Xpeditis :

Rule: Block rate search abuse
  If: (http.request.uri.path contains "/api/v1/rates/search") AND (rate(1m) > 60)
  Then: Block

Rule: Protect Stripe webhook
  If: (http.request.uri.path eq "/api/v1/subscriptions/webhook") AND (not ip.src in {151.101.0.0/17})
  Then: Block  ← Autorise uniquement les IPs Stripe

5. Cache Rules (pour les assets frontend)

Cloudflare → Caching → Cache Rules :

Rule: Cache Next.js static assets
  If: (http.request.uri.path contains "/_next/static/")
  Then: Cache Everything, TTL 1 year

Vérification du certificat TLS (cert-manager)

Après le déploiement de l'Ingress :

# Vérifier l'état du certificat
kubectl get certificate -n xpeditis-prod
# NAME               READY   SECRET             AGE
# xpeditis-tls-prod  True    xpeditis-tls-prod  5m  ← READY=True = succès

# Si READY=False, debugger :
kubectl describe certificate xpeditis-tls-prod -n xpeditis-prod
kubectl describe certificaterequest -n xpeditis-prod
kubectl logs -n cert-manager deployment/cert-manager | tail -50

# Voir les challenges ACME en cours
kubectl get challenge -n xpeditis-prod
# Si des challenges sont en attente, vérifier que le DNS Cloudflare pointe bien vers le LB

Tester la chaîne TLS

# Tester le certificat
curl -I https://api.xpeditis.com/api/v1/health
# HTTP/2 200
# server: traefik
# content-type: application/json

# Détails du certificat
openssl s_client -connect api.xpeditis.com:443 -servername api.xpeditis.com 2>/dev/null | openssl x509 -noout -dates
# notBefore=Apr  1 00:00:00 2026 GMT
# notAfter=Jun 30 00:00:00 2026 GMT  ← Let's Encrypt = 90 jours, renouvellement auto à 60 jours

Configuration WebSocket Socket.IO

Socket.IO nécessite une configuration spécifique pour fonctionner derrière Traefik + Cloudflare.

Cloudflare WebSocket

Cloudflare → Votre domaine → Network → WebSockets :

  • Activer WebSockets (désactivé par défaut sur le plan Free)

Note : Sur le plan Free Cloudflare, les WebSockets sont supportés mais avec un timeout de 100s. Pour les connexions persistantes Socket.IO, configurez des reconnexions côté client.

Traefik Sticky Sessions

La configuration des sticky sessions dans k8s/07-ingress.yaml garantit que les reconnexions WebSocket retombent sur le même pod (important pour Socket.IO avant l'implémentation Redis adapter) :

annotations:
  traefik.ingress.kubernetes.io/service.sticky.cookie: "true"
  traefik.ingress.kubernetes.io/service.sticky.cookie.name: "XPEDITIS_BACKEND"
  traefik.ingress.kubernetes.io/service.sticky.cookie.secure: "true"

Test WebSocket

# Test avec wscat (npm install -g wscat)
wscat -c "wss://api.xpeditis.com/notifications" \
  -H "Authorization: Bearer <JWT_TOKEN>"

# La connexion doit s'établir et recevoir :
# {"event":"unread_count","data":{"count":0}}
# {"event":"recent_notifications","data":[...]}

Traefik Dashboard (accès restreint)

# Traefik a un dashboard utile pour debugger les routes
# Activer l'accès avec authentification

# Générer un mot de passe htpasswd
htpasswd -nb admin <MOT_DE_PASSE> | base64

# Créer un Middleware BasicAuth
cat > /tmp/traefik-auth.yaml << 'EOF'
apiVersion: v1
kind: Secret
metadata:
  name: traefik-dashboard-auth
  namespace: kube-system
type: kubernetes.io/basic-auth
stringData:
  username: admin
  password: <MOT_DE_PASSE>
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: dashboard-auth
  namespace: kube-system
spec:
  basicAuth:
    secret: traefik-dashboard-auth
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: traefik-dashboard
  namespace: kube-system
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
    traefik.ingress.kubernetes.io/router.middlewares: "kube-system-dashboard-auth@kubernetescrd"
spec:
  ingressClassName: traefik
  tls:
  - hosts:
    - traefik.xpeditis.com
    secretName: traefik-tls
  rules:
  - host: traefik.xpeditis.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: traefik
            port:
              number: 9000
EOF

kubectl apply -f /tmp/traefik-auth.yaml

Checklist TLS

echo "=== Test endpoints ==="
curl -sf https://api.xpeditis.com/api/v1/health | jq .
curl -sf https://app.xpeditis.com/ | head -5

echo "=== Certificats ==="
kubectl get certificate -n xpeditis-prod
kubectl get certificaterequest -n xpeditis-prod

echo "=== Ingress ==="
kubectl get ingress -n xpeditis-prod

echo "=== Test HTTPS force ==="
curl -L http://api.xpeditis.com/api/v1/health
# Doit être redirigé vers HTTPS