1906e619b9
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
367 lines
14 KiB
Markdown
367 lines
14 KiB
Markdown
<div align="center">
|
|
|
|
<img src="frontend/public/favicon.svg" width="96" height="96" alt="" />
|
|
|
|
# Stupid Simple Network Inventory
|
|
|
|
**Application web auto-hébergée pour l'inventaire réseau et la visualisation de topologie logique**
|
|
|
|
[English](README.md) · [Español](README.es.md)
|
|
|
|

|
|

|
|

|
|

|
|

|
|

|
|
[](LICENSE)
|
|
|
|
</div>
|
|
|
|
---
|
|
|
|

|
|
|
|
---
|
|
|
|
## ✨ Fonctionnalités
|
|
|
|
- 🗂️ **Inventaire manuel** — ajout et gestion d'équipements (21 types) avec IPs, VLANs, descriptions et liens web optionnels
|
|
- 🗺️ **Vue topologie** — disposition en cards par réseau (LAN / VLAN 802.1Q), avec sections WAN et passerelle
|
|
- 🌐 **Vue adressage IP** — liste des IPs groupée par réseau, groupes repliables individuellement ou en un clic
|
|
- 📡 **Ping ICMP** — vérification de l'accessibilité de tous les hôtes connus en un clic
|
|
- 🔍 **Découverte automatique** — ping sweep + lookup PTR DNS sur un sous-réseau pour importer de nouveaux hôtes
|
|
- 🏷️ **Logos de marques** — détection automatique sur le nom et la description via [simple-icons](https://simpleicons.org/) (3 000+ logos) ; alias custom pour les variantes courantes (`pve`, `unifi`, `k3s`…)
|
|
- 🖥️ **Détection d'OS** — icône du système d'exploitation déduite automatiquement de la description (51 distributions reconnues)
|
|
- 🔐 **Authentification** — connexion JWT avec changement de mot de passe forcé à la première utilisation
|
|
- 🌙 **Mode sombre** — bascule thème clair / sombre
|
|
- 🌍 **i18n** — français, anglais, espagnol
|
|
|
|
## 🛠️ Stack
|
|
|
|
| Couche | Technologie |
|
|
|--------|------------|
|
|
| Backend | FastAPI + SQLAlchemy + SQLite (Python 3.11) |
|
|
| Frontend | Vue 3 + Vite, servi par Nginx |
|
|
| Auth | JWT HS256, expiration 24h |
|
|
| Runtime | Docker Compose |
|
|
|
|
---
|
|
|
|
## 🚀 Démarrage rapide
|
|
|
|
```bash
|
|
# 1. Cloner et entrer dans le projet
|
|
git clone https://git.raspot.in/olivier/stupid-simple-network-inventory.git
|
|
cd stupid-simple-network-inventory
|
|
|
|
# 2. Créer le répertoire de données appartenant à l'utilisateur courant
|
|
mkdir -p db_data
|
|
|
|
# 3. Configurer l'environnement (requis pour la propriété correcte du bind-mount)
|
|
cp .env.example .env
|
|
# Éditer .env :
|
|
# DOCKER_UID / DOCKER_GID → résultat de : id -u && id -g
|
|
# INITIAL_ADMIN_PASSWORD → à définir pour éviter le bootstrap admin/admin par défaut
|
|
|
|
# 4. Construire et démarrer
|
|
docker compose --env-file .env up --build -d
|
|
|
|
# 5. Ouvrir http://localhost:8080 dans votre navigateur
|
|
```
|
|
|
|
### Première connexion
|
|
|
|
| Cas | Identifiants | Comportement |
|
|
|-----|-------------|--------------|
|
|
| `INITIAL_ADMIN_PASSWORD` défini | `admin` / `<votre mot de passe>` | Connexion normale |
|
|
| `INITIAL_ADMIN_PASSWORD` non défini | `admin` / `admin` | Changement de mot de passe forcé avant d'accéder à l'application |
|
|
|
|
---
|
|
|
|
## 🔄 Mise à jour
|
|
|
|
Les données sont stockées dans `./db_data/` (bind-mount), jamais touché lors d'une reconstruction des conteneurs. La mise à jour est sans risque :
|
|
|
|
```bash
|
|
git pull
|
|
docker compose up --build -d
|
|
```
|
|
|
|
Les migrations de base de données s'exécutent automatiquement au démarrage — aucune étape manuelle requise.
|
|
|
|
---
|
|
|
|
## ⚙️ Configuration
|
|
|
|
Toute la configuration se fait via des variables d'environnement. Voir `.env.example` pour la liste complète avec descriptions.
|
|
|
|
| Variable | Défaut | Description |
|
|
|----------|--------|-------------|
|
|
| `SECRET_KEY` | auto-générée | Clé de signature JWT. À définir explicitement en production. |
|
|
| `INITIAL_ADMIN_PASSWORD` | _(vide)_ | Mot de passe admin de bootstrap. Si non défini, `admin/admin` est utilisé avec changement forcé. |
|
|
| `ALLOWED_ORIGINS` | `*` | Origines CORS autorisées (séparées par des virgules). À définir sur votre domaine en production. |
|
|
| `BIND_ADDRESS` | `0.0.0.0` | Adresse IP d'écoute. À définir sur l'interface face au reverse proxy. |
|
|
| `DNS_SERVER` | _(vide)_ | Serveur DNS pour les lookups PTR lors de la découverte automatique. Pré-remplit le champ dans l'UI (modifiable par scan). Laisser vide pour désactiver le reverse DNS. |
|
|
| `DOCKER_UID` / `DOCKER_GID` | `1000` | UID/GID pour le processus backend. Doit correspondre à l'utilisateur propriétaire de `./db_data/`. |
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
# Éditer .env — définir au minimum DOCKER_UID, DOCKER_GID, INITIAL_ADMIN_PASSWORD
|
|
docker compose --env-file .env up --build -d
|
|
```
|
|
|
|
---
|
|
|
|
## 🔒 Sécurité
|
|
|
|
### Gestion des secrets
|
|
|
|
Deux options selon vos exigences de sécurité.
|
|
|
|
#### Option A — Secret auto-généré (recommandé pour un nœud unique)
|
|
|
|
Laisser `SECRET_KEY` non défini (ou vide) dans `.env`. Au premier démarrage, le backend génère une clé hexadécimale aléatoire de 64 caractères, l'écrit dans `db_data/secret_key.txt` avec les permissions **0600**, et la réutilise à chaque redémarrage. Le secret n'apparaît jamais dans une variable d'environnement, un fichier compose ou un log.
|
|
|
|
```bash
|
|
# .env — laisser la ligne vide ou la supprimer
|
|
SECRET_KEY=
|
|
```
|
|
|
|
La seule exigence est que `db_data/` soit sauvegardé (il contient déjà la base de données).
|
|
|
|
#### Option B — Secret via fichier Docker Compose
|
|
|
|
Stocke le secret dans un fichier sur l'hôte, hors du contrôle de version, et le monte dans le conteneur. La valeur n'apparaît jamais dans une variable d'environnement.
|
|
|
|
```bash
|
|
# Générer et stocker le secret en dehors du répertoire du projet
|
|
mkdir -p ~/.secrets
|
|
python3 -c "import secrets; print(secrets.token_hex(32))" > ~/.secrets/topologie_secret_key
|
|
chmod 600 ~/.secrets/topologie_secret_key
|
|
```
|
|
|
|
Décommenter ensuite les blocs `secrets:` dans `docker-compose.yml` (voir les commentaires dans ce fichier) et supprimer `SECRET_KEY` de `.env`. Docker Compose fusionne la surcharge automatiquement :
|
|
|
|
```bash
|
|
docker compose up -d
|
|
```
|
|
|
|
### Rotation de clé
|
|
|
|
Pour faire tourner le secret JWT (invalide toutes les sessions actives) :
|
|
|
|
```bash
|
|
# Option A — variable d'environnement (recommandé)
|
|
# Définir un nouveau SECRET_KEY dans la configuration de déploiement et redémarrer
|
|
|
|
# Option B — rotation par fichier
|
|
docker compose stop backend
|
|
rm db_data/secret_key.txt
|
|
docker compose start backend
|
|
# Tous les utilisateurs devront se reconnecter
|
|
```
|
|
|
|
### HTTPS
|
|
|
|
Cette application ne termine pas TLS. Pour un usage en production, placez-la derrière un reverse proxy gérant HTTPS.
|
|
|
|
#### Nginx
|
|
|
|
```nginx
|
|
server {
|
|
listen 443 ssl;
|
|
server_name inventory.example.com;
|
|
|
|
ssl_certificate /etc/letsencrypt/live/inventory.example.com/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/inventory.example.com/privkey.pem;
|
|
|
|
location / {
|
|
proxy_pass http://127.0.0.1:8080;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Traefik — Labels Docker
|
|
|
|
Ajouter ces labels au service `frontend` et le connecter au réseau partagé avec Traefik :
|
|
|
|
```yaml
|
|
# docker-compose.override.yml
|
|
services:
|
|
frontend:
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.http.routers.inventory.rule=Host(`inventory.example.com`)"
|
|
- "traefik.http.routers.inventory.entrypoints=websecure"
|
|
- "traefik.http.routers.inventory.tls.certresolver=letsencrypt"
|
|
- "traefik.http.services.inventory.loadbalancer.server.port=8080"
|
|
networks:
|
|
- internal
|
|
- traefik_public # réseau partagé avec votre instance Traefik
|
|
|
|
networks:
|
|
traefik_public:
|
|
external: true
|
|
```
|
|
|
|
#### Traefik — Configuration dynamique (file provider)
|
|
|
|
Si Traefik ne tourne pas dans Docker (ou si vous préférez le file provider), déposez un fichier dans votre répertoire de configuration dynamique :
|
|
|
|
```yaml
|
|
# /etc/traefik/dynamic/inventory.yml
|
|
http:
|
|
routers:
|
|
inventory:
|
|
rule: "Host(`inventory.example.com`)"
|
|
entryPoints:
|
|
- websecure
|
|
tls:
|
|
certResolver: letsencrypt
|
|
service: inventory-svc
|
|
|
|
services:
|
|
inventory-svc:
|
|
loadBalancer:
|
|
servers:
|
|
- url: "http://127.0.0.1:8080"
|
|
```
|
|
|
|
Traefik prend en compte le fichier automatiquement — aucun redémarrage requis.
|
|
|
|
Pour un usage local uniquement, lier à la boucle locale pour éviter une exposition LAN accidentelle :
|
|
|
|
```yaml
|
|
# docker-compose.override.yml
|
|
services:
|
|
frontend:
|
|
ports:
|
|
- "127.0.0.1:8080:8080"
|
|
```
|
|
|
|
### Durcissement des conteneurs
|
|
|
|
| Mesure | Backend | Frontend |
|
|
|--------|---------|----------|
|
|
| Utilisateur non-root | `DOCKER_UID:DOCKER_GID` (utilisateur hôte) | `nginx` (UID 101) |
|
|
| `cap_drop: ALL` | ✓ | ✓ |
|
|
| `cap_add: NET_RAW` | ✓ (ping) | — |
|
|
| `no-new-privileges` | — ¹ | ✓ |
|
|
| Healthcheck | ✓ | ✓ |
|
|
|
|
¹ Omis sur le backend : le ping utilise les file capabilities (`cap_net_raw=ep`) ; `no-new-privileges` supprimerait le bit effectif du fichier et empêcherait le sous-processus d'acquérir `CAP_NET_RAW` dans son ensemble effectif même si le parent le détient dans son ensemble permis.
|
|
|
|
---
|
|
|
|
## 💾 Persistance des données
|
|
|
|
Toutes les données sont stockées dans `./db_data/` :
|
|
|
|
| Fichier | Description |
|
|
|---------|-------------|
|
|
| `topology.db` | Base de données SQLite |
|
|
| `secret_key.txt` | Secret JWT auto-généré (permissions 0600) |
|
|
|
|
**Sauvegarde** : `cp -r db_data/ db_data.bak/`
|
|
|
|
**Restauration** : arrêter la stack, remplacer `db_data/`, redémarrer.
|
|
|
|
---
|
|
|
|
## 🏷️ Détection des logos de marques
|
|
|
|
La détection s'effectue automatiquement sur le **nom** et la **description** de l'équipement (insensible à la casse, correspondance sur mot entier). Aucune configuration manuelle nécessaire.
|
|
|
|
Les logos s'affichent à deux endroits :
|
|
- **Vue topologie** — petits SVGs inline sur chaque chip d'équipement
|
|
- **Liste des équipements** — badge(s) coloré(s) dans la card de l'équipement
|
|
|
|
Plusieurs logos peuvent s'afficher simultanément si plusieurs mots-clés correspondent.
|
|
|
|
**Source** : la bibliothèque [simple-icons](https://simpleicons.org/) est intégralement importée (~3 000 logos). La détection fonctionne en deux passes :
|
|
|
|
1. **Aliases curatés** — ~50 entrées pour les cas non couverts par le titre exact : alias courants (`pve` → Proxmox, `unifi` → Ubiquiti, `k3s` → Kubernetes, `routeros` → MikroTik…), icônes absentes de simple-icons (Eaton, Riello, Vertiv, Zabbix, Centreon, Nagios, PRTG, Free, Bouygues, SFR, Windows…), couleurs ou formes overridées.
|
|
2. **Auto-détection** — tous les logos simple-icons restants (titre ≥ 4 caractères) sont automatiquement disponibles : Grafana, Gitea, Portainer, GitLab, Immich, et des milliers d'autres, sans aucun code supplémentaire.
|
|
|
|
---
|
|
|
|
## 🧑💻 Développement
|
|
|
|
### Tests backend
|
|
|
|
```bash
|
|
cd backend
|
|
pip install -r requirements.txt -r requirements-test.txt
|
|
pytest tests/ -v
|
|
```
|
|
|
|
### Dev local (sans Docker)
|
|
|
|
```bash
|
|
# Backend
|
|
cd backend
|
|
pip install -r requirements.txt
|
|
uvicorn main:app --reload
|
|
|
|
# Frontend (terminal séparé)
|
|
cd frontend
|
|
npm install
|
|
npm run dev # Serveur dev Vite sur :5173, proxifie /api/ vers :8000
|
|
```
|
|
|
|
---
|
|
|
|
## 🔧 Dépannage
|
|
|
|
### Le scan remonte des centaines de faux positifs (proxy-ARP / UniFi)
|
|
|
|
**Symptôme** — La découverte automatique retourne autant d'hôtes qu'il y a d'adresses dans le sous-réseau (par exemple 254 pour un `/24`), alors que seules quelques machines sont réellement présentes.
|
|
|
|
**Cause** — Certains équipements réseau (notamment UniFi Security Gateway, Dream Machine et équipements similaires) activent le proxy-ARP et répondent aux pings ICMP pour **toutes** les IP du sous-réseau, en usurpant l'IP source de la réponse. La vérification de l'IP source intégrée au scan ne peut donc pas filtrer ces faux positifs.
|
|
|
|
**Solution** — Cocher l'option **"Scan TCP (anti proxy-ARP)"** dans l'écran de configuration du scan. Cette option utilise TCP au lieu de ICMP pour détecter les hôtes actifs (ports 22, 80, 443, 8080, 8443) :
|
|
|
|
- Un **vrai hôte** répond avec RST (port fermé) ou accepte la connexion → considéré vivant.
|
|
- Une **IP fantôme** : le gateway droppe silencieusement le SYN sans répondre → timeout → éliminée.
|
|
|
|
> **Note** : un équipement dont le pare-feu bloque silencieusement (*DROP*, sans RST) **tous** les ports sondés ne sera pas découvert automatiquement et devra être ajouté manuellement.
|
|
|
|
### Des équipements manquent dans le scan (rate-limiting ICMP)
|
|
|
|
**Symptôme** — Quelques hôtes (AP Wi-Fi, switchs, appareils IoT) répondent à un ping direct mais n'apparaissent pas lors d'un scan complet du sous-réseau.
|
|
|
|
**Cause** — Quand 100 workers ICMP concurrents inondent un `/24`, certains équipements ou switchs managés bridant les réponses ICMP. L'équipement ignore la sonde pendant le scan alors qu'un ping simple fonctionne.
|
|
|
|
**Solution** — Deux options :
|
|
|
|
- Activer **"Scan doux (ICMP lent)"** : réduit la concurrence de 100 à 10 workers. Le scan prend plus de temps mais les sondes ICMP sont étalées, évitant le rate-limiting. Idéal pour les réseaux sans proxy-ARP.
|
|
- Activer **"Scan TCP (anti proxy-ARP)"** : contourne ICMP entièrement. Les sondes TCP ne sont pas soumises au même rate-limiting. Idéal si proxy-ARP et rate-limiting sont tous deux présents.
|
|
|
|
---
|
|
|
|
## 🏗️ Architecture
|
|
|
|
Voir [`docs/architecture.md`](docs/architecture.md) pour le flux de requêtes détaillé, la configuration Docker et le modèle d'authentification.
|
|
|
|
---
|
|
|
|
## 🤖 Développé avec l'aide de l'IA
|
|
|
|
Cette application a été développée entièrement avec l'aide d'assistants de code IA :
|
|
|
|
- **[Claude Code](https://claude.ai/code)** (Anthropic) — utilisé tout au long du projet pour la conception de l'architecture, l'API backend (FastAPI, SQLAlchemy, authentification, découverte ICMP), les composants frontend (Vue 3, layout CSS topologie, i18n, mode sombre), la configuration Docker & Nginx, et la documentation.
|
|
- **[Codex](https://openai.com/codex)** (OpenAI) — également utilisé pendant le développement pour la génération de code et les suggestions sur l'ensemble du projet.
|
|
|
|
---
|
|
|
|
## 📄 Licence
|
|
|
|
Ce projet est distribué sous la [licence GNU General Public License v3.0](LICENSE).
|
|
|
|
Vous êtes libre d'utiliser, modifier et redistribuer ce logiciel dans les conditions de la GPL v3. Tout travail dérivé doit être distribué sous la même licence.
|