From 5828f6c46b0810909fecae3d2c20aa15d420d175 Mon Sep 17 00:00:00 2001 From: Olivier Date: Sun, 17 May 2026 10:11:04 +0200 Subject: [PATCH] docs: add French and Spanish translations of README Co-Authored-By: Claude Sonnet 4.6 --- README.es.md | 231 +++++++++++++++++++++++++++++++++++++++++++++++++++ README.fr.md | 231 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 462 insertions(+) create mode 100644 README.es.md create mode 100644 README.fr.md diff --git a/README.es.md b/README.es.md new file mode 100644 index 0000000..9377fd8 --- /dev/null +++ b/README.es.md @@ -0,0 +1,231 @@ +# Stupid Simple Network Inventory + +![Python](https://img.shields.io/badge/Python-3.11-3776AB?logo=python&logoColor=white) +![FastAPI](https://img.shields.io/badge/FastAPI-009688?logo=fastapi&logoColor=white) +![Vue.js](https://img.shields.io/badge/Vue.js-3-4FC08D?logo=vue.js&logoColor=white) +![SQLite](https://img.shields.io/badge/SQLite-003B57?logo=sqlite&logoColor=white) +![Nginx](https://img.shields.io/badge/Nginx-009639?logo=nginx&logoColor=white) +![Docker](https://img.shields.io/badge/Docker-2496ED?logo=docker&logoColor=white) + +Aplicación web autoalojada para inventario manual de red y visualización de topología de red lógica. + +![Vista de Topología](img/tpology.png) + +## Características + +- **Inventario manual** — añade y gestiona dispositivos (18 tipos) con IPs, VLANs, descripciones y enlaces web opcionales +- **Vista de topología** — disposición en tarjetas por red (LAN / VLAN 802.1Q), con secciones WAN y puerta de enlace +- **Ping ICMP** — comprueba la accesibilidad de todos los hosts conocidos con un clic +- **Descubrimiento automático** — ping sweep + consulta PTR DNS en una subred para importar nuevos hosts +- **Autenticación** — inicio de sesión JWT con cambio de contraseña obligatorio en el primer uso +- **Modo oscuro** — alternancia entre tema claro y oscuro +- **i18n** — francés, inglés, español + +## Stack + +| Capa | Tecnología | +|------|-----------| +| Backend | FastAPI + SQLAlchemy + SQLite (Python 3.11) | +| Frontend | Vue 3 + Vite, servido por Nginx | +| Auth | JWT HS256, expiración 24h | +| Runtime | Docker Compose | + +--- + +## Inicio rápido + +```bash +# 1. Clonar y entrar en el proyecto +git clone https://git.raspot.in/olivier/stupid-simple-network-inventory.git +cd stupid-simple-network-inventory + +# 2. Crear el directorio de datos propiedad del usuario actual +mkdir -p db_data + +# 3. Configurar el entorno (necesario para la propiedad correcta del bind-mount) +cp .env.example .env +# Editar .env: +# DOCKER_UID / DOCKER_GID → resultado de: id -u && id -g +# INITIAL_ADMIN_PASSWORD → definir para evitar el bootstrap admin/admin por defecto + +# 4. Construir e iniciar +docker compose --env-file .env up --build -d + +# 5. Abrir http://localhost:8080 en el navegador +``` + +### Primer inicio de sesión + +| Caso | Credenciales | Comportamiento | +|------|-------------|----------------| +| `INITIAL_ADMIN_PASSWORD` definido | `admin` / `` | Inicio de sesión normal | +| `INITIAL_ADMIN_PASSWORD` no definido | `admin` / `admin` | Cambio de contraseña obligatorio antes de acceder a la aplicación | + +--- + +## Configuración + +Toda la configuración se realiza mediante variables de entorno. Ver `.env.example` para la lista completa con descripciones. + +| Variable | Por defecto | Descripción | +|----------|-------------|-------------| +| `SECRET_KEY` | auto-generada | Clave de firma JWT. Definir explícitamente en producción. | +| `INITIAL_ADMIN_PASSWORD` | _(vacío)_ | Contraseña de bootstrap del admin. Si no se define, se usa `admin/admin` con cambio forzado. | +| `ALLOWED_ORIGINS` | `*` | Orígenes CORS permitidos (separados por comas). Definir en tu dominio en producción. | +| `BIND_ADDRESS` | `0.0.0.0` | Dirección IP de escucha. Definir en la interfaz frente al reverse proxy. | +| `DOCKER_UID` / `DOCKER_GID` | `1000` | UID/GID para el proceso backend. Debe coincidir con el usuario propietario de `./db_data/`. | + +### Usar .env con Docker Compose + +```bash +cp .env.example .env +# Editar .env — definir como mínimo DOCKER_UID, DOCKER_GID, INITIAL_ADMIN_PASSWORD +docker compose --env-file .env up --build -d +``` + +--- + +## Seguridad + +### Gestión de secretos + +Dos opciones según sus requisitos de seguridad. + +#### Opción A — Secreto auto-generado (recomendado para nodo único) + +Dejar `SECRET_KEY` sin definir (o vacío) en `.env`. En el primer arranque, el backend genera una clave hexadecimal aleatoria de 64 caracteres, la escribe en `db_data/secret_key.txt` con permisos **0600** y la reutiliza en cada reinicio posterior. El secreto nunca aparece en una variable de entorno, un archivo compose o un log. + +```bash +# .env — dejar la línea vacía o eliminarla +SECRET_KEY= +``` + +El único requisito es que `db_data/` esté respaldado (ya contiene la base de datos). + +#### Opción B — Secreto mediante archivo Docker Compose + +Almacena el secreto en un archivo en el host, fuera del control de versiones, y lo monta en el contenedor. El valor nunca aparece en una variable de entorno. + +```bash +# Generar y almacenar el secreto fuera del directorio del proyecto +mkdir -p ~/.secrets +python3 -c "import secrets; print(secrets.token_hex(32))" > ~/.secrets/topologie_secret_key +chmod 600 ~/.secrets/topologie_secret_key +``` + +Luego descomentar los bloques `secrets:` en `docker-compose.yml` (ver los comentarios en ese archivo) y eliminar `SECRET_KEY` de `.env`. Docker Compose fusiona la anulación automáticamente: + +```bash +docker compose up -d +``` + +--- + +### Rotación de clave + +Para rotar el secreto JWT (invalida todas las sesiones activas): + +```bash +# Opción A — variable de entorno (recomendado) +# Definir un nuevo SECRET_KEY en la configuración de despliegue y reiniciar + +# Opción B — rotación por archivo +docker compose stop backend +rm db_data/secret_key.txt +docker compose start backend +# Todos los usuarios deberán iniciar sesión de nuevo +``` + +### HTTPS + +Esta aplicación no termina TLS. Para uso en producción, colócala detrás de un reverse proxy que gestione HTTPS: + +```nginx +# Ejemplo de reverse proxy nginx (externo, en el host o en un contenedor dedicado) +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; + } +} +``` + +Para uso solo local, enlazar a loopback para evitar exposición accidental en la LAN: + +```yaml +# docker-compose.override.yml +services: + frontend: + ports: + - "127.0.0.1:8080:8080" +``` + +### Endurecimiento de contenedores + +Los contenedores se ejecutan con privilegios reducidos: + +| Medida | Backend | Frontend | +|--------|---------|----------| +| Usuario no-root | `DOCKER_UID:DOCKER_GID` (usuario host) | `nginx` (UID 101) | +| `cap_drop: ALL` | ✓ | ✓ | +| `cap_add: NET_RAW` | ✓ (ping) | — | +| `no-new-privileges` | — ¹ | ✓ | +| Healthcheck | ✓ | ✓ | + +¹ Omitido en el backend: el ping usa file capabilities (`cap_net_raw=ep`); `no-new-privileges` suprimiría el bit efectivo del archivo e impediría al subproceso adquirir `CAP_NET_RAW` en su conjunto efectivo aunque el padre lo tenga en su conjunto permitido. + +--- + +## Persistencia de datos + +Todos los datos se almacenan en `./db_data/`: + +| Archivo | Descripción | +|---------|-------------| +| `topology.db` | Base de datos SQLite | +| `secret_key.txt` | Secreto JWT auto-generado (permisos 0600) | + +**Copia de seguridad**: `cp -r db_data/ db_data.bak/` + +**Restauración**: detener la stack, reemplazar `db_data/`, reiniciar. + +--- + +## Desarrollo + +### Tests del backend + +```bash +cd backend +pip install -r requirements.txt -r requirements-test.txt +pytest tests/ -v +``` + +### Dev local (sin Docker) + +```bash +# Backend +cd backend +pip install -r requirements.txt +uvicorn main:app --reload + +# Frontend (terminal separada) +cd frontend +npm install +npm run dev # Servidor dev Vite en :5173, proxifica /api/ hacia :8000 +``` + +--- + +## Arquitectura + +Ver [`docs/architecture.md`](docs/architecture.md) para el flujo de solicitudes detallado, la configuración Docker y el modelo de autenticación. diff --git a/README.fr.md b/README.fr.md new file mode 100644 index 0000000..2fe67f4 --- /dev/null +++ b/README.fr.md @@ -0,0 +1,231 @@ +# Stupid Simple Network Inventory + +![Python](https://img.shields.io/badge/Python-3.11-3776AB?logo=python&logoColor=white) +![FastAPI](https://img.shields.io/badge/FastAPI-009688?logo=fastapi&logoColor=white) +![Vue.js](https://img.shields.io/badge/Vue.js-3-4FC08D?logo=vue.js&logoColor=white) +![SQLite](https://img.shields.io/badge/SQLite-003B57?logo=sqlite&logoColor=white) +![Nginx](https://img.shields.io/badge/Nginx-009639?logo=nginx&logoColor=white) +![Docker](https://img.shields.io/badge/Docker-2496ED?logo=docker&logoColor=white) + +Application web auto-hébergée pour l'inventaire manuel de réseau et la visualisation de topologie réseau logique. + +![Vue Topologie](img/tpology.png) + +## Fonctionnalités + +- **Inventaire manuel** — ajout et gestion d'équipements (18 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 +- **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 +- **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` / `` | Connexion normale | +| `INITIAL_ADMIN_PASSWORD` non défini | `admin` / `admin` | Changement de mot de passe forcé avant d'accéder à l'application | + +--- + +## 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. | +| `DOCKER_UID` / `DOCKER_GID` | `1000` | UID/GID pour le processus backend. Doit correspondre à l'utilisateur propriétaire de `./db_data/`. | + +### Utilisation de .env avec Docker Compose + +```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 +# Exemple de reverse proxy nginx (externe, sur l'hôte ou un conteneur dédié) +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; + } +} +``` + +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 + +Les conteneurs s'exécutent avec des privilèges réduits : + +| 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é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 +``` + +--- + +## 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.