Pulsa ESC para cerrar

Curso WordpressCurso Wordpress

Respaldos cifrados de cPanel a Google Drive gratis con rclone

Un respaldo que vive en el mismo servidor no sirve si el servidor se pierde, se daña o es comprometido. La solución es tener una copia fuera, y una cuenta gratuita de Google Drive (15 GB) alcanza para varias tiendas pequeñas.

Usaremos rclone para subir los respaldos cifrados: en Drive solo se ven archivos ilegibles, y solo se abren con tus claves. Y subir-respaldos.sh lo deja automático: sube, verifica, ordena y avisa si algo falla.

Antes de empezar

  • Necesitas un VPS o servidor dedicado con cPanel/WHM y acceso root (desde WHM → Terminal basta).
  • Los scripts se probaron en AlmaLinux 10 con cPanel 138, PHP 8.3 de EasyApache (/opt/cpanel/ea-php83) y WP-CLI instalado en /usr/local/bin/wp. Si tu servidor usa otra versión de PHP, cambia la ruta ea-php83 en el script.
  • Pruébalos primero en un horario tranquilo y lee lo que hace cada uno antes de ejecutarlo. Se comparten tal cual, sin garantía: adáptalos a tu servidor.

Necesitas además una cuenta de Google dedicada solo a los respaldos (recomendado) y respaldos de cPanel funcionando (ver la entrada anterior).

1. Instalar rclone

curl -s https://rclone.org/install.sh | bash
rclone version | head -1

2. Crear tu propio acceso en Google Cloud

rclone trae un acceso compartido a Google Drive, pero tiene límites y Google lo restringe cada vez más. Lo recomendable es crear uno propio (es gratis):

  1. Entra a https://console.cloud.google.com con la cuenta de los respaldos y crea un proyecto nuevo.
  2. En APIs y servicios → Biblioteca, habilita Google Drive API.
  3. En Google Auth Platform (pantalla de consentimiento): tipo Externo, completa el nombre de la app, tu correo, la página principal y la de privacidad de tu sitio, y el dominio autorizado. Sin estos datos, el botón para publicar la app queda gris.
  4. En Acceso a datos, agrega el permiso .../auth/drive.file: rclone solo podrá ver y modificar los archivos que él mismo cree, no el resto de tu Drive.
  5. En Público, pulsa Publicar app. Si la dejas «en prueba», la autorización vence cada 7 días.
  6. En Clientes, crea un cliente OAuth de tipo App de escritorio y guarda su ID y su secreto en tu gestor de contraseñas.

El ID y el secreto del cliente, el código de autorización y las claves de cifrado son credenciales: no los pegues en chats, correos ni foros.

3. Configurar rclone en el servidor

Corre rclone config y crea un remoto nuevo llamado gdrive, de tipo drive. Pega tu client_id y client_secret, elige el permiso drive.file y responde n a «Use web browser». rclone te mostrará un comando rclone authorize "drive" "...": córrelo en un computador con navegador y rclone instalado, entra con la cuenta de los respaldos y pega en el servidor el código que te devuelve.

Después crea el remoto cifrado sobre una carpeta de Drive. Inventa dos claves largas y guárdalas en tu gestor de contraseñas: sin ellas, los respaldos no se pueden abrir.

rclone config create gdrive-cifrado crypt remote=gdrive:respaldos-servidor \
  filename_encryption=off directory_name_encryption=false \
  password=$(rclone obscure "CLAVE_LARGA_1") password2=$(rclone obscure "CLAVE_LARGA_2")
rclone lsd gdrive: && echo "Conexion con Drive OK"

Con filename_encryption=off los nombres de carpetas y archivos quedan visibles (para ubicar las fechas), pero el contenido va cifrado.

4. El script: subir-respaldos.sh

  • Sube el último respaldo diario de las cuentas que elijas a diarios/FECHA y el último mensual a mensual/FECHA.
  • Después de subir cada archivo, lo compara con el original (rclone cryptcheck) y deja un COMPLETO.txt como marca de subida terminada.
  • Deja en Drive los últimos 3 diarios y 1 mensual, con un tope de espacio (TOPE_GB). Si se pasa, borra el diario más antiguo, nunca el último.
  • Si algo falla (sin conexión, autorización vencida o archivo que no coincide), te avisa por correo.
  • Tiene dos modos más: estado (qué hay en Drive) y bajar FECHA CUENTA (descarga y descifra un respaldo).

Instalación

Entra a WHM → Terminal (como root) y pega este bloque completo de una vez. Crea el archivo /root/subir-respaldos.sh, le da permisos y comprueba que quedó bien escrito:

