feat: application n8n Pilot complète (Expo managed workflow)

- Stack : Expo Router, Axios, Zustand, React Native Paper (thème sombre), date-fns
- Sécurité : secrets dans Android Keystore via expo-secure-store, TLS obligatoire,
  headers X-N8N-API-KEY + X-App-Token injectés par intercepteur Axios
- API : client.ts centralisé + workflows.ts + executions.ts (TypeScript strict)
- Store : Zustand appStore avec chargement depuis secure store au démarrage
- Hooks : usePolling (générique), useWorkflows, useExecutions
- Composants : StatusBadge, WorkflowCard, ExecutionCard, SkeletonLoader
- Screens : Dashboard, Workflows, Executions, Logs (détail exécution), Settings
- Navigation Expo Router : 4 tabs + stack Logs + écran Setup initial
- Docs : INSTALL.md, UPDATE.md, BACKUP.md, HAPROXY.md, SECURITY.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-20 17:31:55 +02:00
parent ea1705d3b0
commit 92e67d0769
41 changed files with 4891 additions and 58 deletions
+147
View File
@@ -0,0 +1,147 @@
# Sécurité — n8n Pilot
## Modèle de menaces
### Acteurs et vecteurs
| Menace | Impact | Probabilité | Mitigation |
|--------|--------|-------------|-----------|
| Vol de l'appareil Android | Accès aux credentials n8n | Moyen | Android Keystore chiffré, biométrie optionnelle |
| Interception réseau (MITM) | Exposition de la clé API | Faible (TLS) | TLS obligatoire, refus HTTP |
| Fuite du token HAProxy | Accès non autorisé à n8n | Moyen | Rotation régulière, token hors bundle |
| Compromission du build APK | Backdoor dans l'app | Faible | Keystore signé, builds EAS reproductibles |
| Brute force API | Épuisement quotas n8n | Faible | Rate limiting HAProxy (429) |
| Logs applicatifs exposés | Fuite de secrets | Faible | Jamais de log de secrets, même en dev |
---
## Principes appliqués
### Least Privilege (moindre privilège)
La clé API n8n doit être créée avec le **scope minimal** nécessaire :
```
Recommandé :
✓ workflows:list — lister les workflows
✓ workflows:read — lire les détails
✓ workflows:update — activer/désactiver (toggle)
✓ executions:list — voir l'historique
✓ executions:read — lire les logs
✓ executions:delete — supprimer des exécutions
✓ workflows:run — déclencher manuellement
Non recommandé :
✗ credentials:* — inutile pour cette app
✗ users:* — inutile pour cette app
✗ admin:* — jamais
```
**Comment créer la clé** dans n8n :
1. Settings > n8n API
2. Create API Key
3. Nommer la clé "n8n-pilot-mobile"
4. Définir les scopes minimaux ci-dessus
5. Copier la clé — elle n'est affichée qu'une seule fois
---
### Defense in Depth (défense en profondeur)
L'accès à l'API n8n passe par **deux barrières indépendantes** :
```
Requête → [TLS] → [X-App-Token HAProxy] → [X-N8N-API-KEY n8n] → Données
```
Si un token est compromis :
- Compromission du `X-App-Token` seul → HAProxy bloque, n8n jamais atteint
- Compromission de la clé API seul → sans le `X-App-Token`, HAProxy bloque à 403
- Les deux compromis → rotation immédiate des deux (procédures dans HAPROXY.md et BACKUP.md)
---
### Zero Trust (réseau local inclus)
n8n ne doit **pas** être accessible directement sur le réseau, même en local.
Toutes les requêtes passent par HAProxy qui valide le token.
```
# Bloquer l'accès direct au port n8n (iptables)
iptables -A INPUT -p tcp --dport 5678 -s 127.0.0.1 -j ACCEPT
iptables -A INPUT -p tcp --dport 5678 -j DROP
```
---
## Gestion des secrets — cycle de vie
### Stockage
```
┌─────────────────────────────────────────────────────┐
│ Appareil Android │
│ ┌─────────────────────────────────────────────┐ │
│ │ Android Keystore (expo-secure-store) │ │
│ │ n8n_base_url → https://n8n.example.com │ │
│ │ n8n_api_key → clé API (chiffrée) │ │
│ │ n8n_app_token → token HAProxy (chiffré) │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ État React (Zustand) — en mémoire uniquement : │
│ config.baseUrl → copie non sensible │
│ config.isConfigured → booléen │
│ JAMAIS : apiKey, appToken en mémoire JS │
└─────────────────────────────────────────────────────┘
```
### Transit
- Les secrets sont lus depuis le Keystore **à chaque requête** HTTP dans `client.ts`
- Ils sont injectés dans les headers HTTP et **ne sont pas loggués**
- Ils ne sont **jamais** :
- Sérialisés dans AsyncStorage
- Inclus dans des logs (même en dev)
- Embarqués dans le bundle JS (`app.json`, `package.json`, code source)
- Envoyés à des services tiers (analytics, crash reporters)
### Rotation
| Secret | Fréquence recommandée | Procédure |
|--------|-----------------------|-----------|
| Clé API n8n | Tous les 180 jours, ou en cas de compromission | Créer nouvelle clé dans n8n, mettre à jour dans l'app |
| X-App-Token | Tous les 90 jours | Voir docs/HAPROXY.md |
| Keystore EAS | Jamais (sauf compromission) | Voir docs/BACKUP.md |
---
## Checklist avant mise en production
### Application
- [ ] `app.json` : `userInterfaceStyle` = `"dark"` (pas d'info sensible en clair)
- [ ] `.env` non commité dans git (`git status` ne montre pas `.env`)
- [ ] Aucun `console.log` de valeurs sensibles dans le code
- [ ] `__DEV__` gate sur tous les logs dans `errorHandler.ts`
- [ ] Expo SDK à jour — vérifier les CVE : `npx expo-doctor`
- [ ] Dépendances à jour : `npm audit`
### Infrastructure
- [ ] HAProxy : TLS 1.2 minimum, TLS 1.0/1.1 désactivés
- [ ] Certificat TLS valide et non expiré
- [ ] n8n inaccessible directement sur le port 5678 depuis l'extérieur
- [ ] Rate limiting HAProxy activé (429 configuré)
- [ ] Logs HAProxy ne contiennent pas les valeurs des headers secrets
### n8n
- [ ] Clé API avec scope minimal (voir § Least Privilege ci-dessus)
- [ ] n8n mis à jour vers la dernière version stable
- [ ] Sauvegardes n8n actives (workflows, credentials n8n)
### Appareil
- [ ] Verrouillage d'écran activé (PIN, pattern ou biométrie)
- [ ] Biométrie configurée dans l'app si disponible
- [ ] Appareil Android 8+ (API 26+) pour garantir la robustesse du Keystore