7.3 KiB
Audit de sécurité CodexPi
Date de révision : 13 septembre 2026.
Architecture réseau
Ordinateur du LAN ── HTTP :8765 ─────────┐
▼
Tailscale Serve :443 (Funnel interdit) ─► nginx Docker
├── Django/Uvicorn :8001 (réseau Docker uniquement)
└── FastAPI/Uvicorn :8000 (réseau Docker uniquement)
Le binding Compose publie uniquement nginx sur 0.0.0.0:8765; FastAPI et Django restent internes au réseau Docker. Le port est destiné au LAN de confiance et ne doit jamais être redirigé sur la box. Le projet ne crée aucune règle UPnP, DMZ, redirection de port ou Tailscale Funnel.
Menaces et protections
| Menace | Protection |
|---|---|
| exposition Internet accidentelle | aucune redirection de box, Tailscale Serve privé, refus de Funnel |
| interception d'identifiants | HTTPS via Tailscale recommandé ; accès HTTP limité au LAN de confiance ; cookies HttpOnly/SameSite Strict |
| brute force | limite par IP, délai progressif, verrouillage temporaire, journal des refus |
| détournement de session/CSRF | session Django côté serveur, jeton CSRF de session, contrôle Origin WebSocket |
| traversal/symlink | chemins canonisés, UUID projet, racines autorisées et interdites |
| injection de commandes | arguments structurés et validateurs stricts, aucune saisie dans un shell |
| téléversement malveillant | PNG/JPEG/WebP vérifiés par signature, 10 Mo max, UUID serveur, stockage privé, lecture authentifiée |
| action destructive | confirmation explicite, surface Git/Docker limitée, aucun prune global |
| élévation de privilèges | conteneurs non-root, capabilités supprimées, no-new-privileges, aucun sudo Codex |
| fuite de secrets | .env, bases, clés et logs ignorés par Git ; journaux sans credentials |
| mode développement | DEBUG=False, aucun --reload, contrôles Django de déploiement |
Le socket Docker monté dans le conteneur API demeure une capacité privilégiée nécessaire aux commandes Docker. L'accès CodexPi et au tailnet doit donc être réservé à des utilisateurs de confiance ; FULL ACCESS doit rester exceptionnel.
Authentification et journalisation
Django gère les comptes, les mots de passe Argon2 et les sessions en base. FastAPI valide ces mêmes sessions avant tout accès REST ou WebSocket. Les cookies sont HttpOnly et SameSite=Strict; l'attribut Secure est désactivé lorsque l'accès direct HTTP par IP est activé.
data/security.log enregistre : connexion réussie/refusée, IP source/Tailscale transmise par nginx, date, session créée/supprimée, refus WebSocket, changement de paramètres de sécurité et activation/désactivation de FULL ACCESS.
Le journal ne reçoit jamais : mots de passe, cookies, jetons CSRF, tokens Codex, credentials OpenAI ou contenu de .env.
Les photos jointes restent dans data/uploads/, hors Git. Elles ne sont jamais publiées par nginx comme répertoire statique : FastAPI contrôle la session avant chaque lecture. Les prompts en attente et leurs références d'images sont persistés en SQLite et traités un par un par un worker non-root.
Ports CodexPi
| Port | Exposition | Usage |
|---|---|---|
| 443 | interface Tailscale uniquement | HTTPS privé Tailscale Serve |
| 8765 | réseau local | nginx, accès par IP et cible de Tailscale Serve |
| 8000 | réseau Docker seulement | FastAPI/Uvicorn |
| 8001 | réseau Docker seulement | Django/Uvicorn |
Les autres ports visibles par ss -tulpn appartiennent à l'hôte ou à d'autres applications et ne sont pas modifiés par CodexPi.
Firewall et fail2ban
Si UFW est actif, autorisez éventuellement 8765/tcp uniquement depuis le sous-réseau local de confiance. Ne publiez jamais 8000 ou 8001 et n'autorisez pas 8765 depuis Internet.
Une jail fail2ban optionnelle est fournie sous deploy/fail2ban/. Le verrouillage applicatif est déjà actif ; avant toute activation fail2ban, sauvegardez la configuration existante, exécutez fail2ban-client -t et testez le filtre avec fail2ban-regex.
Procédure d'accès
Sur le Pi :
cd /home/config/CodexPi
./scripts/install.sh
./scripts/create-admin.sh
./scripts/tailscale-serve.sh --check
./scripts/tailscale-serve.sh --apply
Le script affiche l'adresse MagicDNS exacte, typiquement https://codexpi.<tailnet>.ts.net. Elle doit être ouverte uniquement depuis un appareil connecté au même tailnet. La page Django demande ensuite l'identifiant CodexPi.
Sur iPhone/iPad : installez Tailscale depuis l'App Store, acceptez le profil VPN, connectez-vous au même compte/tailnet, activez Tailscale, puis ouvrez l'adresse HTTPS affichée par le script dans Safari. Le site peut ensuite être ajouté à l'écran d'accueil comme PWA.
Vérifier l'absence d'accès public
- Sur le téléphone, coupez le Wi-Fi et désactivez Tailscale.
- Ouvrez l'adresse
https://codexpi.<tailnet>.ts.net: elle doit être inaccessible. - Ne testez pas un port interne en l'ouvrant sur la box ; utilisez un scanner externe sur l'IP publique et confirmez qu'aucun service
8765,8000ou8001n'apparaît. - Sur le Pi, exécutez
ss -tulpn: CodexPi peut montrer0.0.0.0:8765, mais jamais0.0.0.0:8000ou0.0.0.0:8001. - Réactivez Tailscale : la même adresse HTTPS doit fonctionner et demander la connexion Django.
Révocation d'accès
- Révoquer toutes les sessions web :
./scripts/revoke-sessions.sh, puis saisirREVOKE. - Retirer la publication privée :
tailscale serve --https=443 off; utiliseztailscale serve resetseulement si le Pi ne publie rien d'autre. - Déconnecter le Pi :
sudo tailscale down. - Révoquer définitivement un appareil depuis la page Machines de l'administration Tailscale.
- Changer le mot de passe Django avec
docker compose run --rm django-web python3 manage.py changepassword <utilisateur>.
Audit exécutable
cd /home/config/CodexPi
./scripts/security-audit.sh
./scripts/check-secrets.sh --worktree
./scripts/check-secrets.sh --history
Les secrets applicatifs sont externes au code : .env et data/ sont ignorés,
les identifiants GitLab/Gitea et SSH sont chiffrés dans les données persistantes,
les clés GitHub sont créées sous data/github/, et la session du Codex CLI reste
dans le dossier .codex monté depuis l'hôte. Pour un gestionnaire de secrets,
montez des fichiers en lecture seule et définissez CODEXPI_SECRET_KEY_FILE et
CODEXPI_DJANGO_SECRET_KEY_FILE.
Le script vérifie en lecture seule les ports, Compose, Tailscale/Funnel, UFW, fail2ban, permissions, propriétaires, secrets Git, headers HTTP, refus anonyme, debug Django/FastAPI et contrôles de déploiement.
Résultat de la revue du code
- Architecture Compose : conforme — seul nginx déclare
ports, sur le port LAN 8765. - Authentification/cookies/CSRF/WebSocket : conforme dans l'implémentation — Django reste l'autorité de session et FastAPI la valide.
- Traversal/injection/actions destructrices : protections conservées — validation de projet et commandes structurées.
- Secrets/debug : conforme dans le dépôt — secrets ignorés, Django
DEBUG=False, aucun reload. - État réseau réel du Pi : à confirmer après démarrage avec
./scripts/security-audit.sh, car un partage SMB ne permet pas d'interrogerss, UFW, Tailscale ou les conteneurs en cours d'exécution.