cat > /root/subir-respaldos.sh <<'EOF'
#!/bin/bash
# Copia cifrada de los respaldos de las tiendas a Google Drive (rclone, remoto "gdrive-cifrado").
# Guarda en Drive los ultimos 3 respaldos diarios y el mensual mas reciente, con un tope de espacio.
# Uso: /root/subir-respaldos.sh                 sube lo nuevo y ordena Drive (lo corre el cron cada dia)
#      /root/subir-respaldos.sh estado          muestra que hay en Drive
#      /root/subir-respaldos.sh bajar FECHA CUENTA   descarga y descifra un respaldo a /root/restaurar-drive
CUENTAS="cuenta1 cuenta2 cuenta3"    # usuarios de cPanel que se copian a Drive
MANTENER=3
TOPE_GB=13
REMOTO="gdrive-cifrado:"
DIR=${BACKUP_DIR:-/backup}
LOG=${LOG:-/var/log/subir-respaldos.log}
AVISO=root
TOPE=$((TOPE_GB*1024*1024*1024))
h(){ numfmt --to=iec --suffix=B "$1"; }
log(){ echo "$(date '+%F %T') $1" | tee -a "$LOG"; }
fallo(){ log "ERROR: $1"
  printf "To: %s\nSubject: [$(hostname -s)] FALLO la copia de respaldos a Google Drive\n\n%s\n\nUltimas lineas del registro (%s):\n%s\n" \
    "$AVISO" "$1" "$LOG" "$(tail -15 "$LOG")" | /usr/sbin/sendmail -t 2>/dev/null; exit 1; }
listar(){ rclone lsf --dirs-only "$REMOTO$1" 2>/dev/null | tr -d / | grep -E '^20[0-9]{2}-[0-9]{2}-[0-9]{2}$' | sort; }

case "$1" in
estado)
  echo "=== Respaldos en Google Drive"; for t in diarios mensual; do for f in $(listar $t/); do
    echo "$t/$f  $(rclone size --json "$REMOTO$t/$f" 2>/dev/null | grep -oE '"bytes":[0-9]+' | cut -d: -f2 | xargs -I{} numfmt --to=iec --suffix=B {})  $(rclone cat "$REMOTO$t/$f/COMPLETO.txt" 2>/dev/null | head -1)"; done; done
  echo -n "=== Total: "; rclone size "$REMOTO" 2>/dev/null | grep -i "total size"; exit 0 ;;
bajar)
  [ -n "$2" ] && [ -n "$3" ] || { echo "Uso: $0 bajar FECHA CUENTA   (ej: $0 bajar 2026-10-18 cuenta1)"; exit 1; }
  for t in diarios mensual; do rclone lsf "$REMOTO$t/$2/$3.tar.gz" >/dev/null 2>&1 && SRC="$REMOTO$t/$2/$3.tar.gz"; done
  [ -n "$SRC" ] || { echo "No existe $2/$3.tar.gz en Drive. Ver: $0 estado"; exit 1; }
  mkdir -p /root/restaurar-drive && rclone copy -P "$SRC" /root/restaurar-drive/$2/ && echo "Descargado y descifrado en /root/restaurar-drive/$2/$3.tar.gz"; exit 0 ;;
esac

pgrep -f "cpbackup|pkgacct|bin/backup" >/dev/null 2>&1 && { log "Hay un respaldo en curso: se intentara mas tarde"; exit 0; }
rclone lsd "$REMOTO" >/dev/null 2>&1 || rclone mkdir "$REMOTO" >/dev/null 2>&1 || fallo "No hay conexion con Google Drive (sin internet o autorizacion vencida: repetir 'rclone config reconnect gdrive:')"

subir(){ # $1 carpeta local del respaldo, $2 destino (diarios|mensual)
  local D="$1" F; F=$(basename "$D"); local DEST="$REMOTO$2/$F"
  rclone lsf "$DEST/COMPLETO.txt" >/dev/null 2>&1 && return 0
  log "Subiendo $2/$F"
  local n=0 tot=0
  for U in $CUENTAS; do
    local A="$D/accounts/$U.tar.gz"
    [ -f "$A" ] || { log "  (no existe $U.tar.gz en $F, se omite)"; continue; }
    rclone copy --retries 5 --low-level-retries 20 "$A" "$DEST/" >>"$LOG" 2>&1 || fallo "No se pudo subir $U.tar.gz de $F"
    rclone cryptcheck "$D/accounts" "$DEST" --include "$U.tar.gz" >>"$LOG" 2>&1 || fallo "La copia de $U.tar.gz de $F no coincide con el original"
    n=$((n+1)); tot=$((tot+$(stat -c %s "$A")))
  done
  [ $n -gt 0 ] || fallo "El respaldo $F no tiene ninguna de las cuentas: $CUENTAS"
  echo "Completo: $n cuentas, $(h $tot), subido $(date '+%F %T')" | rclone rcat "$DEST/COMPLETO.txt" || fallo "No se pudo cerrar $F"
  log "  listo: $n cuentas, $(h $tot)"
}

