[Core/Infra] Provisionamiento SSH en pc-dasu0 y unificación Nginx tailscale
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# adn/tools/cli/tailscale.rb — Subcomando para gestión de identidades Tailscale
|
||||
# ==============================================================================
|
||||
# Este subcomando permite alternar entre diferentes perfiles de Tailscale
|
||||
# (ej. DASUTEN vs Personal) de forma segura utilizando candados.
|
||||
#
|
||||
# Usa `candados.rb sudo` como primitiva reutilizable para ejecutar
|
||||
# comandos con privilegios (no implementa sudo propio).
|
||||
#
|
||||
# Perfiles:
|
||||
# - dasuten: pcdasu0@frlr.utn.edu.ar (ecosistema DASUTEN)
|
||||
# - personal: rmonla@gmail.com (otros ecosistemas/topologías)
|
||||
|
||||
require_relative '../core/colores'
|
||||
|
||||
module ADN
|
||||
class SubcomandoTailscale
|
||||
CANDADOS_PATH = File.expand_path('../seguridad/candados.rb', __dir__)
|
||||
|
||||
def initialize(args, logger)
|
||||
@args = args
|
||||
@logger = logger
|
||||
end
|
||||
|
||||
def ejecutar
|
||||
if @args.empty? || @args[0] == 'ayuda'
|
||||
mostrar_ayuda
|
||||
return
|
||||
end
|
||||
|
||||
accion = @args.shift
|
||||
case accion
|
||||
when 'switch'
|
||||
ejecutar_switch
|
||||
when 'status', 'estado'
|
||||
ejecutar_status
|
||||
when 'list', 'listar'
|
||||
ejecutar_list
|
||||
when 'setup'
|
||||
ejecutar_setup
|
||||
else
|
||||
puts "#{Color::RED}✗ Acción desconocida: '#{accion}'#{Color::RESET}"
|
||||
mostrar_ayuda
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def mostrar_ayuda
|
||||
puts <<~AYUDA
|
||||
#{Color::CYAN}🌐 Gestión de Identidades Tailscale#{Color::RESET}
|
||||
|
||||
#{Color::YELLOW}Uso:#{Color::RESET} ./adn/tools/run tailscale <acción> [perfil]
|
||||
|
||||
#{Color::YELLOW}Acciones:#{Color::RESET}
|
||||
switch <perfil> Alternar a un perfil (dasuten, personal)
|
||||
status Ver estado actual de Tailscale y cuenta activa
|
||||
list Listar perfiles disponibles en Tailscale
|
||||
setup Configurar operador (una vez, evita pedir sudo)
|
||||
|
||||
#{Color::YELLOW}Perfiles:#{Color::RESET}
|
||||
dasuten Ecosistema DASUTEN (pcdasu0@frlr.utn.edu.ar)
|
||||
personal Otros ecosistemas (rmonla@gmail.com)
|
||||
|
||||
#{Color::YELLOW}Ejemplos:#{Color::RESET}
|
||||
./adn/tools/run tailscale setup
|
||||
./adn/tools/run tailscale switch dasuten
|
||||
./adn/tools/run tailscale switch personal
|
||||
./adn/tools/run tailscale status
|
||||
AYUDA
|
||||
end
|
||||
|
||||
# ─── Status ───────────────────────────────────────────────────────
|
||||
def ejecutar_status
|
||||
puts "#{Color::CYAN}🔍 Estado actual de Tailscale:#{Color::RESET}"
|
||||
puts "#{Color::DIM}#{'─' * 60}#{Color::RESET}"
|
||||
system('tailscale status')
|
||||
puts ""
|
||||
|
||||
cuenta = `tailscale status --json 2>/dev/null | jq -r '.User[].LoginName' 2>/dev/null`.strip
|
||||
if cuenta.empty? || cuenta == 'null'
|
||||
puts "#{Color::YELLOW}⚠ No se pudo determinar la cuenta activa.#{Color::RESET}"
|
||||
else
|
||||
puts "#{Color::BOLD}Cuenta activa:#{Color::RESET} #{Color::GREEN}#{cuenta}#{Color::RESET}"
|
||||
end
|
||||
|
||||
operador_ok = system('tailscale switch --list > /dev/null 2>&1')
|
||||
if operador_ok
|
||||
puts "#{Color::GREEN}✓ Operador configurado (switch sin sudo)#{Color::RESET}"
|
||||
else
|
||||
puts "#{Color::YELLOW}⚠ Operador NO configurado. Ejecutá: ./adn/tools/run tailscale setup#{Color::RESET}"
|
||||
end
|
||||
end
|
||||
|
||||
# ─── List ─────────────────────────────────────────────────────────
|
||||
def ejecutar_list
|
||||
puts "#{Color::CYAN}📋 Perfiles disponibles en Tailscale:#{Color::RESET}"
|
||||
|
||||
# Intentar sin sudo (operador configurado)
|
||||
output = `tailscale switch --list 2>&1`
|
||||
if $?.success?
|
||||
puts output
|
||||
else
|
||||
# Fallback: usar candados sudo (reutilizable)
|
||||
candados_sudo('tailscale', 'switch', '--list')
|
||||
end
|
||||
end
|
||||
|
||||
# ─── Setup ────────────────────────────────────────────────────────
|
||||
def ejecutar_setup
|
||||
@logger.info("Configurando operador Tailscale para #{ENV['USER']}...")
|
||||
puts "#{Color::CYAN}⚙️ Configurando operador Tailscale...#{Color::RESET}"
|
||||
puts "#{Color::DIM}Esto permite gestionar Tailscale sin sudo en el futuro.#{Color::RESET}"
|
||||
|
||||
success = candados_sudo('tailscale', 'set', "--operator=#{ENV['USER']}")
|
||||
|
||||
if success
|
||||
puts "#{Color::GREEN}✅ Operador configurado exitosamente.#{Color::RESET}"
|
||||
@logger.exito("Operador Tailscale configurado para #{ENV['USER']}")
|
||||
else
|
||||
puts "#{Color::RED}✗ No se pudo configurar el operador.#{Color::RESET}"
|
||||
puts " Verificá la clave sudo en la bóveda: ruby candados.rb ayuda"
|
||||
end
|
||||
end
|
||||
|
||||
# ─── Switch ───────────────────────────────────────────────────────
|
||||
def ejecutar_switch
|
||||
perfil = @args.shift
|
||||
if perfil.nil?
|
||||
puts "#{Color::RED}✗ Falta nombre del perfil (dasuten|personal)#{Color::RESET}"
|
||||
return
|
||||
end
|
||||
|
||||
case perfil.downcase
|
||||
when 'dasuten', 'd'
|
||||
switch_to('dasuten')
|
||||
when 'personal', 'p'
|
||||
switch_to('personal')
|
||||
else
|
||||
puts "#{Color::YELLOW}⚠ Perfil no reconocido. Perfiles válidos: dasuten, personal#{Color::RESET}"
|
||||
end
|
||||
end
|
||||
|
||||
def switch_to(tipo)
|
||||
@logger.info("Cambiando perfil Tailscale a: #{tipo}")
|
||||
|
||||
# 1. Obtener usuario de la bóveda
|
||||
clave_user = tipo == 'dasuten' ? 'tailscale_dasuten_user' : 'tailscale_personal_user'
|
||||
user = obtener_secreto(clave_user)
|
||||
|
||||
if user.nil? || user.empty?
|
||||
@logger.error("No se encontró '#{clave_user}' en la bóveda.")
|
||||
puts "#{Color::RED}✗ Credencial '#{clave_user}' no encontrada en candados.#{Color::RESET}"
|
||||
return
|
||||
end
|
||||
|
||||
puts "#{Color::CYAN}🔄 Cambiando a perfil: #{Color::BOLD}#{user}#{Color::RESET}..."
|
||||
|
||||
# 2. Intentar sin sudo (operador configurado)
|
||||
if system("tailscale switch '#{user}' > /dev/null 2>&1")
|
||||
exito_switch(tipo, user)
|
||||
return
|
||||
end
|
||||
|
||||
# 3. Fallback: usar candados sudo (primitiva reutilizable)
|
||||
puts "#{Color::DIM}Usando candados sudo...#{Color::RESET}"
|
||||
if candados_sudo('tailscale', 'switch', user)
|
||||
exito_switch(tipo, user)
|
||||
return
|
||||
end
|
||||
|
||||
# 4. Perfil sin sesión → login manual
|
||||
@logger.advertencia("El perfil #{tipo} no tiene sesión activa.")
|
||||
puts "#{Color::YELLOW}⚠ El perfil '#{user}' no tiene sesión en este equipo.#{Color::RESET}"
|
||||
puts " Necesitás iniciar sesión manualmente:"
|
||||
puts " #{Color::BOLD}ruby candados.rb sudo tailscale login#{Color::RESET}"
|
||||
end
|
||||
|
||||
def exito_switch(tipo, user)
|
||||
puts "#{Color::GREEN}✅ Perfil Tailscale cambiado a '#{tipo}' (#{user}).#{Color::RESET}"
|
||||
@logger.exito("Perfil Tailscale cambiado a #{tipo} (#{user})")
|
||||
puts ""
|
||||
ejecutar_status
|
||||
end
|
||||
|
||||
# ─── Utilidades ───────────────────────────────────────────────────
|
||||
|
||||
# Obtiene un secreto de la bóveda (requiere sesión abierta)
|
||||
def obtener_secreto(clave)
|
||||
output = `ruby #{CANDADOS_PATH} get #{clave} 2>/dev/null`.strip
|
||||
return nil if output.empty? || output.include?('Error') || output.include?('✗')
|
||||
output
|
||||
end
|
||||
|
||||
# Ejecuta un comando con sudo usando candados.rb sudo (primitiva reutilizable)
|
||||
def candados_sudo(*cmd_parts)
|
||||
system("ruby #{CANDADOS_PATH} sudo #{cmd_parts.join(' ')}")
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user