Files
codexpi/docs/SECURITY_AUDIT.md
CodexPi 995b17a6e2
Some checks are pending
Qualité / tests (push) Waiting to run
feat: publish CodexPi contest snapshot
2026-11-14 20:23:59 +01:00

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

  1. Sur le téléphone, coupez le Wi-Fi et désactivez Tailscale.
  2. Ouvrez l'adresse https://codexpi.<tailnet>.ts.net : elle doit être inaccessible.
  3. 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, 8000 ou 8001 n'apparaît.
  4. Sur le Pi, exécutez ss -tulpn : CodexPi peut montrer 0.0.0.0:8765, mais jamais 0.0.0.0:8000 ou 0.0.0.0:8001.
  5. 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 saisir REVOKE.
  • Retirer la publication privée : tailscale serve --https=443 off ; utilisez tailscale serve reset seulement 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'interroger ss, UFW, Tailscale ou les conteneurs en cours d'exécution.