ULT=$(ls -1d "$DIR"/20??-??-?? 2>/dev/null | sort | tail -1)
[ -n "$ULT" ] || fallo "No hay respaldos diarios en $DIR"
subir "$ULT" diarios
MEN=$(ls -1d "$DIR"/monthly/20??-??-?? 2>/dev/null | sort | tail -1)
[ -n "$MEN" ] && subir "$MEN" mensual

# limpiar: dejar los ultimos $MANTENER diarios completos y 1 mensual; borrar subidas incompletas antiguas
for F in $(listar diarios/); do rclone lsf "${REMOTO}diarios/$F/COMPLETO.txt" >/dev/null 2>&1 || { [ "$F" != "$(basename "$ULT")" ] && rclone purge "${REMOTO}diarios/$F" && log "Borrada subida incompleta diarios/$F"; }; done
L=($(listar diarios/)); while [ ${#L[@]} -gt $MANTENER ]; do rclone purge "${REMOTO}diarios/${L[0]}" && log "Borrado de Drive: diarios/${L[0]}"; L=("${L[@]:1}"); done
M=($(listar mensual/)); while [ ${#M[@]} -gt 1 ]; do rclone purge "${REMOTO}mensual/${M[0]}" && log "Borrado de Drive: mensual/${M[0]}"; M=("${M[@]:1}"); done
# tope de espacio: si se pasa, borrar el diario mas antiguo (nunca el ultimo)
T=$(rclone size --json "$REMOTO" 2>/dev/null | grep -oE '"bytes":[0-9]+' | cut -d: -f2)
L=($(listar diarios/))
while [ "${T:-0}" -gt "$TOPE" ] && [ ${#L[@]} -gt 1 ]; do
  rclone purge "${REMOTO}diarios/${L[0]}" && log "Borrado por tope de ${TOPE_GB}GB: diarios/${L[0]}"; L=("${L[@]:1}")
  T=$(rclone size --json "$REMOTO" 2>/dev/null | grep -oE '"bytes":[0-9]+' | cut -d: -f2)
done
log "En Drive: ${#L[@]} diarios + $(listar mensual/ | wc -l) mensual, total $(h ${T:-0}) de ${TOPE_GB}GB"
EOF
chmod 700 /root/subir-respaldos.sh && bash -n /root/subir-respaldos.sh && echo SCRIPT_OK

Si al final aparece SCRIPT_OK, el script está listo. Si no aparece, el bloque se pegó incompleto o con comillas cambiadas: vuelve a pegarlo.

Ajusta al comienzo del script CUENTAS (los usuarios de cPanel que quieres copiar), MANTENER, TOPE_GB (deja margen bajo los 15 GB gratis) y AVISO (a quién avisar; root llega al correo de contacto del servidor). Pruébalo a mano y luego prográmalo:

/root/subir-respaldos.sh
/root/subir-respaldos.sh estado
(crontab -l 2>/dev/null; echo "30 9 * * * /root/subir-respaldos.sh >/dev/null 2>&1") | crontab - && crontab -l | grep subir

La hora del cron es la del servidor (normalmente UTC): elige una hora en que los respaldos de cPanel ya hayan terminado.

5. Prueba que puedes recuperar

Un respaldo que nunca se probó no es un respaldo. Descarga uno y compáralo con el original:

/root/subir-respaldos.sh bajar AAAA-MM-DD cuenta1
cmp /root/restaurar-drive/AAAA-MM-DD/cuenta1.tar.gz /backup/AAAA-MM-DD/accounts/cuenta1.tar.gz && echo "RESPALDO RECUPERABLE OK"
rm -rf /root/restaurar-drive

Si el servidor se pierde

Desde cualquier equipo con Linux: instala rclone, crea gdrive con tu mismo cliente de Google y crea gdrive-cifrado con las mismas dos claves. Luego descarga con rclone copy -P gdrive-cifrado:diarios/AAAA-MM-DD ./recuperado/ y restaura los .tar.gz en el servidor nuevo desde WHM → Transfers → Restore a Full Backup/cpmove File.

Renovar el secreto o la autorización

  1. En Google Cloud → Clientes → tu cliente → Agregar secreto.
  2. En el servidor: rclone config update gdrive client_secret NUEVO_SECRETO.
  3. Reautoriza: rclone config reconnect gdrive: (Refresh token: y; Use web browser: n) y completa el rclone authorize desde tu computador.
  4. Comprueba: rclone lsd gdrive: && /root/subir-respaldos.sh estado.
  5. Recién entonces inhabilita y borra el secreto anterior en Google Cloud.