Cliente SSH Python: controla tu equipo Ubiquiti

Cliente SSH Python: controla tu equipo Ubiquiti
  • Save

Encontrarás por ahí un sinfín de herramientas para gestionar y monitorizar tus dispositivos Ubiquiti, pero ¿qué tal si te dijéramos que puedes crear tu propio cliente SSH personalizado? No solo te permitirá conectarte y ejecutar comandos en tus equipos Ubiquiti Legacy Foro, airMAX, UniFi o EdgeRouter, sino que también te facilitará la vida con una interfaz cómoda e intuitiva.



  • Save

Un cliente SSH a tu medida

Este cliente SSH, desarrollado en Python, se diferencia de otros por su enfoque en la comodidad y la sencillez. Olvídate de memorizar comandos o escribirlos a mano. Esta herramienta cuenta con una interfaz gráfica de usuario (GUI) basada en la librería CustomTkinter, que te proporciona:

  • Menú lateral con comandos predefinidos: Accede rápidamente a los comandos más utilizados para consultar el estado de la red, reiniciar dispositivos, ver información del sistema y mucho más. No necesitas recordar la sintaxis exacta, simplemente selecciona el comando deseado de la lista.
  • Cuadro de entrada de comandos: ¿Necesitas ejecutar un comando específico que no se encuentra en la lista? No hay problema. Escribe el comando directamente en el cuadro de entrada y listo.
  • Ventana de salida: Visualiza en tiempo real la respuesta de los comandos ejecutados en tus dispositivos Ubiquiti.
  • Botón de reconexión: Facilita la reconexión al dispositivo en caso de que la sesión se interrumpa.

Instala las librerías necesarias

pip install tkinter json fabric paramiko customtkinter CTkListbox

Código

# This code is licensed under the GNU General Public License v3.0 (GPLv3).
#
# You are free to use, modify, and distribute this code under the terms of the GPLv3.
# See https://www.gnu.org/licenses/gpl-3.0.html for more details.
import tkinter as tk
import json
from fabric import Connection, Config
from paramiko import AutoAddPolicy, SSHClient
import customtkinter
from CTkListbox import CTkListbox

# JSON configuration for commands utils
commands = {
    "Comandos": [
        "ping -c 4 8.8.8.8",
        "iwlist ath0 frequency",
        "iwlist ath0 channel",
        "iwlist ath0 bitrate",
        "iwlist ath0 rate",
        "iwlist ath0 auth",
        "iwconfig",
        "iwlist ath0 genie",
        "iwlist ath0 txpower",
        "reboot",
        "cat /tmp/system.cfg | grep http",
        "iwlist ath0 scan",
        "ifconfig",
        "ls /etc/",
        "ls",
        "passwd",
        "discover",
        "cp /usr/etc/system.cfg /tmp/system.cfg;",
        "save",
        "reboot"
    ]
}

# Read configuration from the config.json file
with open('./util/config.json', 'r') as f:
    config = json.load(f)

username = config['username']
password = config['password']
default_ip = config['default_ip']

# Configure SSH algorithms
preferred_kex = [
    'diffie-hellman-group1-sha1',
    'diffie-hellman-group14-sha1',
    'diffie-hellman-group-exchange-sha1'
]
preferred_ciphers = [
    'aes128-ctr', 'aes192-ctr', 'aes256-ctr',
    'aes128-cbc', '3des-cbc', 'aes192-cbc', 'aes256-cbc'
]
preferred_macs = [
    'hmac-sha1', 'hmac-md5'
]
preferred_compression = ['none']

client = SSHClient()
client.set_missing_host_key_policy(AutoAddPolicy())

