jetbackup-remote - Manual de uso

Orquestador remoto que serializa trabajos de backup JetBackup5 de múltiples servidores para evitar la saturación del controlador de almacenamiento. 
 El problema 
 Cuando varios servidores JetBackup5 lanzan backups simultáneamente hacia un NAS conectado por USB (controlador JMicron JMS567 RAID en Raspberry Pi), el controlador se satura y el rendimiento cae a ~1KB/s. Backups que deberían tardar 1-2 horas pasan a tardar días. 
 La solución 
 jetbackup-remote ejecuta en la Raspberry Pi y dispara los trabajos de backup uno a uno (cola FIFO), esperando a que termine cada uno antes de lanzar el siguiente. El NAS solo maneja un flujo de escritura a la vez, manteniendo el rendimiento a velocidad máxima. 
 Características 
 
 Cola FIFO : los trabajos se ejecutan uno a uno en todos los servidores 
 SSH + jetbackup5api : dispara y monitoriza trabajos remotamente vía la CLI de JetBackup5 
 SSH con restricción de comando : un script gate limita el acceso de la Pi a solo funciones autorizadas de la API 
 Timeouts no abortivos : los trabajos que exceden el timeout se reportan, nunca se matan (evita corrupción de backup) 
 Notificaciones por email : alertas en caso de fallo, timeout o finalización 
 Apagado limpio : gestión de señales SIGTERM/SIGINT 
 Fichero de bloqueo : impide ejecuciones simultáneas 
 Cero dependencias : Python stdlib puro, sin paquetes pip 
 
 Requisitos 
 
 Orquestador (Raspberry Pi): Python 3.9+, acceso SSH a los servidores 
 Servidores JetBackup: JetBackup5 con jetbackup5api CLI disponible 
 
 Instalación 
 1. Clonar el repositorio en la Raspberry Pi 
 git clone https://github.com/AichaDigital/jetbackup-remote.git
cd jetbackup-remote

 
 2. Instalar el ejecutable 
 En sistemas Debian 12+ (Bookworm) con Python gestionado externamente, crear un wrapper: 
 # Copiar el código
sudo cp -r . /opt/jetbackup-remote

# Crear wrapper en /usr/local/bin
sudo tee /usr/local/bin/jetbackup-remote << 'WRAPPER'
#!/bin/bash
exec env PYTHONPATH=/opt/jetbackup-remote/src python3 -m jetbackup_remote "$@"
WRAPPER
sudo chmod +x /usr/local/bin/jetbackup-remote

# Verificar
jetbackup-remote --help

 
 Alternativa con pip (si el sistema lo permite): 
 pip3 install .

 
 3. Configurar 
 # Crear directorio de configuración
sudo mkdir -p /etc/jetbackup-remote

# Copiar y editar config
sudo cp config.example.json /etc/jetbackup-remote/config.json
sudo nano /etc/jetbackup-remote/config.json

 
 Campos a personalizar: 
 
 
 ssh_key : ruta a la clave privada SSH (ej: /root/.ssh/id_ed25519 ) 
 
 
 servers : hostnames y puertos de los servidores JetBackup 
 
 
 jobs : IDs de los backup jobs — obtener con: 
 ssh -p PUERTO root@SERVIDOR "jetbackup5api -F listBackupJobs -O json"

 
 
 
 notification : configuración SMTP si se desean alertas por email 
 
 
 4. Configurar SSH en los servidores 
 En cada servidor JetBackup , instalar el filtro SSH que restringe lo que la Pi puede hacer: 
 # Copiar el gate script al servidor
scp -P 51514 server/jetbackup-ssh-gate.sh root@servidor:/usr/local/bin/
ssh -p 51514 root@servidor "chmod +x /usr/local/bin/jetbackup-ssh-gate.sh"

 
 Añadir la clave pública de la Pi al authorized_keys del servidor con restricción de comando: 
 # En el servidor, editar /root/.ssh/authorized_keys y añadir:
command="/usr/local/bin/jetbackup-ssh-gate.sh",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-ed25519 AAAA... root@raspberrypinas

 
 Esto asegura que aunque la clave sea comprometida, solo puede ejecutar funciones autorizadas de la API JetBackup. 
 5. Verificar conectividad 
 # Test SSH + JetBackup API en todos los servidores
jetbackup-remote test

# Solo un servidor
jetbackup-remote test --server server4

# Validar configuración
jetbackup-remote validate

