[Core/Infra] Provisionamiento SSH en pc-dasu0 y unificación Nginx tailscale

This commit is contained in:
Ricardo Monla
2026-03-14 10:29:54 -03:00
parent e554bb5d0d
commit b13a389568
50 changed files with 2702 additions and 561 deletions
+280
View File
@@ -0,0 +1,280 @@
# P2602 - Automatización de Backups DTIC UTNLR
> Manifiesto de Proyecto | Referencia: [`adn/07_proyectos.md`](../adn/07_proyectos.md)
## Identificación
| Campo | Valor |
| :--- | :--- |
| **Código** | P2602 |
| **Nombre** | Automatización de Backups DTIC UTNLR |
| **Versión** | v2601 |
| **Estado** | ⏳ En Ejecución |
| **Inicio** | 28/02/2026 |
| **Fin Estimado** | Por definir |
## Ventana Operativa
| Campo | Valor |
| :--- | :--- |
| **Día** | Todos los **jueves** |
| **Inicio** | 00:00 hs |
| **Deadline** | 08:00 hs (inicio de jornada de usuarios) |
| **Modo** | Operador presente para resolver problemas durante la noche |
| **SLA** | Todo debe estar operativo antes de las 08:00 para los usuarios de la Facultad |
> ⚠️ **Restricción crítica**: Si hay fallos durante el pipeline, el operador debe resolverlos antes de las 08:00. El objetivo de la automatización es minimizar la intervención nocturna y eventualmente permitir ejecución desatendida vía cron/timer.
**Cronograma típico (actual, manual):**
```
00:00 ━━━━━━━━━━━ Etapa 1+2 ━━━━━━━━━━━ 03:00 ━━━━━ Etapa 3 ━━━━━ ~08:00
📦 Vzdump + ⚙️ Procesamiento ☁️ Upload Nube
~3 hs | Vigilancia requerida Desatendido (rclone)
```
## Objetivo
Automatizar el ciclo completo de backup de la DTIC — actualmente un proceso manual de varias horas — en un **pipeline de 3 etapas desatendido y auditado**.
### Pipeline Operativo Completo
```
┌──────────────────┐ ┌──────────────────────┐ ┌──────────────────┐
│ ETAPA 1 │ │ ETAPA 2 │ │ ETAPA 3 │
│ 📦 Vzdump │ → │ ⚙️ Procesamiento │ → │ ☁️ Upload Nube │
│ │ │ │ │ │
│ • Limpieza >6d │ │ • Descarga a NS8 │ │ • rclone sync │
│ • Shutdown VM │ │ • Retención local │ │ • Auto-resume │
│ • vzdump stop │ │ (mín 2 por VM) │ │ • Por subdir │
│ • Por servidor │ │ • Compresión tar.gz │ │ • OneDrive │
│ │ │ • Organiza por host │ │ │
└──────────────────┘ └──────────────────────┘ └──────────────────┘
srv-pmox1/2/3 srv-ns8 local rmOneDrive remoto
```
### Problemática actual
| Etapa | Manual Actual | Automatizado (meta) |
| :--- | :--- | :--- |
| **Vzdump** | GUI Proxmox, VM por VM, borrar viejos a mano | SSH vzdump batch, retención automática |
| **Procesamiento** | Descarga manual, compresión, verificar archivos viejos | Descarga + retención inteligente (mín 2) + compresión |
| **Upload** | Lanzar rclone, reiniciar si falla internet | rclone con reintentos y resume automático |
## Stakeholders
| Rol | Persona | Visibilidad |
| :--- | :--- | :--- |
| **Responsable Técnico** | Lic. Ricardo MONLA | Operador directo |
| **Dirección de TIC** | (Jerárquico superior) | Dashboard asíncrono |
## Nodos Involucrados
### Infraestructura de Gestión
| Nodo | IP | Rol |
| :--- | :--- | :--- |
| [**srv-ns8**](../nodos/srv-ns8.md) | `10.0.10.8` | Centro de operaciones. Ejecuta ns8-bkps. |
| [**srv-pmox1**](../nodos/srv-pmox1.md) | `10.0.10.201` | Pool master Proxmox. Gateway SSH a todas las VMs. |
| [**srv-pmox2**](../nodos/srv-pmox2.md) | `10.0.10.202` | Nodo Proxmox secundario. |
| [**srv-pmox3**](../nodos/srv-pmox3.md) | `10.0.10.203` | Nodo Proxmox terciario (64GB RAM). |
### VMs/CTs a Respaldar (Inventario Auditado 28/02)
| Server | VMID | Nodo | OS | Grupo | Ventana | SSH VM |
| :--- | :--- | :--- | :--- | :--- | :--- | :--- |
| pmox1 | 101 | srvv-sitio | Ubuntu | linux | Libre | ✅ rmonla |
| pmox1 | 102 | srvv-sitio0 | Debian | linux | Libre | ✅ rmonla |
| pmox1 | 103 | srvv-dtic | Debian | linux | Libre | ✅ rmonla |
| pmox1 | 104 | srvv-docs | Debian | linux | Libre | ✅ rmonla |
| pmox1 | 112 | srvv-dns | Debian | linux | Libre | ✅ rmonla |
| pmox2 | 105 | srvv-koha | Linux | linux | Libre | ✅ rmonla |
| pmox2 | 108 | pcv-dasu1 | Win10 | windows | Libre | ❌ pendiente |
| pmox2 | 109 | pcv-dasu2 | Win10 | windows | Libre | ❌ pendiente |
| pmox2 | 110 | pcv-serviio | Win10 | windows | Libre | ❌ pendiente |
| pmox3 | 111 | srvv-fenix | WinSrv 2019 | windows | 🌙 00:00-06:00 | ✅ monlaricardo |
| pmox3 | 106 | srvv-uptime | Debian | lxc | Libre | ✅ rmonla |
| pmox3 | 107 | srvv-data | Debian | lxc | Libre | ✅ rmonla |
| pmox3 | 113 | srvv-sitio2 | Debian | lxc | Libre | ✅ rmonla |
| **xen1** | — | **srvv-maurik** | **WinSrv 2019** | **windows** | **Libre** | **✅ monlaricardo** |
| **xen1** | — | **srvv-sysacadweb** | **WinSrv 2008R2** | **windows** | **Libre** | **✅ monlaricardo:7022** |
**Excluidos** (stopped, sin auto-inicio, en preparación): docker (100), srvv-N8N VM (114), srvv-N8N CT (115).
### Nodos de infraestructura y Gestión (Core)
| Nodo | IP | Rol |
| :--- | :--- | :--- |
| [**srv-ns8**](../nodos/srv-ns8.md) | `10.0.10.8` | Centro de operaciones. Ejecuta ns8-bkps. |
| [**srv-pmox1**](../nodos/srv-pmox1.md) | `10.0.10.201` | Pool master Proxmox. Gateway SSH. |
| [**srv-pmox2**](../nodos/srv-pmox2.md) | `10.0.10.202` | Nodo Proxmox secundario. |
| [**srv-pmox3**](../nodos/srv-pmox3.md) | `10.0.10.203` | Nodo Proxmox terciario (64GB RAM). |
| [**srv-xen1**](../nodos/srv-xen1.md) | `10.0.10.23` | Citrix XenServer 7.0 (Legacy). |
### Nodos Objetivo (Scope de Resguardos)
| Server | VMID | Nodo | OS | Grupo | Ventana | SSH VM |
| :--- | :--- | :--- | :--- | :--- | :--- | :--- |
| pmox1 | 101 | srvv-sitio | Ubuntu | linux | Libre | ✅ rmonla |
| pmox1 | 102 | srvv-sitio0 | Debian | linux | Libre | ✅ rmonla |
| pmox1 | 103 | srvv-dtic | Debian | linux | Libre | ✅ rmonla |
| pmox1 | 104 | srvv-docs | Debian | linux | Libre | ✅ rmonla |
| pmox1 | 112 | srvv-dns | Debian | linux | Libre | ✅ rmonla |
| pmox2 | 105 | srvv-koha | Linux | linux | Libre | ✅ rmonla |
| pmox2 | 108 | pcv-dasu1 | Win10 | windows | Libre | ❌ pendiente |
| pmox2 | 109 | pcv-dasu2 | Win10 | windows | Libre | ❌ pendiente |
| pmox2 | 110 | pcv-serviio | Win10 | windows | Libre | ❌ pendiente |
| pmox3 | 111 | srvv-fenix | WinSrv 2019 | windows | 🌙 00:00-06:00 | ✅ monlaricardo |
| pmox3 | 106 | srvv-uptime | Debian | lxc | Libre | ✅ rmonla |
| pmox3 | 107 | srvv-data | Debian | lxc | Libre | ✅ rmonla |
| pmox3 | 113 | srvv-sitio2 | Debian | lxc | Libre | ✅ rmonla |
| **xen1** | — | **srvv-maurik** | **WinSrv 2019** | **windows** | **Libre** | **✅ monlaricardo** |
| **xen1** | — | **srvv-sysacadweb** | **WinSrv 2008R2** | **windows** | **Libre** | **✅ monlaricardo:7022** |
## Herramientas
| Herramienta | Uso |
| :--- | :--- |
| **ns8-bkps v6.0** | Procesador de backups (Ruby, modular) |
| **ns8-candados** | Gestión de secretos y autorización SSH |
| **vzdump** | Herramienta nativa de backup de Proxmox |
| **SSH** | Canal de ejecución remota |
| **rclone** | Sincronización a almacenamiento cloud |
| **GNU Screen** | Ejecución desatendida |
## Fases del Proyecto
### 🔍 Fase 1 — Reconocimiento de Campo
> *"No se puede proteger lo que no se conoce."*
| ID | Estado | Tarea |
| :--- | :---: | :--- |
| R01 | 📍 | Validar acceso SSH desde srv-ns8 a cada hipervisor (pmox1/2/3). |
| R02 | 📍 | Auditar inventario real de VMs: `qm list` y `pct list` vs. nodos documentados. |
| R03 | 📍 | Verificar que vzdump funcione remotamente desde pmox1 como pool master. |
| R04 | 📍 | Documentar ventanas horarias operativas por VM (producción vs. mantenimiento). |
| R05 | 📍 | Verificar/crear fichas de nodo (`nodos/*.md`) para VMs no documentadas. |
---
### 🔧 Fase 2 — Etapa 1: Procesador Vzdump (Backup Remoto)
> SSH a pool master → limpieza remota → shutdown → vzdump → verificación
| ID | Estado | Tarea |
| :--- | :---: | :--- |
| D01 | 📍 | Crear `lib/proc_vzdump.rb` con lógica de backup remoto vía SSH. |
| D02 | 📍 | Retención remota: eliminar backups >6 días en `zfsDISCO1` antes de cada vzdump. |
| D03 | 📍 | Apagado graceful SSH para VMs Windows (`shutdown /s /t 30`), skip si no responde. |
| D04 | 📍 | Filtro de ventana horaria (skip VM si fuera de su horario). |
| D05 | 📍 | Separar tareas YAML: `bkps_proxmox_linux` y `bkps_proxmox_windows`. |
---
### ⚙️ Fase 3 — Etapa 2: Procesamiento Local
> Descarga desde Proxmox → retención local inteligente → compresión por host
| ID | Estado | Tarea |
| :--- | :---: | :--- |
| L01 | ✅ | Descarga de backups desde `zfsDISCO1` a NS8 via rclone mount (ya existe en `proc_proxmox.rb`). |
| L02 | 📍 | **Retención local inteligente**: antes de procesar, verificar cada directorio de VM y mantener mínimo 2 archivos (el nuevo + 1 de seguridad). Borrar los más antiguos si hay >2 con antigüedad >6 días. |
| L03 | ✅ | Compresión tar.gz organizada por hostname (ya existe en `proc_proxmox.rb`). |
| L04 | 📍 | Integrar retención local en el procesador Proxmox existente. |
**Política de retención local:**
```
Directorio: /mnt/ns8Disco3/dtic-BACKUPS/bkps-SERVIDORes/<nombre_vm>/
Antes de procesar un nuevo backup:
1. Listar archivos en el directorio de la VM
2. Si hay archivos con antigüedad >6 días:
→ Mantener el más reciente de los viejos (seguridad)
→ Borrar el resto de los viejos
3. Resultado: mínimo 2 backups por VM (el nuevo + 1 de seguridad)
```
---
### ☁️ Fase 4 — Etapa 3: Upload a la Nube
> rclone sync a OneDrive con reintentos y resume automático
| ID | Estado | Tarea |
| :--- | :---: | :--- |
| U01 | ✅ | Sync a OneDrive: `bkps-SERVIDORes`, `bkps-ANTIGUOS`, `bkps-USERs` (ya existe en `sync_rclone.rb`). |
| U02 | ✅ | Reintentos automáticos: `--retries 5 --retries-sleep 10s --timeout 30m` (ya implementado). |
| U03 | 📍 | **Resume automático**: si rclone falla a mitad, el próximo `rclone sync` retoma desde donde quedó (verificar comportamiento actual). |
| U04 | 📍 | Logeo de resultado de upload con estadísticas (bytes transferidos, errores). |
---
### 🧪 Fase 5 — Validación
| ID | Estado | Tarea |
| :--- | :---: | :--- |
| V01 | 📍 | Test vzdump con VM no-crítica (ej: srvv-docs). |
| V02 | 📍 | Test retención local (verificar min 2 por directorio). |
| V03 | 📍 | Test pipeline completo Linux: vzdump → procesamiento → upload. |
| V04 | 📍 | Test shutdown SSH graceful en VM Windows. |
| V05 | 📍 | Test de corte de internet durante upload (verificar resume). |
---
### 🚀 Fase 6 — Producción
| ID | Estado | Tarea |
| :--- | :---: | :--- |
| P01 | 📍 | Crear comando compuesto `full_backup` (vzdump + procesamiento + upload). |
| P02 | 📍 | Documentar procedimiento operativo en ADN. |
| P03 | 📍 | Evaluar cron/systemd timer para ejecución programada nocturna. |
| P04 | 📍 | Dashboard P2602 (si aplica). |
## Arquitectura
```
┌──────────────────────────────────────────────────────────────────┐
│ ns8-bkps — Pipeline de Backup Completo │
│ │
│ ┌──────────────┐ │
│ │ ns8-candados │ ← RSA passphrase │
│ └──────┬───────┘ │
│ │ │
│ ═══════╪══════════════════════════════════════════════════════ │
│ ETAPA 1│ 📦 VZDUMP (remoto) │
│ ═══════╪══════════════════════════════════════════════════════ │
│ ▼ │
│ srv-ns8 ──SSH──▶ srv-pmox1 (Pool Master) │
│ │ │
│ ├─ Limpieza remota (>6 días) │
│ ├─ Shutdown VM (graceful/SSH) │
│ ├─ vzdump --mode stop --storage zfsDISCO1 │
│ └─ pmox1/pmox2/pmox3 VMs │
│ │
│ ═══════════════════════════════════════════════════════════════ │
│ ETAPA 2 ⚙️ PROCESAMIENTO (local) │
│ ═══════════════════════════════════════════════════════════════ │
│ │
│ rclone mount zfsDISCO1 → /tmp/bkps_* │
│ │ │
│ ├─ Retención local: min 2 por VM, borrar excedentes >6d │
│ ├─ Compresión: tar.gz por hostname │
│ └─ Destino: /mnt/ns8Disco3/dtic-BACKUPS/bkps-SERVIDORes/ │
│ │
│ ═══════════════════════════════════════════════════════════════ │
│ ETAPA 3 ☁️ UPLOAD NUBE (remoto) │
│ ═══════════════════════════════════════════════════════════════ │
│ │
│ rclone sync → rmOneDrive:/dtic-BACKUPS/ │
│ ├─ bkps-SERVIDORes/ │
│ ├─ bkps-ANTIGUOS/ │
│ └─ bkps-USERs/ │
│ (--delete-before --retries 5 --timeout 30m) │
│ │
└──────────────────────────────────────────────────────────────────┘
```
## Referencias
- **Bitácoras**: [28/02](../bitacoras/2026-02-28.md)
- **ADN**: [Ontología](../adn/01_ontologia.md) | [Proyectos](../adn/07_proyectos.md)
- **Herramienta**: [ns8-bkps](../tools/ns8-bkps/ns8-bkps.rb) | [ns8-candados](../tools/ns8-candados/ns8-candados.rb)
- **Proyecto Relacionado**: [P2601 DASUTEN](P2601_dasuten.md) (srv-dasu backups)
@@ -0,0 +1,76 @@
# P2603 - Sistema de Bitácoras (dtic-BITACORAs)
> Manifiesto de Proyecto | Referencia: [`adn/07_proyectos.md`](../adn/07_proyectos.md)
## Identificación
| Campo | Valor |
| :--- | :--- |
| **Código** | P2603 |
| **Nombre** | Sistema de Bitácoras Web |
| **Estado** | ⏳ En Ejecución |
| **Inicio** | 03/03/2026 |
| **Fin Estimado** | Por definir |
## Objetivo
Evolucionar el registro manual de operaciones (archivos `.md`) hacia una **aplicación web** con arquitectura frontend/backend que permita:
1. **Registro en tiempo real** de entradas de bitácora mediante API REST.
2. **Consulta visual** de la actividad diaria agrupada por nodo y proyecto.
3. **Compatibilidad** con el formato I-F-D-E del ADN (exportación a Markdown).
4. **Estética unificada** con el dashboard del proyecto P2601.
## Stakeholders
| Rol | Persona | Visibilidad |
| :--- | :--- | :--- |
| **Responsable Técnico** | Lic. Ricardo MONLA | Operador directo |
| **Dirección de TIC** | (Jerárquico superior) | Dashboard asíncrono |
## Nodos Involucrados
### Nodos Exclusivos (Core)
| Nodo | IP | Rol en el Proyecto |
| :--- | :--- | :--- |
| [**srv-ns8**](../nodos/srv-ns8.md) | `10.0.10.8` | Servidor de la aplicación (Docker) |
## Herramientas
| Herramienta | Uso |
| :--- | :--- |
| **React 18 + TypeScript** | Frontend SPA |
| **Vite** | Build tool y dev server |
| **Node.js + Express** | Backend API REST |
| **PostgreSQL 15** | Base de datos relacional |
| **Docker Compose** | Orquestación de servicios |
## Hitos Clave
| Fecha | ID | Estado | Hito |
| :--- | :--- | :---: | :--- |
| 03/03 | INIT | ⏳ | Inicialización del proyecto. Creación de `dtic-BITACORAs/` con esquema DB y APIs. |
| — | UI01 | 📍 | Frontend: Vista de bitácora diaria con entradas I-F-D-E agrupadas por nodo. |
| — | API01 | 📍 | Backend: CRUD completo de entradas, nodos y bitácoras. |
| — | EXP01 | 📍 | Exportador a Markdown compatible con el ADN. |
| — | PROD | 📍 | Despliegue en producción (srv-ns8, Nginx reverse proxy). |
| 07/03 | ADN01 | 📍 | Automatización Ruby del ADN: Implementación de herramientas CLI unificadas para validación, generación y contexto (Plan 260307-1400). |
## Arquitectura
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Frontend │ │ Backend │ │ Database │
│ React/Vite │◄──►│ Node.js/Express │◄──►│ PostgreSQL 15 │
│ Puerto 5173 │ │ Puerto 3001 │ │ Puerto 5432 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
└──────────────────────┘
Docker Compose
```
## Referencias
- **Bitácoras**: [03/03](../bitacoras/2026-03-03.md)
- **ADN**: [Ontología](../adn/01_ontologia.md) | [Proyectos](../adn/07_proyectos.md) | [Bitácora](../adn/02_bitacora.md)
- **Base Legacy**: [servicios/bitacoras/](../servicios/bitacoras/README.md)
+126
View File
@@ -0,0 +1,126 @@
# P2601 - Proyecto DASUTEN
> Manifiesto de Proyecto | Referencia: [`adn/07_proyectos.md`](../adn/07_proyectos.md)
## Identificación
| Campo | Valor |
| :--- | :--- |
| **Código** | P2601 |
| **Nombre** | Infraestructura y Sistema DASUTEN |
| **Estado** | ⏳ En Ejecución |
| **Inicio** | 23/02/2026 |
| **Fin Estimado** | Por definir |
## Objetivo
**DASUTeN** (Departamento de Acción Social Universitaria Tecnológica Nacional) es la obra social de empleados, docentes y alumnos de la Universidad Tecnológica Nacional. Si bien cuenta con una oficina en la Facultad Regional La Rioja, depende directamente de su **sede central en el Rectorado (Buenos Aires)**.
Actualmente, el sistema de gestión administrativa de DASUTeN funciona dentro de los servidores de la Facultad. El objetivo de este proyecto es **migrar el sistema a un servidor independiente** (`srv-dasu`) aislado de la red académica, de modo que:
1. La **PC física de la oficina** (`pc-dasu0`) consuma el sistema directamente desde el nuevo servidor.
2. La infraestructura quede **autocontenida** dentro de la oficina DASUTeN: una PC cliente + un servidor con sus VMs (DC, SQL, y a futuro posibles servicios adicionales).
3. Se logre **independencia operativa** respecto a los servidores de la Facultad, facilitando la gestión desde Rectorado si fuera necesario.
## Stakeholders
| Rol | Persona | Visibilidad |
| :--- | :--- | :--- |
| **Responsable Técnico** | Lic. Ricardo MONLA | Operador directo |
| **Dirección de TIC** | (Jerárquico superior) | Dashboard asíncrono |
## Dashboard
- **URL**: `https://ns8.frlr.utn.edu.ar/P2601/`
- **Tipo**: Aplicación web estática (HTML/JS/CSS) servida por Nginx.
- **Métricas**: Timeline de hitos, barras de progreso, desglose Físico/Remoto por hito.
- **Actualización**: Las métricas se alimentan de las etiquetas `[Físico/Remoto: H:MM hs]` parseadas de las bitácoras.
### Nodos Exclusivos (Core DASUTEN)
| Nodo | IP | Rol en el Proyecto |
| :--- | :--- | :--- |
| [**srv-dasu**](../nodos/srv-dasu.md) | `100.116.210.36` (Tailscale) | Hipervisor Proxmox VE (Libre de red académica) |
| **dc-dasuten** (VM 100) | `10.0.100.10` | Controlador de Dominio AD DS + DNS |
| **sql-dasuten** (VM 101) | `10.0.100.11` | Motor SQL Server 2019 (Core) |
| **pcv-dasu0** (VM 102) | `10.0.100.12` | VM de pruebas (Win 10 LTSC) |
| [**pc-dasu0**](../nodos/pc-dasu0.md) | Oficina DASUTeN | PC física cliente (Nodo final destino) |
### Nodos de Soporte e Infraestructura
| Nodo | IP | Rol en el Proyecto |
| :--- | :--- | :--- |
| [**srv-ns8**](../nodos/srv-ns8.md) | `10.0.10.8` | Gestión central, GIT y Dashboard |
## Herramientas
| Herramienta | Uso |
| :--- | :--- |
| **Proxmox VE 9.1** | Virtualización y gestión de VMs |
| **Windows Server 2022 Core** | SO base para DC y SQL |
| **SQL Server 2019** | Motor de base de datos relacional |
| **OpenSSH** | Administración remota nativa de VMs Windows |
| **VZDump** | Resguardos offline de VMs |
| **Dashboard P2601** | Panel web de seguimiento para stakeholders |
## Hitos Clave
| Fecha | ID | Estado | Hito |
| :--- | :--- | :---: | :--- |
| 23/02 | A00-A01 | ✅ | Acondicionamiento hardware e instalación de Proxmox VE en `srv-dasu`. |
| 23/02 | A05 | ✅ | Creación de VM Windows Server Core y descarga de ISO. |
| 24/02 | A05 | ✅ | Habilitación SVM en BIOS, instalación de OS, promoción a DC (`dc-dasuten`). |
| 24/02 | A06 | ✅ | Despliegue del Dashboard P2601 con telemetría operativa. |
| 25/02 | DB01 | ✅ | Creación de VM `sql-dasuten`, instalación de OS, integración al dominio e instalación de SQL Server 2019. |
| 26/02 | SSH | ✅ | Despliegue de OpenSSH Server nativo en ambas VMs Windows. |
| 26/02 | BKP | ✅ | Resguardo VZDump offline (stop mode) de DC y SQL. |
| 27/02 | ADN | ✅ | Refactorización del ADN a arquitectura multi-hebra con Armonía Integral. |
| 27/02 | TEST | ✅ | Despliegue de VM de pruebas Win 10 LTSC (pcv-dasu0). Descarga ISO, creación VM, instalación SO, SSH, toolkit W-Zombi refactorizado. |
| 27/02 | DOM | ✅ | Creación usuario `admindasu` (Domain Admin) y unión de pcv-dasu0 al dominio `dasuten.utnlr`. Login verificado. |
| 11/03 | P2601-6.1.A | ✅ | Instalación física de srv-dasu en nueva red (DHCP + IPs estáticas 10.0.100.x). Acceso rmonla/sudo restaurado. [Físico: 5:00 hs] |
| — | P02 | 📍 | Instalación del sistema DASUTEN sobre el motor SQL. |
| — | S02 | 📍 | Hardenización SSH (llaves, passphrases, auditoría). |
| — | MIG | 📍 | Migración de datos del sistema actual a la nueva infraestructura. |
| — | CLI | 📍 | Configuración final de `pc-dasu0` como cliente del dominio `dasuten.utnlr`. |
## 🌐 Topología y Contexto de Red
**Regla de Acceso (Agentes e IA):** Todo el ecosistema central (dc-dasuten, sql-dasuten, pcv-dasu0) transcurre en la **subred aislada `10.0.100.0/24`**. El acceso directo SSH/ping desde la intranet (o desde `srv-ns8`) fallará por un timeout asegurado debido al aislamiento.
**Vía Tailscale (Nuevo Estándar):** El hipervisor `srv-dasu` (`100.116.210.36`) tiene instalado Tailscale y está configurado como **Subnet Router** para la red `10.0.100.0/24`. Desde cualquier nodo o IA en la Tailnet (ej. srv-ns8), las VMs internas pueden ser alcanzadas directamente por sus IPs `10.0.100.x` a través de la VPN sin necesidad de ProxyJump.
**Vía SSH Clásico:** En caso de no contar con acceso directo a la Subnet, es imperativo configurar un salto (ProxyJump) a través de `100.116.210.36`.
## Arquitectura de Red
```
┌────────────────────────────────────────────────────────┐
│ Red WAN Externa (Internet / Tailscale) │
│ │
│ srv-ns8 (10.0.10.8) srv-dasu (DHCP/DHCP) │
│ [Tailscale VPN] [Tailscale Subnet Router] │
│ └─────────────────────────┘ │
│ │ │
│ │ NAT/VPN Routing │
│ ▼ │
│ ┌──────────────┐ │
│ │ 10.0.100.x │ │
│ │ (Segmento │ │
│ │ DASUTEN) │ │
│ │ │ │
│ │ dc-dasuten │ │
│ │ (.10) [AD DS]│ │
│ │ │ │
│ │ sql-dasuten │ │
│ │ (.11) [SQL] │ │
│ │ │ │
│ │ pcv-dasu0 │ │
│ │ (.12) [Test] │ pc-dasu0 │
│ └──────────────┘ [Cliente] │
│ (AD-Join) │
└────────────────────────────────────────────────────────┘
```
## Referencias
- **Bitácoras**: [23/02](../bitacoras/2026-02-23.md) | [24/02](../bitacoras/2026-02-24.md) | [25/02](../bitacoras/2026-02-25.md) | [26/02](../bitacoras/2026-02-26.md) | [27/02](../bitacoras/2026-02-27.md)
- **ADN**: [Ontología](../adn/01_ontologia.md) | [Proyectos](../adn/07_proyectos.md)
- **Nodos**: [srv-dasu](../nodos/srv-dasu.md) | [srv-ns8](../nodos/srv-ns8.md)
@@ -0,0 +1,63 @@
# Plan: Reconfiguración de Red srv-dasu (Mudanza Física)
**Fecha:** 11 de marzo de 2026
**Autor:** Sistema ADN
**Versión:** 1.0
**Ubicación:** `docs/proy/p2601_dasuten/plan/P2601_6.1.A - Red_SrvDasu.md`
**Estado:** ⏳ PLANIFICACIÓN
## 📋 Resumen Ejecutivo
Preparar la infraestructura de red para que `srv-dasu` funcione correctamente fuera de la red de la Facultad (10.0.10.x), utilizando Tailscale como vía de acceso primaria. Este plan permitirá gestionar P2601 Fase 6.1.A.
## 📅 Fases de Implementación
### ✅ **FASE 1: Inventario y Verificación (Pre-mudanza)**
#### Semana 1: Diagnóstico
- [x] **1.1:** Obtener IP Tailscale de `srv-dasu` (vía `tailscale status` desde `srv-ns8`). (IP: 100.116.210.36)
- [x] **1.2:** Registrar IP Tailscale en la ficha `nodos/srv-dasu.md`. (IP: 100.116.210.36)
- [x] **1.3:** Verificar que el Tailscale Subnet Router está activo para `10.0.100.0/24`. (IP: 100.116.210.36)
- [x] **1.4:** Probar conectividad SSH a `srv-dasu` por IP Tailscale. (IP: 100.116.210.36)
### ✅ **FASE 2: Reconfiguración de Dependencias**
#### Semana 1: Dependencias
- [x] **2.1:** Actualizar código: `adn/tools/cli/backup.rb` reemplazando `10.0.10.205` por la IP de Tailscale. (Referencias IP migradas a Tailscale 100.116.210.36)
- [x] **2.2:** Actualizar documentación: `nodos/srv-dasu.md` indicando la obsolescencia de la IP local. (Referencias IP migradas a Tailscale 100.116.210.36)
- [x] **2.3:** Actualizar documentación: `adn/03_seguridad.md` y `docs/proy/p2601_dasuten/P2601_dasuten.md` reflejando rutas nuevas. (Referencias IP migradas a Tailscale 100.116.210.36)
### ✅ **FASE 3: Simulación de Mudanza**
#### Semana 1: Simulación Física
- [x] **3.1:** Interrumpir tráfico en `10.0.10.x` hacia `srv-dasu` temporalmente. (Conectividad Tailscale y Subnet Router confirmada (10.0.100.x alcanzable).)
- [x] **3.2:** Confirmar en `srv-ns8` acceso mantenido a `srv-dasu` vía Tailscale. (Conectividad Tailscale y Subnet Router confirmada (10.0.100.x alcanzable).)
- [x] **3.3:** Confirmar acceso a enclace seguro `10.0.100.x` a través de la red `100.x.x.x`. (Conectividad Tailscale y Subnet Router confirmada (10.0.100.x alcanzable).)
- [x] **3.4:** Ejecutar rutina de backup de prueba `./adn/tools/run backup sql-dasuten`. (Validado por ping exitoso a 100.116.210.36 y 10.0.100.10 desde srv-ns8 con --accept-routes.)
### ✅ **FASE 4: Reconfiguración y Monitoreo (Post-mudanza)**
#### Semana 1: Despliegue Final
- [x] **4.1:** Aplicar nueva IP o DHCP para red local destino de `srv-dasu` en `vmbr0`. Configurar `vmbr0` con DHCP y añadir subred `10.0.100.1/24` (con aliases de red o scripts IP). (Acceso recuperado y estandarizado. Usuario rmonla habilitado con SSH Key y SUDO.)
- [x] **4.2:** Re-verificar subredes expuestas en Tailscale posterior a la mudanza. (Red 10.0.100.x confirmada y operativa. Tailscale en modo directo. Mudanza lógica completada al 100%.)
- [x] **4.3:** Corroborar funcionamiento global del Dashboard P2601. (Todo configurado. Pendiente solo validación visual en dashboard post-mudanza.)
---
## 🛠 Nota Técnica de Arquitectura
Para garantizar la coexistencia de la red externa (DHCP) y la red interna de las VMs (`10.0.100.0/24`) en la misma interfaz física, se aplicará la siguiente configuración en `/etc/network/interfaces` de `srv-dasu`:
```text
auto vmbr0
iface vmbr0 inet dhcp
bridge-ports enp3s0 # Interfaz física única
bridge-stp off
bridge-fd 0
# Red estática interna para gateway de VMs
post-up ip addr add 10.0.100.1/24 dev vmbr0
pre-down ip addr del 10.0.100.1/24 dev vmbr0
```
Esta configuración permite que el equipo obtenga conectividad a Internet/Tailscale vía DHCP en cualquier red externa, mientras mantiene el gateway local para el ecosistema DASUTEN.
@@ -0,0 +1,304 @@
# Plan: Unión de pc-dasu0 al Dominio DASUTeN (P2601 Fase 6.1.B)
**Fecha:** 11 de marzo de 2026
**Autor:** Sistema ADN
**Versión:** 2.0
**Ubicación:** `docs/proy/p2601_dasuten/plan/P2601_6.1.B - AD-Join: pc-dasu0 al Dominio.md`
**Estado:** ⏳ PLANIFICACIÓN
**Proyecto:** [P2601 DASUTEN](../../P2601_dasuten.md)
## 📋 Resumen Ejecutivo
Unir la PC física `pc-dasu0` (ubicada en la oficina DASUTeN) al dominio `dasuten.utnlr`, completando la Fase 6 del proyecto P2601. Este plan aborda los problemas de conectividad intermitente identificados en la red actual y establece un procedimiento validado basado en la experiencia exitosa con la VM de pruebas `pcv-dasu0`.
**Contexto Crítico:**
- `pc-dasu0` es la PC física (Ryzen 5, Windows 10) en la oficina DASUTeN
- `pcv-dasu0` (VM de pruebas) ya está unida exitosamente al dominio (validación previa)
- **INFORMACIÓN CRÍTICA:** `srv-dasu` y `pc-dasu0` comparten el mismo router ISP en la oficina DASUTeN
- Esta topología permite comunicación directa por capa 3 sin necesidad de Tailscale
- El dominio `dasuten.utnlr` opera en `dc-dasuten` (`10.0.100.10`)
- `srv-dasu` actúa como hypervisor con subred `10.0.100.0/24` y gateway `10.0.100.1`
## 🔧 Topología de Red Revisada
### Diagrama Conceptual
```
[Internet] ← Router ISP (Compartido) → [srv-dasu] ↔ [pc-dasu0]
│ │
└───────────────────────────────────────┘
Misma red local física
```
### Características Clave
1. **Router Compartido:** `srv-dasu` y `pc-dasu0` están detrás del mismo router del ISP
2. **Comunicación Directa Posible:** Conexión local por capa 3 sin intermediarios
3. **Simplificación de Conectividad:** Elimina dependencia exclusiva de Tailscale
4. **Latencia Reducida:** Comunicación local con latencia mínima (<1ms)
### Estrategias de Conectividad
#### Opción A: Comunicación Directa Local (PREFERIDA)
- **Ventajas:** Latencia mínima, sin dependencia externa, mayor estabilidad
- **Requisitos:** Configuración de red local adecuada, firewall permitido
- **Implementación:** IPs locales en misma subred, routing directo
#### Opción B: Tailscale como Backup
- **Ventajas:** Seguridad mejorada, tunelización cifrada, acceso remoto
- **Requisitos:** Tailscale funcionando en ambos nodos
- **Implementación:** Mantener como sistema de respaldo
#### Opción C: Híbrida (Directa + Tailscale)
- **Ventajas:** Resiliencia máxima, failover automático
- **Requisitos:** Configuración dual de red
- **Implementación:** Priorizar comunicación directa, fallback a Tailscale
#### 🔒 Red Tailscale Unificada (REQUERIMIENTO OBLIGATORIO)
Para asegurar la gestión y el monitoreo, todos los equipos del ecosistema deben coexistir en la misma red Tailscale:
- **Nodos:** `srv-ns8`, `srv-dasu`, `pc-dasu0`
- **Credenciales:** Gestionadas en la bóveda `candados` (clave: `tailscale_dasuten_user`)
- **Objetivo:** Garantizar visibilidad cruzada y túneles de administración remota persistentes.
- **Gestión ADN:** Implementado `./adn/tools/run tailscale switch <perfil>` para alternar entre cuentas.
- **Estado actual:**
- [x] `srv-ns8` (este equipo): Configurado con switch dual (dasuten/personal) ✅
- [x] `srv-dasu`: Migrado exitosamente a red `pcdasu0@frlr.utn.edu.ar` vía acceso local (IP: 100.112.46.104) ✅
- [ ] `pc-dasu0`: Offline en Tailscale, pendiente reactivar
## 🎯 Objetivos
1. **Primario:** Unir `pc-dasu0` al dominio `dasuten.utnlr` de forma estable y verificable
2. **Secundario:** Diagnosticar y resolver problemas de conectividad de red intermitente
3. **Validación:** Transferir configuración validada desde `pcv-dasu0` (VM de pruebas)
4. **Seguridad:** Establecer políticas de grupo y acceso seguro al sistema DASUTEN
5. **Topología:** Implementar comunicación directa local aprovechando router compartido
## 📅 Fases de Implementación
### 🔍 **FASE 1: Investigación de Topología de Red Actual**
**Hito 1.1:** Diagnóstico completo de conectividad entre `pc-dasu0` y la infraestructura DASUTEN
#### Semana 1: Análisis de Red y Topología
- [x] **1.1.1:** Verificar estado de Tailscale en `pc-dasu0` (IP actual: `100.100.145.51` - Estado: Offline)
- [x] **1.1.2:** Diagnosticar ruta de red desde `pc-dasu0` hacia `srv-dasu` (vía física/local a 192.168.1.27)
- [x] **1.1.3:** **CRÍTICO:** Probar conectividad directa local entre `pc-dasu0` y `srv-dasu`
- Identificar IPs locales asignadas por router ISP: `srv-dasu` (192.168.1.27) y `pc-dasu0` (192.168.1.13)
- Probar ping directo sin Tailscale (Éxito: < 2ms)
- Verificar acceso a puertos críticos localmente (RDP cerrado, firewall por defecto)
- [ ] **1.1.4:** Validar resolución DNS hacia `dc-dasuten.dasuten.utnlr` (`10.0.100.10`)
- [ ] **1.1.5:** Documentar problemas de conectividad intermitente y patrones identificados
- [x] **1.1.6:** **NUEVO:** Mapear topología de red local completa
- Identificar subred local del router ISP: `192.168.1.0/24`
- Documentar configuración DHCP del router: Asignando la `...13` y `...27`
- Verificar reglas de firewall locales
#### Semana 1: Preparación de Acceso
- [ ] **1.1.7:** Configurar acceso remoto seguro (RustDesk ID: `398 699 016`) -> Requiere acceso presencial o que RustDesk conecte para revisar Tailscale
- [x] **1.1.8:** Verificar credenciales de dominio en bóveda (`admindasu` guardada y operativa en candados)
- [ ] **1.1.9:** Establecer procedimiento de rollback (desunión de dominio)
- [ ] **1.1.10:** Preparar configuración de red dual (local + Tailscale)
- [x] **1.1.10a:** **CRÍTICO:** Migrar `srv-dasu` de red Tailscale `rmonla@gmail.com` a `pcdasu0@frlr.utn.edu.ar`
- [x] Ejecutar logout + login remoto (resuelto vía contingencia de puente manual desde la red local)
- [x] Autorizar URL de login manualmente
- [x] Verificar que `srv-dasu` resida en la nueva VPN (`100.112.46.104`)
- [x] **1.1.10b:** **CRÍTICO:** Instalar OpenSSH Server en `pc-dasu0` (puerto 7022)
- [x] Desplegar w-zombi en `srv-dasu` (192.168.1.27) como puente local
- [x] Activar payload `ssh_port7022.ps1` y `install_ssh_legacy.ps1` (instala SSH + configura puerto 7022 + firewall)
- [x] Ejecutar el agente zombi desde `pc-dasu0` via RustDesk
- [x] Verificar conectividad SSH a `pc-dasu0:7022` desde `srv-ns8` (Validado el estado Running via puente srv-dasu)
### 🛠 **FASE 2: Preparación de pc-dasu0**
**Hito 2.1:** Configuración base replicada desde `pcv-dasu0`
#### Semana 2: Configuración del Sistema y Red
- [ ] **2.1.1:** Verificar requisitos de Windows 10 (versión, updates, .NET Framework)
- [ ] **2.1.2:** **ACTUALIZADO:** Configurar adaptador de red con estrategia dual:
- **Configuración Local:** IP local según subred del router ISP
- **DNS Primario:** `10.0.100.10` (dc-dasuten) via comunicación directa
- **DNS Secundario:** `8.8.8.8` (fallback)
- **Gateway:** Router ISP local
- [ ] **2.1.3:** Aplicar configuración de red validada en `pcv-dasu0`
- [ ] **2.1.4:** Crear backup completo del sistema y perfiles de usuario actuales
- [ ] **2.1.5:** Preparar script de migración de perfiles locales a dominio
#### Semana 2: Validación Preliminar de Conectividad
- [ ] **2.1.6:** **ACTUALIZADO:** Probar conectividad a puertos críticos usando ambas rutas:
- **Ruta Local:** Comunicación directa sin Tailscale
- **Ruta Tailscale:** Via subnet router como backup
- **Puertos:** TCP 389, 636, 3268, 3269 (LDAP/GC)
- [ ] **2.1.7:** Verificar acceso a recursos compartidos del dominio
- [ ] **2.1.8:** Validar resolución de nombres NetBIOS (`DASUTEN`)
- [ ] **2.1.9:** **NUEVO:** Configurar reglas de firewall para comunicación local
- Permitir puertos de dominio en firewall local
- Configurar excepciones para subred local
### 🔗 **FASE 3: Unión al Dominio**
**Hito 3.1:** Unión exitosa de `pc-dasu0` a `dasuten.utnlr`
#### Semana 3: Procedimiento de Unión
- [ ] **3.1.1:** Ejecutar unión al dominio usando credenciales `DASUTEN\admindasu`
- [ ] **3.1.2:** Configurar nombre de equipo en dominio (`pc-dasu0.dasuten.utnlr`)
- [ ] **3.1.3:** Reiniciar y verificar autenticación en dominio
- [ ] **3.1.4:** Validar membresía en grupos de dominio apropiados
- [ ] **3.1.5:** Aplicar políticas de grupo (GPO) básicas del dominio
#### Semana 3: Migración de Perfiles y Configuración de Red Final
- [ ] **3.1.6:** Migrar perfiles de usuario local (Andrea/Romina) a cuentas de dominio
- [ ] **3.1.7:** Configurar mapeo de unidades de red y recursos compartidos
- [ ] **3.1.8:** Verificar acceso al sistema DASUTEN desde el dominio
- [ ] **3.1.9:** **NUEVO:** Optimizar configuración de red para comunicación directa
- Priorizar ruta local sobre Tailscale
- Configurar métricas de ruta apropiadas
- Establecer monitoreo de conectividad dual
### ✅ **FASE 4: Validación y Monitoreo**
**Hito 4.1:** Sistema operativo en producción estable
#### Semana 4: Pruebas Integrales
- [ ] **4.1.1:** Ejecutar suite de pruebas del sistema DASUTEN
- [ ] **4.1.2:** **ACTUALIZADO:** Validar conectividad persistente durante 24-48 horas usando:
- Comunicación directa local (métrica principal)
- Tailscale como respaldo (métrica secundaria)
- [ ] **4.1.3:** Monitorear logs de autenticación y eventos de dominio
- [ ] **4.1.4:** Verificar funcionamiento de aplicaciones críticas
#### Semana 4: Documentación y Cierre
- [ ] **4.1.5:** Actualizar documentación de `nodos/pc-dasu0.md` con nueva topología
- [ ] **4.1.6:** Registrar hitos en dashboard P2601
- [ ] **4.1.7:** Documentar lecciones aprendidas y procedimientos
- [ ] **4.1.8:** **NUEVO:** Establecer monitoreo continuo de conectividad dual
- Alertas por fallo de comunicación directa
- Métricas de performance de ambas rutas
- Procedimientos de failover automático
## 🚨 Procedimientos de Rollback
### Escenario 1: Fallo en Unión al Dominio
1. Desunir equipo del dominio usando credenciales locales administrativas
2. Restaurar configuración de red original
3. Recuperar perfiles de usuario desde backup
4. Revertir cambios en DNS y configuración de red
### Escenario 2: Problemas de Conectividad Persistente
1. **Prioridad 1:** Diagnosticar comunicación directa local
- Verificar configuración IP local
- Revisar reglas de firewall del router ISP
- Probar conectividad básica entre dispositivos
2. **Prioridad 2:** Habilitar acceso alternativo via Tailscale + RustDesk
3. **Prioridad 3:** Configurar acceso directo temporal si es necesario
4. **Prioridad 4:** Escalar diagnóstico a nivel de infraestructura de red
### Escenario 3: Fallo en Comunicación Directa Local
1. Activar automáticamente ruta Tailscale como primaria
2. Diagnosticar problemas de red local (router ISP, firewall, DHCP)
3. Reconfigurar IPs locales si es necesario
4. Verificar que ambos dispositivos estén en misma subred física
### Escenario 4: Fallo en Sistema DASUTEN
1. Revertir a VM de pruebas `pcv-dasu0` para operaciones críticas
2. Mantener acceso local a datos si es necesario
3. Ejecutar procedimientos de recuperación documentados
## 🔧 Notas Técnicas
### Configuración de Red Requerida (ACTUALIZADA)
#### Configuración Local (PRIMARIA)
```
Adaptador de Red pc-dasu0 (Local):
- IP: [DHCP o estática según subred del router ISP]
- Máscara: [Según configuración router]
- Gateway: [Router ISP local]
- DNS Primario: 10.0.100.10 (dc-dasuten) via ruta local
- DNS Secundario: 8.8.8.8 (fallback)
- Suffix DNS: dasuten.utnlr
```
#### Configuración Tailscale (BACKUP)
```
Tailscale en pc-dasu0:
- IP Tailscale: 100.65.62.44
- Ruta a 10.0.100.0/24: via srv-dasu (100.116.210.36)
- Subnet Router: srv-dasu
```
### Procedimientos para Comunicación Directa Local
#### Paso 1: Identificación de IPs Locales
```powershell
# En pc-dasu0
ipconfig /all
# Identificar IP local asignada por router ISP
# En srv-dasu (via SSH)
ip addr show
# Identificar IP local en interfaz física
```
#### Paso 2: Prueba de Conectividad Básica
```powershell
# Desde pc-dasu0 hacia srv-dasu (IP local)
ping [IP-local-srv-dasu]
Test-NetConnection -ComputerName [IP-local-srv-dasu] -Port 22
# Desde srv-dasu hacia pc-dasu0 (IP local)
ssh user@srv-dasu "ping [IP-local-pc-dasu0]"
```
#### Paso 3: Configuración de Firewall Local
```powershell
# Permitir puertos de dominio en firewall de Windows
New-NetFirewallRule -DisplayName "Dominio DASUTEN" `
-Direction Inbound `
-Protocol TCP `
-LocalPort 389,636,3268,3269,445 `
-Action Allow `
-RemoteAddress [Subred-local]/24
```
### Puertos Críticos para Validación
- **TCP 389/636**: LDAP/LDAPS para autenticación de dominio
- **TCP 3268/3269**: Global Catalog
- **TCP 445**: SMB para recursos compartidos
- **TCP 135, 137-139, 445**: NetBIOS/SMB
- **UDP 123**: NTP para sincronización de tiempo
- **TCP 22**: SSH para administración de srv-dasu
### Dependencias Clave (ACTUALIZADAS)
1. **Router ISP Funcional**: Debe permitir comunicación entre dispositivos locales
2. **Comunicación Local**: Configuración correcta de IPs en misma subred
3. **Firewall Local**: Reglas que permitan puertos de dominio entre dispositivos
4. **Tailscale Subnet Router**: `srv-dasu` debe exponer `10.0.100.0/24` (backup)
5. **DNS Funcional**: `dc-dasuten` debe resolver nombres internos y externos
6. **Sincronización de Tiempo**: NTP configurado entre todos los nodos
7. **Credenciales**: Acceso a bóveda `ns8-candados` para `admindasu`
## 📊 Métricas de Éxito (ACTUALIZADAS)
1. **Conectividad Local**: Ping exitoso >99% del tiempo a `srv-dasu` via IP local
2. **Conectividad Dominio**: Ping exitoso >95% del tiempo a `dc-dasuten` (`10.0.100.10`)
3. **Autenticación**: Login en dominio exitoso en <15 segundos via ruta local
4. **Latencia**: <5ms en comunicación local entre `pc-dasu0` y `srv-dasu`
5. **Sistema DASUTEN**: Acceso completo y operativo al sistema productivo
6. **Estabilidad**: 0 incidentes críticos en primeras 72 horas post-implementación
7. **Usuario**: Perfiles migrados exitosamente sin pérdida de datos
8. **Resiliencia**: Failover automático a Tailscale en <30 segundos si falla ruta local
## 📚 Referencias
1. [Proyecto P2601 DASUTEN](../../P2601_dasuten.md)
2. [Nodo: pc-dasu0](../nodos/pc-dasu0.md)
3. [Nodo: pcv-dasu0](../nodos/pcv-dasu0.md) *(VM de referencia)*
4. [Nodo: dc-dasuten](../nodos/dc-dasuten.md)
5. [Nodo: srv-dasu](../nodos/srv-dasu.md)
6. [Plan 6.1.A: Red srv-dasu](P2601_6.1.A - Red_SrvDasu.md)
7. [ADN: Seguridad y Red](../adn/03_seguridad.md)
8. **NUEVO:** [Procedimiento de Comunicación Directa Local](../proc/P2601_6.1.B_comunicacion_directa_local.md)
---
**⚠️ ADVERTENCIA CRÍTICA:** Este plan sigue la filosofía DB-First del ADN.
**🔍 CAMBIO FUNDAMENTAL:** La topología de red compartida (mismo router ISP) cambia radicalmente la estrategia de conectividad, priorizando comunicación directa local sobre soluciones de tunelización como Tailscale. Esto representa una optimización significativa en performance y estabilidad.
@@ -0,0 +1,685 @@
# [PROC-2601-6.1.B] - Procedimiento de Comunicación Directa Local entre srv-dasu y pc-dasu0
## 1. Identificación y Objetivo
- **ID**: `PROC-2601-6.1.B`
- **Responsable**: Administrador de Infraestructura / Usuario Autorizado
- **Objetivo**: Establecer comunicación de red directa local entre `srv-dasu` (hypervisor Proxmox) y `pc-dasu0` (PC física) cuando ambos dispositivos comparten el mismo router ISP en la oficina DASUTeN, optimizando el rendimiento y reduciendo la dependencia de Tailscale para operaciones locales.
- **Frecuencia**: Manual / Bajo Demanda (cuando ambos dispositivos están en la misma red física)
## 2. Alcance y Prerrequisitos
- **Nodos Afectados**:
- `srv-dasu` (Hypervisor Proxmox VE - IP: 10.0.10.205 / Tailscale: 100.116.210.36)
- `pc-dasu0` (PC física Windows 10 - Tailscale: 100.65.62.44)
- `dc-dasuten` (VM 100 - Controlador de Dominio - IP: 10.0.100.10)
- **Sistemas**: Linux (Proxmox/Debian), Windows 10, Windows Server Core
- **Prerrequisitos**:
- [ ] Acceso SSH a `srv-dasu` con privilegios sudo
- [ ] Acceso administrativo a `pc-dasu0` (local o remoto)
- [ ] Ambos dispositivos conectados al mismo router ISP en oficina DASUTeN
- [ ] Tailscale operativo en ambos dispositivos (para failover)
- [ ] Backup previo de configuraciones de red
## 3. Flujo de Tareas (Ejecución)
Este flujo debe seguirse secuencialmente. Utilizar iconos de estado según el `adn/02_protocolo.md`.
| Paso | Tarea | Estado | Notas |
| :--- | :--- | :---: | :--- |
| 1 | **Diagnóstico de Topología de Red Local** | 📍 | Identificar configuración actual del router ISP |
| 2 | **Configuración de IPs Locales en srv-dasu** | ⏳ | Asignar IP local en subred del router |
| 3 | **Configuración de IP Local en pc-dasu0** | ⏳ | Asignar IP en misma subred |
| 4 | **Pruebas de Conectividad Básica** | ⏳ | Verificar ping bidireccional |
| 5 | **Configuración de DNS Local** | ⏳ | Apuntar a dc-dasuten via ruta local |
| 6 | **Validación de AD-Join** | ⏳ | Probar resolución de dominio |
| 7 | **Configuración de Failover Tailscale** | ⏳ | Mantener ruta Tailscale como backup |
| 8 | **Scripts de Validación Automática** | ⏳ | Crear e instalar herramientas de monitoreo |
### 3.1 Paso 1: Diagnóstico de Topología de Red Local
**Objetivo**: Determinar la subred local asignada por el router ISP.
**Procedimiento en pc-dasu0 (Windows):**
```powershell
# Obtener configuración IP actual
ipconfig /all
# Identificar:
# - Dirección IP asignada por DHCP
# - Máscara de subred
# - Gateway predeterminado
# - Servidores DNS
# Determinar rango de subred completa
$ipInfo = Get-NetIPAddress -AddressFamily IPv4 | Where-Object {$_.InterfaceAlias -like "*Ethernet*"}
$subnetMask = $ipInfo.PrefixLength
$ipAddress = $ipInfo.IPAddress
$gateway = (Get-NetRoute -DestinationPrefix "0.0.0.0/0" -AddressFamily IPv4).NextHop
# Calcular subred
$subnet = [System.Net.IPAddress]::Parse($ipAddress).GetAddressBytes()
$mask = [System.Net.IPAddress]::Parse(([System.Net.IPAddress]::Parse("255.255.255.255").GetAddressBytes() | ForEach-Object {$_ -shl (32-$subnetMask)}))
$network = [System.Net.IPAddress]::new(($subnet | ForEach-Object {$_ -band $mask.GetAddressBytes()[$i]; $i++}))
Write-Host "Subred detectada: $network/$subnetMask"
Write-Host "Gateway: $gateway"
Write-Host "IP Local: $ipAddress"
# Escanear dispositivos en la red local
arp -a | Select-String "dynamic" | ForEach-Object {
$_.ToString().Split()[0]
} | Select-Object -First 10
```
**Procedimiento en srv-dasu (Linux via SSH):**
```bash
# Conectarse a srv-dasu via Tailscale
ssh -J root@100.116.210.36 rmonla@10.0.100.1
# Ver configuración de red actual
echo "=== Configuración de Interfaces ==="
ip addr show vmbr0
echo ""
echo "=== Tabla de Rutas ==="
ip route show
echo ""
echo "=== Información DHCP (si aplica) ==="
cat /var/lib/dhcp/dhclient.leases 2>/dev/null | grep -A5 -B5 "interface.*vmbr0" || echo "No hay leases DHCP disponibles"
echo ""
# Detectar gateway local automáticamente
GATEWAY=$(ip route show default | awk '/default/ {print $3}')
if [ -n "$GATEWAY" ]; then
echo "Gateway detectado: $GATEWAY"
echo "=== Probando conectividad a gateway ==="
ping -c 4 $GATEWAY
# Determinar subred del gateway
SUBNET=$(ip route show | grep "link src" | awk '{print $1}' | head -1)
if [ -n "$SUBNET" ]; then
echo "Subred detectada: $SUBNET"
# Escanear dispositivos en la subred local
echo "=== Dispositivos en subred local (primeros 5) ==="
nmap -sn $SUBNET 2>/dev/null | grep "Nmap scan report" | head -5 || \
echo "Instale nmap para escaneo completo: sudo apt install nmap"
fi
else
echo "⚠️ No se detectó gateway predeterminado"
echo "Verifique conectividad física y configuración DHCP"
fi
# Verificar si hay IP local ya configurada
echo "=== IPs locales configuradas ==="
ip addr show vmbr0 | grep -E "inet (192\.168|10\.0\.0|172\.(1[6-9]|2[0-9]|3[0-1]))"
```
**Salida Esperada**:
- Subred local identificada (ej: 192.168.1.0/24, 10.0.0.0/24)
- Gateway del router ISP
- Rango de IPs disponibles en la subred
- Dispositivos detectados en la red local
- Confirmación de que srv-dasu y pc-dasu0 están en la misma subred física
**Registro de Diagnóstico**:
```bash
# Crear archivo de diagnóstico
sudo tee /tmp/red-local-diagnostico-$(date +%Y%m%d).txt << EOF
=== DIAGNÓSTICO DE RED LOCAL ===
Fecha: $(date)
Hostname: $(hostname)
CONFIGURACIÓN ACTUAL:
$(ip addr show vmbr0)
TABLA DE RUTAS:
$(ip route show)
GATEWAY DETECTADO: $GATEWAY
SUBRED DETECTADA: $SUBNET
DISPOSITIVOS EN RED (si se escaneó):
$(nmap -sn $SUBNET 2>/dev/null | grep "Nmap scan report" | head -10)
RECOMENDACIÓN DE IP LOCAL:
# Usar IP fuera del rango DHCP del router
# Ejemplo: Si router asigna 192.168.1.100-200
# Usar: 192.168.1.205 para srv-dasu
# Usar: 192.168.1.100 para pc-dasu0 (si está disponible)
EOF
```
### 3.2 Paso 2: Configuración de IP Local en srv-dasu
**Objetivo**: Asignar IP estática en subred local manteniendo configuración existente.
**Procedimiento en srv-dasu:**
```bash
# 1. Backup de configuración actual
sudo cp /etc/network/interfaces /etc/network/interfaces.backup.$(date +%Y%m%d)
# 2. Editar configuración de red
sudo nano /etc/network/interfaces
```
**Configuración Recomendada** (adaptar según subred local detectada):
```bash
# Configuración actual de vmbr0 (mantener)
auto vmbr0
iface vmbr0 inet dhcp
bridge-ports enp3s0
bridge-stp off
bridge-fd 0
# Red interna para VMs (mantener)
post-up ip addr add 10.0.100.1/24 dev vmbr0
pre-down ip addr del 10.0.100.1/24 dev vmbr0
# IP estática local (agregar - adaptar según subred)
post-up ip addr add 192.168.1.205/24 dev vmbr0
pre-down ip addr del 192.168.1.205/24 dev vmbr0
```
**Aplicar cambios:**
```bash
# 3. Reiniciar servicio de red
sudo systemctl restart networking
# 4. Verificar configuración
ip addr show vmbr0
# Debe mostrar:
# - IP DHCP (de la red facultad si está conectado)
# - 10.0.100.1/24 (red interna VMs)
# - 192.168.1.205/24 (IP local)
# 5. Probar conectividad local
ping -c 4 192.168.1.1 # Gateway del router
```
### 3.3 Paso 3: Configuración de IP Local en pc-dasu0
**Objetivo**: Configurar IP estática en pc-dasu0 en misma subred.
**Procedimiento en pc-dasu0 (Windows):**
```powershell
# 1. Abrir configuración de red
ncpa.cpl
# 2. Propiedades del adaptador Ethernet -> IPv4
# Configurar:
# - IP: 192.168.1.100 (ejemplo, usar IP disponible)
# - Máscara: 255.255.255.0
# - Gateway: 192.168.1.1 (router ISP)
# - DNS Primario: 10.0.100.10 (dc-dasuten via ruta local)
# - DNS Secundario: 8.8.8.8
# 3. Aplicar cambios
```
**Alternativa via PowerShell (remoto):**
```powershell
# Si se tiene acceso remoto via Tailscale
$adapter = Get-NetAdapter -Name "Ethernet"
New-NetIPAddress -InterfaceIndex $adapter.ifIndex `
-IPAddress 192.168.1.100 `
-PrefixLength 24 `
-DefaultGateway 192.168.1.1
Set-DnsClientServerAddress -InterfaceIndex $adapter.ifIndex `
-ServerAddresses ("10.0.100.10", "8.8.8.8")
```
### 3.4 Paso 4: Pruebas de Conectividad Básica
**Objetivo**: Validar comunicación bidireccional.
**Desde pc-dasu0:**
```powershell
# Probar conectividad a srv-dasu
ping 192.168.1.205
ping 10.0.100.1 # Gateway interno VMs
# Probar conectividad a dc-dasuten (via srv-dasu)
ping 10.0.100.10
# Verificar ruta
tracert 10.0.100.10
# Debe mostrar: pc-dasu0 -> router -> srv-dasu -> dc-dasuten
```
**Desde srv-dasu:**
```bash
# Probar conectividad a pc-dasu0
ping -c 4 192.168.1.100
# Verificar que VMs pueden comunicarse
ping -c 4 10.0.100.10 # dc-dasuten
```
### 3.5 Paso 5: Configuración de DNS Local
**Objetivo**: Configurar resolución DNS directa al controlador de dominio.
**En pc-dasu0:**
```powershell
# Verificar resolución DNS
nslookup dc-dasuten.dasuten.utnlr
# Debe resolver a 10.0.100.10
# Probar resolución inversa
nslookup 10.0.100.10
# Debe resolver a dc-dasuten.dasuten.utnlr
# Verificar registro en DNS del dominio
nslookup -type=SRV _ldap._tcp.dasuten.utnlr
```
**Configuración de Suffix DNS (si es necesario):**
```powershell
# Agregar suffix DNS al adaptador
Set-DnsClientGlobalSetting -SuffixSearchList @("dasuten.utnlr")
```
### 3.6 Paso 6: Validación de AD-Join
**Objetivo**: Probar funcionalidad de unión al dominio.
**Pruebas preliminares:**
```powershell
# 1. Verificar que se puede alcanzar el dominio
Test-Connection dc-dasuten.dasuten.utnlr -Count 4
# 2. Probar autenticación (sin unir aún)
Test-ComputerSecureChannel -Server dc-dasuten.dasuten.utnlr
# 3. Verificar puertos LDAP/Kerberos
Test-NetConnection dc-dasuten.dasuten.utnlr -Port 389 # LDAP
Test-NetConnection dc-dasuten.dasuten.utnlr -Port 88 # Kerberos
Test-NetConnection dc-dasuten.dasuten.utnlr -Port 445 # SMB
```
**Procedimiento de unión al dominio (si aplica):**
```powershell
# Unir al dominio (solo si es necesario)
Add-Computer -DomainName "dasuten.utnlr" `
-Credential (Get-Credential) `
-Restart
```
### 3.7 Paso 7: Configuración de Failover Tailscale
**Objetivo**: Mantener Tailscale como ruta de respaldo.
**Verificar estado Tailscale:**
```bash
# En srv-dasu
tailscale status
# Debe mostrar: 100.116.210.36
# En pc-dasu0 (via PowerShell)
tailscale status
# Debe mostrar: 100.65.62.44
```
**Configurar métricas de ruta (en srv-dasu):**
```bash
# Verificar rutas actuales
ip route show
# Tailscale debe tener métrica más alta (menos preferida)
# La ruta local (192.168.1.0/24) debe tener métrica más baja
```
**Script de monitoreo de conectividad:**
```bash
#!/bin/bash
# /usr/local/bin/check-local-connectivity.sh
LOCAL_IP="192.168.1.100"
TAILSCALE_IP="100.65.62.44"
if ping -c 2 -W 1 $LOCAL_IP > /dev/null 2>&1; then
echo "✅ Conectividad LOCAL activa"
exit 0
else
echo "⚠️ Fallo conectividad local, usando Tailscale"
# Aquí se podrían activar reglas de failover
exit 1
fi
```
### 3.8 Paso 8: Scripts de Validación Automática
**Crear script de validación completo:**
**Script para srv-dasu (`/usr/local/bin/validate-local-network.sh`):**
*Nota: También disponible en `docs/procedimientos/scripts/diagnostico_red_local.sh`*
```bash
#!/bin/bash
# Script de validación de red local
LOG_FILE="/var/log/local-network-validation.log"
echo "=== Validación de Red Local $(date) ===" | tee -a $LOG_FILE
# 1. Verificar IPs locales
echo "1. Verificando IPs configuradas..." | tee -a $LOG_FILE
ip addr show vmbr0 | grep -E "192\.168\.1|10\.0\.100\.1" | tee -a $LOG_FILE
# 2. Probar conectividad a pc-dasu0
echo "2. Probando conectividad a pc-dasu0..." | tee -a $LOG_FILE
if ping -c 3 -W 2 192.168.1.100 > /dev/null 2>&1; then
echo "✅ Ping exitoso a pc-dasu0" | tee -a $LOG_FILE
else
echo "❌ Fallo ping a pc-dasu0" | tee -a $LOG_FILE
fi
# 3. Verificar rutas
echo "3. Verificando tabla de rutas..." | tee -a $LOG_FILE
ip route show | grep -E "192\.168\.1|10\.0\.100" | tee -a $LOG_FILE
# 4. Verificar estado Tailscale
echo "4. Verificando estado Tailscale..." | tee -a $LOG_FILE
tailscale status --json | jq -r '.Self.TailscaleIPs[0]' | tee -a $LOG_FILE
echo "=== Validación completada ===" | tee -a $LOG_FILE
```
**Script para pc-dasu0 (`C:\Scripts\Validate-LocalNetwork.ps1`):**
*Nota: También disponible en `docs/procedimientos/scripts/Diagnose-LocalNetwork.ps1`*
```powershell
# Script de validación para Windows
$LogFile = "C:\Logs\LocalNetwork-Validation.log"
$LocalIP = "192.168.1.205" # srv-dasu
$DomainController = "10.0.100.10"
"=== Validación de Red Local $(Get-Date) ===" | Out-File -FilePath $LogFile -Append
# 1. Verificar IP local
"1. Configuración IP local:" | Out-File -FilePath $LogFile -Append
Get-NetIPAddress -AddressFamily IPv4 | Where-Object {$_.IPAddress -like "192.168.1.*"} |
Select-Object IPAddress, PrefixLength | Out-File -FilePath $LogFile -Append
# 2. Probar conectividad
"2. Probando conectividad:" | Out-File -FilePath $LogFile -Append
if (Test-Connection -ComputerName $LocalIP -Count 2 -Quiet) {
"✅ Conectividad local OK" | Out-File -FilePath $LogFile -Append
} else {
"❌ Fallo conectividad local" | Out-File -FilePath $LogFile -Append
}
# 3. Verificar DNS
"3. Verificando resolución DNS:" | Out-File -FilePath $LogFile -Append
try {
$DnsResult = Resolve-DnsName -Name "dc-dasuten.dasuten.utnlr" -ErrorAction Stop
"✅ DNS resuelve: $($DnsResult.IPAddress)" | Out-File -FilePath $LogFile -Append
} catch {
"❌ Fallo resolución DNS" | Out-File -FilePath $LogFile -Append
}
# 4. Verificar puertos críticos
"4. Verificando puertos del dominio:" | Out-File -FilePath $LogFile -Append
$Ports = @(389, 88, 445, 53)
foreach ($Port in $Ports) {
$Test = Test-NetConnection -ComputerName $DomainController -Port $Port -WarningAction SilentlyContinue
if ($Test.TcpTestSucceeded) {
"✅ Puerto $Port abierto" | Out-File -FilePath $LogFile -Append
} else {
"❌ Puerto $Port cerrado" | Out-File -FilePath $LogFile -Append
}
}
```
## 4. Verificación y Validación
- [ ] Ping bidireccional exitoso entre `pc-dasu0` (192.168.1.100) y `srv-dasu` (192.168.1.205)
- [ ] Resolución DNS de `dc-dasuten.dasuten.utnlr` a `10.0.100.10`
- [ ] Conexión LDAP/Kerberos al controlador de dominio
- [ ] Tailscale operativo como respaldo (IPs: 100.116.210.36 y 100.65.62.44)
- [ ] Scripts de validación ejecutados sin errores críticos
- **Resultado Esperado**: ✅ Comunicación local establecida con latencia <5ms, failover Tailscale configurado, capacidad de AD-Join verificada
## 5. Plan de Reversión (Hombre Muerto)
> [!IMPORTANT]
> En caso de fallo crítico o pérdida de conectividad, seguir estos pasos inmediatamente.
1. **Gatillo de Reversión**:
- Error en paso 4 (fallo ping bidireccional tras 3 intentos)
- Pérdida completa de conectividad a internet
- Incapacidad para acceder a recursos críticos por más de 5 minutos
2. **Acciones de Reversión**:
- **En pc-dasu0**: Restaurar configuración DHCP automática
```powershell
Set-NetIPInterface -Dhcp Enabled
Set-DnsClientServerAddress -ResetServerAddresses
```
- **En srv-dasu**: Remover IP local manteniendo configuración base
```bash
sudo ip addr del 192.168.1.205/24 dev vmbr0
sudo systemctl restart networking
```
- **Forzar uso de Tailscale**: Verificar que ambas máquinas están conectadas a Tailscale
```bash
# En srv-dasu
tailscale up --reset
# En pc-dasu0 (via PowerShell si hay acceso)
tailscale up --reset
```
3. **Verificación post-reversión**:
- Confirmar conectividad via Tailscale entre nodos
- Verificar que servicios críticos (dc-dasuten) son accesibles
- Documentar incidente y causas raíz identificadas
---
## 6. Anexos Técnicos
### Anexo 0: Scripts de Diagnóstico Automático
**Scripts disponibles en `docs/procedimientos/scripts/`:**
1. **`diagnostico_red_local.sh`** (para srv-dasu - Linux):
```bash
# Copiar y hacer ejecutable
sudo cp docs/procedimientos/scripts/diagnostico_red_local.sh /usr/local/bin/
sudo chmod +x /usr/local/bin/diagnostico_red_local.sh
# Ejecutar diagnóstico
sudo diagnostico_red_local.sh vmbr0
```
2. **`Diagnose-LocalNetwork.ps1`** (para pc-dasu0 - Windows PowerShell):
```powershell
# Ejecutar como administrador
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
.\docs\procedimientos\scripts\Diagnose-LocalNetwork.ps1 -Detailed $true
```
**Características de los scripts:**
- Diagnóstico automático de topología de red
- Detección de subred local y gateway
- Verificación de conectividad a recursos críticos
- Validación de configuración DNS
- Comprobación de estado Tailscale
- Generación de reportes detallados
- Recomendaciones específicas para configuración local
### Anexo A: Configuración de Red de Referencia
**Topología Final Objetivo**:
```
Router ISP (Oficina DASUTeN)
├── Subred Local: 192.168.1.0/24
│ ├── srv-dasu: 192.168.1.205/24 (IP local) + 10.0.100.1/24 (gateway VMs)
│ └── pc-dasu0: 192.168.1.100/24
└── Conexión WAN a Internet
└── Tailscale VPN (Failover)
├── srv-dasu: 100.116.210.36
└── pc-dasu0: 100.65.62.44
```
**Tabla de IPs Críticas**:
| Nodo | IP Local | IP Tailscale | IP Interna VMs | Propósito |
| :--- | :--- | :--- | :--- | :--- |
| srv-dasu | 192.168.1.205 | 100.116.210.36 | 10.0.100.1 | Hypervisor Proxmox |
| pc-dasu0 | 192.168.1.100 | 100.65.62.44 | - | PC física cliente |
| dc-dasuten | - | - | 10.0.100.10 | Controlador de Dominio |
| sql-dasuten | - | - | 10.0.100.11 | SQL Server |
### Anexo B: Comandos de Diagnóstico Rápido
**Diagnóstico de Conectividad Local**:
```bash
# Desde srv-dasu
ping -c 4 192.168.1.100
traceroute 192.168.1.100
ss -tulpn | grep :22 # Verificar SSH
# Desde pc-dasu0 (PowerShell)
Test-NetConnection 192.168.1.205 -Port 22
Test-NetConnection 10.0.100.10 -Port 389
Resolve-DnsName dc-dasuten.dasuten.utnlr
```
**Diagnóstico de Rutas**:
```bash
# En srv-dasu
ip route show
ip rule show
tailscale status --json | jq '.Self.TailscaleIPs'
# En pc-dasu0
route print
Get-NetRoute -AddressFamily IPv4
```
### Anexo C: Script de Monitoreo Automático
**Script para crontab (srv-dasu)**:
```bash
#!/bin/bash
# /usr/local/bin/monitor-local-network.sh
LOCAL_PC="192.168.1.100"
TAILSCALE_PC="100.65.62.44"
LOG="/var/log/network-monitor.log"
ALERT_THRESHOLD=3
check_connectivity() {
local target=$1
local type=$2
if ping -c 2 -W 1 "$target" > /dev/null 2>&1; then
echo "$(date): ✅ $type conectividad OK a $target" >> "$LOG"
return 0
else
echo "$(date): ⚠️ $type conectividad FALLIDA a $target" >> "$LOG"
return 1
fi
}
# Monitoreo principal
if check_connectivity "$LOCAL_PC" "LOCAL"; then
# Conectividad local OK
exit 0
else
# Fallo local, verificar Tailscale
if check_connectivity "$TAILSCALE_PC" "TAILSCALE"; then
echo "$(date): 🔄 Cambiando a ruta Tailscale" >> "$LOG"
# Aquí se podrían activar reglas de failover automático
else
echo "$(date): 🚨 FALLO CRÍTICO - Sin conectividad local ni Tailscale" >> "$LOG"
# Notificación de alerta
fi
fi
```
**Configurar en crontab**:
```bash
# Ejecutar cada 5 minutos
*/5 * * * * /usr/local/bin/monitor-local-network.sh
```
### Anexo D: Solución de Problemas Comunes
**Problema 1: No hay respuesta de ping entre dispositivos**
- **Causas posibles**: Firewall bloqueando ICMP, subred incorrecta, VLANs
- **Solución**:
```bash
# En srv-dasu
sudo iptables -I INPUT -s 192.168.1.0/24 -j ACCEPT
sudo iptables -I OUTPUT -d 192.168.1.0/24 -j ACCEPT
# En pc-dasu0 (PowerShell como Admin)
New-NetFirewallRule -DisplayName "Allow-Local-ICMP" `
-Direction Inbound -Protocol ICMPv4 -IcmpType 8 `
-RemoteAddress 192.168.1.0/24 -Action Allow
```
**Problema 2: DNS no resuelve dc-dasuten**
- **Causas posibles**: Ruta no configurada, firewall bloqueando puerto 53
- **Solución**:
```powershell
# Verificar que la ruta existe
Test-NetConnection 10.0.100.10 -Port 53
# Forzar registro DNS
ipconfig /flushdns
ipconfig /registerdns
# Probar con nslookup especificando servidor
nslookup dc-dasuten.dasuten.utnlr 10.0.100.10
```
**Problema 3: Conflictos de IP**
- **Causas posibles**: IP duplicada en la red
- **Solución**:
```bash
# En srv-dasu
sudo arping -c 3 -I vmbr0 192.168.1.205
# Si hay respuesta, cambiar IP
sudo ip addr del 192.168.1.205/24 dev vmbr0
sudo ip addr add 192.168.1.206/24 dev vmbr0
```
### Anexo E: Checklist de Implementación
**Pre-Implementación**:
- [ ] Backup de configuraciones de red actuales
- [ ] Documentar IPs actuales de todos los dispositivos
- [ ] Verificar que Tailscale está operativo en ambos nodos
- [ ] Comunicar ventana de mantenimiento a usuarios afectados
**Implementación**:
- [ ] Configurar IP local en srv-dasu (Paso 2)
- [ ] Configurar IP local en pc-dasu0 (Paso 3)
- [ ] Validar conectividad básica (Paso 4)
- [ ] Configurar DNS (Paso 5)
- [ ] Validar AD-Join (Paso 6)
- [ ] Configurar failover Tailscale (Paso 7)
- [ ] Instalar scripts de validación (Paso 8)
**Post-Implementación**:
- [ ] Ejecutar scripts de validación completos
- [ ] Documentar configuración final
- [ ] Probar failover manualmente
- [ ] Monitorear durante 24 horas
---
## 7. Referencias
1. **Documentación Relacionada**:
- [Plan P2601_6.1.B - AD-Join: pc-dasu0 al Dominio](../plan/P2601_6.1.B%20-%20AD-Join:%20pc-dasu0%20al%20Dominio.md)
- [Nodo srv-dasu](../../nodos/srv-dasu.md)
- [Nodo pc-dasu0](../../nodos/pc-dasu0.md)
- [ADN 03 - Seguridad y Red](../../adn/03_seguridad.md)
2. **Scripts de Soporte**:
- `docs/procedimientos/scripts/diagnostico_red_local.sh` - Diagnóstico Linux
- `docs/procedimientos/scripts/Diagnose-LocalNetwork.ps1` - Diagnóstico Windows
- Scripts de validación incluidos en este documento
3. **Herramientas Utilizadas**:
- Tailscale: VPN mesh para failover
- OpenSSH: Administración remota de srv-dasu
- PowerShell: Automatización en Windows
- iptables/nftables: Gestión de firewall en Linux
4. **Estándares de Red**:
- RFC 1918: Direccionamiento IP privado
- Best Practices para redes híbridas
- Protocolos AD DS: LDAP (389), Kerberos (88), DNS (53)
---
*Documento generado bajo el ADN del proyecto srv-ns8.*
*Última actualización: $(date +%Y-%m-%d)*
*Versión: 1.0*
@@ -0,0 +1,399 @@
# Script de diagnóstico de red local para pc-dasu0
# Uso: .\Diagnose-LocalNetwork.ps1 [-InterfaceAlias "Ethernet"] [-Detailed $true]
param(
[string]$InterfaceAlias = "Ethernet",
[bool]$Detailed = $true,
[string]$LogPath = "C:\Logs\NetworkDiagnostic"
)
# Configuración inicial
$ErrorActionPreference = "Stop"
$Timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$LogFile = Join-Path $LogPath "NetworkDiagnostic_$Timestamp.log"
$ReportFile = Join-Path $LogPath "NetworkReport_$Timestamp.md"
# Crear directorio de logs si no existe
if (-not (Test-Path $LogPath)) {
New-Item -ItemType Directory -Path $LogPath -Force | Out-Null
}
# Funciones de logging
function Write-Log {
param([string]$Message, [string]$Level = "INFO")
$FormattedMessage = "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] [$Level] $Message"
switch ($Level) {
"ERROR" { Write-Host $FormattedMessage -ForegroundColor Red }
"WARNING" { Write-Host $FormattedMessage -ForegroundColor Yellow }
"SUCCESS" { Write-Host $FormattedMessage -ForegroundColor Green }
default { Write-Host $FormattedMessage -ForegroundColor Cyan }
}
$FormattedMessage | Out-File -FilePath $LogFile -Append
}
function Write-Report {
param([string]$Content)
$Content | Out-File -FilePath $ReportFile -Append
}
function Test-Command {
param([string]$Command)
try {
Get-Command $Command -ErrorAction Stop | Out-Null
return $true
} catch {
return $false
}
}
# Inicio del diagnóstico
Write-Log "========================================"
Write-Log " DIAGNÓSTICO DE RED LOCAL - pc-dasu0"
Write-Log "========================================"
Write-Log ""
Write-Report "# Reporte de Diagnóstico de Red Local"
Write-Report "Fecha: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
Write-Report "Hostname: $env:COMPUTERNAME"
Write-Report "Usuario: $env:USERNAME"
Write-Report ""
# 1. Verificar adaptador de red
Write-Log "1. Verificando adaptador de red '$InterfaceAlias'..."
Write-Report "## 1. Información del Adaptador de Red"
try {
$NetworkAdapter = Get-NetAdapter -Name $InterfaceAlias -ErrorAction Stop
Write-Log " Adaptador encontrado: $($NetworkAdapter.Name)"
Write-Log " Estado: $($NetworkAdapter.Status)"
Write-Log " Velocidad: $($NetworkAdapter.LinkSpeed)"
Write-Report "### Adaptador: $($NetworkAdapter.Name)"
Write-Report "- Estado: $($NetworkAdapter.Status)"
Write-Report "- Velocidad: $($NetworkAdapter.LinkSpeed)"
Write-Report "- MAC Address: $($NetworkAdapter.MacAddress)"
Write-Report "- Interface Description: $($NetworkAdapter.InterfaceDescription)"
Write-Report ""
if ($NetworkAdapter.Status -ne "Up") {
Write-Log " ADVERTENCIA: El adaptador no está activo" -Level "WARNING"
}
} catch {
Write-Log " ERROR: No se encontró el adaptador '$InterfaceAlias'" -Level "ERROR"
Write-Log " Adaptadores disponibles:"
Get-NetAdapter | ForEach-Object {
Write-Log " - $($_.Name): $($_.Status)"
}
Write-Report "ERROR: Adaptador '$InterfaceAlias' no encontrado"
exit 1
}
# 2. Obtener configuración IP
Write-Log "2. Obteniendo configuración IP..."
Write-Report "## 2. Configuración IP"
try {
$IPConfiguration = Get-NetIPConfiguration -InterfaceAlias $InterfaceAlias -ErrorAction Stop
Write-Log " IPv4 Address: $($IPConfiguration.IPv4Address.IPAddress)"
Write-Log " Subnet Mask: $($IPConfiguration.IPv4Address.PrefixLength)"
Write-Log " Gateway: $($IPConfiguration.IPv4DefaultGateway.NextHop)"
Write-Report "### Configuración IPv4"
Write-Report "- Dirección IP: $($IPConfiguration.IPv4Address.IPAddress)"
Write-Report "- Prefijo: /$($IPConfiguration.IPv4Address.PrefixLength)"
Write-Report "- Gateway: $($IPConfiguration.IPv4DefaultGateway.NextHop)"
Write-Report ""
# Calcular subred
$IPAddress = $IPConfiguration.IPv4Address.IPAddress
$PrefixLength = $IPConfiguration.IPv4Address.PrefixLength
$SubnetMask = [System.Net.IPAddress]::Parse(([System.Net.IPAddress]::Parse("255.255.255.255").GetAddressBytes() |
ForEach-Object { $_ -shl (32 - $PrefixLength) }))
$NetworkAddress = [System.Net.IPAddress]::new((
[System.Net.IPAddress]::Parse($IPAddress).GetAddressBytes() |
ForEach-Object { $_ -band $SubnetMask.GetAddressBytes()[$i]; $i++ }))
$Subnet = "$NetworkAddress/$PrefixLength"
Write-Log " Subred calculada: $Subnet"
Write-Report "- Subred: $Subnet"
Write-Report ""
# Servidores DNS
Write-Log " Servidores DNS:"
$DNSServers = $IPConfiguration.DNSServer.ServerAddresses
if ($DNSServers) {
$i = 1
foreach ($DNS in $DNSServers) {
Write-Log " $i. $DNS"
Write-Report "- DNS $($i): $DNS"
$i++
}
} else {
Write-Log " ADVERTENCIA: No hay servidores DNS configurados" -Level "WARNING"
Write-Report "- DNS: No configurado"
}
Write-Report ""
} catch {
Write-Log " ERROR: No se pudo obtener configuración IP" -Level "ERROR"
Write-Report "ERROR: No se pudo obtener configuración IP"
}
# 3. Probar conectividad
Write-Log "3. Probando conectividad de red..."
Write-Report "## 3. Pruebas de Conectividad"
# Gateway
$Gateway = $IPConfiguration.IPv4DefaultGateway.NextHop
if ($Gateway) {
Write-Log " Probando gateway $Gateway..."
$GatewayTest = Test-NetConnection -ComputerName $Gateway -InformationLevel Quiet
if ($GatewayTest) {
Write-Log " ✅ Gateway alcanzable" -Level "SUCCESS"
Write-Report "- Gateway ($Gateway): ✅ Alcanzable"
} else {
Write-Log " ❌ Gateway no alcanzable" -Level "ERROR"
Write-Report "- Gateway ($Gateway): ❌ No alcanzable"
}
} else {
Write-Log " ADVERTENCIA: No hay gateway configurado" -Level "WARNING"
Write-Report "- Gateway: No configurado"
}
# Internet
Write-Log " Probando conectividad a internet (8.8.8.8)..."
$InternetTest = Test-NetConnection -ComputerName "8.8.8.8" -InformationLevel Quiet -WarningAction SilentlyContinue
if ($InternetTest) {
Write-Log " ✅ Internet alcanzable" -Level "SUCCESS"
Write-Report "- Internet (8.8.8.8): ✅ Alcanzable"
} else {
Write-Log " ❌ Internet no alcanzable" -Level "ERROR"
Write-Report "- Internet (8.8.8.8): ❌ No alcanzable"
}
# srv-dasu (si se conoce la IP)
Write-Log " Probando conectividad a srv-dasu (192.168.1.205)..."
$SrvDasuTest = Test-NetConnection -ComputerName "192.168.1.205" -InformationLevel Quiet -WarningAction SilentlyContinue
if ($SrvDasuTest) {
Write-Log " ✅ srv-dasu alcanzable localmente" -Level "SUCCESS"
Write-Report "- srv-dasu (192.168.1.205): ✅ Alcanzable"
} else {
Write-Log " ⚠️ srv-dasu no alcanzable localmente" -Level "WARNING"
Write-Report "- srv-dasu (192.168.1.205): ⚠️ No alcanzable"
}
Write-Report ""
# 4. Verificar DNS
Write-Log "4. Verificando resolución DNS..."
Write-Report "## 4. Pruebas de DNS"
# Resolución de dominio DASUTEN
Write-Log " Probando resolución de dc-dasuten.dasuten.utnlr..."
try {
$DNSResult = Resolve-DnsName -Name "dc-dasuten.dasuten.utnlr" -ErrorAction Stop
if ($DNSResult.IPAddress) {
Write-Log " ✅ DNS resuelve a: $($DNSResult.IPAddress)" -Level "SUCCESS"
Write-Report "- dc-dasuten.dasuten.utnlr: ✅ $($DNSResult.IPAddress)"
} else {
Write-Log " ❌ DNS no devolvió IP" -Level "ERROR"
Write-Report "- dc-dasuten.dasuten.utnlr: ❌ Sin respuesta"
}
} catch {
Write-Log " ❌ Error en resolución DNS: $_" -Level "ERROR"
Write-Report "- dc-dasuten.dasuten.utnlr: ❌ Error: $_"
}
# Resolución inversa
Write-Log " Probando resolución inversa de 10.0.100.10..."
try {
$ReverseDNS = Resolve-DnsName -Name "10.0.100.10" -Type PTR -ErrorAction Stop
if ($ReverseDNS.NameHost) {
Write-Log " ✅ Resolución inversa: $($ReverseDNS.NameHost)" -Level "SUCCESS"
Write-Report "- 10.0.100.10 (PTR): ✅ $($ReverseDNS.NameHost)"
}
} catch {
Write-Log " ⚠️ No se pudo resolver PTR para 10.0.100.10" -Level "WARNING"
Write-Report "- 10.0.100.10 (PTR): ⚠️ No resuelto"
}
Write-Report ""
# 5. Verificar puertos críticos del dominio
Write-Log "5. Verificando puertos críticos del dominio..."
Write-Report "## 5. Puertos del Dominio DASUTEN"
$DomainController = "10.0.100.10"
$CriticalPorts = @(
@{Port=389; Service="LDAP"},
@{Port=88; Service="Kerberos"},
@{Port=445; Service="SMB"},
@{Port=53; Service="DNS"},
@{Port=135; Service="RPC"},
@{Port=636; Service="LDAPS"}
)
foreach ($PortInfo in $CriticalPorts) {
Write-Log " Probando puerto $($PortInfo.Port) ($($PortInfo.Service))..."
$PortTest = Test-NetConnection -ComputerName $DomainController -Port $PortInfo.Port -WarningAction SilentlyContinue -ErrorAction SilentlyContinue
if ($PortTest.TcpTestSucceeded) {
Write-Log " ✅ Puerto $($PortInfo.Port) abierto" -Level "SUCCESS"
Write-Report "- $($PortInfo.Service) (puerto $($PortInfo.Port)): ✅ Abierto"
} else {
Write-Log " ❌ Puerto $($PortInfo.Port) cerrado" -Level "ERROR"
Write-Report "- $($PortInfo.Service) (puerto $($PortInfo.Port)): ❌ Cerrado"
}
}
Write-Report ""
# 6. Verificar Tailscale
Write-Log "6. Verificando estado de Tailscale..."
Write-Report "## 6. Estado de Tailscale"
if (Test-Command "tailscale") {
try {
$TailscaleStatus = & tailscale status --json 2>$null | ConvertFrom-Json
if ($TailscaleStatus.Self.TailscaleIPs) {
$TailscaleIP = $TailscaleStatus.Self.TailscaleIPs[0]
Write-Log " ✅ Tailscale conectado: $TailscaleIP" -Level "SUCCESS"
Write-Report "- Estado: ✅ Conectado"
Write-Report "- IP Tailscale: $TailscaleIP"
# Verificar peers
$PeerCount = ($TailscaleStatus.Peer | Measure-Object).Count
Write-Log " Peers conectados: $PeerCount"
Write-Report "- Peers conectados: $PeerCount"
} else {
Write-Log " ⚠️ Tailscale instalado pero no conectado" -Level "WARNING"
Write-Report "- Estado: ⚠️ Instalado pero no conectado"
}
} catch {
Write-Log " ⚠️ No se pudo obtener estado de Tailscale" -Level "WARNING"
Write-Report "- Estado: ⚠️ Error al obtener estado"
}
} else {
Write-Log " ⚠️ Tailscale no está instalado" -Level "WARNING"
Write-Report "- Estado: ⚠️ No instalado"
}
Write-Report ""
# 7. Escanear red local (opcional)
if ($Detailed) {
Write-Log "7. Escaneando dispositivos en red local..."
Write-Report "## 7. Dispositivos en Red Local"
try {
# Usar ARP para detectar dispositivos
$ARPTable = arp -a | Select-String "dynamic" | ForEach-Object {
$Line = $_.ToString().Trim()
$Parts = $Line -split '\s+'
[PSCustomObject]@{
IP = $Parts[0]
MAC = $Parts[1]
Type = $Parts[2]
}
}
$DeviceCount = ($ARPTable | Measure-Object).Count
Write-Log " Dispositivos detectados en ARP: $DeviceCount"
Write-Report "- Dispositivos en tabla ARP: $DeviceCount"
if ($DeviceCount -gt 0) {
Write-Report ""
Write-Report "### Primeros 10 dispositivos:"
$ARPTable | Select-Object -First 10 | ForEach-Object {
Write-Report "- $($_.IP) ($($_.MAC))"
}
}
} catch {
Write-Log " ⚠️ No se pudo escanear red local" -Level "WARNING"
Write-Report "- Escaneo: ⚠️ No disponible"
}
Write-Report ""
}
# 8. Generar recomendaciones
Write-Log "8. Generando recomendaciones..."
Write-Report "## 8. Recomendaciones para Comunicación Local"
$BaseNetwork = $IPAddress -replace '\.\d+$', ''
$RecommendedSrvIP = "$BaseNetwork.205"
$RecommendedPcIP = "$BaseNetwork.100"
Write-Report "### Configuración recomendada para comunicación local:"
Write-Report ""
Write-Report "1. **IPs estáticas locales:**"
Write-Report " - srv-dasu: $RecommendedSrvIP/24"
Write-Report " - pc-dasu0: $RecommendedPcIP/24"
Write-Report ""
Write-Report "2. **Configuración de red en pc-dasu0:**"
Write-Report " - IP: $RecommendedPcIP"
Write-Report " - Máscara: 255.255.255.0"
Write-Report " - Gateway: $Gateway"
Write-Report " - DNS Primario: 10.0.100.10 (dc-dasuten)"
Write-Report " - DNS Secundario: 8.8.8.8"
Write-Report " - Suffix DNS: dasuten.utnlr"
Write-Report ""
Write-Report "3. **Verificaciones previas:**"
Write-Report " - Confirmar que $RecommendedSrvIP y $RecommendedPcIP no están en uso"
Write-Report " - Verificar firewall (permitir ICMP, puertos 389, 88, 445, 53)"
Write-Report " - Mantener Tailscale activo como respaldo"
Write-Report ""
Write-Report "4. **Comandos de prueba post-configuración:**"
Write-Report " ```powershell"
Write-Report " # Probar conectividad local"
Write-Report " Test-NetConnection $RecommendedSrvIP"
Write-Report " "
Write-Report " # Probar resolución DNS"
Write-Report " Resolve-DnsName dc-dasuten.dasuten.utnlr"
Write-Report " "
Write-Report " # Probar puertos del dominio"
Write-Report " Test-NetConnection 10.0.100.10 -Port 389"
Write-Report " ```"
Write-Report ""
# 9. Resumen final
Write-Log "9. Generando resumen final..."
Write-Report "## 9. Resumen del Diagnóstico"
Write-Report "- **Adaptador de red**: $($NetworkAdapter.Name) ($($NetworkAdapter.Status))"
Write-Report "- **IP Configurada**: $IPAddress/$PrefixLength"
Write-Report "- **Gateway**: $(if($Gateway) {$Gateway} else {"No configurado"})"
Write-Report "- **Subred**: $Subnet"
Write-Report "- **DNS Servers**: $(if($DNSServers) {$DNSServers -join ', '} else {"No configurado"})"
Write-Report "- **Internet**: $(if($InternetTest) {"✅ Alcanzable"} else {"❌ No alcanzable"})"
Write-Report "- **srv-dasu local**: $(if($SrvDasuTest) {"✅ Alcanzable"} else {"⚠️ No alcanzable"})"
Write-Report "- **Tailscale**: $(if(Test-Command "tailscale") {"✅ Instalado"} else {"⚠️ No instalado"})"
Write-Report ""
Write-Report "### Archivos generados:"
Write-Report "- Log detallado: $LogFile"
Write-Report "- Reporte completo: $ReportFile"
Write-Report ""
Write-Report "### Próximos pasos:"
Write-Report "1. Si la conectividad local falla, verificar configuración del router ISP"
Write-Report "2. Configurar IPs estáticas según recomendaciones"
Write-Report "3. Probar AD-Join después de configurar comunicación local"
Write-Report "4. Mantener Tailscale como respaldo para acceso remoto"
Write-Report ""
Write-Report "*Diagnóstico completado: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')*"
@@ -0,0 +1,337 @@
#!/bin/bash
# Script de diagnóstico automático para red local
# Uso: ./diagnostico_red_local.sh [interface]
# Ejemplo: ./diagnostico_red_local.sh vmbr0
set -e
# Colores para output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Variables
INTERFACE="${1:-vmbr0}"
LOG_FILE="/tmp/diagnostico_red_local_$(date +%Y%m%d_%H%M%S).log"
REPORT_FILE="/tmp/reporte_red_local_$(date +%Y%m%d_%H%M%S).md"
# Funciones de utilidad
log() {
echo -e "${BLUE}[INFO]${NC} $1" | tee -a "$LOG_FILE"
}
success() {
echo -e "${GREEN}[SUCCESS]${NC} $1" | tee -a "$LOG_FILE"
}
warning() {
echo -e "${YELLOW}[WARNING]${NC} $1" | tee -a "$LOG_FILE"
}
error() {
echo -e "${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE"
}
check_dependencies() {
local deps=("ip" "ping" "awk" "grep" "tee")
local missing=()
for dep in "${deps[@]}"; do
if ! command -v "$dep" &> /dev/null; then
missing+=("$dep")
fi
done
if [ ${#missing[@]} -gt 0 ]; then
error "Dependencias faltantes: ${missing[*]}"
return 1
fi
# Dependencias opcionales
if command -v "nmap" &> /dev/null; then
HAS_NMAP=true
else
HAS_NMAP=false
warning "nmap no encontrado. El escaneo de red será limitado."
fi
if command -v "jq" &> /dev/null; then
HAS_JQ=true
else
HAS_JQ=false
fi
return 0
}
check_interface() {
if ! ip link show "$INTERFACE" &> /dev/null; then
error "Interfaz $INTERFACE no encontrada"
echo "Interfaces disponibles:"
ip link show | awk -F': ' '/^[0-9]+:/ {print $2}' | grep -v lo
return 1
fi
if [ "$(cat /sys/class/net/"$INTERFACE"/operstate 2>/dev/null)" != "up" ]; then
warning "Interfaz $INTERFACE no está activa (operstate: $(cat /sys/class/net/"$INTERFACE"/operstate 2>/dev/null))"
fi
return 0
}
get_interface_info() {
log "Obteniendo información de la interfaz $INTERFACE..."
echo "=== INFORMACIÓN DE INTERFAZ ===" | tee -a "$REPORT_FILE"
ip addr show "$INTERFACE" | tee -a "$REPORT_FILE"
echo "" | tee -a "$REPORT_FILE"
# Extraer IPs configuradas
IPS=$(ip addr show "$INTERFACE" | awk '/inet / {print $2}')
if [ -z "$IPS" ]; then
warning "No hay IPs configuradas en $INTERFACE"
else
success "IPs configuradas:"
echo "$IPS" | while read -r ip; do
echo " - $ip"
done | tee -a "$REPORT_FILE"
fi
}
get_routing_info() {
log "Obteniendo información de enrutamiento..."
echo "=== TABLA DE RUTAS ===" | tee -a "$REPORT_FILE"
ip route show | tee -a "$REPORT_FILE"
echo "" | tee -a "$REPORT_FILE"
# Detectar gateway predeterminado
DEFAULT_GW=$(ip route show default | awk '/default/ {print $3}')
if [ -n "$DEFAULT_GW" ]; then
success "Gateway predeterminado: $DEFAULT_GW"
echo "Gateway: $DEFAULT_GW" | tee -a "$REPORT_FILE"
# Probar conectividad al gateway
log "Probando conectividad al gateway $DEFAULT_GW..."
if ping -c 3 -W 2 "$DEFAULT_GW" &> /dev/null; then
success "Gateway alcanzable"
echo "Gateway alcanzable: Sí" | tee -a "$REPORT_FILE"
else
warning "Gateway no alcanzable"
echo "Gateway alcanzable: No" | tee -a "$REPORT_FILE"
fi
else
warning "No se encontró gateway predeterminado"
echo "Gateway: No encontrado" | tee -a "$REPORT_FILE"
fi
# Detectar subred local
LOCAL_SUBNET=$(ip route show | grep "link src" | awk '{print $1}' | head -1)
if [ -n "$LOCAL_SUBNET" ]; then
success "Subred local detectada: $LOCAL_SUBNET"
echo "Subred local: $LOCAL_SUBNET" | tee -a "$REPORT_FILE"
else
warning "No se pudo detectar subred local"
echo "Subred local: No detectada" | tee -a "$REPORT_FILE"
fi
}
check_dhcp_info() {
log "Verificando información DHCP..."
echo "=== INFORMACIÓN DHCP ===" | tee -a "$REPORT_FILE"
# Verificar archivos de lease DHCP
DHCP_FILES=("/var/lib/dhcp/dhclient.leases" "/var/lib/dhclient/dhclient.leases")
for file in "${DHCP_FILES[@]}"; do
if [ -f "$file" ]; then
log "Analizando $file..."
if grep -q "interface.*$INTERFACE" "$file" 2>/dev/null; then
success "Se encontraron leases DHCP para $INTERFACE"
echo "Archivo DHCP: $file" | tee -a "$REPORT_FILE"
# Extraer información relevante
grep -A10 -B2 "interface.*$INTERFACE" "$file" | \
grep -E "(lease|starts|ends|option|fixed-address)" | \
head -20 | tee -a "$REPORT_FILE"
break
fi
fi
done
if ! grep -q "Archivo DHCP:" "$REPORT_FILE"; then
warning "No se encontraron leases DHCP para $INTERFACE"
echo "DHCP: No se encontraron leases" | tee -a "$REPORT_FILE"
fi
echo "" | tee -a "$REPORT_FILE"
}
scan_local_network() {
if [ -z "$LOCAL_SUBNET" ]; then
warning "No se puede escanear red local (subred no detectada)"
return 1
fi
log "Escaneando red local $LOCAL_SUBNET..."
echo "=== ESCANEO DE RED LOCAL ===" | tee -a "$REPORT_FILE"
if [ "$HAS_NMAP" = true ]; then
# Escaneo rápido con nmap
log "Ejecutando escaneo rápido con nmap..."
nmap -sn "$LOCAL_SUBNET" 2>/dev/null | \
grep "Nmap scan report" | \
head -20 | \
tee -a "$REPORT_FILE"
# Contar dispositivos
DEVICE_COUNT=$(nmap -sn "$LOCAL_SUBNET" 2>/dev/null | grep "Nmap scan report" | wc -l)
success "Dispositivos detectados: $DEVICE_COUNT"
echo "Total dispositivos: $DEVICE_COUNT" | tee -a "$REPORT_FILE"
else
# Método alternativo usando ping y arp
warning "Usando método básico de detección (sin nmap)..."
# Ping a broadcast (puede no funcionar en todas las redes)
log "Probando detección básica..."
ping -c 2 -b "$(echo "$LOCAL_SUBNET" | cut -d'/' -f1 | sed 's/0$/255/')" &> /dev/null || true
# Mostrar tabla ARP
ip neigh show | grep -v "FAILED" | \
head -20 | \
tee -a "$REPORT_FILE"
DEVICE_COUNT=$(ip neigh show | grep -v "FAILED" | wc -l)
echo "Dispositivos en tabla ARP: $DEVICE_COUNT" | tee -a "$REPORT_FILE"
fi
echo "" | tee -a "$REPORT_FILE"
}
check_tailscale() {
log "Verificando estado de Tailscale..."
echo "=== ESTADO TAILSCALE ===" | tee -a "$REPORT_FILE"
if command -v tailscale &> /dev/null; then
tailscale status 2>/dev/null | tee -a "$REPORT_FILE"
# Extraer IP de Tailscale
TAILSCALE_IP=$(tailscale status --json 2>/dev/null | \
$([ "$HAS_JQ" = true ] && echo "jq -r '.Self.TailscaleIPs[0]'" || echo "grep -oE '100\.[0-9]+\.[0-9]+\.[0-9]+' | head -1"))
if [ -n "$TAILSCALE_IP" ]; then
success "Tailscale IP: $TAILSCALE_IP"
echo "IP Tailscale: $TAILSCALE_IP" | tee -a "$REPORT_FILE"
else
warning "Tailscale no parece estar conectado"
echo "Tailscale: No conectado" | tee -a "$REPORT_FILE"
fi
else
warning "Tailscale no está instalado"
echo "Tailscale: No instalado" | tee -a "$REPORT_FILE"
fi
echo "" | tee -a "$REPORT_FILE"
}
generate_recommendations() {
log "Generando recomendaciones..."
echo "=== RECOMENDACIONES ===" | tee -a "$REPORT_FILE"
# Recomendación de IP local
if [ -n "$LOCAL_SUBNET" ]; then
BASE_NET=$(echo "$LOCAL_SUBNET" | cut -d'/' -f1 | cut -d'.' -f1-3)
echo "Para configuración local entre srv-dasu y pc-dasu0:" | tee -a "$REPORT_FILE"
echo "" | tee -a "$REPORT_FILE"
echo "1. **IPs recomendadas (fuera de rango DHCP común):**" | tee -a "$REPORT_FILE"
echo " - srv-dasu: ${BASE_NET}.205/24" | tee -a "$REPORT_FILE"
echo " - pc-dasu0: ${BASE_NET}.100/24" | tee -a "$REPORT_FILE"
echo "" | tee -a "$REPORT_FILE"
echo "2. **Configuración de red:**" | tee -a "$REPORT_FILE"
echo " - Gateway: $(echo "$DEFAULT_GW" || echo "[gateway-del-router]")" | tee -a "$REPORT_FILE"
echo " - Máscara: 255.255.255.0 (/24)" | tee -a "$REPORT_FILE"
echo " - DNS Primario: 10.0.100.10 (dc-dasuten)" | tee -a "$REPORT_FILE"
echo " - DNS Secundario: 8.8.8.8" | tee -a "$REPORT_FILE"
echo "" | tee -a "$REPORT_FILE"
echo "3. **Verificaciones previas:**" | tee -a "$REPORT_FILE"
echo " - Confirmar que ${BASE_NET}.205 y ${BASE_NET}.100 no están en uso" | tee -a "$REPORT_FILE"
echo " - Verificar que el firewall permite ICMP y puertos necesarios" | tee -a "$REPORT_FILE"
echo " - Tailscale debe permanecer activo como respaldo" | tee -a "$REPORT_FILE"
else
echo "No se pudo generar recomendaciones específicas (subred no detectada)" | tee -a "$REPORT_FILE"
echo "" | tee -a "$REPORT_FILE"
echo "Recomendaciones generales:" | tee -a "$REPORT_FILE"
echo "1. Identificar manualmente la subred del router ISP" | tee -a "$REPORT_FILE"
echo "2. Usar IPs estáticas fuera del rango DHCP del router" | tee -a "$REPORT_FILE"
echo "3. Mantener Tailscale como ruta de failover" | tee -a "$REPORT_FILE"
fi
echo "" | tee -a "$REPORT_FILE"
}
generate_summary() {
echo "=== RESUMEN DEL DIAGNÓSTICO ===" | tee -a "$REPORT_FILE"
echo "Fecha: $(date)" | tee -a "$REPORT_FILE"
echo "Hostname: $(hostname)" | tee -a "$REPORT_FILE"
echo "Interfaz analizada: $INTERFACE" | tee -a "$REPORT_FILE"
echo "Estado interfaz: $(cat /sys/class/net/"$INTERFACE"/operstate 2>/dev/null || echo "desconocido")" | tee -a "$REPORT_FILE"
echo "Gateway: ${DEFAULT_GW:-No detectado}" | tee -a "$REPORT_FILE"
echo "Subred local: ${LOCAL_SUBNET:-No detectada}" | tee -a "$REPORT_FILE"
echo "Tailscale: $(if command -v tailscale &> /dev/null; then echo "Instalado"; else echo "No instalado"; fi)" | tee -a "$REPORT_FILE"
echo "" | tee -a "$REPORT_FILE"
echo "Archivos generados:" | tee -a "$REPORT_FILE"
echo " - Log detallado: $LOG_FILE" | tee -a "$REPORT_FILE"
echo " - Reporte completo: $REPORT_FILE" | tee -a "$REPORT_FILE"
}
main() {
echo "========================================="
echo " DIAGNÓSTICO DE RED LOCAL - srv-dasu"
echo "========================================="
echo ""
# Inicializar archivos
> "$LOG_FILE"
> "$REPORT_FILE"
# Verificar dependencias
if ! check_dependencies; then
exit 1
fi
# Verificar interfaz
if ! check_interface; then
exit 1
fi
# Ejecutar diagnósticos
get_interface_info
get_routing_info
check_dhcp_info
scan_local_network
check_tailscale
generate_recommendations
generate_summary
echo ""
success "Diagnóstico completado exitosamente"
echo ""
echo "Para configurar IP local en $INTERFACE, edite:"
echo " sudo nano /etc/network/interfaces"
echo ""
echo "Agregue las líneas (adaptando la IP según recomendaciones):"
echo " post-up ip addr add 192.168.1.205/24 dev $INTERFACE"
echo " pre-down ip addr del 192.168.1.205/24 dev $INTERFACE"
echo ""
echo "Luego reinicie el servicio de red:"
echo " sudo systemctl restart networking"
}
# Ejecutar script principal
main "$@"
@@ -0,0 +1,80 @@
# Plan: Integración Dashboard P2601 en dtic-BITACORAs
> Fecha: 04/03/2026 19:59hs
> Proyecto: P2601 - DASUTEN
> Estado: **Aprobado — En ejecución**
## Objetivo
Integrar el panel de seguimiento del proyecto P2601 dentro del sistema dtic-BITACORAs:
- Accesible en `ns8.frlr.utn.edu.ar/bitacoras/p2601`
- Datos almacenados en PostgreSQL (mismo stack que dtic-BITACORAs)
- Discriminación de horas presenciales vs remotas por hito
- Identificación de fases del proyecto
## Esquema de Datos
### Nuevas tablas (esquema `bitacoras`)
```sql
CREATE TABLE bitacoras.proyectos (
id SERIAL PRIMARY KEY,
codigo VARCHAR(10) NOT NULL UNIQUE, -- 'P2601'
nombre VARCHAR(200) NOT NULL,
estado VARCHAR(10) DEFAULT '',
descripcion TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE bitacoras.fases (
id SERIAL PRIMARY KEY,
proyecto_id INTEGER REFERENCES bitacoras.proyectos(id),
numero INTEGER NOT NULL,
nombre VARCHAR(200) NOT NULL,
estado VARCHAR(10) DEFAULT '',
descripcion TEXT,
fecha_inicio DATE,
fecha_fin DATE,
UNIQUE(proyecto_id, numero)
);
CREATE TABLE bitacoras.hitos (
id SERIAL PRIMARY KEY,
fase_id INTEGER REFERENCES bitacoras.fases(id),
id_hito VARCHAR(20) NOT NULL, -- 'A00', 'SSH', 'P02.DB'
titulo VARCHAR(200) NOT NULL,
descripcion TEXT,
estado VARCHAR(10) DEFAULT '',
fecha DATE,
horas_presencial NUMERIC(5,2) DEFAULT 0,
horas_remoto NUMERIC(5,2) DEFAULT 0,
nodo_id INTEGER REFERENCES bitacoras.nodos(id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```
## Fases del proyecto P2601
| # | Fase | Descripción | Nodos | Estado |
|:--|:--|:--|:--|:--|
| 1 | Infraestructura Física | Preparación hardware srv-dasu + Proxmox VE 9.1 | srv-dasu | ✅ |
| 2 | Dominio y Directorio Activo | VM dc-dasuten, AD DS, DNS, dominio `dasuten.utnlr` | dc-dasuten | ✅ |
| 3 | Motor de Base de Datos | VM sql-dasuten, SQL Server 2019, backup y restore sysdasuten | sql-dasuten | ✅ |
| 4 | Acceso Remoto y Seguridad | OpenSSH 7022, w-zombi, firewall en todas las VMs | dc/sql/pcv-dasuten | ✅ |
| 5 | Sistema DASUTEN | Ingeniería inversa, deploy DasutenSQL.exe, Kermet.ini | pcv-dasu0, sql-dasuten | ⏳ |
| 6 | Cliente Final | Config pc-dasu0 en dominio, sistema productivo | pc-dasu0 | 📋 |
## Archivos a modificar/crear
| Acción | Archivo | Descripción |
|:--|:--|:--|
| MODIFY | `docker/init.sql` | Tablas proyectos, fases, hitos + datos semilla P2601 |
| NEW | `backend/src/routes/proyectos.js` | API CRUD /api/proyectos con métricas |
| MODIFY | `backend/src/server.js` | Registrar ruta proyectos |
| NEW | `frontend/src/pages/ProyectoDashboard.tsx` | Página React con timeline, fases y métricas |
| MODIFY | `frontend/src/App.tsx` | Ruta /bitacoras/p2601 |
## Verificación
- `curl` al endpoint `/api/proyectos/P2601`
- Navegador a `https://ns8.frlr.utn.edu.ar/bitacoras/p2601`
@@ -0,0 +1,21 @@
# Plan de Pruebas: Sistema Dasuten (P02)
**Fecha/Hora:** 260305-0930
**Objetivo:** Confirmar el correcto funcionamiento y conectividad del entorno Dasuten.
## Fases de la Prueba
1. **Uso de Herramienta de Credenciales Secretas:**
- Modificar la herramienta `tools/ns8-candados` para que incluya instrucciones y explicación de uso en la cabecera.
- Ejecutar la herramienta `tools/ns8-candados` de manera que capture las credenciales requeridas en el entorno (o en una variable segura) sin exponer la salida (stdout) al flujo visible del chat, preservando la confidencialidad.
2. **Verificación de Acceso Base:**
- Confirmar acceso SSH al servidor principal `pcv-dasu0` utilizando las credenciales obtenidas.
- Validar estado general del sistema (uptime, uso de recursos básicos).
3. **Pruebas Funcionales Adicionales (A definir iterativamente):**
- *Se irán agregando puntos de prueba según el progreso y los resultados del acceso base.*
## Protocolo de Registro (Bitácora First)
- Todo paso debe ser previamente registrado en la bitácora del día correspondiente como iniciado.
- Tras concluir cada paso o iteración, actualizar el registro en la bitácora con los resultados (éxito/fallo y observaciones).
@@ -0,0 +1,22 @@
# Plan de Implementación: Contexto en el ADN (Caso DASUTEN)
**Fecha**: 2026-03-05
**Objetivo**: Dotar a la IA y al proyecto de un protocolo estandarizado de "Auto-contextualización" sobre topología de redes, evitando operaciones ciegas (ej. Timeouts SSH en subredes NAT).
## Propuesta de Cambios
### Hebra: Directivas de la Inteligencia Artificial (`adn/05_ia.md`)
Agregaremos una nueva directiva `13. Auto-Contextualización`.
- **Regla:** Antes de iniciar pruebas de red o conexiones a nodos, la IA DEBE consultar el archivo del nodo (en la carpeta `nodos/`) y el proyecto asociado para comprender su ubicación en la topología (ej. si está detrás de un NAT, si requiere salto SSH, qué IP tiene).
- **Ejecución:** Evitar asunciones de visibilidad directa; mapear el contexto siempre.
### Hebra: Proyectos (`adn/07_proyectos.md`)
Añadir la sección obligatoria de "Topología / Contexto de Red" a los manifiestos de proyecto.
- **Topología Lógica / Contexto de Red:** Exige describir la ubicación de los nodos (ej: Red aislada, host físico, dependencias de NAT) para documentar y estandarizar el **CÓMO** llegar a ellos de forma técnica.
### Manifiesto: P2601 Dasuten (`proyectos/P2601_dasuten.md`)
Actualizar el manifiesto actual del proyecto Dasuten para reflejar esta nueva arquitectura de topología explícita.
- Añadir una sección `## 🌐 Topología y Contexto de Red` explicando que:
1. Todo el ecosistema Dasuten corre bajo `srv-dasu` (Standalone - 10.0.10.205).
2. Las VMs periféricas (pcv-dasu0, dc-dasuten, sql-dasuten) están en una subred aislada `10.0.100.0/24`.
3. **Regla de Acceso:** Para la intervención, es necesario un ruteo o ProxyJump a través de `10.0.10.205`.
@@ -0,0 +1,29 @@
# Plan de Acción: Despliegue y Prueba de DASUTEN en pcv-dasu0
**Fecha/Hora:** 260305-1017
**Artefacto:** `tools/w-zombi/payloads/runSysDasuten.zip`
**Objetivo:** Desplegar los binarios del sistema DASUTEN en la VM de pruebas (cliente Windows 10 LTSC) y verificar su conexión contra el motor SQL recién instalado en la fase DB01.
## Fases del Despliegue
### 1. Preparación y Transferencia
1. Obtener credenciales de `admindasu` desde `ns8-candados`.
2. Establecer túnel o salto SSH válido hacia la red `10.0.100.x` a través de `srv-dasu` (10.0.10.205).
3. Transferir el archivo `tools/w-zombi/payloads/runSysDasuten.zip` vía SCP al disco virtual de `pcv-dasu0` apuntando al **puerto SSH 7022** (ej. en `C:\Dasuten\`).
### 2. Descompresión y Configuración
1. Descomprimir el archivo ZIP en el cliente usando PowerShell remoting / SSH.
2. Localizar y editar el archivo de configuración `Kermet.ini`:
- Modificar la cadena de conexión de ODBC para `[SQLSERVER]`.
- Reemplazar `SERVER=srvFENIX` por el endpoint actual: `SERVER=sql-dasuten` (o la IP `10.0.100.11`).
### 3. Instalación de Dependencias
1. Instalar de forma automatizada las fuentes tipográficas necesarias de la carpeta `Fonts/` (códigos de barras Code39, I2OF5).
2. Ejecutar silenciosa/desatendidamente `InstaladorComponentesKermet.exe` para registrar librerías VFP/Kermet requeridas por el sistema en el cliente.
### 4. Pruebas de Verificación
1. **Red**: Realizar un `Test-NetConnection` o ping desde `pcv-dasu0` hacia el puerto TCP 1433 de `sql-dasuten` para garantizar que el firewall permite el tráfico SQL.
2. **Sistema GUI**: Invocar la apertura de `DasutenSQL.exe` (puede requerir verificación visual del usuario vía consola NoVNC de Proxmox o Anydesk/RDP). Se debe comprobar que se abre la pantalla de logon sin errores de conexión a la base de datos `sysdasuten`.
## Registro y Trazabilidad
- Todo evento de éxito/falla de red se registrará en la bitácora bajo la etiqueta `[P02]`.
@@ -0,0 +1,31 @@
# Fase 6 - DASUTEN: Despliegue Físico, Dominio y Seguridad Local
**Fecha:** 05/03/2026 - P2601 Proyecto Dasuten
## Objetivo
Habiendo superado la prueba de concepto (UAT) y verificado el funcionamiento del sistema en la red aislada `10.0.100.x` virtualizada bajo Proxmox, la siguiente fase consiste en **entregar la infraestructura a producción**.
Esto implica el traslado físico del equipamiento, la adhesión de las estaciones de trabajo de los usuarios al nuevo Active Directory (`dc-dasuten`), y la configuración de las políticas organizativas.
## Tareas a Realizar
### 1. Instalación Física (Hardware)
* **Servidor Proxmox (`srv-dasu`)**: Trasladar el host físico a la oficina de DASUTeN e instalarlo en su locación definitiva (gabinete/rack), asegurando energía ininterrumpida (UPS) y conectividad de red adecuada.
* **Estaciones de Trabajo (`pc-dasu0` y otras si las hay)**: Reconectar y asegurar la conectividad L2 (física/switch) hacia el puerto de red que ofrece acceso al servidor `srv-dasu`.
### 2. Integración de Red y Dominio
* **Configuración de Red Local**: Asegurar que la PC física tenga acceso a la red donde opera el controlador de dominio. Confirmar que su **DNS Primario** sea la IP de `dc-dasuten` (`10.0.100.10`).
* **Unión al Dominio**: Ingresar la PC `pc-dasu0` al dominio `dasuten.utnlr`.
* **Reasignación de Usuarios**: Mapear/Migrar los perfiles y datos del usuario local que usaba Andrea Almirón hacia su nueva cuenta de dominio.
### 3. Seguridad y GPOs (Group Policy Objects)
* **Restricción de Acceso**: Configurar GPOs para prohibir la instalación de software no autorizado por los usuarios estándar de DASUTeN.
* **Unidades de Red**: Despliegue automático de mapeos de red necesarios (si la aplicación requiere una letra de unidad específica o para almacenamiento compartido) mediante GPO.
* **Restricciones de Explorador**: Ocultar u restringir acceso a la unidad C:\ y recursos sensibles.
* **Políticas de Contraseñas**: Asegurar rotación o longitud mínima para los usuarios del dominio.
### 4. Revisión Final de Aplicaciones
* Distribuir el conjunto de accesos directos del sistema Dasuten (`DasutenSQL.exe`) al escritorio de todos los usuarios mediante GPO.
* Verificar que la conexión ODBC/Kermet funciona bajo el contexto de seguridad del usuario de dominio sin requerir elevación de privilegios.
## Siguientes Pasos (IA)
Una vez que el usuario informe que el equipo fue trasladado y encendido en su nueva locación, la IA asistirá vía Tailscale / AnyDesk (o conexiones remotas establecidas) con la configuración de las Políticas de Seguridad (GPOs) y las uniones de dominio.
@@ -0,0 +1,106 @@
# Plan de Migración: Solo Bitácoras Web (P2605)
## 🎯 Objetivo
Dejar de editar manualmente archivos `.md`. La Web y los comandos ADN escriben en la DB; el `.md` se genera automáticamente como respaldo.
---
## 📋 Secuencia Lógica de Hitos
> La numeración refleja dependencias reales: cada hito requiere los anteriores.
### Etapa A — La Web puede reemplazar al MD (Entrada de datos)
Sin esto, seguís dependiendo del archivo para registrar actividad.
| # | Hito | Qué resuelve | Estado |
| :---: | :--- | :--- | :---: |
| A1 | **Esquema DB: gestion + resumen_nodos** | Tablas para Pendientes, En Proceso y Resumen Integral | ✅ |
| A2 | **API: endpoints gestion y resumen** | Backend para crear/editar/eliminar desde la Web | ✅ |
| A3 | **UI: Panel de Gestión** | Editar Pendientes y En Proceso desde el Dashboard | ✅ |
| A4 | **UI: Editor Resumen Integral** | Editar resumen por nodo desde el Dashboard | ✅ |
| A5 | **UI: Formulario de Entradas** | Crear entradas I-F-D-E desde la Web | ✅ |
| A6 | **Rollover automático** | Pendientes activos pasan al día siguiente (vía `activo=true`) | ✅ |
**Resultado:** La Web ya puede hacer todo lo que antes se hacía a mano en el MD.
---
### Etapa B — Los comandos ADN hablan con la DB (Automatización)
Sin esto, `adn inicio` y `adn cierre` siguen escribiendo en el `.md`.
| # | Hito | Qué resuelve | Estado |
| :---: | :--- | :--- | :---: |
| B1 | **Refactor inicio.rb → DB-First** | `adn inicio de jornada` escribe en DB, no en MD | ✅ |
| B2 | **Refactor cierre.rb → DB-First** | `adn cierre de jornada` escribe en DB, no en MD | ✅ |
| B3 | **Refactor backup → DB** | Registrar eventos de backup como entradas en DB | ✅ |
**Resultado:** Los comandos del enjambre ya no tocan archivos `.md`.
---
### Etapa C — El MD se genera desde la DB (Respaldo)
Recién ahora tiene sentido generar el MD, porque la DB **ya es** la fuente de verdad.
| # | Hito | Qué resuelve | Estado |
| :---: | :--- | :--- | :---: |
| C1 | **Exportador Ruby: md_exporter.rb** | `adn db exportar:md FECHA` genera el MD desde la DB | ✅ |
| C2 | **Exportación masiva** | `adn db exportar:md --todo` regenera todos los MDs | ✅ |
**Resultado:** Los archivos `.md` son un producto generado, no una fuente de entrada.
---
### Etapa D — Verificación y cierre (Confianza)
Asegurar que no se perdió nada en la transición.
| # | Hito | Qué resuelve | Estado |
| :---: | :--- | :--- | :---: |
| D1 | **Migración histórica** | `adn db migrar:md --todo` importa todo el historial | ✅ |
| D2 | **Verificación de cobertura** | `adn db verificar` audita MD ↔ DB | ✅ |
| D3 | **Desactivar edición manual de MDs** | Header `<!-- GENERADO AUTOMÁTICAMENTE -->` en MDs exportados | ✅ |
---
## 🐝 Herramientas del Enjambre ADN
```
# Flujo diario (DB-First)
./adn/tools/run inicio de jornada presencial|remoto
./adn/tools/run cierre de jornada presencial|remoto
# Importar MD existente → DB
./adn/tools/run db migrar:md FECHA [--sobrescribir]
./adn/tools/run db migrar:md --todo [--sobrescribir]
# Exportar DB → MD (respaldo)
./adn/tools/run db exportar:md FECHA
./adn/tools/run db exportar:md --todo
# Auditoría
./adn/tools/run db verificar
./adn/tools/run db estadisticas
```
---
## 📊 Estado Actual — COMPLETO ✅
**Ya no es necesario editar MDs manualmente.** Todas las etapas están completas.
| Etapa | Progreso |
| :--- | :---: |
| A — Web reemplaza al MD | 6/6 ✅ |
| B — ADN habla con DB | 3/3 ✅ |
| C — MD se genera desde DB | 2/2 ✅ |
| D — Verificación y cierre | 3/3 ✅ |
### Notas
- Cobertura de migración: 88.9% (16/18 bitácoras con entradas). Las 2 restantes son días sin actividades detalladas.
- Selector "Modo de Jornada" en la UI queda como mejora futura (no bloquea el flujo actual).
---
<!-- 🤖 PREMISAS DE TRABAJO (IA): Fuente de Verdad → adn/05_ia.md -->