Ir al contenido

Copias de seguridad cifradas

El SDK puede guardar su base de datos o cualquier archivo/carpeta en la bóveda zero-knowledge de Quiet Guard. Todo se cifra en su servidor antes del envío, con una clave que solo su equipo puede desbloquear. El servidor no almacena más que un blob opaco y nunca ve ni sus datos ni su clave.

Esta página describe la última versión del paquete. Los comandos y opciones añadidos después de la v0.1 indican la versión que los introdujo; si alguno responde «command not found» u «option does not exist», su instalación es anterior. Compruebe la suya con composer show quiet-guard/laravel-monitor y actualice con composer update quiet-guard/laravel-monitor.

Requisitos

  • El cifrado debe estar activado para su equipo en el dashboard de Quiet Guard (esto genera el par de claves del equipo y está protegido por una passphrase de equipo). La bóveda de copias de seguridad es una funcionalidad de pago sujeta a una cuota de almacenamiento. Consulte la documentación del servidor.
  • PHP compilado con libsodium (ext-sodium, incluida por defecto desde PHP 7.2).
  • Para las bases de datos que no sean SQLite, el binario de volcado correspondiente en el host: mysqldump (MySQL / MariaDB) o pg_dump (PostgreSQL). Las bases SQLite se copian directamente.

Crear una copia de seguridad

bash
# Guardar la conexión de base de datos por defecto
php artisan monitor:backup --database

# Guardar un archivo o una carpeta
php artisan monitor:backup --path=/var/www/storage/app

# Añadir una etiqueta legible
php artisan monitor:backup --database --name="nightly"

# Conservar solo las siete copias de base de datos más recientes
php artisan monitor:backup --database --name="nightly" --keep=7

Opciones

OpciónDescripción
--databaseGuarda la conexión de base de datos por defecto (config('database.default')).
--path=Guarda un archivo único o una carpeta entera.
--name=Etiqueta opcional almacenada con la copia de seguridad. ASCII imprimible, sin barras, 255 caracteres como máximo: la etiqueta se convierte en el nombre de archivo con el que se ofrece la descarga, y un separador de ruta no es algo que una cabecera HTTP pueda transportar. El servidor responde 422 y nombra el campo cuando rechaza una.
--keep=Desde v0.2.1. Tras un envío correcto, conserva las N copias más recientes del mismo tipo y elimina el resto. Como mínimo 1.
--exclude-table=Desde v0.5.0. Con --database, conserva la estructura de esta tabla pero deja sus filas fuera de la copia. Repetible. Solo letras, cifras, _, $ y -: un nombre calificado (base.tabla) o un comodín se rechaza, y el comando se detiene en lugar de copiar la tabla de todos modos.

Debe pasar --database o bien --path, y eso se comprueba antes de la llamada de red: a un comando sin objetivo se le dice, en lugar de responderle algo sin relación sobre el cifrado. Cuando MONITOR_ENABLED=false, el comando no hace nada.

Cuando se rechaza un paso, el comando muestra la frase del propio servidor en lugar de una suposición: un 422 que nombra el campo rechazado, un 413 que nombra la cuota de almacenamiento llena, un 403 en un plan sin copias de seguridad. Esos cuatro rechazos llegaban antes como un mismo consejo, que no arreglaba en absoluto tres de ellos.

Cómo se construye el archivo de copia

  • --database: las conexiones SQLite se copian tal cual; MySQL/MariaDB se exportan con mysqldump, PostgreSQL con pg_dump. La contraseña se pasa por el entorno (MYSQL_PWD / PGPASSWORD), nunca en la línea de comandos. El volcado se ejecuta con un timeout de 600 segundos. La copia se registra con el tipo database.
  • --path: una carpeta se empaqueta en un archivo tar (un archivo único se añade a un tar), registrado con el tipo files.

Excluir tablas

Desde v0.5.0. Algunas tablas no deben sobrevivir en una copia de seguridad: sales diarias que una política de privacidad promete destruir en pocas horas, sesiones, una caché. Una copia nocturna las conservaría durante toda la retención de la bóveda.

bash
php artisan monitor:backup --database --exclude-table=salts --exclude-table=sessions --keep=7

Se conserva la estructura de la tabla y solo se dejan fuera sus filas: una base restaurada a la que le falta una tabla rompe la aplicación que la restaura, mientras que una tabla vacía es exactamente lo que se pidió.

  • MySQL / MariaDB: dos pasadas de mysqldump, cada una con --single-transaction. La primera escribe la estructura de todas las tablas (--no-data), la segunda las filas de todas salvo las excluidas (--no-create-info --skip-triggers --ignore-table=…).
  • PostgreSQL: una pasada de pg_dump con --exclude-table-data=….
  • SQLite: la copia se vacía de las tablas excluidas y después se compacta (VACUUM); sin ello, las filas borradas seguirían legibles en las páginas libres del archivo. La base original no se toca.

Una tabla que no existe se ignora, igual que mysqldump ignora un --ignore-table que no designa nada: no hay filas que dejar fuera.

Rotar las copias antiguas

Desde v0.2.1.

En el lado del servidor nada elimina una copia de seguridad por usted: las purgas de retención cubren los datos de supervisión, no la bóveda. Una planificación nocturna llena por tanto la asignación de almacenamiento del plan y luego falla cada noche, desde el planificador, donde nadie lee. --keep=N cierra eso:

bash
php artisan monitor:backup --database --name=nightly --keep=7