# Simulación sin ejecutar backups
jetbackup-remote run --dry-run

 
 Uso de la CLI 
 La configuración por defecto se lee de /etc/jetbackup-remote/config.json . Se puede especificar otra ruta con -c : 
 jetbackup-remote -c /ruta/a/config.json COMANDO

 
 run — Ejecutar backups 
 Ejecuta los trabajos de la cola FIFO, uno a uno: 
 # Todos los trabajos de todos los servidores
jetbackup-remote run

# Solo los trabajos de un servidor
jetbackup-remote run --server server1

# Solo un trabajo específico (por ID)
jetbackup-remote run --job aaaaaaaaaaaaaaaaaaaaaaaa

# Simulación (muestra la cola sin ejecutar nada)
jetbackup-remote run --dry-run

 
 status — Ver estado 
 Consulta el estado de los trabajos de backup en cada servidor: 
 # Todos los servidores
jetbackup-remote status

# Solo un servidor
jetbackup-remote status --server server4

# Salida JSON
jetbackup-remote status --json

 
 test — Verificar conectividad 
 Prueba SSH y la API JetBackup en cada servidor: 
 jetbackup-remote test
jetbackup-remote test --server server3

 
 list — Listar trabajos 
 Lista los trabajos configurados y los disponibles en cada servidor: 
 jetbackup-remote list
jetbackup-remote list --json

 
 stop — Detener un trabajo 
 Detiene un grupo de cola en ejecución: 
 jetbackup-remote stop --server server4 QUEUE_GROUP_ID

 
 validate — Validar configuración 
 Verifica que el fichero de configuración es correcto: 
 jetbackup-remote validate

 
 Ejecución manual en tmux 
 Para ejecutar un ciclo completo (todos los servidores, todos los trabajos) en segundo plano: 
 # Crear sesión tmux
tmux new-session -d -s backup 'jetbackup-remote run'

# Conectar a la sesión para ver progreso
tmux attach -t backup

# Desconectarse sin parar (Ctrl+B, luego D)

 
 Ejecución automática con systemd 
 Copiar los ficheros de servicio: 
 sudo cp systemd/jetbackup-remote.service /etc/systemd/system/
sudo cp systemd/jetbackup-remote.timer /etc/systemd/system/

# Editar la hora si es necesario (por defecto: 02:00 diario)
sudo nano /etc/systemd/system/jetbackup-remote.timer

# Activar
sudo systemctl daemon-reload
sudo systemctl enable --now jetbackup-remote.timer

# Verificar
systemctl list-timers | grep jetbackup

 
 El timer incluye un delay aleatorio de hasta 5 minutos para evitar colisiones. 
 Flujo de ejecución 
 1. Cargar configuración

2. Adquirir lock file (fcntl.flock) → impedir ejecuciones simultáneas

3. Construir cola FIFO (servidor1/job1, servidor1/job2, servidor2/job1...)

4. Por cada trabajo:
 a. Verificar conectividad SSH (echo test)
 b. Consultar estado del job (getBackupJob)
 c. Si ya está ejecutándose, esperar; si no, disparar (runBackupJobManually)
 d. Sondear cada 30s: comprobar que running=False
 e. Si excede timeout (4h): notificar y pasar al siguiente (NO abortar)
 f. Registrar resultado

5. Enviar resumen por email (si está configurado)