class SSHClientApp(customtkinter.CTk):
    def __init__(self):
        super().__init__()
        self.title("SSH Client")
        self.geometry("1024x600")
        self.iconbitmap("ssh.ico")
        customtkinter.set_default_color_theme("dark-blue")
        customtkinter.set_appearance_mode("dark")

        self.create_widgets()
        self.conn = None
        self.connect_ssh()

    def create_widgets(self):
        self.create_top_bar()
        self.create_side_menu()
        self.create_main_body()

    def create_top_bar(self):
        self.top_bar = customtkinter.CTkFrame(self, height=50)
        self.top_bar.pack(side=tk.TOP, fill='both')

        self.menu_button = customtkinter.CTkButton(self.top_bar, text="☰", command=self.toggle_side_menu)
        self.menu_button.pack(side=tk.LEFT, padx=10)

        self.title_label = customtkinter.CTkLabel(self.top_bar, text="Consola SSH")
        self.title_label.pack(side=tk.LEFT, padx=10)

        self.mode_button = customtkinter.CTkButton(self.top_bar, text="Switch to Light Mode", command=self.switch_mode)
        self.mode_button.pack(side=tk.RIGHT, padx=10)

        self.close_button = customtkinter.CTkButton(self.top_bar, text="Cerrar", command=self.quit)
        self.close_button.pack(side=tk.RIGHT, padx=10)

    def switch_mode(self):
        current_mode = customtkinter.get_appearance_mode()
        new_mode = "light" if current_mode == "Dark" else "dark"
        customtkinter.set_appearance_mode(new_mode)
        self.mode_button.configure(text=f"Switch to {'Dark' if new_mode == 'light' else 'Light'} Mode")

        # Update colors dynamically based on the new mode
        if new_mode == "light":
            self.update_colors("#ccc", "#fff", "#aaa", "#eee", "black")
        else:
            self.update_colors("#333", "#000", "#555", "#222", "white")

    def update_colors(self, top_bar_color, main_body_color, menu_cursor_color, menu_lateral_color, text_color):
        self.top_bar.configure(fg_color=top_bar_color)
        self.main_body.configure(fg_color=main_body_color)
        self.side_menu.configure(fg_color=menu_lateral_color)
        self.profile_label.configure(fg_color=menu_lateral_color, text_color=text_color)
        self.command_listbox.configure(fg_color=menu_lateral_color, text_color=text_color)
        self.output_text.configure(fg_color=main_body_color, text_color=text_color)
        self.command_entry.configure(fg_color=main_body_color, text_color=text_color)
        self.menu_button.configure(fg_color=top_bar_color, hover_color=menu_cursor_color, text_color=text_color)
        self.title_label.configure(fg_color=top_bar_color, text_color=text_color)
        self.mode_button.configure(fg_color=top_bar_color, hover_color=menu_cursor_color, text_color=text_color)
        self.close_button.configure(fg_color=top_bar_color, hover_color=menu_cursor_color, text_color=text_color)
        self.connect_button.configure(fg_color=top_bar_color, hover_color=menu_cursor_color, text_color=text_color)

    def create_side_menu(self):
        self.side_menu = customtkinter.CTkFrame(self, width=150)
        self.side_menu.pack(side=tk.LEFT, fill='both', expand=False)

        self.profile_label = customtkinter.CTkLabel(self.side_menu, text="Comandos más usados")
        self.profile_label.pack(side=tk.TOP, pady=10)

        self.command_listbox = CTkListbox(self.side_menu)
        for command in commands["Comandos"]:
            self.command_listbox.insert(tk.END, command)
        self.command_listbox.pack(side=tk.TOP, fill='both', expand=True)
        self.command_listbox.bind('<<ListboxSelect>>', self.on_command_select)

    def create_main_body(self):
        self.main_body = customtkinter.CTkFrame(self)
        self.main_body.pack(side=tk.RIGHT, fill='both', expand=True)

        self.output_text = customtkinter.CTkTextbox(self.main_body, wrap='word')
        self.output_text.pack(padx=15, pady=15, fill='both', expand=True)

        self.command_entry = customtkinter.CTkEntry(self.main_body, placeholder_text="Command")
        self.command_entry.pack(side=tk.LEFT, padx=15, pady=5, fill='x', expand=True)
        self.command_entry.bind('<Return>', self.execute_command)

        self.connect_button = customtkinter.CTkButton(self.main_body, text="Reconectar", command=self.connect_ssh)
        self.connect_button.pack(side=tk.LEFT, padx=5, pady=5)

    def toggle_side_menu(self):
        if self.side_menu.winfo_ismapped():
            self.side_menu.pack_forget()
        else:
            self.side_menu.pack(side=tk.LEFT, fill='both', expand=False)

    def connect_ssh(self):
        try:
            configure = Config(overrides={'sudo': {'password': password}})
            self.conn = Connection(default_ip, user=username, config=configure, connect_kwargs={"password": password})
            self.append_to_output(f"Conectado a {default_ip} como {username}\n")
        except Exception as e:
            self.append_to_output(f"Error al conectar: {str(e)}\n")

    def execute_command(self, event=None):
        command = self.command_entry.get()
        self.command_entry.delete(0, tk.END)
        self.append_to_output(f"Ingresando comando: {command}\n")

        if self.conn:
            try:
                result = self.conn.run(command)
                output_text = result.stdout
                self.append_to_output(output_text)
            except Exception as e:
                self.append_to_output(f"Error al ejecutar comando: {str(e)}\n")

    def on_command_select(self, event):
        selected_command = self.command_listbox.get(self.command_listbox.curselection())
        self.command_entry.delete(0, tk.END)
        self.command_entry.insert(0, selected_command)

    def append_to_output(self, text):
        self.output_text.insert(tk.END, text)
        self.output_text.see(tk.END)

