feat: Automating node scanning (scripts/alta_nodo.rb, candado.rb) and updated ontology
This commit is contained in:
@@ -21,3 +21,5 @@ logs/
|
|||||||
tmp/
|
tmp/
|
||||||
.temp/
|
.temp/
|
||||||
.cache/
|
.cache/
|
||||||
|
scripts/.agent_token
|
||||||
|
scripts/.agent_token
|
||||||
|
|||||||
@@ -4,6 +4,16 @@ Este documento registra la evolución, cambios y eventos significativos en el se
|
|||||||
|
|
||||||
> **Referencias:** ✅ Completado | ⏳ En Proceso | 🧪 Verificado | ⚠️ Alerta | 📍 Definido
|
> **Referencias:** ✅ Completado | ⏳ En Proceso | 🧪 Verificado | ⚠️ Alerta | 📍 Definido
|
||||||
|
|
||||||
|
## 2026-02-18
|
||||||
|
|
||||||
|
| Hora | Nodo | Detalle |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| 00:55 | Grid | ✅ **Documentación ONTO**: Se actualiza `01_ontologia.md` y se crean fichas para `srv-xen01` y `cam-hikvision-24`. |
|
||||||
|
| 00:45 | cam-hikvision-24 | ⚠️ **Sonda de Campo**: **Detección HTTP**. IP `10.0.10.24`. Puerto 80 abierto (Hikvision Login), SSH cerrado. Rol: Cámara/DVR. |
|
||||||
|
| 00:35 | srv-xen01 | ✅ **Sonda de Campo**: **Corrección de Rol**. IP `10.0.10.23`. Identificado como **Citrix XenServer 7.0** (previamente catalogado como Debian). Evidencia forense de procesos Xen y Open vSwitch. |
|
||||||
|
| 00:10 | Grid | 🔒 **Protocolo de Seguridad**: Implementación de **Candado JIT** (`scripts/candado.rb`). Autorización temporal (5 min) basada en desafío de passphrase SSH. El Agente requiere token activo para operar sondas. |
|
||||||
|
| 00:00 | Grid | 🔧 **Automatización**: Despliegue de `scripts/alta_nodo.rb` (Ruby). Sonda de relevamiento autónoma con detección de roles y soporte SSH legacy. |
|
||||||
|
|
||||||
## 2026-02-16
|
## 2026-02-16
|
||||||
|
|
||||||
| Hora | Nodo | Detalle |
|
| Hora | Nodo | Detalle |
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ Este documento define la identidad, propósito y componentes principales del ser
|
|||||||
- **[srvv-UPTIME](nodos/srvv-UPTIME.md)** (`10.0.10.9`): LXC Debian 12 / Monitorización (⚠️ Disk 77%).
|
- **[srvv-UPTIME](nodos/srvv-UPTIME.md)** (`10.0.10.9`): LXC Debian 12 / Monitorización (⚠️ Disk 77%).
|
||||||
- **[srvv-DATA](nodos/srvv-DATA.md)** (`10.0.10.13`): LXC Debian 12 / Datos.
|
- **[srvv-DATA](nodos/srvv-DATA.md)** (`10.0.10.13`): LXC Debian 12 / Datos.
|
||||||
- **[srvv-SITIO2](nodos/srvv-SITIO2.md)** (`10.0.10.19`): LXC Debian 12 / Web Secundario (Acceso `root`).
|
- **[srvv-SITIO2](nodos/srvv-SITIO2.md)** (`10.0.10.19`): LXC Debian 12 / Web Secundario (Acceso `root`).
|
||||||
|
- **[srv-xen01](nodos/srv-xen01.md)** (`10.0.10.23`): **Citrix XenServer 7.0** / Hypervisor (4 Cores Xeon).
|
||||||
|
- **[cam-hikvision-24](nodos/cam-hikvision-24.md)** (`10.0.10.24`): Disp. Hikvision / Cámara o DVR (Detectado vía HTTP).
|
||||||
- **[srvv-DNS](nodos/srvv-DNS.md)** (`10.0.10.2`): VM Debian 12 / Servidor DNS.
|
- **[srvv-DNS](nodos/srvv-DNS.md)** (`10.0.10.2`): VM Debian 12 / Servidor DNS.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Nodo: cam-hikvision-24 (Detectado)
|
||||||
|
|
||||||
|
## Información General
|
||||||
|
- **Hostname**: `cam-hikvision-24` (Asignado provisionalmente)
|
||||||
|
- **IP**: `10.0.10.24`
|
||||||
|
- **Rol**: **Cámara IP / DVR** (Hipótesis Alta)
|
||||||
|
- **Sistema Operativo**: Firmware Propietario (Hikvision Embedded)
|
||||||
|
- **Estado**: 🟢 Online (HTTP Accesible)
|
||||||
|
|
||||||
|
## Evidencia de Sondeo
|
||||||
|
- **SSH**: 🔴 Cerrado / Rechazado.
|
||||||
|
- **HTTP (Puerto 80)**: 🟢 Abierto.
|
||||||
|
- **Firma Web**: Redirección a `/doc/page/login.asp`.
|
||||||
|
- **Fabricante Probable**: Hikvision.
|
||||||
|
|
||||||
|
## Observaciones
|
||||||
|
- Este nodo no admite gestión SSH estándar.
|
||||||
|
- Se recomienda aislar en VLAN de videovigilancia si es posible.
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Nodo: srv-xen01
|
||||||
|
|
||||||
|
## Información General
|
||||||
|
- **Hostname**: `srv-xen01`
|
||||||
|
- **IP**: `10.0.10.23`
|
||||||
|
- **Rol**: **Citrix XenServer Hypervisor** (Detectado)
|
||||||
|
- **Sistema Operativo**: XenServer 7.0.0-125380c
|
||||||
|
- **Kernel**: Linux (Dom0)
|
||||||
|
- **Estado**: 🟢 Online (Relevado el 2026-02-18)
|
||||||
|
|
||||||
|
## Hardware (Dom0)
|
||||||
|
- **CPU**: Intel(R) Xeon(R) CPU E31230 @ 3.20GHz (4 Cores)
|
||||||
|
- **RAM**: ~658 MB (Asignada a Dom0) / Total Físico Desconocido (Probable 16GB+ dado el rol)
|
||||||
|
- **Almacenamiento**:
|
||||||
|
- `sda`: 298,1G (WDC WD3200AAKS-0)
|
||||||
|
- `sdb`: 931,5G (WDC WD10EZEX-00W) - Posible Storage Repository (SR).
|
||||||
|
- `sdc`: 931,5G (WDC WD10EZEX-00W) - Posible Storage Repository (SR).
|
||||||
|
- `tda`: 120G
|
||||||
|
- `tdb`: 64G
|
||||||
|
|
||||||
|
## Evidencia Forense (Roles Detectados)
|
||||||
|
- **Hypervisor**: ✅ **Citrix XenServer** (Marcadores: `/etc/xen`, `/opt/xensource/bin/xe`).
|
||||||
|
- **Gestión**: `xapi` (XenAPI), `xenopsd`.
|
||||||
|
- **Red**: `ovs-vswitchd` (Open vSwitch).
|
||||||
|
- **Puertos**: 80, 443 (Gestión Web/API), 22 (SSH), 5900-5902 (VNC Consoles para VMs).
|
||||||
|
|
||||||
|
## Bitácora de Alta
|
||||||
|
- **Fecha**: 2026-02-18
|
||||||
|
- **Acción**: Re-Sondeo con Script `alta_nodo.rb`.
|
||||||
|
- **Descubrimiento**: El nodo fue identificado previamente como Debian 12 (falso positivo o re-instalación), pero la evidencia actual confirma **Citrix XenServer 7.0**.
|
||||||
Executable
+231
@@ -0,0 +1,231 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
# script: alta_nodo.rb
|
||||||
|
# Uso: ruby scripts/alta_nodo.rb <IP> <HOSTNAME> [USUARIO]
|
||||||
|
# Descripción: Automatiza la configuración SSH y la documentación del ADN para un nuevo nodo.
|
||||||
|
|
||||||
|
require 'fileutils'
|
||||||
|
require 'date'
|
||||||
|
require 'json'
|
||||||
|
require 'time'
|
||||||
|
|
||||||
|
# --- Configuración ---
|
||||||
|
ADN_DIR = File.expand_path('../adn', __dir__)
|
||||||
|
NODOS_DIR = File.join(ADN_DIR, 'nodos')
|
||||||
|
ONTOLOGIA_FILE = File.join(ADN_DIR, '01_ontologia.md')
|
||||||
|
SSH_CONFIG_FILE = File.expand_path('~/.ssh/config')
|
||||||
|
SSH_PUB_KEY = File.expand_path('~/.ssh/id_rsa.pub')
|
||||||
|
|
||||||
|
# --- Argumentos ---
|
||||||
|
ip = ARGV[0]
|
||||||
|
user = ARGV[1] || 'rmonla'
|
||||||
|
|
||||||
|
unless ip
|
||||||
|
puts "❌ Error: Falta la IP."
|
||||||
|
puts "Uso correcto: ruby scripts/alta_nodo.rb <IP> [USUARIO]"
|
||||||
|
puts "Ejemplo: ruby scripts/alta_nodo.rb 10.0.10.205 rmonla"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
puts "--- 🚀 Iniciando Alta de Nodo: #{ip} con Usuario: #{user} ---"
|
||||||
|
|
||||||
|
# --- 🚨 PROTOCOLO DE SEGURIDAD (CANDADO JIT) ---
|
||||||
|
TOKEN_FILE = File.expand_path('../.agent_token', __dir__)
|
||||||
|
|
||||||
|
def verificar_autorizacion!
|
||||||
|
unless File.exist?(TOKEN_FILE)
|
||||||
|
puts "\n⛔ ACCESO DENEGADO: No se encontró token de autorización."
|
||||||
|
puts "🔒 Protocolo de seguridad activo. El Agente requiere permiso explícito."
|
||||||
|
puts "👉 Ejecuta: ruby scripts/candado.rb"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
token = JSON.parse(File.read(TOKEN_FILE))
|
||||||
|
expires_at = Time.parse(token['expires_at'])
|
||||||
|
|
||||||
|
if Time.now > expires_at
|
||||||
|
puts "\n⛔ ACCESO DENEGADO: El token de autorización ha EXPIRADO."
|
||||||
|
puts "💀 Expiró a las: #{expires_at.strftime('%H:%M:%S')}"
|
||||||
|
puts "👉 Ejecuta nuevamente: ruby scripts/candado.rb"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
puts "🔓 Autorización Verificada (Válida hasta #{expires_at.strftime('%H:%M:%S')})"
|
||||||
|
rescue => e
|
||||||
|
puts "\n❌ Error al verificar token: #{e.message}"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
verificar_autorizacion!
|
||||||
|
# -----------------------------------------------
|
||||||
|
|
||||||
|
# Variable para trackear si tenemos acceso sin password/interactivo
|
||||||
|
ssh_access_ok = false
|
||||||
|
|
||||||
|
# Detectamos si necesitamos flags legacy
|
||||||
|
# Intentamos primero con flags modernos, si falla, probamos con legacy
|
||||||
|
ssh_flags = "-o ConnectTimeout=5 -o StrictHostKeyChecking=no"
|
||||||
|
ssh_legacy_flags = "#{ssh_flags} -o PubkeyAcceptedAlgorithms=+ssh-rsa -o HostKeyAlgorithms=+ssh-rsa"
|
||||||
|
|
||||||
|
# --- 0. Detección de Hostname y Verificación de Acceso ---
|
||||||
|
puts "\n🔍 0. Detectando Hostname y Verificando Acceso..."
|
||||||
|
begin
|
||||||
|
# Probamos conexión con BatchMode para ver si ya entramos sin password
|
||||||
|
check_cmd = "ssh -o BatchMode=yes #{ssh_legacy_flags} #{user}@#{ip} hostname 2>/dev/null"
|
||||||
|
hostname = `#{check_cmd}`.strip
|
||||||
|
|
||||||
|
if $?.success? && !hostname.empty?
|
||||||
|
puts "✅ Acceso SSH verificado (Sin contraseña)."
|
||||||
|
puts "✅ Hostname detectado: #{hostname}"
|
||||||
|
ssh_access_ok = true
|
||||||
|
else
|
||||||
|
puts "⚠️ Acceso directo no disponible. Intentando conexión interactiva (pedirá password)..."
|
||||||
|
# Usamos flags legacy aquí también por si acaso
|
||||||
|
hostname = `ssh #{ssh_legacy_flags} #{user}@#{ip} hostname`.strip
|
||||||
|
|
||||||
|
if $?.success? && !hostname.empty?
|
||||||
|
puts "✅ Hostname detectado: #{hostname}"
|
||||||
|
else
|
||||||
|
puts "❌ No se pudo obtener el hostname. Verifica la IP y credenciales."
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
rescue => e
|
||||||
|
puts "❌ Error al intentar conectar: #{e.message}"
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# --- 1. Configuración SSH Local ---
|
||||||
|
puts "\n🔧 1. Configurando SSH Local..."
|
||||||
|
|
||||||
|
if File.read(SSH_CONFIG_FILE).include?("Host #{hostname}")
|
||||||
|
puts "ℹ️ Host '#{hostname}' ya existe en #{SSH_CONFIG_FILE}."
|
||||||
|
else
|
||||||
|
puts "📝 Agregando '#{hostname}' a #{SSH_CONFIG_FILE}..."
|
||||||
|
File.open(SSH_CONFIG_FILE, 'a') do |f|
|
||||||
|
f.puts "\nHost #{hostname}"
|
||||||
|
f.puts " HostName #{ip}"
|
||||||
|
f.puts " User #{user}"
|
||||||
|
f.puts " StrictHostKeyChecking no"
|
||||||
|
f.puts " PubkeyAcceptedAlgorithms +ssh-rsa"
|
||||||
|
f.puts " HostKeyAlgorithms +ssh-rsa"
|
||||||
|
end
|
||||||
|
puts "✅ Configuración SSH actualizada (con soporte legacy)."
|
||||||
|
end
|
||||||
|
|
||||||
|
# --- 2. Copia de Claves SSH ---
|
||||||
|
if ssh_access_ok
|
||||||
|
puts "\n🔑 2. Copia de Claves SSH: OMITIDO (Ya tienes acceso)."
|
||||||
|
else
|
||||||
|
puts "\n🔑 2. Instalando Clave SSH en #{user}@#{ip}..."
|
||||||
|
if File.exist?(SSH_PUB_KEY)
|
||||||
|
# ssh-copy-id pedirá password una única vez aquí
|
||||||
|
# Pasamos opciones legacy explícitamente
|
||||||
|
system("ssh-copy-id #{ssh_legacy_flags} -i #{SSH_PUB_KEY} #{user}@#{ip}")
|
||||||
|
|
||||||
|
if $?.success?
|
||||||
|
puts "✅ Clave instalada exitosamente."
|
||||||
|
ssh_access_ok = true
|
||||||
|
else
|
||||||
|
puts "⚠️ Falló ssh-copy-id. Quizás debas hacerlo manualmente o verificar password."
|
||||||
|
end
|
||||||
|
else
|
||||||
|
puts "❌ No se encontró clave pública en #{SSH_PUB_KEY}."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
require 'json'
|
||||||
|
|
||||||
|
# --- 3. Relevamiento de Hardware (Extracción Cruda & Procesamiento Local) ---
|
||||||
|
puts "\n🔍 3. Extrayendo Datos Crudos del Nodo (Procesamiento Local)..."
|
||||||
|
|
||||||
|
# Script remoto minimalista: Solo "cat" de archivos clave y lsblk
|
||||||
|
# Usamos delimitadores únicos para separar bloques
|
||||||
|
# Agregamos recolección de evidencia para detección de roles
|
||||||
|
remote_script = <<~SHELL
|
||||||
|
echo "---START_HOSTNAME---"; cat /etc/hostname
|
||||||
|
echo "---START_CPUINFO---"; cat /proc/cpuinfo
|
||||||
|
echo "---START_MEMINFO---"; cat /proc/meminfo
|
||||||
|
echo "---START_OSRELEASE---"; cat /etc/os-release
|
||||||
|
echo "---START_LSBLK---"; lsblk -d -o NAME,SIZE,MODEL,TYPE -J 2>/dev/null || lsblk -d -o NAME,SIZE,MODEL,TYPE
|
||||||
|
echo "---START_PROCESSES---"; ps -ef --sort=-%mem | head -n 20
|
||||||
|
echo "---START_PORTS---"; ss -tuln 2>/dev/null || netstat -tuln 2>/dev/null
|
||||||
|
echo "---START_MARKERS---"
|
||||||
|
[ -d "/etc/xen" ] && echo "xen_config_dir_exists"
|
||||||
|
[ -d "/etc/pve" ] && echo "pve_config_dir_exists"
|
||||||
|
[ -d "/var/lib/docker" ] && echo "docker_lib_dir_exists"
|
||||||
|
[ -f "/usr/bin/dockerd" ] && echo "dockerd_bin_exists"
|
||||||
|
[ -f "/opt/xensource/bin/xe" ] && echo "citrix_xe_bin_exists"
|
||||||
|
SHELL
|
||||||
|
|
||||||
|
safe_script = remote_script.gsub('"', '\\"')
|
||||||
|
# Usamos flags legacy para la sonda también
|
||||||
|
raw_output = `ssh #{ssh_legacy_flags} #{hostname} "#{safe_script}"`
|
||||||
|
|
||||||
|
# Parsers Locales (Host-Side Logic)
|
||||||
|
def parse_block(output, key)
|
||||||
|
return "" unless output.include?("---START_#{key}---")
|
||||||
|
output.split("---START_#{key}---")[1].split("---START_")[0].strip
|
||||||
|
rescue
|
||||||
|
""
|
||||||
|
end
|
||||||
|
|
||||||
|
hostname_raw = parse_block(raw_output, 'HOSTNAME')
|
||||||
|
cpuinfo_raw = parse_block(raw_output, 'CPUINFO')
|
||||||
|
meminfo_raw = parse_block(raw_output, 'MEMINFO')
|
||||||
|
osrelease_raw = parse_block(raw_output, 'OSRELEASE')
|
||||||
|
lsblk_raw = parse_block(raw_output, 'LSBLK')
|
||||||
|
processes_raw = parse_block(raw_output, 'PROCESSES')
|
||||||
|
ports_raw = parse_block(raw_output, 'PORTS')
|
||||||
|
markers_raw = parse_block(raw_output, 'MARKERS')
|
||||||
|
|
||||||
|
# Analizador de CPU (Local)
|
||||||
|
cpu_model = cpuinfo_raw.scan(/model name\s+:\s+(.*)/).flatten.first || "Unknown"
|
||||||
|
cpu_cores = cpuinfo_raw.scan(/^processor\s+:/).count
|
||||||
|
cpu_arch = "x86_64" # Default (podría sacarse de flags en cpuinfo 'lm' = long mode)
|
||||||
|
|
||||||
|
# Analizador de RAM (Local)
|
||||||
|
mem_total_kb = meminfo_raw.scan(/MemTotal:\s+(\d+)\s+kB/).flatten.first.to_i
|
||||||
|
mem_total_human = "#{(mem_total_kb / 1024.0 / 1024.0).round(2)} GiB"
|
||||||
|
|
||||||
|
# Analizador de OS (Local)
|
||||||
|
os_distro = osrelease_raw.scan(/PRETTY_NAME="(.*)"/).flatten.first || "Unknown Linux"
|
||||||
|
os_kernel_ver = "Linux (Kernel ver. unknown)" # Kernel version requiring 'uname -r', keeping simple for now or parsing from /proc/version if needed later
|
||||||
|
|
||||||
|
params = {}
|
||||||
|
params[:timestamp] = Time.now.to_s
|
||||||
|
params[:connector] = { ip: ip, user: user, hostname_detected: hostname_raw.empty? ? hostname : hostname_raw }
|
||||||
|
|
||||||
|
params[:cpu] = { model: cpu_model, cores: cpu_cores, arch: cpu_arch }
|
||||||
|
params[:ram] = { total: mem_total_human, raw: mem_total_kb * 1024 }
|
||||||
|
params[:os] = { distro: os_distro, kernel: os_kernel_ver } # Simplified
|
||||||
|
|
||||||
|
# Procesamiento de Discos (Local)
|
||||||
|
if lsblk_raw.strip.start_with?("{")
|
||||||
|
begin
|
||||||
|
params[:disks] = JSON.parse(lsblk_raw)
|
||||||
|
rescue
|
||||||
|
params[:disks] = { format: 'raw', content: lsblk_raw }
|
||||||
|
end
|
||||||
|
else
|
||||||
|
params[:disks] = { format: 'text', content: lsblk_raw }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Evidencia para Inteligencia de Agente
|
||||||
|
params[:evidence] = {
|
||||||
|
processes: processes_raw.lines.map(&:strip),
|
||||||
|
ports: ports_raw.lines.map(&:strip),
|
||||||
|
file_markers: markers_raw.lines.map(&:strip)
|
||||||
|
}
|
||||||
|
|
||||||
|
puts " ✅ Datos crudos extraídos y procesados localmente."
|
||||||
|
|
||||||
|
# --- 4. Salida para el Agente (STDOUT) ---
|
||||||
|
puts "\n💾 4. Telemetría Final (Para consumo del Agente)..."
|
||||||
|
puts "---START_PAYLOAD---"
|
||||||
|
puts JSON.pretty_generate(params)
|
||||||
|
puts "---END_PAYLOAD---"
|
||||||
|
|
||||||
|
puts "\n✅ Proceso finalizado. Copia el bloque JSON anterior si necesitas procesarlo manualmente, o deja que el Agente lo lea."
|
||||||
|
|
||||||
Executable
+51
@@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
# script: candado.rb
|
||||||
|
# Uso: ruby scripts/candado.rb [minutos]
|
||||||
|
# Descripción: Genera un token de autorización temporal para que el Agente pueda ejecutar tareas críticas.
|
||||||
|
# Requiere que el usuario tenga claves SSH cargadas (prueba de identidad).
|
||||||
|
|
||||||
|
require 'json'
|
||||||
|
require 'fileutils'
|
||||||
|
|
||||||
|
TOKEN_FILE = File.expand_path('../.agent_token', __dir__)
|
||||||
|
DEFAULT_DURATION_MINS = 5
|
||||||
|
|
||||||
|
# 1. Validación de Identidad (Proof of User Presence)
|
||||||
|
puts "🔐 Requerimiento de Protocolo: Autorización FRESCA."
|
||||||
|
puts "🧹 Limpiando identidades previas en el agente SSH para exigir validación..."
|
||||||
|
system("ssh-add -D > /dev/null 2>&1") # Borra claves cacheadas para forzar el prompt
|
||||||
|
|
||||||
|
puts "🔑 Por favor, ingresa tu passphrase para autorizar al Agente:"
|
||||||
|
# Intentamos cargar las claves interactivamente (ahora sí pedirá pass porque borramos las anteriores)
|
||||||
|
system("ssh-add")
|
||||||
|
|
||||||
|
# Verificamos que la carga haya sido exitosa
|
||||||
|
system("ssh-add -l > /dev/null 2>&1")
|
||||||
|
unless $?.success?
|
||||||
|
puts "❌ ERROR: No se detectaron identidades SSH."
|
||||||
|
puts " Debes completar el desafío de identidad (passphrase) para continuar."
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# 2. Generación del Token
|
||||||
|
duration = (ARGV[0] || DEFAULT_DURATION_MINS).to_i
|
||||||
|
expires_at = Time.now + (duration * 60)
|
||||||
|
|
||||||
|
token_data = {
|
||||||
|
created_at: Time.now.to_s,
|
||||||
|
expires_at: expires_at.to_s,
|
||||||
|
duration_mins: duration,
|
||||||
|
authorized_by: ENV['USER'] || 'unknown',
|
||||||
|
signature: rand(36**8).to_s(36) # Firma aleatoria simple
|
||||||
|
}
|
||||||
|
|
||||||
|
File.write(TOKEN_FILE, JSON.pretty_generate(token_data))
|
||||||
|
FileUtils.chmod(0600, TOKEN_FILE) # Solo lectura para el usuario
|
||||||
|
|
||||||
|
puts "\n🔓 VACANCIA DE SEGURIDAD ACTIVADA"
|
||||||
|
puts "--------------------------------"
|
||||||
|
puts "✅ Agente AUTORIZADO para operaciones críticas."
|
||||||
|
puts "⏳ Ventana de tiempo: #{duration} minutos."
|
||||||
|
puts "💀 Expira: #{expires_at.strftime('%H:%M:%S')}"
|
||||||
|
puts "🔑 Token: #{TOKEN_FILE}"
|
||||||
|
puts "\n>> Ahora puedes pedirle al Agente que ejecute sus tareas."
|
||||||
Reference in New Issue
Block a user