From 00b2c5db855c772356c8bb76fe0e1613d0ae73c7 Mon Sep 17 00:00:00 2001 From: billisdead Date: Wed, 20 May 2026 18:33:06 +0200 Subject: [PATCH] docs: simplify HAPROXY.md for existing setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Config ciblée sur le backend n8n existant — juste le delta à appliquer. Token requis uniquement hors LAN/IP fixe, LAN reste en accès direct. Co-Authored-By: Claude Sonnet 4.6 --- docs/HAPROXY.md | 215 +++++++++++++----------------------------------- 1 file changed, 58 insertions(+), 157 deletions(-) diff --git a/docs/HAPROXY.md b/docs/HAPROXY.md index d8bfab7..1f03b42 100644 --- a/docs/HAPROXY.md +++ b/docs/HAPROXY.md @@ -1,195 +1,96 @@ # Configuration HAProxy pour n8n Pilot -## Architecture +## Contexte -``` -Android App → HTTPS → HAProxy (443) → HTTP → n8n (5678) - ↓ - Validation X-App-Token - Rate limiting - TLS termination -``` - -HAProxy agit comme reverse proxy devant n8n. Il : -1. Termine TLS (certificat Let's Encrypt ou autosigné) -2. Valide le header `X-App-Token` (token secret partagé) -3. Applique du rate limiting pour protéger l'API -4. Forward les requêtes vers n8n en HTTP interne +n8n est déjà exposé via HAProxy à `https://n8n.gyozamancave.fr`. +Il suffit d'ajouter la validation du `X-App-Token` dans le backend existant **sans toucher au frontend**. --- -## Configuration HAProxy complète +## Modification à apporter — backend n8n uniquement + +Remplacer le backend `n8n-backend` actuel par : ```haproxy -#--------------------------------------------------------------------- -# global : paramètres du processus HAProxy -#--------------------------------------------------------------------- -global - log /dev/log local0 - log /dev/log local1 notice - maxconn 2000 - daemon +backend n8n-backend + mode http + balance source -#--------------------------------------------------------------------- -# defaults : valeurs par défaut pour tous les frontends/backends -#--------------------------------------------------------------------- -defaults - log global - mode http - option httplog - option dontlognull - timeout connect 5s - timeout client 30s - timeout server 30s - # Timeout plus long pour les webhooks n8n qui peuvent prendre du temps - timeout tunnel 3600s + # IPs qui ne nécessitent pas le X-App-Token (LAN + IP fixe perso) + acl is_local src 192.168.1.0/24 192.168.2.0/24 82.67.3.126/32 -#--------------------------------------------------------------------- -# frontend : point d'entrée HTTPS sur le port 443 -#--------------------------------------------------------------------- -frontend n8n_pilot_frontend - bind *:443 ssl crt /etc/ssl/n8n/fullchain.pem alpn h2,http/1.1 - - # Sécurité TLS : désactiver les versions obsolètes - ssl-min-ver TLSv1.2 - - # Headers de sécurité injectés sur toutes les réponses - http-response set-header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" - http-response set-header X-Content-Type-Options "nosniff" - http-response set-header X-Frame-Options "DENY" - - # ACL : vérification du X-App-Token - # La valeur doit correspondre exactement au token configuré dans l'app + # Validation du token pour les accès extérieurs (mobile app) acl valid_app_token req.hdr(X-App-Token) -m str "VOTRE_TOKEN_ICI" + http-request deny deny_status 403 if !is_local !valid_app_token - # Refuser les requêtes sans token valide avec 403 - http-request deny deny_status 403 if !valid_app_token - - # Rate limiting : max 60 requêtes par minute par IP - # Protège contre les scripts de scan et les abus - stick-table type ip size 100k expire 60s store http_req_rate(60s) - http-request track-sc0 src - http-request deny deny_status 429 if { sc_http_req_rate(0) gt 60 } - - # Redirection HTTP → HTTPS (optionnel si port 80 ouvert) - # redirect scheme https if !{ ssl_fc } - - default_backend n8n_backend - -#--------------------------------------------------------------------- -# backend : instance n8n locale -#--------------------------------------------------------------------- -backend n8n_backend - balance roundrobin - - # Supprimer le X-App-Token avant de forwarder à n8n (ne doit pas atteindre n8n) + # Supprimer le token avant de forwarder à n8n (n8n ne doit pas le voir) http-request del-header X-App-Token - # Health check : n8n répond sur /healthz - option httpchk GET /healthz - http-check expect status 200 + http-request set-header X-Forwarded-Proto https if { ssl_fc } + http-request set-header X-Real-IP %[src] + http-request set-header X-Forwarded-For %[src] + http-request set-header Host %[req.hdr(host)] + option forwardfor + server n8n 192.168.1.56:5678 +``` - server n8n_local 127.0.0.1:5678 check inter 10s fall 3 rise 2 +**Logique** : +- Depuis le LAN (`192.168.1/2.x`) ou ton IP fixe → accès direct, pas de token requis (navigation normale dans n8n) +- Depuis l'extérieur (mobile app 4G/5G) → `X-App-Token` obligatoire, sinon 403 + +--- + +## Appliquer la modification + +```bash +# Valider la syntaxe avant rechargement +haproxy -f /etc/haproxy/haproxy.cfg -c + +# Rechargement gracieux (sans coupure) +systemctl reload haproxy ``` --- -## Rotation du X-App-Token +## Générer un token solide -Le token doit être tourné régulièrement (recommandé : tous les 90 jours minimum). +```bash +openssl rand -hex 32 +``` -### Procédure de rotation sans downtime +Copier la valeur générée dans : +1. La config HAProxy (`VOTRE_TOKEN_ICI` ci-dessus) +2. L'app Android : Paramètres > Token HAProxy > Sauvegarder -1. **Générer un nouveau token** : - ```bash - openssl rand -hex 32 - # Exemple : a3f8b2c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1 - ``` +--- -2. **Phase de transition — accepter les deux tokens** : - ```haproxy - # Accepter l'ancien ET le nouveau pendant la transition - acl valid_app_token req.hdr(X-App-Token) -m str "ANCIEN_TOKEN" - acl valid_app_token_new req.hdr(X-App-Token) -m str "NOUVEAU_TOKEN" - http-request deny deny_status 403 if !valid_app_token !valid_app_token_new - ``` +## Rotation du token -3. **Mettre à jour l'app** sur tous les appareils (Paramètres > Token HAProxy > Sauvegarder) +1. Générer un nouveau token (`openssl rand -hex 32`) +2. Ajouter temporairement les **deux** ACLs dans HAProxy pour éviter les coupures : -4. **Retirer l'ancien token** de HAProxy une fois tous les appareils mis à jour : - ```haproxy - acl valid_app_token req.hdr(X-App-Token) -m str "NOUVEAU_TOKEN" - http-request deny deny_status 403 if !valid_app_token - ``` +```haproxy + acl valid_app_token req.hdr(X-App-Token) -m str "ANCIEN_TOKEN" + acl valid_app_token_new req.hdr(X-App-Token) -m str "NOUVEAU_TOKEN" + http-request deny deny_status 403 if !is_local !valid_app_token !valid_app_token_new +``` -5. **Recharger HAProxy** sans coupure : - ```bash - haproxy -f /etc/haproxy/haproxy.cfg -c # Valider la config - systemctl reload haproxy # Rechargement gracieux - ``` +3. Mettre à jour le token dans l'app (Paramètres > Token HAProxy) +4. Retirer l'ancien token de la config HAProxy +5. `systemctl reload haproxy` --- ## Troubleshooting -### Erreur 403 Forbidden - -**Cause** : `X-App-Token` absent ou incorrect. - -**Diagnostic** : +**403 depuis l'app** → token absent ou incorrect. Tester : ```bash -# Tester manuellement avec curl -curl -v -H "X-App-Token: VOTRE_TOKEN" https://n8n.votre-domaine.com/api/v1/workflows +curl -v -H "X-App-Token: VOTRE_TOKEN" https://n8n.gyozamancave.fr/api/v1/workflows ``` -**Solutions** : -- Vérifier que le token dans l'app (Paramètres) correspond exactement à la config HAProxy -- Contrôler les espaces ou caractères invisibles dans le token -- Vérifier les logs HAProxy : `journalctl -u haproxy -f` +**403 depuis le navigateur** → vérifier que l'IP source est bien dans `is_local`. Depuis un VPN ou tethering, l'IP peut ne pas matcher. ---- - -### Erreur 429 Too Many Requests - -**Cause** : rate limit dépassé (> 60 req/min). - -**Solutions** : -- Augmenter l'intervalle de polling dans l'app (Paramètres > Intervalle) -- Ajuster la limite dans HAProxy si l'usage légitime est plus élevé -- Vérifier qu'aucun processus tiers ne spam l'endpoint - ---- - -### Timeout / connexion refusée - -**Cause** : n8n inaccessible sur le port 5678, ou HAProxy mal configuré. - -**Diagnostic** : +**Logs HAProxy en direct** : ```bash -# Vérifier que n8n tourne -curl http://127.0.0.1:5678/healthz - -# Vérifier le statut HAProxy -systemctl status haproxy -haproxy -f /etc/haproxy/haproxy.cfg -c - -# Logs HAProxy en temps réel journalctl -u haproxy -f ``` - ---- - -### Certificat TLS expiré - -```bash -# Renouveler avec Certbot -certbot renew --quiet -# Recharger HAProxy pour prendre en compte le nouveau certificat -systemctl reload haproxy -``` - -Automatiser le renouvellement : -```bash -# Ajouter dans crontab (root) -0 3 * * 1 certbot renew --quiet && systemctl reload haproxy -```