if __name__ == "__main__":
    app = SSHClientApp()
    app.mainloop()

Ruta util/config.json asegúrate de tenerla, más abajo explico del código.

{"username": "ubnt", "password": "ubnt", "default_ip": "172.16.100.1"}

Profundizando en el código Python

El código detrás de este cliente SSH personalizado está escrito en Python y se apoya en varias librerías para proporcionarte la funcionalidad deseada. Aquí te mostramos un breve resumen de los componentes principales:

  • Tkinter y CustomTkinter: Estas librerías forman la base de la interfaz gráfica de usuario. CustomTkinter ofrece una capa adicional de personalización sobre Tkinter, permitiendo una apariencia más moderna y acorde a tus gustos.
  • Fabric: Esta librería simplifica la tarea de establecer conexiones SSH y ejecutar comandos en dispositivos remotos.
  • Paramiko: Interactúa con el protocolo SSH a bajo nivel, permitiendo la comunicación segura con tus equipos Ubiquiti.

Manos a la obra

Si te animas a construir tu propio cliente SSH personalizado para gestionar tu red Ubiquiti, puedes utilizar este código como base. Ten en cuenta que deberás reemplazar los valores por defecto de la configuración (dirección IP, nombre de usuario y contraseña) por los datos correspondientes a tu red.

Además, este código se puede ampliar para incluir nuevas funcionalidades, como por ejemplo:

  • Guardar y cargar configuraciones de dispositivos.
  • Crear perfiles para diferentes equipos Ubiquiti.
  • Automatizar tareas mediante la ejecución de scripts.

Con un poco de conocimiento de Python y algo de dedicación, puedes tener tu propio cliente SSH personalizado que te ayude a sacar el máximo partido a tu red Ubiquiti.

Datos dentro del archivo JSON

El archivo JSON ./util/config.json que se utiliza en el código contiene la siguiente información:

{"username": "ubnt", "password": "ubnt", "default_ip": "172.16.100.1"}

1. Nombre de usuario (username):

  • Valor: “ubnt”
  • Descripción: Este es el nombre de usuario que se utiliza para acceder a los dispositivos Ubiquiti a través de SSH. Es importante reemplazar este valor por el nombre de usuario real que configures en tus equipos.

2. Contraseña (password):

  • Valor: “ubnt”
  • Descripción: Esta es la contraseña correspondiente al nombre de usuario “ubnt”. Al igual que el nombre de usuario, debes cambiar este valor por la contraseña real que hayas configurado en tus dispositivos Ubiquiti.

3. Dirección IP predeterminada (default_ip):

  • Valor: “172.16.100.1”
  • Descripción: Esta es la dirección IP del dispositivo Ubiquiti al que se conectará el cliente SSH de forma predeterminada. Si tu red tiene varias configuraciones de IP, asegúrate de actualizar este valor con la dirección IP correcta del dispositivo al que deseas conectarte principalmente.

Recomendaciones:

  • Seguridad: Es crucial mantener la información de tu red Ubiquiti segura. Cambia los valores por defecto de “ubnt” para el nombre de usuario y la contraseña por credenciales únicas y seguras.
  • Personalización: Si tienes varios dispositivos Ubiquiti, puedes crear un archivo JSON para cada uno con sus respectivas configuraciones de IP, nombre de usuario y contraseña.
  • Facilidad de uso: El archivo JSON facilita la configuración de la conexión SSH. Puedes editarlo fácilmente para cambiar la información de acceso a tus dispositivos.

Nota Final: Actualmente, el código no está diseñado para ejecutar editores de texto como VI o Nano, debido a que no proporciona una forma de guardar los cambios realizados durante su ejecución. Este programa está optimizado principalmente para realizar comandos breves y directos en el sistema.

Futuras Mejoras: Se contempla la posibilidad de incorporar funcionalidades adicionales que permitan la ejecución y gestión de editores de texto dentro del entorno del programa.