# 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 :
```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 <JWT_TOKEN>"

# 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 <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

```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
```