6. Liberar lock file

 
 Configuración de ejemplo 
 {
 "ssh_key": "/root/.ssh/id_ed25519",

 "servers": {
 "server1": {
 "host": "server1.example.com",
 "port": 51514,
 "user": "root"
 },
 "server4": {
 "host": "server4.example.com",
 "port": 51514,
 "user": "root"
 }
 },

 "jobs": [
 {
 "job_id": "aaaaaaaaaaaaaaaaaaaaaaaa",
 "server": "server1",
 "label": "Accounts Backup",
 "type": "accounts",
 "priority": 0
 },
 {
 "job_id": "222222222222222222222222",
 "server": "server4",
 "label": "Database Backup",
 "type": "database",
 "priority": 0
 }
 ],

 "orchestrator": {
 "poll_interval": 30,
 "job_timeout": 14400,
 "lock_file": "/tmp/jetbackup-remote.lock",
 "log_file": "/var/log/jetbackup-remote.log",
 "log_max_bytes": 10485760,
 "log_backup_count": 5
 },

 "notification": {
 "enabled": false,
 "smtp_host": "localhost",
 "smtp_port": 25,
 "from_address": "jetbackup-remote@raspberrypinas",
 "to_addresses": ["admin@example.com"],
 "on_failure": true,
 "on_timeout": true,
 "on_complete": false
 }
}

 
 Parámetros del orquestador 
 
 
 
 Parámetro 
 Valor por defecto 
 Descripción 
 
 
 
 
 poll_interval 
 30 
 Segundos entre sondeos del estado del trabajo 
 
 
 job_timeout 
 14400 
 Timeout por trabajo en segundos (4 horas) 
 
 
 lock_file 
 /tmp/jetbackup-remote.lock 
 Ruta del fichero de bloqueo 
 
 
 log_file 
 /var/log/jetbackup-remote.log 
 Ruta del log 
 
 
 log_max_bytes 
 10485760 
 Tamaño máximo del log antes de rotar (10 MB) 
 
 
 log_backup_count 
 5 
 Número de ficheros de log rotados a conservar 
 
 
 
 Tipos de trabajo 
 
 
 
 Tipo 
 Descripción 
 
 
 
 
 accounts 
 Backup de cuentas de hosting 
 
 
 directories 
 Backup de directorios específicos 
 
 
 database 
 Backup de bases de datos 
 
 
 
 Modelo de seguridad 
 Capas de protección 
 1. Clave SSH con restricción de comando 
 La clave SSH de la Pi está restringida en cada servidor mediante command= en authorized_keys . Incluso si un atacante obtiene la clave privada: 
 
 No puede abrir shell en los servidores 
 No puede ejecutar comandos arbitrarios 
 No puede crear túneles ni reenviar puertos 
 Solo puede ejecutar funciones autorizadas de la API JetBackup5 
 
 2. Gate script con whitelist 
 El script jetbackup-ssh-gate.sh valida SSH_ORIGINAL_COMMAND contra una lista blanca: 
 
 jetbackup5api -F getBackupJob — consultar estado 
 jetbackup5api -F runBackupJobManually — disparar backup 
 jetbackup5api -F listBackupJobs — listar trabajos 
 jetbackup5api -F listQueueGroups — consultar colas 
 jetbackup5api -F stopQueueGroup — detener cola 
 echo — test de conectividad 
 
 Todo lo demás es DENIED y registrado vía syslog. 
 3. Opciones SSH deshabilitadas 
 
 no-port-forwarding : impide túneles SSH 
 no-X11-forwarding : impide forwarding X11 
 no-agent-forwarding : impide reutilizar el agente SSH 
 no-pty : impide obtener terminal interactiva 
 
 4. Hardening systemd 
 El servicio systemd usa: ProtectSystem=strict , PrivateTmp=true , NoNewPrivileges=true , ProtectHome=true , ProtectKernelModules=true , ProtectKernelTunables=true . 
 Auditoría 
 # Intentar comando no autorizado desde la Pi
ssh -i /root/.ssh/id_ed25519 -p 51514 root@servidor "ls /"
# Esperado: "ERROR: Command not allowed: ls /"

# Verificar en syslog del servidor
journalctl -t jetbackup-ssh-gate | tail -5

# Verificar restricciones en authorized_keys
grep "jetbackup-ssh-gate" /root/.ssh/authorized_keys

 
 Rotación de claves 
 Si se sospecha compromiso de la clave SSH: 
 # 1. En la Pi, generar nueva clave
ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519_new -N ""

# 2. En cada servidor, reemplazar la clave pública vieja
# (usar acceso alternativo, no la clave comprometida)

# 3. Verificar acceso con la nueva clave
jetbackup-remote test

# 4. Eliminar la clave vieja
rm /root/.ssh/id_ed25519_old*

 
 Logs 
 # Log del orquestador
tail -f /var/log/jetbackup-remote.log

# Log del timer systemd
journalctl -u jetbackup-remote.service -f

# Logs del gate SSH (en cada servidor)
journalctl -t jetbackup-ssh-gate

 
 Tests 
 # Ejecutar todos los tests
cd /ruta/a/jetbackup-remote
python3 -m unittest discover -s tests

# Test específico
python3 -m unittest tests.test_orchestrator

 
 Licencia 
 GNU Affero General Public License v3.0 — ver fichero LICENSE . 
 Autor 
 Abdelkarim Mateos — abdelkarim@aichadigital.es