Tres propiedades conviene conocerlas:

  • la rotación solo se ejecuta tras un envío correcto, de modo que la noche en que falla el volcado nunca cambia una bóveda llena por una vacía;
  • solo elimina copias del mismo tipo, así que una rotación nocturna --database deja en paz la copia de carpeta que usted tomó a mano;
  • una eliminación que falla se informa pero no hace fallar el comando: la nueva copia está enviada y a salvo, la bóveda solo está más llena de lo pedido, y decir lo contrario a un planificador equivaldría a anunciar que la copia no se hizo.

Listar lo que contiene la bóveda

Desde v0.2.1.

monitor:restore toma un identificador de copia como único argumento obligatorio, y el dashboard era el único sitio donde leerlo. monitor:backups lo imprime desde el shell:

bash
php artisan monitor:backups

Una fila por copia, la más reciente primero, con id, type, name, size y created, seguida de la línea monitor:restore para copiar. Una bóveda vacía lo dice y remite a monitor:backup --database. Si la llamada se rechaza, se muestra lo que respondió el servidor: una clave equivocada, una dirección equivocada y un plan sin copias de seguridad ya no se leen igual.

Restaurar una copia de seguridad

bash
php artisan monitor:restore <id>

# Elegir dónde escribir la salida descifrada
php artisan monitor:restore <id> --output=/tmp/restore.sql

# Proporcionar la passphrase de forma no interactiva (si no, se solicita)
php artisan monitor:restore <id> --passphrase="…"

# Descifrar un blob ya descargado desde el panel
php artisan monitor:restore --file=copia.enc --output=/tmp/restore.sql

Opciones

Argumento / opciónDescripción
id (argumento)El identificador de la copia: lo muestra monitor:backups, lo devuelve monitor:backup tras el envío, y aparece en la columna Identificador de la pestaña Copias del panel.
--output=Dónde escribir el resultado descifrado. Por defecto restored-<id>.out en la ruta base de la aplicación.
--passphrase=La passphrase del equipo. Se solicita de forma segura (entrada oculta) si se omite.
--file=Aún sin publicar. Descifra un blob que ya ha descargado, en lugar de ir a buscarlo. El identificador pasa a ser opcional; su archivo nunca se elimina.

El botón Descargar del panel entrega el archivo .enc, es decir, el blob sellado: el servidor no guarda nada más y nada lo abre a mano. --file= es lo que lo descifra. El material de clave se sigue pidiendo al servidor, porque la clave privada solo existe envuelta por su passphrase y nunca se almacena localmente; sin identificador, la suma de comprobación no se compara con nada, y el comando lo dice en lugar de dejarlo suponer.

Antes de descifrar nada, el cliente compara el blob descargado con el sha256 que publica el servidor para él. Una suma que no coincide se detiene ahí, con un mensaje propio, y no se escribe nada en --output. Una descarga dañada fallaba antes con «Decryption failed», es decir, exactamente lo que produce una passphrase equivocada, y esas dos causas no se reparan de la misma manera. Cuando el servidor no publica suma para esa copia, o la lista está fuera de alcance, la restauración continúa: esta comprobación protege de una transferencia dañada, no es una autorización.

La salida descifrada es el archivo en bruto: un volcado SQL (o un archivo SQLite) para las copias --database, o un archivo tar para las copias --path. Como la salida por defecto es restored-<id>.out, pase --output con una extensión apropiada para usarla directamente, por ejemplo --output=restore.sql o --output=files.tar.

Cómo funciona el cifrado

Las copias de seguridad usan un cifrado híbrido en flujo, compartido con todos los demás clientes Quiet Guard a través del núcleo quiet-guard/monitor-php:

  1. Una clave simétrica aleatoria cifra el archivo con el secretstream de libsodium (AEAD XChaCha20-Poly1305 en bloques de 64 KiB); los volcados grandes se procesan así en flujo, sin cargarse nunca enteros en memoria.
  2. Esa clave simétrica se sella hacia la clave pública X25519 de su equipo (crypto_box_seal). La clave sellada se antepone al blob.
  3. El blob cifrado se envía a /api/v1/backups. El servidor lo almacena de forma opaca, con sus metadatos (tipo, nombre, tamaño), y aplica su cuota de almacenamiento.

Para restaurar, el SDK recupera su material de clave desde /api/v1/encryption-key (clave pública, clave privada envuelta por la passphrase y sal KDF), y luego desbloquea la clave privada localmente a partir de su passphrase: una derivación Argon2id (límites MODERATE) reproduce la clave de cifrado de claves y abre una secretbox de libsodium, exactamente igual que hace la envoltura en el lado del servidor. La clave desbloqueada abre la clave simétrica sellada, que descifra el flujo.

Es zero-knowledge: la passphrase y la clave privada desbloqueada nunca salen de su servidor, y el operador no puede leer jamás sus copias de seguridad. La contrapartida: una passphrase perdida significa una copia de seguridad irrecuperable, no existe ningún restablecimiento.

Planificación

Planifique copias de seguridad nocturnas de la base de datos desde el planificador de su aplicación:

php
use Illuminate\Support\Facades\Schedule;

Schedule::command('monitor:backup --database --name=nightly --keep=7')->dailyAt('02:00');

Pase --keep en una copia planificada, salvo que elimine las antiguas de otra forma. Sin ello la bóveda solo crece, y lo primero que sabrá de la asignación de almacenamiento será la noche en que todas las copias empiecen a fallar.

Relación con la bóveda

La bóveda, la cuota en GB y la interfaz del blob cifrado viven en el lado del servidor. Consulte la documentación del servidor para explorar las copias de seguridad, descargar el blob cifrado en bruto y gestionar el almacenamiento.

Está leyendo la documentación Laravel SDK v1.0.