# 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.