[A06] Resolución BUG SSH Legacy en info.rb, conclusión F1 de Compresión XVA, y actualizaciones de métricas y planes.
This commit is contained in:
@@ -12,5 +12,6 @@
|
||||
"deepseek_key": "eyJpdiI6InRCc2IvL3J4SGNsejgvL04iLCJ0YWciOiJBbjRlMTNXNlZzUGhNSWowSUU1a01nPT0iLCJkYXRhIjoiQXR4azk1aU9sb2QydzNwa0FMTHpjVkJKQTIvZU4wY1U2V2crK3NBbVd1UnRmUGh6In0=",
|
||||
"srv-ns8:rmonla": "eyJpdiI6IlJ4cHZkbFpJQWVsTTl3T1YiLCJ0YWciOiJ3WTZGMDEvNHNsZm1wZm5jMEF4Q1lBPT0iLCJkYXRhIjoibFljUHRYTm44bUxvQjhCa2tBPT0ifQ==",
|
||||
"dasu-sql3:Administrador": "eyJpdiI6IlRiMDh3U1N2aVhTcDYzbzciLCJ0YWciOiI5NVNyUDFlMXlrM0dGRlNqUXhLbEt3PT0iLCJkYXRhIjoiVkp2V09zSjczcS96UktTSlZTOFdBdz09In0=",
|
||||
"srvv-nginx-rm:rmonla": "eyJpdiI6ImFsQnk5UjhLaDFYWHJsWnciLCJ0YWciOiJJaEtCS215ekkwYVdFR0ZHNzFVN0VBPT0iLCJkYXRhIjoiUkcvSS9sMjhoQnFpRmZobFZBPT0ifQ=="
|
||||
}
|
||||
"srvv-nginx-rm:rmonla": "eyJpdiI6ImFsQnk5UjhLaDFYWHJsWnciLCJ0YWciOiJJaEtCS215ekkwYVdFR0ZHNzFVN0VBPT0iLCJkYXRhIjoiUkcvSS9sMjhoQnFpRmZobFZBPT0ifQ==",
|
||||
"srv-xen01:rmonla": "eyJpdiI6ImdURFJuRDNucGpETkxVVjAiLCJ0YWciOiJWdnNzdmRxRGthS0RnUTBWeE1xYUpRPT0iLCJkYXRhIjoiaGszT2lYRkNONVVWakFhQjdnPT0ifQ=="
|
||||
}
|
||||
|
||||
@@ -22,7 +22,9 @@ module ADN
|
||||
puerto_ssh: detectar_puerto_ssh(contenido) || 22,
|
||||
usuario: detectar_usuario(contenido),
|
||||
clave_boveda: detectar_clave_boveda(contenido),
|
||||
auth_type: detectar_auth_type(contenido)
|
||||
auth_type: detectar_auth_type(contenido),
|
||||
clave_ssh: detectar_clave_ssh(contenido),
|
||||
ssh_opts: detectar_ssh_opts(contenido)
|
||||
}
|
||||
end
|
||||
|
||||
@@ -100,8 +102,8 @@ module ADN
|
||||
# | Sistema Operativo | ... | o - **Sistema Operativo**: ...
|
||||
if contenido =~ /(?:\||\-)\s*\*\*?(?:Sistema Operativo|SO|OS)\*\*?\s*[:\|]\s*([^\|\n]+)/i
|
||||
so_raw = $1.strip
|
||||
return :linux if so_raw =~ /linux|ubuntu|debian|centos|rocky|proxmox|xen/i
|
||||
return :windows if so_raw =~ /windows|server|win/i
|
||||
return :linux if so_raw =~ /linux|ubuntu|debian|centos|rocky|proxmox/i
|
||||
end
|
||||
:linux # Default
|
||||
end
|
||||
@@ -143,6 +145,11 @@ module ADN
|
||||
end
|
||||
|
||||
def self.detectar_auth_type(contenido)
|
||||
# Detectar 'RSA Legacy' para XenServer 7.0 (OpenSSH 6.6.1)
|
||||
if contenido =~ /RSA Legacy/i
|
||||
return :rsa_legacy
|
||||
end
|
||||
# Detectar otros tipos de autenticación
|
||||
if contenido =~ /\*\*SSH\*\*:.*\((Passphrase|Password|RSA)/i
|
||||
match = $1.downcase
|
||||
return :passphrase if match == 'passphrase'
|
||||
@@ -151,5 +158,27 @@ module ADN
|
||||
end
|
||||
:password # Default para VMs Windows
|
||||
end
|
||||
|
||||
# Detectar clave SSH específica para el nodo
|
||||
def self.detectar_clave_ssh(contenido)
|
||||
# Buscar en formato: **Clave SSH**: `~/.ssh/id_rsa_xen`
|
||||
if contenido =~ /\*\*Clave SSH\*\*:\s*`?([~a-zA-Z0-9_\-\.\/]+)`?/i
|
||||
return $1
|
||||
end
|
||||
# Fallback: buscar en línea de comando SSH
|
||||
if contenido =~ /ssh -i ([~a-zA-Z0-9_\-\.\/]+)\s/i
|
||||
return $1
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
# Detectar opciones SSH especiales (para servidores legacy)
|
||||
def self.detectar_ssh_opts(contenido)
|
||||
# Buscar opciones -o PubkeyAcceptedAlgorithms o -o HostkeyAlgorithms
|
||||
if contenido =~ /(-o PubkeyAcceptedAlgorithms=[^\s`]+)\s+(-o HostkeyAlgorithms=[^\s`]+)/i
|
||||
return "#{$1} #{$2}"
|
||||
end
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+84
-3
@@ -122,8 +122,8 @@ module ADN
|
||||
contexto_str = contexto.any? ? " [#{contexto.join(' ')}]" : ""
|
||||
@logger.info("Conectando a #{nodo_nombre}#{contexto_str} (#{config[:ip]}:#{config[:puerto_ssh]})")
|
||||
|
||||
# Autorizar candados
|
||||
autorizar_candados
|
||||
# Autorizar candados solo si es necesario (password o passphrase)
|
||||
autorizar_candados unless config[:auth_type] == :rsa_legacy
|
||||
|
||||
case config[:auth_type]
|
||||
when :passphrase
|
||||
@@ -132,16 +132,26 @@ module ADN
|
||||
ejecutar_con_password(nodo_nombre, config, comando_remoto)
|
||||
when :rsa
|
||||
ejecutar_con_rsa(nodo_nombre, config, comando_remoto)
|
||||
when :rsa_legacy
|
||||
ejecutar_con_rsa_legacy(nodo_nombre, config, comando_remoto)
|
||||
end
|
||||
end
|
||||
|
||||
def obtener_config(nodo_nombre)
|
||||
meta = ADN::NodosInfo.extraer_metadata(nodo_nombre)
|
||||
return nil unless meta && meta[:ip] && meta[:usuario] && meta[:clave_boveda]
|
||||
return nil unless meta && meta[:ip] && meta[:usuario]
|
||||
|
||||
# Extraer IP de LAN si está disponible en la ficha
|
||||
meta[:ip_lan] = ADN::NodosInfo.extraer_ip_lan(nodo_nombre)
|
||||
|
||||
# Extraer clave SSH específica (para nodos con múltiples claves)
|
||||
ruta = File.join(ADN::NODOS_DIR, "#{nodo_nombre}.md")
|
||||
contenido = File.read(ruta, encoding: 'UTF-8') if File.exist?(ruta)
|
||||
if contenido
|
||||
meta[:clave_ssh] = ADN::NodosInfo.detectar_clave_ssh(contenido)
|
||||
meta[:ssh_opts] = ADN::NodosInfo.detectar_ssh_opts(contenido)
|
||||
end
|
||||
|
||||
# Inferir proxy: si el nodo tiene host (es VM), usar el host como proxy
|
||||
if meta[:host] && meta[:host] != nodo_nombre
|
||||
proxy_meta = ADN::NodosInfo.extraer_metadata(meta[:host])
|
||||
@@ -360,6 +370,77 @@ module ADN
|
||||
end
|
||||
end
|
||||
|
||||
def ejecutar_con_rsa_legacy(nodo_nombre, config, comando_remoto)
|
||||
# XenServer 7.0 (OpenSSH 6.6.1) requiere algoritmos legacy ssh-rsa (SHA-1)
|
||||
proxy_config = config[:proxy] ? ADN::NodosInfo.extraer_metadata(config[:proxy]) : nil
|
||||
|
||||
# Determinar clave SSH específica si está configurada
|
||||
clave_ssh = File.expand_path(config[:clave_ssh] || "~/.ssh/id_rsa")
|
||||
@logger.info("Usando clave SSH: #{clave_ssh}")
|
||||
|
||||
ssh_target = "#{config[:usuario]}@#{config[:ip]}"
|
||||
|
||||
# Opciones SSH legacy para OpenSSH < 7.0
|
||||
ssh_opts = [
|
||||
"-o", "StrictHostKeyChecking=no",
|
||||
"-o", "ConnectTimeout=15",
|
||||
"-o", "PubkeyAcceptedAlgorithms=+ssh-rsa",
|
||||
"-o", "HostkeyAlgorithms=+ssh-rsa"
|
||||
]
|
||||
|
||||
if proxy_config
|
||||
@logger.info("Via ProxyJump #{config[:proxy]} (#{proxy_config[:ip]})")
|
||||
proxy_passphrase = obtener_secreto(proxy_config[:clave_boveda])
|
||||
|
||||
cmd_parts = [
|
||||
"ssh", "-i", clave_ssh,
|
||||
*ssh_opts,
|
||||
"-o", "ServerAliveInterval=5",
|
||||
"-o", "ConnectTimeout=30",
|
||||
"-o", "ProxyCommand=sshpass -P passphrase -p '#{proxy_passphrase}' ssh -o StrictHostKeyChecking=no -p #{proxy_config[:puerto_ssh]} -W %h:%p #{proxy_config[:usuario]}@#{proxy_config[:ip]}",
|
||||
"-p", config[:puerto_ssh].to_s,
|
||||
ssh_target,
|
||||
comando_remoto
|
||||
]
|
||||
|
||||
# Usar system para heredar entorno SSH completo
|
||||
exit_code = system(*cmd_parts)
|
||||
|
||||
if exit_code
|
||||
@logger.exito("Comando ejecutado exitosamente en #{nodo_nombre} (RSA legacy)")
|
||||
else
|
||||
ADN::ErrorHandler.error("Fallo en ejecución remota")
|
||||
exit 1
|
||||
end
|
||||
|
||||
elsif @opciones[:interactivo]
|
||||
@logger.info("Abriendo sesión interactiva a #{nodo_nombre} (RSA legacy)...")
|
||||
cmd = "ssh -i #{clave_ssh} #{ssh_opts.join(' ')} -p #{config[:puerto_ssh]} #{ssh_target}"
|
||||
exec(cmd)
|
||||
else
|
||||
# Usar script bash para asegurar que el entorno SSH se herede correctamente
|
||||
script = <<~BASH
|
||||
#!/bin/bash
|
||||
ssh -i #{clave_ssh} #{ssh_opts.join(' ')} -p #{config[:puerto_ssh]} #{ssh_target} '#{comando_remoto.gsub("'", "'\\''")}'
|
||||
BASH
|
||||
|
||||
tmpscript = "/tmp/.adn_ssh_rsa_legacy_#{Process.pid}.sh"
|
||||
File.write(tmpscript, script)
|
||||
File.chmod(0700, tmpscript)
|
||||
|
||||
# Ejecutar con bash para heredar entorno
|
||||
exit_code = system("/bin/bash", tmpscript)
|
||||
File.delete(tmpscript) if File.exist?(tmpscript)
|
||||
|
||||
if exit_code
|
||||
@logger.exito("Comando ejecutado exitosamente en #{nodo_nombre} (RSA legacy)")
|
||||
else
|
||||
ADN::ErrorHandler.error("Fallo en ejecución remota")
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def listar_nodos
|
||||
puts "#{Color::CYAN}📡 Nodos SSH disponibles (leídos de fichas nodos/*.md):#{Color::RESET}"
|
||||
puts "#{Color::DIM}#{'─' * 75}#{Color::RESET}"
|
||||
|
||||
Reference in New Issue
Block a user