diff --git a/adn/tools/cli/ssh.rb b/adn/tools/cli/ssh.rb index a7b7f3b2..47426622 100644 --- a/adn/tools/cli/ssh.rb +++ b/adn/tools/cli/ssh.rb @@ -13,6 +13,7 @@ require 'optparse' require 'open3' +require 'shellwords' require_relative '../core/colores' require_relative '../core/constants' require_relative '../core/error_handler' @@ -33,7 +34,7 @@ module ADN }, 'dc-dasuten' => { ip: '10.0.100.10', - usuario: 'Administrador', + usuario: 'DASUTEN\admindasu', puerto: 7022, auth: :password, clave_boveda: 'admindasu', @@ -41,7 +42,7 @@ module ADN }, 'sql-dasuten' => { ip: '10.0.100.11', - usuario: 'Administrador', + usuario: 'DASUTEN\admindasu', puerto: 7022, auth: :password, clave_boveda: 'admindasu', @@ -49,7 +50,7 @@ module ADN }, 'pcv-dasu0' => { ip: '10.0.100.12', - usuario: 'admindasu', + usuario: 'DASUTEN\admindasu', puerto: 7022, auth: :password, clave_boveda: 'admindasu', @@ -203,32 +204,59 @@ module ADN password = obtener_secreto(config[:clave_boveda]) proxy_config = config[:proxy] ? NODOS[config[:proxy]] : nil - ssh_opts = "-o ConnectTimeout=15 -o StrictHostKeyChecking=no" + ssh_opts = "-o StrictHostKeyChecking=no -o ConnectTimeout=15" ssh_target = "#{config[:usuario]}@#{config[:ip]}" - # Si hay proxy, primero conectamos al proxy y desde ahí ejecutamos if proxy_config - @logger.info("Via proxy #{config[:proxy]} (#{proxy_config[:ip]})") + @logger.info("Via ProxyJump #{config[:proxy]} (#{proxy_config[:ip]})") proxy_passphrase = obtener_secreto(proxy_config[:clave_boveda]) - # Comando anidado: SSH al proxy → SSH al destino - inner_cmd = "sshpass -p '#{password}' ssh #{ssh_opts} -p #{config[:puerto]} #{ssh_target} '#{comando_remoto.gsub("'", "'\\''")}'" - outer_cmd = "sshpass -P 'passphrase' -p '#{proxy_passphrase}' ssh #{ssh_opts} -p #{proxy_config[:puerto]} #{proxy_config[:usuario]}@#{proxy_config[:ip]} \"#{inner_cmd.gsub('"', '\\"')}\"" + # ProxyCommand con sshpass: el proxy usa passphrase RSA, + # el destino usa password. Todo se ejecuta localmente via bash. + script = <<~BASH + export SSHPASS=#{password.shellescape} + sshpass -e ssh \ + -o StrictHostKeyChecking=no \ + -o ConnectTimeout=30 \ + -o ServerAliveInterval=5 \ + -o ProxyCommand="sshpass -P passphrase -p '#{proxy_passphrase}' ssh -o StrictHostKeyChecking=no -p #{proxy_config[:puerto]} -W %h:%p #{proxy_config[:usuario]}@#{proxy_config[:ip]}" \ + -p #{config[:puerto]} \ + '#{ssh_target}' \ + "#{comando_remoto.gsub('"', '\\"')}" + BASH + + # Escribir script a archivo temporal para evitar problemas de escape + tmpscript = "/tmp/.adn_ssh_cmd_#{Process.pid}.sh" + tmpout = "/tmp/.adn_ssh_out_#{Process.pid}" + File.write(tmpscript, script) + File.chmod(0700, tmpscript) + + system("script", "-qc", tmpscript, tmpout) + status_success = $?.success? + stdout = File.exist?(tmpout) ? File.read(tmpout).gsub(/\r\n?/, "\n").strip : "" + File.delete(tmpscript) if File.exist?(tmpscript) + File.delete(tmpout) if File.exist?(tmpout) - stdout, stderr, status = Open3.capture3(outer_cmd) else - cmd = "sshpass -p '#{password}' ssh #{ssh_opts} -p #{config[:puerto]} #{ssh_target} '#{comando_remoto.gsub("'", "'\\''")}'" - stdout, stderr, status = Open3.capture3(cmd) + ENV['SSHPASS'] = password + cmd_parts = ["sshpass", "-e", "ssh"] + + ssh_opts.split(' ') + + ["-p", config[:puerto].to_s, + ssh_target, + comando_remoto] + stdout, stderr, status = Open3.capture3(*cmd_parts) + ENV.delete('SSHPASS') end - if status.success? - puts stdout unless stdout.empty? + success = defined?(status_success) ? status_success : status.success? + + if success + puts stdout unless stdout.nil? || stdout.empty? @logger.exito("Comando ejecutado exitosamente en #{nodo_nombre}") else - puts stdout unless stdout.empty? - $stderr.puts stderr unless stderr.empty? - ADN::ErrorHandler.error("Fallo en ejecución remota (exit: #{status.exitstatus})") - exit status.exitstatus || 1 + puts stdout unless stdout.nil? || stdout.empty? + ADN::ErrorHandler.error("Fallo en ejecución remota") + exit 1 end end diff --git a/adn/tools/seguridad/.session b/adn/tools/seguridad/.session index 2c02c12f..7ffcef86 100644 --- a/adn/tools/seguridad/.session +++ b/adn/tools/seguridad/.session @@ -1 +1 @@ -1773600939 \ No newline at end of file +1773604236 \ No newline at end of file diff --git a/docs/proy/p2601_dasuten/plan/P2601_6.1.B - AD-Join: pc-dasu0 al Dominio.md b/docs/proy/p2601_dasuten/plan/P2601_6.1.B - AD-Join: pc-dasu0 al Dominio.md index fd2e1e45..8d0f36a3 100644 --- a/docs/proy/p2601_dasuten/plan/P2601_6.1.B - AD-Join: pc-dasu0 al Dominio.md +++ b/docs/proy/p2601_dasuten/plan/P2601_6.1.B - AD-Join: pc-dasu0 al Dominio.md @@ -299,6 +299,23 @@ New-NetFirewallRule -DisplayName "Dominio DASUTEN" ` --- +## 📋 Registro de Diagnósticos + +### 15/03/2026 — Diagnóstico remoto via `./adn/tools/run ssh` +**Herramienta:** CLI `ssh` (P2604-S11, nueva herramienta ADN reutilizable) + +| Nodo | Puerto 7022 | Ping | Estado | +|:-----|:-----------:|:----:|:------:| +| srv-dasu | SSH:22 ✅ | ✅ ~95ms via Tailscale | Online (uptime 4d) | +| dc-dasuten | ❌ Cerrado | ❌ timeout | VM apagada o sin SSH | +| sql-dasuten | ✅ Abierto | - | Accesible via proxy srv-dasu | +| pcv-dasu0 | ✅ Abierto | - | Accesible via proxy srv-dasu | +| pc-dasu0 | - | ❌ Offline Tailscale | Offline hace 1d | + +**Conclusión:** `sql-dasuten` y `pcv-dasu0` están activas y accesibles. `dc-dasuten` requiere atención (encender VM o verificar SSH). + +--- + **⚠️ 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. \ No newline at end of file