[P2601/P2604] Herramienta SSH ADN con ProxyJump + diagnóstico remoto
Herramienta CLI ssh (P2604-S11):
- Soporte passphrase RSA (srv-dasu) ✅ verificado
- ProxyJump para VMs DASUTEN via script+tty
- Usuarios corregidos a DASUTEN\admindasu
- Timeouts ajustados (30s connect, 5s alive)
- Listado de nodos: ./adn/tools/run ssh --listar
Plan P2601 6.1.B actualizado:
- Registro diagnóstico 15/03: sql/pcv puertos abiertos, dc cerrado
- kex_exchange via ProxyCommand pendiente investigación
- Conexión manual desde srv-dasu funciona
Eventos: 1075 (S11 herramienta), 1076 (diagnóstico)
This commit is contained in:
+46
-18
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user