# 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 ✅ ON Auto A app ✅ ON Auto A @ ✅ ON Auto ``` Pour obtenir l'IP du Load Balancer Hetzner : ```bash 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 : ```bash # 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 ```bash # 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) : ```yaml 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 ```bash # Test avec wscat (npm install -g wscat) wscat -c "wss://api.xpeditis.com/notifications" \ -H "Authorization: Bearer " # La connexion doit s'établir et recevoir : # {"event":"unread_count","data":{"count":0}} # {"event":"recent_notifications","data":[...]} ``` --- ## Traefik Dashboard (accès restreint) ```bash # 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 | 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: --- 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 ```bash 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 ```