docs: Actualización de contexto IA, Drones, Candados y nuevos nodos
This commit is contained in:
@@ -1,16 +1,15 @@
|
||||
{
|
||||
"rsa": "eyJpdiI6IkpoTmpZRWZMeE12eWhXSVMiLCJ0YWciOiJUUnJGY05qRlVoOTl2REplTms0ZU5RPT0iLCJkYXRhIjoiNkFIWCsyYm9ndzZKbGM3c0hRPT0ifQ==",
|
||||
"domain_admin": "eyJpdiI6IjlYV0JRSVA1MzNLblFycUEiLCJ0YWciOiJ3aHI4a1cwanpLWU5OUmVHby9pd3ZRPT0iLCJkYXRhIjoiT1B3VGNHNkpUSWI0SnJ5NW5adHNVZz09In0=",
|
||||
"admindasu": "eyJpdiI6IjlYV0JRSVA1MzNLblFycUEiLCJ0YWciOiJ3aHI4a1cwanpLWU5OUmVHby9pd3ZRPT0iLCJkYXRhIjoiT1B3VGNHNkpUSWI0SnJ5NW5adHNVZz09In0=",
|
||||
"win_domain": "eyJpdiI6IitrY2V0eEQxektEMFk2cGgiLCJ0YWciOiJtQmdIYU90ZTFFWUlGZElsMXBIOTJBPT0iLCJkYXRhIjoiNVVFanhaN2tIdHViZDRDRnlnPT0ifQ==",
|
||||
"deepseek_key": "eyJpdiI6InRCc2IvL3J4SGNsejgvL04iLCJ0YWciOiJBbjRlMTNXNlZzUGhNSWowSUU1a01nPT0iLCJkYXRhIjoiQXR4azk1aU9sb2QydzNwa0FMTHpjVkJKQTIvZU4wY1U2V2crK3NBbVd1UnRmUGh6In0=",
|
||||
"srv-dasu:rmonla": "eyJpdiI6IkFoczJZWUIwQ2piZzdJNzgiLCJ0YWciOiJJdDhCZndRc1RoZFhVMVQ4OEhaN1J3PT0iLCJkYXRhIjoiaUQycmxndGFsUkE2dVRaTEgrTT0ifQ==",
|
||||
"dominio-utnlarioja.intranet:monlaricardo": "eyJpdiI6InFRQ3VNYWlVSHlKMURNdzciLCJ0YWciOiJ1a3hESUZlaEt6enV3UXZxWUFrWXhRPT0iLCJkYXRhIjoiNVNaekdxVyt6dE5WQmhPaXF3PT0ifQ==",
|
||||
"ns8:rmonla:sudo": "eyJpdiI6IkRqbTBhVlZSYUpVdno3TFkiLCJ0YWciOiJJSnE2ei8zZHIrV2hFQks1VjNpVGN3PT0iLCJkYXRhIjoiT3JxTjRhTmlQK2R3TlJZZk9BPT0ifQ==",
|
||||
"srv-dasu:root": "eyJpdiI6Ik5LY09JR1FvaFVPRXd3bjUiLCJ0YWciOiJwb0NLZ3A4N3VOQUZMQWhMS1hUVWZnPT0iLCJkYXRhIjoicEgvWTFmd0RhSFQ0YWRHRUxyZ1lBQT09In0=",
|
||||
"srv-ns8:rmonla:sudo": "eyJpdiI6InJDOUp2STdYR1crb2NkMTgiLCJ0YWciOiJHSUZVbXM5VnVmZ3d1NXQ3SFVwOUdRPT0iLCJkYXRhIjoib1ZVTzdaLzk1WGdwZDhKREM3aWpnV3c9In0=",
|
||||
"dasu-sql2:Administrator": "eyJpdiI6IlJ0VWJzblVGM2NhWEtvNlgiLCJ0YWciOiJtM0llb2luUmRUZng0NnBsL1JtKzl3PT0iLCJkYXRhIjoiSHVhaklOTFlsSm1PbDJkTjNoYlF0UT09In0=",
|
||||
"srvv-nginx-rm:root": "eyJpdiI6Ijc0STVkTk92Tk5LZEZZWWciLCJ0YWciOiJlOTAwcGpTa21PZFVhN2FJTzIxU1VBPT0iLCJkYXRhIjoiYUJRR3hRakFub1F1OCtyTVdWcW8ifQ==",
|
||||
"srv-ns8:root": "eyJpdiI6Ik9UbHBieDJhU0JZbFFEM0wiLCJ0YWciOiJZeWJ5K0lUUFdOcEQwTmJ5UzhwcVFRPT0iLCJkYXRhIjoiNUE1QW5yenZ0QldjNEdVNXVBPT0ifQ==",
|
||||
"srv-ns8:rsa:passphrase": "eyJpdiI6ImpwRnFPYTZJdEtnUXdxT00iLCJ0YWciOiJMcXRBQ0lOT0paUE50Qjgva3lHSElRPT0iLCJkYXRhIjoiRDUzOFNuY3V1RUZXVHV4U3NnPT0ifQ==",
|
||||
"srv-ns8:rmonla:sudo": "eyJpdiI6Ilg2VzhIMWN6Y2xYVzR3ZzgiLCJ0YWciOiJuWDk0SDcraVhJQTQxd3I3bEpLRFBnPT0iLCJkYXRhIjoiSllyRy9ScXFBRFBFc0l5emRRPT0ifQ==",
|
||||
"srv-ns8:rmonla:rsa": "eyJpdiI6IkpoTmpZRWZMeE12eWhXSVMiLCJ0YWciOiJUUnJGY05qRlVoOTl2REplTms0ZU5RPT0iLCJkYXRhIjoiNkFIWCsyYm9ndzZKbGM3c0hRPT0ifQ==",
|
||||
"srvv-dns:rmonla:sudo": "eyJpdiI6InkyY01pSHlNeGFzZ2lDcnkiLCJ0YWciOiJqYTdkaFM0bTBNMWZhTWxJRUtGTWtBPT0iLCJkYXRhIjoiWmVYbzRWRjNNS244UktuaFVnPT0ifQ==",
|
||||
"srvv-DNS:rmonla:sudo": "eyJpdiI6InkyY01pSHlNeGFzZ2lDcnkiLCJ0YWciOiJqYTdkaFM0bTBNMWZhTWxJRUtGTWtBPT0iLCJkYXRhIjoiWmVYbzRWRjNNS244UktuaFVnPT0ifQ=="
|
||||
"srv-dasu:rmonla": "eyJpdiI6IkFoczJZWUIwQ2piZzdJNzgiLCJ0YWciOiJJdDhCZndRc1RoZFhVMVQ4OEhaN1J3PT0iLCJkYXRhIjoiaUQycmxndGFsUkE2dVRaTEgrTT0ifQ==",
|
||||
"srv-dasu:root": "eyJpdiI6Ik5LY09JR1FvaFVPRXd3bjUiLCJ0YWciOiJwb0NLZ3A4N3VOQUZMQWhMS1hUVWZnPT0iLCJkYXRhIjoicEgvWTFmd0RhSFQ0YWRHRUxyZ1lBQT09In0=",
|
||||
"dasu-dc:admindasu": "eyJpdiI6IjlYV0JRSVA1MzNLblFycUEiLCJ0YWciOiJ3aHI4a1cwanpLWU5OUmVHby9pd3ZRPT0iLCJkYXRhIjoiT1B3VGNHNkpUSWI0SnJ5NW5adHNVZz09In0=",
|
||||
"srvv-nginx-rm:root": "eyJpdiI6Ijc0STVkTk92Tk5LZEZZWWciLCJ0YWciOiJlOTAwcGpTa21PZFVhN2FJTzIxU1VBPT0iLCJkYXRhIjoiYUJRR3hRakFub1F1OCtyTVdWcW8ifQ==",
|
||||
"dominio-utnlarioja.intranet:monlaricardo": "eyJpdiI6InFRQ3VNYWlVSHlKMURNdzciLCJ0YWciOiJ1a3hESUZlaEt6enV3UXZxWUFrWXhRPT0iLCJkYXRhIjoiNVNaekdxVyt6dE5WQmhPaXF3PT0ifQ==",
|
||||
"deepseek_key": "eyJpdiI6InRCc2IvL3J4SGNsejgvL04iLCJ0YWciOiJBbjRlMTNXNlZzUGhNSWowSUU1a01nPT0iLCJkYXRhIjoiQXR4azk1aU9sb2QydzNwa0FMTHpjVkJKQTIvZU4wY1U2V2crK3NBbVd1UnRmUGh6In0=",
|
||||
"srv-ns8:rmonla": "eyJpdiI6IlJ4cHZkbFpJQWVsTTl3T1YiLCJ0YWciOiJ3WTZGMDEvNHNsZm1wZm5jMEF4Q1lBPT0iLCJkYXRhIjoibFljUHRYTm44bUxvQjhCa2tBPT0ifQ==",
|
||||
"dasu-sql3:Administrador": "eyJpdiI6IlRiMDh3U1N2aVhTcDYzbzciLCJ0YWciOiI5NVNyUDFlMXlrM0dGRlNqUXhLbEt3PT0iLCJkYXRhIjoiVkp2V09zSjczcS96UktTSlZTOFdBdz09In0="
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
1775576757
|
||||
1775682230
|
||||
@@ -1,31 +1,43 @@
|
||||
# 🔐 candados: Gestión Segura de Secretos
|
||||
# 🔐 candados: Acceso Seguro a Contraseñas (Sin Exponer)
|
||||
|
||||
## Resumen Ejecutivo
|
||||
`candados` es la herramienta central de seguridad del ecosistema ADN. Cifra y gestiona secretos con **AES-256-GCM**, implementando un sistema de **Candado Temporal** que requiere autorización explícita (5 min) antes de liberar credenciales.
|
||||
`candados` es la herramienta central de seguridad del ecosistema ADN. Cifra y gestiona secretos con **AES-256-GCM**, implementando un sistema de **Candado Temporal** que requiere autorización explícita (30 min) antes de liberar credenciales.
|
||||
|
||||
**Principio fundamental:** Las contraseñas **NUNCA** deben aparecer en pantalla, logs ni archivos temporales.
|
||||
**Principio fundamental:** Las contraseñas **NUNCA** deben aparecer en pantalla, logs ni archivos temporales visibles.
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Arquitectura
|
||||
- **Cripto**: Motor AES-256-GCM con IV aleatorio y autenticación.
|
||||
- **Bóveda**: Persistencia cifrada en `.boveda.json` (permisos 600).
|
||||
- **MFA Temporal**: Sesiones de 5 min via `.session` (permisos 600).
|
||||
- **Master Key**: `.master.key` local (permisos 600, nunca se comparte).
|
||||
## ⚠️ REGLA DE ORO — NUNCA MOSTRAR CONTRASEÑAS
|
||||
|
||||
## 🚀 Comando Primario: `run` ★
|
||||
Esta herramienta está diseñada para que las contraseñas **NUNCA** se muestren en pantalla ni queden registradas en logs, históricos de shell o archivos temporales visibles.
|
||||
|
||||
El comando `run` es la forma **recomendada** de usar credenciales. Inyecta `USR` y `PASS` directamente en el entorno del subproceso sin imprimir nada:
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ 🛡️ NUNCA uses `get` en comandos automatizados o logs │
|
||||
│ │
|
||||
│ ✅ CORRECTO: ruby candados.rb run <clave> 'ssh $USR@host' │
|
||||
│ ❌ INCORRECTO: ruby candados.rb get <clave> | algo │
|
||||
│ │
|
||||
│ El comando `get` imprime la contraseña a stdout → queda en logs │
|
||||
│ El comando `run` inyecta USR/PASS en el entorno → no deja rastro │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📥 MÉTODOS PARA CARGAR CONTRASEÑAS EN VARIABLES
|
||||
|
||||
### ★ MÉTODO 1: `run` (PRIMARIO - Recomendado para comandos)
|
||||
|
||||
Ejecuta un comando con `USR` y `PASS` inyectadas en su entorno. La contraseña **NUNCA** se imprime ni queda en logs.
|
||||
|
||||
```bash
|
||||
# 1. Autorizar (dura 5 min)
|
||||
ruby candados.rb authorize
|
||||
# Sintaxis
|
||||
ruby candados.rb run <clave> 'comando que usa $USR y $PASS'
|
||||
|
||||
# 2. Ejecutar comando con credenciales inyectadas
|
||||
ruby candados.rb run srv-dasu:rmonla 'sshpass -p $PASS ssh $USR@host "comando"'
|
||||
|
||||
# Retrocompatible con variable legacy (SSHPASS, etc.)
|
||||
ruby candados.rb run admindasu SSHPASS 'sshpass -e ssh root@host'
|
||||
# Ejemplos
|
||||
ruby candados.rb run srv-dasu:rmonla 'sshpass -p $PASS ssh $USR@host'
|
||||
ruby candados.rb run srv-ns8:root 'ssh $USR@$HOSTNAME "comando"'
|
||||
```
|
||||
|
||||
**¿Qué pasa internamente?**
|
||||
@@ -35,6 +47,30 @@ ruby candados.rb run admindasu SSHPASS 'sshpass -e ssh root@host'
|
||||
4. Limpia las variables al terminar
|
||||
5. **Nada se imprime en pantalla ni queda en logs**
|
||||
|
||||
### ★ MÉTODO 2: `load` (Para shell interactivo)
|
||||
|
||||
Carga `USR` y `PASS` en variables de entorno del shell actual. Usa un archivo temporal (600) que se auto-elimina tras el source.
|
||||
|
||||
```bash
|
||||
# Sintaxis
|
||||
eval $(ruby candados.rb load <clave>)
|
||||
|
||||
# Ejemplo
|
||||
eval $(ruby candados.rb load srv-dasu:rmonla)
|
||||
# → Carga $USR y $PASS en tu shell actual
|
||||
sshpass -p $PASS ssh $USR@servidor
|
||||
```
|
||||
|
||||
**Seguridad:** El archivo temporal tiene permisos `600` y se auto-borra inmediatamente después del `source`.
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Arquitectura
|
||||
- **Cripto**: Motor AES-256-GCM con IV aleatorio y autenticación.
|
||||
- **Bóveda**: Persistencia cifrada en `.boveda.json` (permisos 600).
|
||||
- **MFA Temporal**: Sesiones de 30 min via `.session` (permisos 600).
|
||||
- **Master Key**: `.master.key` local (permisos 600, nunca se comparte).
|
||||
|
||||
## 🔒 Comando: `sudo`
|
||||
|
||||
Ejecuta comandos con privilegios, resolviendo la contraseña automáticamente:
|
||||
@@ -49,20 +85,7 @@ La clave se busca automáticamente:
|
||||
2. Variantes normalizadas del hostname
|
||||
3. Fallback genérico: `sudo`
|
||||
|
||||
## 📦 Comando Secundario: `load`
|
||||
|
||||
Para uso interactivo cuando se necesitan las variables en el shell actual. **Nunca imprime la contraseña:**
|
||||
|
||||
```bash
|
||||
eval $(ruby candados.rb load srv-dasu:rmonla)
|
||||
# USR y PASS están disponibles en el shell
|
||||
```
|
||||
|
||||
Internamente escribe los exports a un archivo temporal (`tmp/.candados_env_<hex>`) con permisos `600`, y a stdout solo emite el comando `source` + autoborrado.
|
||||
|
||||
**Nota:** Los temporales se guardan en `tmp/` del repositorio (no en `/tmp/`) para cumplir con el principio 11 de `05_ia.md` (Workspace Bounds).
|
||||
|
||||
## 🔑 Resolución Automática de Pares
|
||||
## 🔑 Resolución Automática de Claves
|
||||
|
||||
Los comandos `run` y `load` detectan automáticamente el par usuario/contraseña:
|
||||
|
||||
@@ -77,12 +100,13 @@ Los comandos `run` y `load` detectan automáticamente el par usuario/contraseña
|
||||
| Comando | Seguridad | Descripción |
|
||||
|---|---|---|
|
||||
| `run <clave> <cmd>` | ✅ Nada expuesto | **★ PRIMARIO** - Ejecuta con USR/PASS inyectados |
|
||||
| `sudo <cmd>` | ✅ Nada expuesto | Ejecuta con privilegios desde bóveda |
|
||||
| `load <clave>` | ✅ Archivo temporal 600 | Carga en shell (usar con `eval`) |
|
||||
| `get <clave>` | ⚠ Imprime a stdout | Obtener valor directo (usar con cuidado) |
|
||||
| `authorize` | ✅ | Abrir candado (5 min) |
|
||||
| `sudo <cmd>` | ✅ Nada expuesto | Ejecuta con privilegios desde bóveda |
|
||||
| `authorize` | ✅ | Abrir candado (30 min) |
|
||||
| `cerrar` | ✅ | Cerrar sesión manualmente |
|
||||
| `set <clave>` | ✅ Input sin eco | Guardar secreto |
|
||||
| `list` | ✅ | Listar claves |
|
||||
| `list` | ✅ | Listar claves (no muestra valores) |
|
||||
| `get <clave>` | ⚠ Imprime a stdout | Obtener valor directo (⚠ solo uso manual) |
|
||||
|
||||
## 🛡️ Auditoría
|
||||
|
||||
@@ -93,12 +117,14 @@ Archivo `access.log` (permisos 600) registra:
|
||||
|
||||
## 🧠 Contexto para IA
|
||||
|
||||
1. **Siempre** ejecutar `authorize` antes de operar
|
||||
2. **Usar `run`** como método primario (inyecta, no imprime)
|
||||
3. **Evitar `get`** en comandos automatizados (filtra en logs)
|
||||
4. Cerrar sesión con `cerrar` cuando ya no se necesite
|
||||
1. **Siempre** ejecutar `authorize` antes de operar (sesión de 30 min)
|
||||
2. **Usar `run`** como método primario (inyecta USR/PASS, no imprime)
|
||||
3. **Usar `load`** para shell interactivo (eval + source)
|
||||
4. **Evitar `get`** en automatizaciones (imprime la contraseña)
|
||||
5. **Cerrar sesión** con `cerrar` cuando ya no se necesite
|
||||
|
||||
```bash
|
||||
# Flujo típico
|
||||
ruby candados.rb authorize
|
||||
ruby candados.rb run srv-dasu:rmonla 'sshpass -p $PASS ssh $USR@host "ls"'
|
||||
ruby candados.rb cerrar
|
||||
|
||||
+185
-41
@@ -258,7 +258,14 @@ def cmd_run(cripto, boveda, clave, *args)
|
||||
token_user = boveda.get("#{prefijo}_user")
|
||||
user_value = cripto.decrypt(token_user) if token_user
|
||||
elsif clave.include?(':')
|
||||
user_value = clave.split(':').last
|
||||
# Formato: <nodo>:<usuario>[:<tipo>]
|
||||
# Ej: "srv-ns8:rmonla" → usuario="rmonla"
|
||||
# "srv-ns8:rmonla:sudo" → usuario="rmonla" (no "sudo")
|
||||
partes = clave.split(':')
|
||||
if partes.size >= 2
|
||||
# El usuario es siempre la segunda parte (índice 1)
|
||||
user_value = partes[1]
|
||||
end
|
||||
else
|
||||
token_user = boveda.get("#{clave}_user")
|
||||
user_value = cripto.decrypt(token_user) if token_user
|
||||
@@ -366,6 +373,64 @@ def cmd_load(cripto, boveda, clave)
|
||||
$stderr.puts "#{C::GREEN}✓ Credenciales cargadas en variables USR y PASS (archivo temporal seguro).#{C::RESET}"
|
||||
end
|
||||
|
||||
# ─── Comando ssh (ejecutar comando remoto con password de bóveda) ─────
|
||||
# Uso: candados ssh <clave> <usuario@host> '<comando>'
|
||||
# - clave: <nodo>:<usuario> para SSH (busca automáticamente <nodo>:<usuario>:sudo para sudo)
|
||||
# - Si existe clave :sudo separada, la usa para comandos con sudo
|
||||
def cmd_ssh(cripto, boveda, clave, usuario_host, *cmd_args)
|
||||
abort "#{C::RED}✗ Falta nombre de clave.#{C::RESET}" if clave.nil?
|
||||
abort "#{C::RED}✗ Falta usuario@host.#{C::RESET}" if usuario_host.nil? || usuario_host.empty?
|
||||
abort "#{C::RED}✗ Falta comando a ejecutar.#{C::RESET}" if cmd_args.nil? || cmd_args.empty?
|
||||
|
||||
verificar_sesion!
|
||||
|
||||
# Obtener password SSH
|
||||
token_ssh = boveda.get(clave)
|
||||
abort "#{C::RED}✗ Clave '#{clave}' no encontrada.#{C::RESET}" if token_ssh.nil?
|
||||
ssh_pass = cripto.decrypt(token_ssh)
|
||||
safe_ssh_pass = ssh_pass.gsub("'", "'\\''")
|
||||
|
||||
# Buscar automáticamente clave :sudo si existe
|
||||
comando = cmd_args.join(' ')
|
||||
sudo_pass = ssh_pass
|
||||
|
||||
# Intentar buscar clave :sudo basada en la clave SSH
|
||||
if clave.include?(':') && !clave.end_with?(':sudo')
|
||||
# Construir clave sudo: <nodo>:<usuario>:sudo
|
||||
partes = clave.split(':')
|
||||
if partes.size >= 2
|
||||
clave_sudo = "#{partes[0]}:#{partes[1]}:sudo"
|
||||
token_sudo = boveda.get(clave_sudo)
|
||||
if token_sudo
|
||||
sudo_pass = cripto.decrypt(token_sudo)
|
||||
$stderr.puts "#{C::DIM}ℹ Usando clave sudo: #{clave_sudo}#{C::RESET}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
safe_sudo_pass = sudo_pass.gsub("'", "'\\''")
|
||||
|
||||
registrar_acceso('SSH', "Clave: #{clave}, Host: #{usuario_host}, Cmd: #{comando}")
|
||||
|
||||
# Escapar comando para bash remoto: reemplazar ' con '\''
|
||||
comando_escaped = comando.gsub("'", "'\\''")
|
||||
|
||||
# Crear script temporal con el comando y password para sudo
|
||||
require 'securerandom'
|
||||
require 'fileutils'
|
||||
FileUtils.mkdir_p(TMP_DIR) unless File.exist?(TMP_DIR)
|
||||
script_path = File.join(TMP_DIR, ".ssh_sudo_#{SecureRandom.hex(4)}")
|
||||
File.write(script_path, "#!/bin/bash\nexport DEBIAN_FRONTEND=noninteractive\necho '#{safe_sudo_pass}' | sudo -S -p '' bash -c '#{comando_escaped}'\n")
|
||||
File.chmod(0700, script_path)
|
||||
|
||||
# Ejecutar: sshpass para SSH, luego script remoto que hace sudo
|
||||
result = system("sshpass -p '#{safe_ssh_pass}' ssh -o StrictHostKeyChecking=no -tt #{usuario_host} 'bash -s' < '#{script_path}'")
|
||||
FileUtils.rm_f(script_path)
|
||||
exit result ? 0 : 1
|
||||
end
|
||||
|
||||
# ─── Ayuda mejorada para cmd_help_resumida ─────────────────────────────
|
||||
|
||||
# ─── Comando sudo (primitiva reutilizable) ─────────────────────
|
||||
# Resolución inteligente de la clave sudo:
|
||||
# 1. <hostname>:<usuario>:sudo (convención específica)
|
||||
@@ -426,58 +491,130 @@ def cmd_sudo(cripto, boveda, comando_args)
|
||||
exit $?.exitstatus || 0
|
||||
end
|
||||
|
||||
def cmd_help
|
||||
def cmd_help_resumida
|
||||
puts <<~HELP
|
||||
#{C::CYAN}🔐 candados — Gestión segura de secretos#{C::RESET}
|
||||
#{C::CYAN}╔═══════════════════════════════════════════════════════════════════╗#{C::RESET}
|
||||
#{C::CYAN}║#{C::RESET} 🔐 #{C::BOLD}candados — Acceso Seguro a Contraseñas (Sin Exponer)#{C::RESET} #{C::CYAN}║#{C::RESET}
|
||||
#{C::CYAN}╚═══════════════════════════════════════════════════════════════════╝#{C::RESET}
|
||||
|
||||
#{C::GREEN}Uso:#{C::RESET} ruby adn/tools/seguridad/candados.rb <comando> [args]
|
||||
#{C::RED}⚠️ REGLA DE ORO: NUNCA MOSTRAR CONTRASEÑAS#{C::RESET}
|
||||
|
||||
#{C::YELLOW}━━━ Comandos principales ━━━#{C::RESET}
|
||||
#{C::BOLD}run <clave> <cmd>#{C::RESET} #{C::GREEN}★ PRIMARIO#{C::RESET} Ejecutar comando con USR y PASS inyectados
|
||||
#{C::BOLD}sudo <comando>#{C::RESET} Ejecutar con privilegios (resuelve clave automáticamente)
|
||||
#{C::BOLD}load <clave>#{C::RESET} Cargar USR/PASS en shell actual (usar con eval)
|
||||
#{C::GREEN}┌─────────────────────────────────────────────────────────────────┐#{C::RESET}
|
||||
#{C::GREEN}│#{C::RESET} #{C::BOLD}MÉTODOS PARA CARGAR CONTRASEÑAS EN VARIABLES#{C::RESET} #{C::GREEN}│#{C::RESET}
|
||||
#{C::GREEN}└─────────────────────────────────────────────────────────────────┘#{C::RESET}
|
||||
|
||||
#{C::YELLOW}━━━ Gestión de bóveda ━━━#{C::RESET}
|
||||
authorize Abrir candado (autorización temporal 30 min)
|
||||
cerrar Cerrar candado manualmente
|
||||
set <clave> Guardar secreto (input seguro, sin eco)
|
||||
get <clave> Obtener secreto (⚠ imprime a stdout)
|
||||
rm <clave> Eliminar secreto
|
||||
list Listar claves disponibles
|
||||
#{C::BOLD}★ run <clave> 'comando'#{C::RESET} → Ejecuta con $USR y $PASS inyectadas (PRIMARIO)
|
||||
#{C::BOLD}★ load <clave>#{C::RESET} → Carga $USR y $PASS en shell (usar con eval)
|
||||
|
||||
#{C::DIM}Ejemplo: ruby candados.rb run srv-ns8:rmonla 'sshpass -p $PASS ssh $USR@host'#{C::RESET}
|
||||
|
||||
#{C::YELLOW}━━━ Comandos ━━━#{C::RESET}
|
||||
#{C::BOLD}run#{C::RESET} <clave> <cmd> #{C::DIM}Ejecutar con credenciales inyectadas#{C::RESET}
|
||||
#{C::BOLD}load#{C::RESET} <clave> #{C::DIM}Cargar en shell actual (eval)#{C::RESET}
|
||||
#{C::BOLD}sudo#{C::RESET} <comando> #{C::DIM}Ejecutar con privilegios#{C::RESET}
|
||||
#{C::BOLD}ssh#{C::RESET} <clave> <u@h> <cmd> #{C::DIM}SSH remoto con password de bóveda#{C::RESET}
|
||||
#{C::BOLD}authorize#{C::RESET} #{C::DIM}Abrir candado (30 min)#{C::RESET}
|
||||
#{C::BOLD}list#{C::RESET} #{C::DIM}Listar claves disponibles#{C::RESET}
|
||||
#{C::BOLD}set/rm#{C::RESET} <clave> #{C::DIM}Guardar/Eliminar secreto#{C::RESET}
|
||||
|
||||
#{C::DIM}Usá#{C::RESET} ruby candados.rb --help #{C::DIM}para ayuda completa y ejemplos detallados.#{C::RESET}
|
||||
HELP
|
||||
end
|
||||
|
||||
def cmd_help_completo
|
||||
puts <<~HELP
|
||||
#{C::CYAN}╔═══════════════════════════════════════════════════════════════════╗#{C::RESET}
|
||||
#{C::CYAN}║#{C::RESET} 🔐 #{C::BOLD}candados — Acceso Seguro a Contraseñas (Sin Exponer)#{C::RESET} #{C::CYAN}║#{C::RESET}
|
||||
#{C::CYAN}╚═══════════════════════════════════════════════════════════════════╝#{C::RESET}
|
||||
|
||||
#{C::RED}⚠️ REGLA DE ORO: NUNCA MOSTRAR CONTRASEÑAS#{C::RESET}
|
||||
Esta herramienta está diseñada para que las contraseñas #{C::BOLD}NUNCA#{C::RESET} se muestren en pantalla
|
||||
ni queden registradas en logs, históricos de shell o archivos temporales visibles.
|
||||
|
||||
#{C::GREEN}┌─────────────────────────────────────────────────────────────────┐#{C::RESET}
|
||||
#{C::GREEN}│#{C::RESET} #{C::BOLD}MÉTODOS PARA CARGAR CONTRASEÑAS EN VARIABLES#{C::RESET} #{C::GREEN}│#{C::RESET}
|
||||
#{C::GREEN}└─────────────────────────────────────────────────────────────────┘#{C::RESET}
|
||||
|
||||
#{C::BOLD}★ MÉTODO 1: run (PRIMARIO - Recomendado para comandos)#{C::RESET}
|
||||
Ejecuta un comando con USR y PASS inyectadas en su entorno.
|
||||
La contraseña #{C::BOLD}NUNCA#{C::RESET} se imprime ni queda en logs.
|
||||
|
||||
#{C::DIM}ruby candados.rb run <clave> 'comando que usa $USR y $PASS'#{C::RESET}
|
||||
|
||||
#{C::CYAN}Ejemplos:#{C::RESET}
|
||||
#{C::GREEN}✓#{C::RESET} ruby candados.rb run srv-dasu:rmonla 'sshpass -p $PASS ssh $USR@host'
|
||||
#{C::GREEN}✓#{C::RESET} ruby candados.rb run srv-ns8:root 'ssh $USR@$HOSTNAME "comando"'
|
||||
|
||||
#{C::BOLD}★ MÉTODO 2: load (Para shell interactivo)#{C::RESET}
|
||||
Carga USR y PASS en variables de entorno del shell actual.
|
||||
Usa un archivo temporal (600) que se auto-elimina tras el source.
|
||||
|
||||
#{C::DIM}eval $(ruby candados.rb load <clave>)#{C::RESET}
|
||||
|
||||
#{C::CYAN}Ejemplo:#{C::RESET}
|
||||
#{C::GREEN}✓#{C::RESET} eval $(ruby candados.rb load srv-dasu:rmonla)
|
||||
#{C::DIM} → Carga $USR y $PASS en tu shell actual#{C::RESET}
|
||||
#{C::GREEN}✓#{C::RESET} sshpass -p $PASS ssh $USR@servidor
|
||||
|
||||
#{C::YELLOW}━━━ Otros Comandos ━━━#{C::RESET}
|
||||
#{C::BOLD}sudo <comando>#{C::RESET} Ejecutar con sudo (resuelve contraseña automáticamente)
|
||||
#{C::BOLD}authorize#{C::RESET} Abrir candado (sesión de 30 min, requerida para operar)
|
||||
#{C::BOLD}cerrar#{C::RESET} Cerrar sesión manualmente antes de los 30 min
|
||||
#{C::BOLD}abrir#{C::RESET} Cargar clave SSH en ssh-add (usa passphrase de bóveda)
|
||||
|
||||
#{C::YELLOW}━━━ Gestión de Bóveda ━━━#{C::RESET}
|
||||
#{C::BOLD}set <clave>#{C::RESET} Guardar secreto (input seguro sin eco)
|
||||
#{C::BOLD}list#{C::RESET} Listar claves disponibles (no muestra valores)
|
||||
#{C::BOLD}rm <clave>#{C::RESET} Eliminar una clave de la bóveda
|
||||
|
||||
#{C::YELLOW}━━━ Utilidades ━━━#{C::RESET}
|
||||
abrir Autorizar SSH (cargar passphrase)
|
||||
encrypt <texto> Cifrar texto plano → token
|
||||
decrypt <token> Descifrar token → texto plano
|
||||
#{C::BOLD}encrypt <texto>#{C::RESET} Cifrar texto plano → token (para bóveda)
|
||||
#{C::BOLD}decrypt <token>#{C::RESET} Descifrar token → texto plano
|
||||
|
||||
#{C::YELLOW}Resolución automática de claves:#{C::RESET}
|
||||
#{C::DIM}• run/load: Detecta pares USR/PASS por convención:#{C::RESET}
|
||||
srv-dasu:rmonla → USR=rmonla, PASS=(valor cifrado)
|
||||
tailscale_dasuten → busca _user/_pass automáticamente
|
||||
#{C::DIM}• sudo: Resuelve la clave de sudo automáticamente:#{C::RESET}
|
||||
<hostname>:<usuario>:sudo (ej: ns8:rmonla:sudo)
|
||||
#{C::YELLOW}━━━ Resolución Automática de Claves ━━━#{C::RESET}
|
||||
#{C::DIM}• run/load con formato `servidor:usuario`:#{C::RESET}
|
||||
srv-dasu:rmonla → USR=rmonla, PASS=(descifrado de bóveda)
|
||||
#{C::DIM}• run/load con formato `prefijo_pass`:#{C::RESET}
|
||||
tailscale_dasuten_pass → busca automáticamente tailscale_dasuten_user
|
||||
#{C::DIM}• sudo resuelve la clave automáticamente:#{C::RESET}
|
||||
Busca: <hostname>:<usuario>:sudo → fallback: `sudo`
|
||||
|
||||
#{C::YELLOW}Ejemplos:#{C::RESET}
|
||||
#{C::DIM}# 1. Autorizar (dura 30 min)#{C::RESET}
|
||||
ruby candados.rb authorize
|
||||
#{C::YELLOW}━━━ Flujo de Trabajo Típico ━━━#{C::RESET}
|
||||
#{C::DIM}1. Autorizar (una vez cada 30 min):#{C::RESET}
|
||||
ruby candados.rb authorize
|
||||
|
||||
#{C::GREEN}# 2. Ejecutar comando con credenciales (RECOMENDADO)#{C::RESET}
|
||||
ruby candados.rb run srv-dasu:rmonla 'sshpass -p $PASS ssh $USR@host'
|
||||
ruby candados.rb run admindasu SSHPASS 'sshpass -e ssh root@host'
|
||||
#{C::DIM}2. Ejecutar comandos (Método recomendado):#{C::RESET}
|
||||
ruby candados.rb run srv-ns8:rmonla 'sshpass -p $PASS ssh $USR@host "comando"'
|
||||
|
||||
#{C::DIM}# 3. Ejecutar con sudo#{C::RESET}
|
||||
ruby candados.rb sudo tailscale set --operator=$USER
|
||||
#{C::DIM}3. O cargar en shell interactivo:#{C::RESET}
|
||||
eval $(ruby candados.rb load srv-ns8:rmonla)
|
||||
sshpass -p $PASS ssh $USR@host
|
||||
|
||||
#{C::DIM}# 4. Cargar en shell interactivo (secundario)#{C::RESET}
|
||||
eval $(ruby candados.rb load srv-dasu:rmonla)
|
||||
#{C::RED}⚠️ ADVERTENCIAS DE SEGURIDAD:#{C::RESET}
|
||||
#{C::BOLD}• NUNCA usar `get`#{C::RESET} en automatizaciones → imprime la contraseña a stdout
|
||||
#{C::BOLD}• `run` y `sudo`#{C::RESET} → las credenciales no dejan rastro en logs
|
||||
#{C::BOLD}• `load`#{C::RESET} → archivo temporal con permisos 600, auto-borrado
|
||||
#{C::BOLD}• Siempre ejecutar `authorize`#{C::RESET} antes de operar (sesión de 30 min)
|
||||
|
||||
#{C::RED}⚠ Seguridad:#{C::RESET}
|
||||
#{C::DIM}• run/sudo: Las credenciales NUNCA se imprimen en pantalla ni logs.#{C::RESET}
|
||||
#{C::DIM}• load: Usa archivo temporal (600) con autoborrado, nada visible.#{C::RESET}
|
||||
#{C::DIM}• get: ⚠ Imprime el valor a stdout. Usar solo si es necesario.#{C::RESET}
|
||||
#{C::DIM}Documentación completa: adn/tools/candados/README.md#{C::RESET}
|
||||
HELP
|
||||
end
|
||||
|
||||
alias cmd_help cmd_help_completo
|
||||
|
||||
# ─── Recordatorio de Seguridad (solo en ayuda) ──────────────────
|
||||
def mostrar_recordatorio_seguridad
|
||||
puts <<~RECORDATORIO
|
||||
#{C::CYAN}╔════════════════════════════════════════════════════════════════╗#{C::RESET}
|
||||
#{C::CYAN}║#{C::RESET} 🔐 #{C::BOLD}REGLA DE ORO: NUNCA MOSTRAR CONTRASEÑAS#{C::RESET} #{C::CYAN}║#{C::RESET}
|
||||
#{C::CYAN}║#{C::RESET} #{C::GREEN}✓ CORRECTO:#{C::RESET} ruby candados.rb run <clave> '<cmd>' #{C::CYAN}║#{C::RESET}
|
||||
#{C::CYAN}║#{C::RESET} #{C::RED}✗ INCORRECTO:#{C::RESET} ruby candados.rb get <clave> #{C::CYAN}║#{C::RESET}
|
||||
#{C::CYAN}║#{C::RESET} → `run` inyecta USR/PASS sin mostrar. `get` imprime y deja rastro. #{C::CYAN}║#{C::RESET}
|
||||
#{C::CYAN}╚════════════════════════════════════════════════════════════════╝#{C::RESET}
|
||||
RECORDATORIO
|
||||
puts
|
||||
end
|
||||
|
||||
# ─── Main ────────────────────────────────────────────────────────────
|
||||
if __FILE__ == $0
|
||||
comando = ARGV[0]
|
||||
@@ -496,6 +633,10 @@ if __FILE__ == $0
|
||||
cripto = Cripto.new(cargar_master_key)
|
||||
boveda = Boveda.new(BOVEDA)
|
||||
cmd_sudo(cripto, boveda, ARGV[1..])
|
||||
when 'ssh'
|
||||
cripto = Cripto.new(cargar_master_key)
|
||||
boveda = Boveda.new(BOVEDA)
|
||||
cmd_ssh(cripto, boveda, ARGV[1], ARGV[2], *ARGV[3..])
|
||||
when 'encrypt', 'decrypt', 'get', 'load', 'run', 'set', 'rm', 'list'
|
||||
cripto = Cripto.new(cargar_master_key)
|
||||
boveda = Boveda.new(BOVEDA)
|
||||
@@ -509,11 +650,14 @@ if __FILE__ == $0
|
||||
when 'rm' then cmd_rm(boveda, arg)
|
||||
when 'list' then cmd_list(boveda)
|
||||
end
|
||||
when 'help', 'ayuda', '-h', '--help', nil
|
||||
cmd_help
|
||||
when '--help', '-h'
|
||||
cmd_help_completo
|
||||
when 'help', 'ayuda', nil
|
||||
cmd_help_resumida
|
||||
else
|
||||
$stderr.puts "#{C::RED}✗ Comando desconocido: '#{comando}'#{C::RESET}"
|
||||
cmd_help
|
||||
$stderr.puts ""
|
||||
cmd_help_resumida
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
@@ -66,6 +66,7 @@ module Dron
|
||||
lines << "\n#{Color::BOLD}Opciones:#{Color::RESET}"
|
||||
lines << " #{Color::GREEN}--nota \"texto\"#{Color::RESET} Nota descriptiva para la bitácora"
|
||||
lines << " #{Color::GREEN}--evento ID#{Color::RESET} Vincular a evento existente"
|
||||
lines << " #{Color::GREEN}--nodo NOMBRE#{Color::RESET} Nodo para bitácora (auto-detect si no se especifica)"
|
||||
lines << " #{Color::GREEN}--timeout N#{Color::RESET} Timeout en segundos (default: 3600)"
|
||||
lines << " #{Color::GREEN}--dry-run#{Color::RESET} Simulación sin ejecutar"
|
||||
lines << " #{Color::GREEN}--help#{Color::RESET} Mostrar esta ayuda"
|
||||
@@ -107,7 +108,8 @@ module Dron
|
||||
nota: opciones[:nota],
|
||||
evento_id: opciones[:evento_id],
|
||||
timeout: opciones[:timeout],
|
||||
flujo_id: opciones[:flujo_id]
|
||||
flujo_id: opciones[:flujo_id],
|
||||
nodo: opciones[:nodo]
|
||||
)
|
||||
|
||||
# Mostrar resultado
|
||||
@@ -128,6 +130,7 @@ module Dron
|
||||
evento_id: nil,
|
||||
timeout: 3600,
|
||||
flujo_id: nil,
|
||||
nodo: nil,
|
||||
cmd: nil
|
||||
}
|
||||
|
||||
@@ -143,6 +146,8 @@ module Dron
|
||||
opciones[:timeout] = args.shift.to_i
|
||||
when '--flujo'
|
||||
opciones[:flujo_id] = args.shift
|
||||
when '--nodo'
|
||||
opciones[:nodo] = args.shift
|
||||
when '--help', '-h'
|
||||
Dispatcher.mostrar_ayuda
|
||||
exit 0
|
||||
|
||||
@@ -13,15 +13,15 @@ module Dron
|
||||
HEARTBEAT_INTERVAL = 30
|
||||
|
||||
class << self
|
||||
def lanzar(cmd:, nota: nil, evento_id: nil, timeout: DEFAULT_TIMEOUT, flujo_id: nil)
|
||||
def lanzar(cmd:, nota: nil, evento_id: nil, timeout: DEFAULT_TIMEOUT, flujo_id: nil, nodo: nil)
|
||||
Base.logger.info("🛠️ Dron Ejecutor iniciado: #{Process.pid}")
|
||||
dron_id = ADN::DB::DronDB.generar_dron_id
|
||||
|
||||
ADN::DB::DronDB.registrar_inicio(tipo: 'ejecutor', cmd: cmd, flujo_id: flujo_id, metadata: { nota: nota, evento_id: evento_id, timeout: timeout })
|
||||
evento_id = registrar_bitacora(nota, evento_id, 'inicio') if nota
|
||||
ADN::DB::DronDB.registrar_inicio(tipo: 'ejecutor', cmd: cmd, flujo_id: flujo_id, metadata: { nota: nota, evento_id: evento_id, timeout: timeout, nodo: nodo })
|
||||
evento_id = registrar_bitacora(nota, evento_id, 'inicio', nodo) if nota
|
||||
resultado = ejecutar_comando(cmd, dron_id, timeout)
|
||||
ADN::DB::DronDB.registrar_fin(dron_id, resultado[:exit_code], resultado[:output])
|
||||
registrar_bitacora(nota, evento_id, 'fin', resultado) if evento_id
|
||||
registrar_bitacora(nota, evento_id, 'fin', resultado, nodo) if evento_id
|
||||
|
||||
Base.logger.info("🛠️ Dron Ejecutor completado: #{dron_id} - #{resultado[:exit_code] == 0 ? '✅' : '❌'}")
|
||||
{ dron_id: dron_id, exit_code: resultado[:exit_code], duration: resultado[:duration], evento_id: evento_id }
|
||||
@@ -67,14 +67,18 @@ module Dron
|
||||
{ exit_code: exit_code || 1, output: output.join("\n").strip, duration: (Time.now - start_time).round(2) }
|
||||
end
|
||||
|
||||
def registrar_bitacora(nota, evento_id, tipo, resultado = nil)
|
||||
def registrar_bitacora(nota, evento_id, tipo, resultado = nil, nodo = nil)
|
||||
return nil unless nota
|
||||
require_relative '../../db/core/bitacora_db'
|
||||
|
||||
# Detectar nodo automáticamente si no se especifica
|
||||
nodo_id = detectar_nodo_id(nodo)
|
||||
|
||||
if tipo == 'inicio'
|
||||
descripcion = "🛸 dron: #{nota}"
|
||||
BitacorasDB::BitacoraDB.with_connection do |db|
|
||||
db.crear_evento(nodo_id: 1, descripcion: descripcion, inicio: Time.now.strftime('%H:%M'), estado: '⏳')
|
||||
evento = db.crear_evento(nodo_id: nodo_id, descripcion: descripcion, inicio: Time.now.strftime('%H:%M'), estado: '⏳')
|
||||
evento['id'] # Retornar ID para actualizar después
|
||||
end
|
||||
else
|
||||
estado = resultado[:exit_code] == 0 ? '✅' : '❌'
|
||||
@@ -82,11 +86,44 @@ module Dron
|
||||
BitacorasDB::BitacoraDB.with_connection do |db|
|
||||
db.actualizar_evento(evento_id, descripcion: descripcion, fin: Time.now.strftime('%H:%M'))
|
||||
end
|
||||
evento_id # Retornar mismo ID
|
||||
end
|
||||
rescue StandardError => e
|
||||
Base.logger.warn("⚠️ Error en bitácora: #{e.message}")
|
||||
nil
|
||||
end
|
||||
|
||||
def detectar_nodo_id(nodo)
|
||||
return nodo if nodo.is_a?(Integer)
|
||||
|
||||
# Si no se especifica nodo, usar el hostname para detectar
|
||||
require 'socket'
|
||||
hostname = Socket.gethostname.downcase
|
||||
|
||||
# Buscar nodo por nombre en la tabla de nodos
|
||||
require_relative '../../db/core/bitacora_db'
|
||||
sql = 'SELECT id FROM nodos WHERE LOWER(nombre) = $1 LIMIT 1'
|
||||
|
||||
resultado = nil
|
||||
BitacorasDB::BitacoraDB.with_connection do |db|
|
||||
resultado = db.execute(sql, [hostname])
|
||||
end
|
||||
|
||||
if resultado&.any?
|
||||
resultado.first['id']
|
||||
else
|
||||
# Fallback: buscar por prefijo (ej: srv-ns8 coincide con srv-ns8.frlr.utn.edu.ar)
|
||||
sql = 'SELECT id FROM nodos WHERE LOWER(nombre) LIKE $1 LIMIT 1'
|
||||
resultado = nil
|
||||
BitacorasDB::BitacoraDB.with_connection do |db|
|
||||
resultado = db.execute(sql, ["#{hostname}%"])
|
||||
end
|
||||
resultado&.any? ? resultado.first['id'] : 1
|
||||
end
|
||||
rescue StandardError => e
|
||||
Base.logger.warn("⚠️ Error detectando nodo: #{e.message}, usando default (1)")
|
||||
1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -666,20 +666,45 @@ module BitacorasDB
|
||||
|
||||
where_clause = conditions.empty? ? '' : "WHERE #{conditions.join(' AND ')}"
|
||||
|
||||
# Para events, adaptar condiciones (solo las que aplican)
|
||||
where_clause_events = if conditions.empty?
|
||||
''
|
||||
else
|
||||
# Reemplazar referencias a e. por ev. y eliminar condiciones que no aplican a events
|
||||
conditions_events = conditions.reject { |c| c.include?('bitacora_id') || c.include?('tema_id') || c.include?('ambito_id') }
|
||||
"WHERE #{conditions_events.join(' AND ').gsub('e.', 'ev.')}"
|
||||
end
|
||||
|
||||
# Unificar eventos de tabla events y entradas para mostrar todo junto
|
||||
query = <<~SQL
|
||||
SELECT e.id, e.inicio, e.fin, e.descripcion, e.estado, e.modo, e.es_ia,
|
||||
e.tema_id, e.bitacora_id, e.nodo_id, e.ambito_id, e.created_at, e.updated_at,
|
||||
SELECT ev.id, ev.inicio::text as inicio, ev.fin::text as fin, ev.descripcion, ev.estado,
|
||||
'A' as modo, false as es_ia,
|
||||
NULL as tema_id, NULL as bitacora_id, ev.nodo_id, NULL as ambito_id,
|
||||
ev.created_at, ev.updated_at,
|
||||
CURRENT_DATE as bitacora_fecha,
|
||||
n.nombre as nodo_nombre,
|
||||
NULL as tema_titulo,
|
||||
na.nombre as ambito_nombre
|
||||
FROM bitacoras.events ev
|
||||
LEFT JOIN bitacoras.nodos n ON ev.nodo_id = n.id
|
||||
LEFT JOIN bitacoras.ambitos na ON n.ambito_id = na.id
|
||||
#{where_clause_events}
|
||||
UNION ALL
|
||||
SELECT e.id, e.inicio::text, e.fin::text, e.descripcion, e.estado, e.modo, e.es_ia,
|
||||
e.tema_id, e.bitacora_id, e.nodo_id, e.ambito_id,
|
||||
e.created_at, e.updated_at,
|
||||
b.fecha as bitacora_fecha,
|
||||
n.nombre as nodo_nombre,
|
||||
t.titulo as tema_titulo,
|
||||
a.nombre as ambito_nombre
|
||||
COALESCE(a.nombre, na.nombre) as ambito_nombre
|
||||
FROM #{SCHEMA}.#{TABLES[:entradas]} e
|
||||
LEFT JOIN #{SCHEMA}.#{TABLES[:bitacoras]} b ON e.bitacora_id = b.id
|
||||
LEFT JOIN #{SCHEMA}.#{TABLES[:nodos]} n ON e.nodo_id = n.id
|
||||
LEFT JOIN #{SCHEMA}.#{TABLES[:temas]} t ON e.tema_id = t.id
|
||||
LEFT JOIN #{SCHEMA}.ambitos a ON e.ambito_id = a.id
|
||||
LEFT JOIN #{SCHEMA}.ambitos na ON n.ambito_id = na.id
|
||||
#{where_clause}
|
||||
ORDER BY COALESCE(b.fecha, CURRENT_DATE) DESC, e.inicio DESC
|
||||
ORDER BY bitacora_fecha DESC, inicio DESC
|
||||
LIMIT $#{param_index} OFFSET $#{param_index + 1}
|
||||
SQL
|
||||
|
||||
@@ -1007,6 +1032,7 @@ module BitacorasDB
|
||||
public
|
||||
|
||||
# Crear un evento en la tabla events (visible en Bitácora Web)
|
||||
# El ámbito se obtiene automáticamente del nodo mediante JOIN en las consultas
|
||||
def crear_evento(nodo_id:, descripcion:, estado: '⏳', inicio: nil, fin: nil, modo: 'A', metadata: nil)
|
||||
inicio ||= Time.now.strftime('%H:%M')
|
||||
query = <<~SQL
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
**Código:** A04.P005
|
||||
**Fecha:** 27 de marzo de 2026
|
||||
**Autor:** Sistema ADN
|
||||
**Versión:** 2.2
|
||||
**Estado:** 🚧 EN EJECUCIÓN (Fase 2 completada, Fase 3 pendiente — Creación VM PC Cliente)
|
||||
**Versión:** 2.5
|
||||
**Estado:** 🚧 EN EJECUCIÓN (Nueva VM 105 dasu-sql3 - Fase 4.N1-4.N3 completadas)
|
||||
**Dependencia:** A04.P001 (Red srv-dasu)
|
||||
|
||||
## 📋 Resumen Ejecutivo
|
||||
@@ -80,8 +80,22 @@ Crear dos VMs nuevas (SQL Server + PC cliente) en `srv-dasu`, instalar el sistem
|
||||
- [x] **3.6:** Instalar Tailscale VPN (cuenta `pcdasu0@frlr.utn.edu.ar`). Anotar IP Tailscale asignada. ✅ **IP: 100.98.225.100**
|
||||
- [ ] **3.7:** Instalar herramientas cliente SQL (SSMS o sqlcmd) si es necesario.
|
||||
- [ ] **3.8:** Verificar conectividad hacia dasu-sql2 (ping + test TCP 1433).
|
||||
- [ ] **3.9:** ⚠️ PROBLEMA: VMs sin conectividad de red IP. Estado: 192.168.1.x NO responde a ping desde srv-dasu. Investiguando causa (posible conflicto de bridge/red).
|
||||
- [x] **3.9:** Instalar qemu-guest-agent en VM dasu-pcv2 para backups desatendidos (ACPI shutdown) ✅.
|
||||
|
||||
### ⏳ **FASE 4: Nueva VM dasu-sql3 (VM 105)** *(2026-04-08)*
|
||||
|
||||
- [x] **4.N1:** Crear VM 105 (dasu-sql3) ✅. Eliminar VMs 103 y 104 problemáticas, crear VM 105 limpia.
|
||||
- [x] **4.N2:** ISO Windows Server 2022 Español ✅. Usar `Windows_Server_2022_Spanish_Evaluation.iso`.
|
||||
- [x] **4.N3:** Discos SATA ✅. 60GB (sata0) para SO + 120GB (sata1) para datos SQL.
|
||||
- [ ] **4.N4:** Finalizar instalación Windows.
|
||||
- [ ] **4.N5:** Instalar VirtIO drivers.
|
||||
- [ ] **4.N6:** Configurar hostname `dasu-sql3` y Workgroup.
|
||||
- [ ] **4.N7:** Configurar red DHCP.
|
||||
- [ ] **4.N8:** Instalar OpenSSH Server (puerto 7022).
|
||||
- [ ] **4.N9:** Instalar Tailscale.
|
||||
- [ ] **4.N10:** Instalar SQL Server 2019.
|
||||
|
||||
### ⏳ **FASE 4: Instalación del Sistema DASUTEN**
|
||||
|
||||
- [x] **4.1:** Obtener backup/script de la base de datos DASUTEN del sistema actual. ✅ (Descargado localmente archivo de 604MB y confirmado acceso vía SMB al crudo `.bak`)
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
# Plan: Conocimiento del Contexto y Ámbito de Trabajo
|
||||
|
||||
> **Ámbito:** A09 — dtic-DIIAA
|
||||
|
||||
**Código:** A09.P001
|
||||
**Fecha:** 08 de abril de 2026
|
||||
**Autor:** Lic. Ricardo Monla
|
||||
**Versión:** 1.2
|
||||
**Estado:** 🚧 En Curso (Fase 3 completada)
|
||||
**Dependencia:** Ninguna
|
||||
|
||||
## 📋 Resumen Ejecutivo
|
||||
|
||||
Este plan tiene como objetivo establecer el conocimiento completo del contexto y ámbito de trabajo del Departamento de Investigación en Inteligencia Artificial y Automatizaciones (DIIAA).
|
||||
|
||||
**Alcances:**
|
||||
- Comprender la estructura institucional
|
||||
- Identificar nodos y recursos disponibles
|
||||
- Establecer conexiones y accesos
|
||||
- Documentar el estado actual de infraestructura
|
||||
|
||||
## 🎯 Objetivos
|
||||
|
||||
1. **Conocer el contexto institucional** — DTIC, FRLR, UTN
|
||||
2. **Identificar el nodo principal** — srv-ns8 y sus servicios
|
||||
3. **Establecer accesos seguros** — Candados y bóveda
|
||||
4. **Configurar nginx nativo** — Servidor web para aplicaciones DIIAA
|
||||
5. **Documentar estado actual** — Infraestructura y servicios existentes
|
||||
|
||||
## 📅 Fases de Implementación
|
||||
|
||||
### FASE 1: Reconocimiento Institucional ✅ COMPLETADA
|
||||
|
||||
- [x] Documentar estructura organizacional (DTIC → DIIAA)
|
||||
- [x] Identificar responsables y roles
|
||||
- [x] Mapear dependencias institucionales
|
||||
|
||||
### FASE 2: Reconocimiento de Infraestructura ✅ COMPLETADA
|
||||
|
||||
- [x] Identificar nodos disponibles
|
||||
- [x] Verificar conectividad y accesos
|
||||
- [x] Listar servicios existentes en srv-ns8
|
||||
|
||||
### FASE 3: Configuración de Accesos ✅ COMPLETADA
|
||||
|
||||
- [x] Optimizar herramienta `candados` (help resumida/completa)
|
||||
- [x] Documentar métodos para cargar contraseñas en variables
|
||||
- [x] Actualizar README de candados
|
||||
- [x] Sanear bóveda (eliminar duplicados, renombrar claves)
|
||||
|
||||
### FASE 3.5: Mejora de Drones ✅ COMPLETADA
|
||||
|
||||
- [x] Auto-detección de nodo por hostname
|
||||
- [x] Ámbito automático heredado del nodo
|
||||
- [x] Unificación de vistas (events + entradas)
|
||||
- [x] Registro automático en bitácora sin intervención manual
|
||||
|
||||
### FASE 4: Setup de nginx Nativo ⏳ PENDIENTE
|
||||
|
||||
- [ ] Instalar nginx en srv-ns8 (nativo, no Docker)
|
||||
- [ ] Configurar servidor virtual para dtic-DIIAA
|
||||
- [ ] Habilitar acceso web al ámbito
|
||||
|
||||
### FASE 5: Documentación y Puesta en Marcha ⏳ EN CURSO
|
||||
|
||||
- [x] Crear manifiesto del ámbito A09
|
||||
- [x] Crear plan A09.P001
|
||||
- [ ] Crear ficha del nodo srv-ns8 (ya existe en `nodos/`)
|
||||
- [ ] Documentar configuración nginx
|
||||
- [x] Registrar en bitácora actividades
|
||||
|
||||
## 🎯 Criterios de Éxito
|
||||
|
||||
| Criterio | Verificación |
|
||||
| :--- | :--- |
|
||||
| **Manifiesto creado** | `A09_dtic-DIIAA.md` existe |
|
||||
| **Plan documentado** | `A09.P001_Contexto-y-Ambito.md` existe |
|
||||
| **Accesos configurados** | SSH a srv-ns8 funcional |
|
||||
| **nginx operativo** | Servicio web accesible |
|
||||
| **Bitácora actualizada** | Eventos registrados |
|
||||
|
||||
## 📝 Notas Técnicas
|
||||
|
||||
### Stack Tecnológico
|
||||
|
||||
- **Servidor Web:** nginx (nativo en srv-ns8)
|
||||
- **Gestión de Secretos:** candados (AES-256-GCM)
|
||||
- **Automatización:** ADN CLI + Drones
|
||||
- **Bitácora:** Sistema ADN con web de visualización
|
||||
|
||||
### Nodo Principal: srv-ns8
|
||||
|
||||
| Campo | Valor |
|
||||
| :--- | :--- |
|
||||
| **Hostname** | srv-ns8.frlr.utn.edu.ar |
|
||||
| **IP** | 10.0.10.8 |
|
||||
| **SO** | Debian GNU/Linux 12 (bookworm) |
|
||||
| **Rol** | Servidor de aplicaciones y gestión |
|
||||
| **Servicios** | nginx, DNS (srvv-dns), otros |
|
||||
|
||||
## 🔄 Armonía Integral
|
||||
|
||||
**Última actualización:** 2026-04-08 09:50 (Fases 1-3 completadas, drones mejorados)
|
||||
|
||||
| Documento | Estado | Notas |
|
||||
| :--- | :--- | :--- |
|
||||
| `A09_dtic-DIIAA.md` | ✅ Creado | Manifiesto del ámbito con pilares ADN |
|
||||
| `A09.P001_Contexto-y-Ambito.md` | ✅ Fases 1-3.5 completadas | Plan actualizado (v1.2) |
|
||||
| `nodos/srv-ns8.md` | ✅ Existe | Ficha técnica en `nodos/` (global) |
|
||||
| `adn/tools/candados/candados.rb` | ✅ Optimizado | Help resumida + completa (--help) |
|
||||
| `adn/tools/candados/README.md` | ✅ Actualizado | Sincronizado con help |
|
||||
| `adn/tools/cli/dron/ejecutor.rb` | ✅ Mejorado | Auto-detección de nodo y ámbito |
|
||||
| `adn/tools/cli/dron/dispatcher.rb` | ✅ Mejorado | Opción --nodo agregada |
|
||||
| `adn/tools/db/core/bitacora_db.rb` | ✅ Mejorado | Vista unificada events + entradas |
|
||||
| `docs/contexto/IA.md` | ✅ Actualizado | Documentación de drones mejorados |
|
||||
| nginx config | ⏳ Pendiente | Configuración nativa en srv-ns8 |
|
||||
|
||||
---
|
||||
|
||||
## Referencias
|
||||
|
||||
- **Ámbito de referencia:** `docs/ambito/dtic-DNS/A07_dtic-DNS.md`
|
||||
- **ADN Ontología:** `adn/01_ontologia.md`
|
||||
- **Bitácoras:** `./adn/tools/run db evento:listar --ambito dtic-DIIAA`
|
||||
@@ -0,0 +1,123 @@
|
||||
# A09 - Ámbito dtic-DIIAA
|
||||
|
||||
> Manifiesto de Ámbito | Referencia: [`adn/01_ontologia.md`](../../../adn/01_ontologia.md)
|
||||
|
||||
## Identificación
|
||||
|
||||
| Campo | Valor |
|
||||
| :--- | :--- |
|
||||
| **Código** | A09 |
|
||||
| **Nombre** | Departamento de Investigación en Inteligencia Artificial y Automatizaciones |
|
||||
| **Estado** | 🚧 En Implementación |
|
||||
| **Inicio** | 08/04/2026 |
|
||||
| **Fin Estimado** | Continuo (investigación y desarrollo) |
|
||||
|
||||
## Objetivo
|
||||
|
||||
El ámbito **dtic-DIIAA** engloba todas las operaciones relacionadas con la investigación, desarrollo y administración de sistemas de Inteligencia Artificial y Automatizaciones de la Facultad.
|
||||
|
||||
Este ámbito cubre:
|
||||
|
||||
1. **Investigación en IA** — Desarrollo y aplicación de modelos de inteligencia artificial.
|
||||
2. **Automatizaciones** — Scripts y sistemas para automatización de tareas operativas.
|
||||
3. **Administración de servidores** — Gestión de servidores y sistemas de altas prestaciones.
|
||||
4. **Capacitación** — Formación y transferencia de conocimientos en IA y automatizaciones.
|
||||
|
||||
## Contexto Institucional
|
||||
|
||||
```
|
||||
Lic. Ricardo MONLA
|
||||
└── Departamento de Investigación en IA y Automatizaciones (DIIAA)
|
||||
└── Dirección de Tecnologías de la Información y Comunicación (DTIC)
|
||||
└── Facultad Regional La Rioja (FRLR)
|
||||
└── Universidad Tecnológica Nacional (UTN)
|
||||
```
|
||||
|
||||
## Funciones Principales
|
||||
|
||||
1. **Administración de servidores y sistemas de altas prestaciones**
|
||||
2. **Investigación y desarrollo en Inteligencia Artificial**
|
||||
3. **Automatización de procesos operativos**
|
||||
4. **Soporte técnico especializado**
|
||||
|
||||
## Ecosistema ADN
|
||||
|
||||
Este ámbito opera bajo el **Ecosistema ADN** — el núcleo autónomo que gestiona la ontología, bitácoras, seguridad y procesos del proyecto.
|
||||
|
||||
### Pilares Fundamentales
|
||||
|
||||
El ADN se rige por tres principios rectores que guían toda decisión operativa:
|
||||
|
||||
| Pilar | Descripción |
|
||||
| :--- | :--- |
|
||||
| **Menos es Más** | Cada componente debe justificar su existencia. Eliminar lo innecesario, consolidar lo disperso, evitar duplicación. |
|
||||
| **Armonía Integral** | Todo está conectado. Si un dato cambia en un sitio, debe propagarse a todos los demás. La consistencia es obligatoria. |
|
||||
| **Mejora Continua** | El ADN evoluciona. Las buenas prácticas emergentes se formalizan como nuevas reglas (Evolución Progresiva). |
|
||||
|
||||
### Hebras del ADN
|
||||
|
||||
| # | Hebra | Descripción |
|
||||
| :--- | :--- | :--- |
|
||||
| **01** | **Ontología** | Define identidad, nodos, ámbitos y topología de red |
|
||||
| **02** | **Bitácora** | Registro diario de eventos, triggers y métricas |
|
||||
| **03** | **Seguridad** | Protocolos de red, candados y bóveda cifrada |
|
||||
| **04** | **Iconografía** | Taxonomía visual de estados e iconos semánticos |
|
||||
| **05** | **Directivas IA** | Premisas de trabajo para Inteligencia Artificial |
|
||||
| **06** | **Gobernanza** | Idioma, nomenclatura, coherencia documental, resguardo |
|
||||
| **07** | **Proyectos** | Gestión de proyectos transversales |
|
||||
| **08** | **Procesos** | Procesos automatizados sobre infraestructura |
|
||||
|
||||
**CLI Principal:** `./adn/tools/run <subcomando>`
|
||||
|
||||
| Categoría | Comandos frecuentes |
|
||||
| :--- | :--- |
|
||||
| **Bitácoras** | `db evento:crear`, `db evento:actualizar`, `jornada iniciar/cerrar` |
|
||||
| **Nodos** | `nodos info <nombre>`, `nodos listar`, `nodos agrupar` |
|
||||
| **Seguridad** | `candados run <clave> '<cmd>'`, `candados authorize` |
|
||||
| **Drones** | `dron lanzar`, `dron flota`, `dron salud`, `dron limpiar` |
|
||||
|
||||
> **Drones:** Subsistema del ADN para tareas en background con auto-bitácora. Ver [`adn/tools/cli/dron.rb`](../../../adn/tools/cli/dron.rb)
|
||||
|
||||
## Nodos Involucrados
|
||||
|
||||
### Nodo Principal
|
||||
|
||||
| Nodo | IP | Rol en el Ámbito |
|
||||
| :--- | :--- | :--- |
|
||||
| **srv-ns8** | `10.0.10.8` | Workstation / Docker Host / Hipervisor — servidor principal de aplicaciones y gestión |
|
||||
|
||||
> **Ficha técnica completa:** [`nodos/srv-ns8.md`](../../../nodos/srv-ns8.md)
|
||||
|
||||
### Nodos de Soporte
|
||||
|
||||
| Nodo | IP | Rol en el Ámbito |
|
||||
| :--- | :--- | :--- |
|
||||
| **srv-pmox1** | `10.0.10.201` | Hipervisor Proxmox (anfitrión de VMs) |
|
||||
| **srvv-dns** | `10.0.10.2` | Servidor DNS (soporte de resolución) |
|
||||
|
||||
> Ver fichas completas en [`nodos/`](../../../nodos/)
|
||||
|
||||
## Planes Asociados
|
||||
|
||||
| Código | Nombre | Estado |
|
||||
| :--- | :--- | :--- |
|
||||
| [A09.P001](A09.P001_Contexto-y-Ambito.md) | Conocimiento del Contexto y Ámbito de Trabajo | 🚧 En Curso |
|
||||
|
||||
## 🔄 Armonía Integral
|
||||
|
||||
**Última actualización:** 2026-04-08 (Creación del ámbito A09 - dtic-DIIAA)
|
||||
|
||||
| Documento | Estado | Notas |
|
||||
| :--- | :--- | :--- |
|
||||
| `A09_dtic-DIIAA.md` | ✅ Creado | Manifiesto del ámbito |
|
||||
| `A09.P001_Contexto-y-Ambito.md` | 🚧 En curso | Plan de conocimiento del contexto |
|
||||
| `nodos/srv-ns8.md` | ✅ Existe | Ficha técnica en `nodos/` |
|
||||
|
||||
---
|
||||
|
||||
## Referencias
|
||||
|
||||
- **Ecosistema ADN**: [`adn/00_indice.md`](../../../adn/00_indice.md)
|
||||
- **Ontología**: [`adn/01_ontologia.md`](../../../adn/01_ontologia.md)
|
||||
- **Bitácoras**: `./adn/tools/run db evento:listar --ambito dtic-DIIAA`
|
||||
- **Nodos**: [`nodos/`](../../../nodos/)
|
||||
@@ -0,0 +1,80 @@
|
||||
#!/bin/bash
|
||||
# Script para agregar registros CNAME de DKIM en srvv-dns
|
||||
# Ejecutar: sudo bash A07.P002_agregar_dkim.sh
|
||||
|
||||
ZONA_FILE="/docker/coreDNS/zones/frlr.utn.edu.ar.db"
|
||||
BACKUP_FILE="/docker/coreDNS/zones/bkp_260407_preDKIM_frlr.utn.edu.ar.db"
|
||||
|
||||
echo "🔧 Agregando registros DKIM para Microsoft 365..."
|
||||
|
||||
# 1. Crear backup
|
||||
echo " 1. Creando backup..."
|
||||
cp "$ZONA_FILE" "$BACKUP_FILE"
|
||||
if [ $? -eq 0 ]; then
|
||||
echo " ✅ Backup creado: $BACKUP_FILE"
|
||||
else
|
||||
echo " ❌ Error al crear backup"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 2. Verificar si los registros ya existen
|
||||
if grep -q "selector1._domainkey" "$ZONA_FILE"; then
|
||||
echo " ⚠️ El registro selector1._domainkey ya existe"
|
||||
else
|
||||
echo " ✅ Registro selector1._domainkey no existe (ok para agregar)"
|
||||
fi
|
||||
|
||||
if grep -q "selector2._domainkey" "$ZONA_FILE"; then
|
||||
echo " ⚠️ El registro selector2._domainkey ya existe"
|
||||
else
|
||||
echo " ✅ Registro selector2._domainkey no existe (ok para agregar)"
|
||||
fi
|
||||
|
||||
# 3. Agregar registros CNAME de DKIM
|
||||
# Los agrego después de los registros NS existentes
|
||||
echo " 2. Agregando registros CNAME de DKIM..."
|
||||
|
||||
# Crear archivo temporal con los nuevos registros
|
||||
cat >> "$ZONA_FILE" << 'EOF'
|
||||
|
||||
; =============================================
|
||||
; Registros CNAME para DKIM (Microsoft 365)
|
||||
; Agregados: 2026-04-07 - A07.P002
|
||||
; =============================================
|
||||
selector1 IN CNAME selector1-frlr-utn-edu-ar._domainkey.o365frlrutneduar.onmicrosoft.com.
|
||||
selector2 IN CNAME selector2-frlr-utn-edu-ar._domainkey.o365frlrutneduar.onmicrosoft.com.
|
||||
|
||||
EOF
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo " ✅ Registros DKIM agregados"
|
||||
else
|
||||
echo " ❌ Error al agregar registros"
|
||||
echo " Restaurando backup..."
|
||||
cp "$BACKUP_FILE" "$ZONA_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 4. Actualizar serial de zona (incrementar último dígito)
|
||||
echo " 3. Actualizando serial de zona..."
|
||||
SERIAL_ACTUAL=$(grep -A5 'IN SOA' "$ZONA_FILE" | grep -oE '[0-9]{10}' | head -1)
|
||||
NUEVO_SERIAL=$((SERIAL_ACTUAL + 1))
|
||||
sed -i "s/$SERIAL_ACTUAL/$NUEVO_SERIAL/" "$ZONA_FILE"
|
||||
echo " Serial: $SERIAL_ACTUAL → $NUEVO_SERIAL"
|
||||
|
||||
# 5. Forzar reload de CoreDNS
|
||||
echo " 4. Forzando reload de CoreDNS..."
|
||||
docker kill --signal=HUP coredns 2>/dev/null || echo " (signal enviado)"
|
||||
echo " ✅ CoreDNS notificado"
|
||||
|
||||
echo ""
|
||||
echo "✅ ¡Completado!"
|
||||
echo ""
|
||||
echo "📋 Verificación:"
|
||||
echo " dig selector1._domainkey.frlr.utn.edu.ar CNAME"
|
||||
echo " dig selector2._domainkey.frlr.utn.edu.ar CNAME"
|
||||
echo ""
|
||||
echo "⏱️ Esperar 15-30 minutos para propagación"
|
||||
echo ""
|
||||
echo "🔐 Luego habilitar DKIM en:"
|
||||
echo " https://security.microsoft.com/dkimv2"
|
||||
@@ -0,0 +1,26 @@
|
||||
=== Backup DNS frlr.utn.edu.ar ===
|
||||
|
||||
== REGISTROS MX ==
|
||||
0 frlr-utn-edu-ar.mail.protection.outlook.com.
|
||||
|
||||
== REGISTROS TXT (SPF, DKIM, etc) ==
|
||||
"v=spf1 mx ip4:190.114.205.2 include:spf.protection.outlook.com -all"
|
||||
"MS=ms22480661"
|
||||
"google-site-verification=vZpwlfgIM_oSwlxfhvCY3zvbLEkRzHz8AFUDsVPmgzo"
|
||||
|
||||
== REGISTROS A ==
|
||||
190.114.205.20
|
||||
|
||||
== REGISTROS CNAME ==
|
||||
|
||||
== REGISTROS NS ==
|
||||
ns1.frlr.utn.edu.ar.
|
||||
ns3.utn.edu.ar.
|
||||
|
||||
== DKIM LOCAL (mail._domainkey) ==
|
||||
"v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOMtst0Un3kUnMr+azAfJ1zho2"
|
||||
"l5+Zg7E575exs30JJi5R044EqIRIB3cqxL8QN5pawb0meiHOn6D5S+LEaGKd4+jxPW"
|
||||
"WY/haivsuUP8tJuNcV53LdlpiTcBcrvtJLPCOsxgQl7XofghR8+dNEZ66uKNI+w7/"
|
||||
"7BoMeOHMivP8auwIDAQAB"
|
||||
|
||||
== DMARC ==
|
||||
@@ -0,0 +1,45 @@
|
||||
Ese mensaje de error es el "mapa del tesoro". Te está confirmando que el sistema no puede habilitar el DKIM porque todavía no has publicado los registros en el DNS de la UTN.
|
||||
|
||||
Aquí tienes los datos exactos que debes copiar y pegar en tu panel de control de DNS (donde gestionas el dominio frlr.utn.edu.ar):
|
||||
|
||||
Debes crear estos 2 registros CNAME:
|
||||
Registro 1:
|
||||
|
||||
Tipo: CNAME
|
||||
|
||||
Nombre (Host): selector1._domainkey
|
||||
|
||||
Valor (Apunta a): selector1-frlr-utn-edu-ar._domainkey.o365frlrutneduar.onmicrosoft.com
|
||||
|
||||
Registro 2:
|
||||
|
||||
Tipo: CNAME
|
||||
|
||||
Nombre (Host): selector2._domainkey
|
||||
|
||||
Valor (Apunta a): selector2-frlr-utn-edu-ar._domainkey.o365frlrutneduar.onmicrosoft.com
|
||||
|
||||
¿Dónde se hace esto?
|
||||
Vuelve a la pantalla que me mostraste antes (la del listado de registros MX, SPF y CNAME).
|
||||
|
||||
Busca un botón que diga "Agregar registro" o "Administrar DNS".
|
||||
|
||||
Carga los dos registros de arriba.
|
||||
|
||||
El paso final (Paciencia)
|
||||
Una vez que los guardes en el DNS:
|
||||
|
||||
Espera al menos 15 a 30 minutos (aunque el cartel diga 4 días, suele ser rápido, pero no instantáneo).
|
||||
|
||||
Regresa a la pantalla de DKIM (la de la imagen anterior).
|
||||
|
||||
Intenta mover el interruptor a Habilitado.
|
||||
|
||||
Nota técnica: Si el panel de DNS te pide el nombre completo, asegúrate de que sea selector1._domainkey.frlr.utn.edu.ar. Algunos paneles añaden el dominio automáticamente, otros no.
|
||||
|
||||
Cuando ese interruptor se ponga en azul, ¡Gmail dejará de rechazarte! ¿Sabes dónde está el panel para agregar estos registros nuevos?
|
||||
|
||||
Enlaces
|
||||
https://security.microsoft.com/dkimv2
|
||||
|
||||
https://admin.cloud.microsoft/?#/Domains/Details/frlr.utn.edu.ar
|
||||
+94
-17
@@ -168,21 +168,28 @@ La vista principal de la Bitácora Web ahora agrupa las entradas **por Ámbito**
|
||||
|
||||
## 🔄 Armonía Integral — Procedimiento de Actualización Documental
|
||||
|
||||
**Cuándo aplicar:** Después de cualquier cambio significativo (nueva herramienta, refactorización, cambio de estructura, plan completado, etc.).
|
||||
### ¿Qué significa "Aplicar Armonía Integral"?
|
||||
|
||||
### Checklist obligatorio (en orden):
|
||||
**Armonía Integral** es el principio rector que establece que **todo está conectado** en el ecosistema ADN. Cuando se dice "Aplicar Armonía", se refiere a:
|
||||
|
||||
| # | Documento | Qué actualizar | Cuándo |
|
||||
|---|-----------|----------------|--------|
|
||||
| 1 | **Bitácora** | Evento con resultados y próximos pasos | Siempre (antes de salir) |
|
||||
| 2 | `docs/contexto/IA.md` | Estado de drones, lecciones aprendidas, quick changes | Si hay cambios operativos o de flujo |
|
||||
| 3 | `docs/ambito/<ámbito>/<Axx>_manifiesto.md` | Tabla de planes, herramientas, nodos, estado | Si el cambio afecta al ámbito |
|
||||
| 4 | `docs/ambito/<ámbito>/<Axx.Pxxx>_plan.md` | Estado del plan, fases, hitos | Si el plan fue modificado/completado |
|
||||
> **Actualizar/optimizar los documentos correspondientes para mantener la consistencia del sistema.**
|
||||
|
||||
Si un dato cambia en un sitio, **debe propagarse** a todos los demás documentos relevantes.
|
||||
|
||||
### 📋 Qué documentos actualizar cuando se dice "Aplicar Armonía"
|
||||
|
||||
| Orden | Documento | Qué actualizar | Cuándo corresponde |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **1** | **Bitácora** | Evento con resultados y próximos pasos | **Siempre** (antes de finalizar sesión) |
|
||||
| **2** | `docs/contexto/IA.md` | Lecciones aprendidas, estado de herramientas, cambios operativos | Si hubo cambios en el flujo de trabajo o herramientas |
|
||||
| **3** | `docs/ambito/<AMBITO>/Axx_<AMBITO>.md` | Tabla de planes, herramientas, nodos, estado del ámbito | Si el cambio afecta al ámbito de trabajo |
|
||||
| **4** | `docs/ambito/<AMBITO>/Axx.Pxxx_*.md` | Estado del plan, fases completadas, hitos alcanzados | Si un plan fue modificado, completado o iniciado |
|
||||
|
||||
### Ejemplo de aplicación (2026-04-06):
|
||||
|
||||
```
|
||||
Cambio: Reestructuración de dtic-DASUTEN a estándar A03
|
||||
|
||||
→ 1. Bitácora: Evento #XXXX registrado
|
||||
→ 2. Contexto/IA.md: No requería (sin cambios operativos)
|
||||
→ 3. A04_dtic-DASUTEN.md: Manifiesto creado + tabla de planes actualizada
|
||||
@@ -226,22 +233,37 @@ Para información completa sobre todas las hebras del ADN, consultar `adn/00_ind
|
||||
| `docs/ambito/dtic-DASUTEN/` | Ámbito A04: Infraestructura DASUTEN — `./adn/tools/run dasuten` |
|
||||
| `docs/ambito/dtic-DNS/` | Ámbito A07: Administración DNS — `./adn/tools/run dns` |
|
||||
| `docs/ambito/dtic-NOTAS/` | Ámbito A08: Sistema de Notas e Informes — Web de plantillas |
|
||||
| `adn/tools/cli/dron.rb` | Vigía tareas largas — `./adn/tools/run dron` |
|
||||
| `docs/ambito/dtic-DIIAA/` | Ámbito A09: IA y Automatizaciones — `./adn/tools/run dron` |
|
||||
| `adn/tools/cli/dron.rb` | Drones: tareas background con auto-bitácora — `./adn/tools/run dron` |
|
||||
|
||||
**Última actualización:** 2026-04-07 22:30 (A08 creado - Web de notas funcional; A07.P002: Triada completa)
|
||||
**Última actualización:** 2026-04-08 09:50 (A09 creado; Drones con auto-bitácora y ámbito automático)
|
||||
**Próxima revisión:** Al inicio de cada sesión
|
||||
|
||||
---
|
||||
|
||||
## 🤖 Estado de Drones (Verificación 2026-04-06 13:17)
|
||||
## 🤖 Drones - Sistema Mejorado (2026-04-08)
|
||||
|
||||
**Flota:** 2 drones detectados → ambos completados y limpiados
|
||||
- `dron_125106_654316` — Evento #1395: "Test diario de vuelo" (`sleep 3`) → ✔ Completado (0min)
|
||||
- `dron_125505_657901` — Evento #1398: "Test diario v2" (`echo Hola...`) → ✔ Completado
|
||||
**Mejora implementada:** Auto-detección de nodo y ámbito automático en bitácora.
|
||||
|
||||
**Diagnóstico:** Drones operaron correctamente pero quedaron como "zombies" (PID muerto, estado=vigilando). El health check (`dron salud`) los auto-reparó y `dron limpiar` los removió.
|
||||
**Cambios realizados:**
|
||||
1. **Auto-detección de nodo:** El dron detecta automáticamente el nodo por hostname
|
||||
2. **Ámbito automático:** El ámbito se hereda del nodo mediante JOIN en la consulta
|
||||
3. **Unificación de vistas:** `evento:listar` muestra tanto `events` (drones) como `entradas` (tradicional)
|
||||
|
||||
**Lección:** El sistema de auto-reparación funciona. Verificar flota al inicio de sesión con `./adn/tools/run dron salud`.
|
||||
**Flujo de trabajo:**
|
||||
```bash
|
||||
# Al iniciar sesión: verificar flota
|
||||
./adn/tools/run dron salud
|
||||
./adn/tools/run dron limpiar # Si hay zombies
|
||||
|
||||
# Para tareas en background (auto-registra en bitácora)
|
||||
./adn/tools/run dron lanzar --nota "Descripción de la tarea" -- <comando>
|
||||
|
||||
# Opcional: especificar nodo manualmente
|
||||
./adn/tools/run dron lanzar --nodo srv-ns8 --nota "Tarea" -- <comando>
|
||||
```
|
||||
|
||||
**Importante:** Los drones registran automáticamente en bitácora con el ámbito correcto. Para SSH interactivo, usar `candados run`.
|
||||
|
||||
---
|
||||
|
||||
@@ -266,4 +288,59 @@ Para información completa sobre todas las hebras del ADN, consultar `adn/00_ind
|
||||
2. **Reputación de dominio** - Gmail requiere período de "calentamiento" (24-72 horas típico)
|
||||
3. **Historial del dominio** - Correos anteriores sin autenticación afectan reputación inicial
|
||||
|
||||
**Próximo paso:** Agregar DMARC con `p=none` (solo monitoreo) para completar triada de autenticación.
|
||||
**Próximo paso:** Agregar DMARC con `p=none` (solo monitoreo) para completar triada de autenticación.
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Optimización de `candados` (2026-04-08)
|
||||
|
||||
**Contexto:** La herramienta `candados.rb` es crítica para la seguridad del ecosistema. Se requiere que cualquier IA sepa usarla correctamente sin exponer contraseñas.
|
||||
|
||||
**Cambios realizados:**
|
||||
|
||||
1. **Help resumida (default):** Se muestra al ejecutar sin argumentos o con comando desconocido
|
||||
- Mensaje conciso con lo esencial
|
||||
- Los dos métodos para cargar variables (`run` y `load`)
|
||||
- Lista de comandos principales
|
||||
- Indica usar `--help` para más detalles
|
||||
|
||||
2. **Help completa (`--help` o `-h`):**
|
||||
- Explicación detallada de cada método
|
||||
- Ejemplos de uso
|
||||
- Flujo de trabajo típico
|
||||
- Advertencias de seguridad
|
||||
|
||||
3. **README actualizado:** Sincronizado con la ayuda integrada
|
||||
|
||||
**Lección:** La documentación integrada en la CLI es más efectiva que archivos externos. Cualquier IA que ejecute `candados` sin argumentos verá inmediatamente cómo usarla de forma segura.
|
||||
|
||||
**Comando para verificar:**
|
||||
```bash
|
||||
ruby adn/tools/candados/candados.rb # Help resumida
|
||||
ruby adn/tools/candados/candados.rb --help # Help completa
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Lecciones Aprendidas - DASUTEN VMs (2026-04-08)
|
||||
|
||||
**Problema:** VMs dasu-sql2 y dasu-pcv2 mostraban "running" pero sin conectividad IP ni respuesta a ping.
|
||||
|
||||
**Diagnóstico:**
|
||||
- Tabla ARP mostraba MACs en bridge pero sin respuesta ICMP
|
||||
- Tailscale reportaba "offline, last seen 1d ago"
|
||||
- qemu-guest-agent no estaba corriendo dentro de las VMs
|
||||
|
||||
**Causa probable:** Windows Server Core iniciaba pero sin stack de red activo o servicios de red fallando.
|
||||
|
||||
**Solución aplicada:** Crear VM 105 (dasu-sql3) limpia con:
|
||||
- ISO Windows Server 2022 **Español** (no Core evaluation)
|
||||
- **Discos SATA** (no VirtIO que puede causar problemas)
|
||||
- 60GB disco para SO + 120GB disco para datos SQL
|
||||
- virtio drivers integrados
|
||||
|
||||
**Nota técnica - LVM-thin:**
|
||||
- `lvcreate -L` crea volúmenes finos pero requiere espacio físico real
|
||||
- `lvcreate -V -T pool/thin` crea volúmenes thin que solo usan espacio cuando se escriben
|
||||
- Verificar con `vgs` y `lvs -a` para diagnosticar espacio
|
||||
- Thin volumes pueden tener metadata más pequeña que el tamaño virtual
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 136 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 50 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 51 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user