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
jetbackup5apiCLI 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 estadojetbackup5api -F runBackupJobManually— disparar backupjetbackup5api -F listBackupJobs— listar trabajosjetbackup5api -F listQueueGroups— consultar colasjetbackup5api -F stopQueueGroup— detener colaecho— test de conectividad
Todo lo demás es DENIED y registrado vía syslog.
3. Opciones SSH deshabilitadas
no-port-forwarding: impide túneles SSHno-X11-forwarding: impide forwarding X11no-agent-forwarding: impide reutilizar el agente SSHno-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