Ir al contenido
Q
QuoteNode

Wiki

Copia de seguridad y recuperación

Cómo QuoteNode gestiona las copias de seguridad de base de datos y archivos: archivos cifrados con age, restauración guiada por manifiesto, kits de recuperación y perfiles de ejecución.

Copia de seguridad y recuperación

QuoteNode incluye un sistema de copias de seguridad integrado que protege tus datos comerciales frente a fallos de hardware, eliminaciones accidentales e incidentes operativos.

Qué se incluye en la copia de seguridad

Cada copia de seguridad crea un archivo que contiene:

  1. Volcado de la base de datos — un volcado completo de PostgreSQL en formato personalizado (pg_dump --format=custom), que incluye todas las tablas, secuencias y restricciones.
  2. Almacenamiento de archivos — todos los archivos subidos (imágenes de productos, logotipos de empresa) y los PDF generados, archivados como files.tar.gz con rutas relativas.
  3. Manifiesto de la copia de seguridad — un archivo backup-manifest.json que contiene la versión de origen, el modo de cifrado, el modo de carga útil de la base de datos, las sumas de comprobación y los metadatos de compatibilidad de restauración.
  4. Sumas de comprobación de integridad — un archivo checksums.sha256 que contiene los hashes SHA-256 de todos los componentes del archivo.

Cifrado del archivo

Las nuevas copias de seguridad cifradas usan el cifrado por destinatario age (AGE_RECIPIENT). Es el único modelo de cifrado admitido para los archivos nuevos.

  • El proceso de copia de seguridad usa únicamente un destinatario age público (age1...) — no se necesita ninguna clave privada en el servidor para crear copias de seguridad.
  • Los archivos cifrados tienen la extensión .tar.age.
  • La suma de comprobación SHA-256 del archivo cifrado se almacena junto al archivo para su verificación por parte del operador.
  • El descifrado requiere la identidad age privada correspondiente, que se conserva exclusivamente en el kit de recuperación del operador.
  • La identidad age privada en bruto nunca se almacena en la base de datos, los registros, los DTO ni los argumentos del proceso tras la confirmación de la configuración.

Copias de seguridad locales sin cifrar

Las copias de seguridad locales sin cifrar (.tar) solo se permiten cuando se cumplen todas las condiciones siguientes:

  • BACKUP_ARCHIVE_ENCRYPTION_MODE=NONE
  • BACKUP_ALLOW_UNENCRYPTED_LOCAL_BACKUP=true
  • El destino es el almacenamiento local
  • BACKUP_DATABASE_PAYLOAD_MODE=APP_ENCRYPTED
  • El administrador ha reconocido explícitamente el riesgo en la interfaz

Los archivos sin cifrar se bloquean para el almacenamiento remoto y para las cargas útiles con PII descifradas.

Configuración (.env). El destinatario age lo genera el kit de recuperación (Administración → Seguridad → Copia de seguridad) y se almacena en la instancia; no colocas ninguna clave en el .env. Define únicamente las dos variables de modo:

BACKUP_ENABLED=true
BACKUP_ARCHIVE_ENCRYPTION_MODE=AGE_RECIPIENT
BACKUP_DATABASE_PAYLOAD_MODE=APP_ENCRYPTED
# Alternative — encrypted archive + decrypted PII (cross-instance portability):
# BACKUP_DATABASE_PAYLOAD_MODE=PII_DECRYPTED
BACKUP_ALLOW_UNENCRYPTED_LOCAL_BACKUP=false

Para desactivar el cifrado del archivo (solo local, no recomendado — los PII siguen cifrados en la carga útil):

BACKUP_ARCHIVE_ENCRYPTION_MODE=NONE
BACKUP_ALLOW_UNENCRYPTED_LOCAL_BACKUP=true

Con AGE_RECIPIENT, si aún no se ha generado ningún destinatario, la copia de seguridad falla de forma explícita en lugar de escribir un archivo sin proteger. La combinación NONE + PII_DECRYPTED se rechaza.

Modos de carga útil de la base de datos

Modo Descripción
APP_ENCRYPTED Predeterminado. Los campos PII permanecen cifrados con la clave de la aplicación DB_ENCRYPTION_KEY. La restauración requiere la misma clave de cifrado o una huella de clave coincidente.
PII_DECRYPTED Los campos PII se descifran en una base de datos temporal antes del archivado. Los hashes de las contraseñas de usuario permanecen sin cambios. Requiere el cifrado de archivo AGE_RECIPIENT. Útil para la portabilidad entre instancias.

Al restaurar una copia de seguridad PII_DECRYPTED en un destino con ENCRYPT_PII=true, el proceso de restauración vuelve a cifrar automáticamente los PII con la DB_ENCRYPTION_KEY del destino antes del arranque normal de la aplicación.

Kit de recuperación

Durante la configuración, el administrador genera (o importa) un destinatario age para las copias de seguridad cifradas. El kit de recuperación es un archivo ZIP que contiene:

  • quotenode-age-identity.txt — la identidad age privada (se devuelve solo una vez)
  • quotenode-age-recipient.txt — el destinatario público y la huella
  • quotenode-backup-verify.sh — script de verificación local (Linux/macOS)
  • quotenode-backup-verify.ps1 — script de verificación local (Windows)
  • README.md — comandos de verificación y restauración

Crítico: La identidad age privada se devuelve solo una vez durante la generación del kit. Si se pierde, los archivos cifrados no podrán descifrarse. Guarda el kit de recuperación en una ubicación sin conexión segura.

El administrador debe confirmar que el kit se ha guardado y probado antes de que el asistente de configuración marque la copia de seguridad como lista para producción.

Verificación local de copias de seguridad

Las copias de seguridad descargadas pueden verificarse fuera de la aplicación en ejecución mediante los scripts del kit de recuperación:

scripts/quotenode-backup-verify.sh \
  --archive /path/backup.tar.age \
  --identity /path/quotenode-age-identity.txt \
  --expected-sha256 <sha256-from-ui-or-sidecar>

El verificador:

  1. Comprueba la suma de comprobación SHA-256 del archivo cifrado frente al valor esperado o al archivo adjunto .sha256.
  2. Descifra con age -d en un directorio temporal privado.
  3. Valida las entradas tar (rechaza rutas absolutas, .., enlaces simbólicos y archivos de dispositivo).
  4. Comprueba que backup-manifest.json existe y tiene una versión admitida.
  5. Ejecuta sha256sum -c checksums.sha256.
  6. Ejecuta pg_restore --list db.dump cuando pg_restore está disponible.
  7. Elimina por defecto los datos temporales descifrados (usa --keep-decrypted para conservarlos).

Copia de seguridad en línea (predeterminada)

Las copias de seguridad en línea se ejecutan mientras la aplicación atiende solicitudes. No hay tiempo de inactividad.

  • pg_dump en formato personalizado crea una instantánea coherente a nivel transaccional sin bloquear las tablas ni las consultas.
  • El almacenamiento de archivos se archiva en paralelo — los archivos subidos y los PDF son inmutables tras su creación, por lo que el archivo siempre es coherente.
  • PostgreSQL no necesita detenerse para obtener un volcado de base de datos coherente.

Cómo se ejecutan las copias de seguridad: elegir una topología

Hay dos formas de ejecutar de forma continua las copias de seguridad programadas. Ambas producen archivos idénticos — solo se diferencian en dónde se ejecuta la tarea de copia de seguridad.

Topología Cuándo usarla Contenedores
Worker dedicado (predeterminado) Producción. Las copias de seguridad se ejecutan en un contenedor backup-worker independiente, por lo que nunca compiten con el tráfico web por memoria o CPU. backend web + backup-worker
En proceso Portátil / evaluación / instalaciones pequeñas de un solo host. El backend web ejecuta también las copias de seguridad — un contenedor menos que operar. solo backend web

La tarea de copia de seguridad en sí es el mismo pipeline de shell (pg_dump → archivado → cifrado) en ambos casos, ejecutado fuera del heap de la JVM, por lo que su coste de memoria es modesto.

Cómo seleccionar una topología

Dos variables de entorno funcionan en conjunto:

  • JOBS_MODE decide qué contenedor ejecuta realmente la tarea de copia de seguridad programada.
  • BACKUP_RUNTIME_PROFILE es una etiqueta que se registra en los registros y manifiestos de las copias de seguridad para que puedas ver, a posteriori, cómo se produjo cada copia.
Topología Backend web Worker de copia de seguridad
Worker dedicado (predeterminado) JOBS_MODE=web + BACKUP_RUNTIME_PROFILE=WORKER contenedor JOBS_MODE=backup-only
En proceso JOBS_MODE=all + BACKUP_RUNTIME_PROFILE=WEB_MAINTENANCE (ninguno)

El archivo Compose de producción incluido usa la topología de worker dedicado. Las guías de portátil/evaluación usan en proceso (JOBS_MODE=all + BACKUP_RUNTIME_PROFILE=WEB_MAINTENANCE) para que solo ejecutes un contenedor de backend.

Referencia de perfiles de ejecución

BACKUP_RUNTIME_PROFILE acepta cuatro valores. Los dos primeros son para las topologías continuas anteriores; los dos últimos, para copias de seguridad puntuales gestionadas por el operador.

Perfil Descripción
WORKER Predeterminado. Un contenedor backup-worker permanente ejecuta las copias de seguridad programadas mediante cron.
WEB_MAINTENANCE El backend web ejecuta directamente las copias de seguridad programadas. No se necesita ningún contenedor worker adicional.
ONE_SHOT Un cron externo o un operador ejecuta scripts/backup-one-shot.sh y finaliza. Sin worker permanente.
OFFLINE_MAINTENANCE Un script automatizado anuncia la inactividad, detiene frontend/backend/backup-worker, mantiene PostgreSQL en ejecución, ejecuta la copia de seguridad, reinicia los servicios y verifica su estado.

Copia de seguridad puntual (one-shot)

COMPOSE_PROJECT_NAME=quotenode COMPOSE_ENV_FILE=infra/.env.prod \
  BACKUP_ARCHIVE_ENCRYPTION_MODE=AGE_RECIPIENT \
  BACKUP_AGE_RECIPIENT=age1... \
  bash scripts/backup-one-shot.sh

Mantenimiento sin conexión

COMPOSE_PROJECT_NAME=quotenode COMPOSE_ENV_FILE=infra/.env.prod \
  BACKUP_OFFLINE_NOTICE_SECONDS=300 \
  bash scripts/backup-offline-maintenance.sh

El script de mantenimiento sin conexión:

  1. Anuncia un mantenimiento programado (período de gracia configurable).
  2. Opcionalmente habilita una página de mantenimiento estática mientras los servicios están detenidos.
  3. Detiene el frontend, el backend y el backup-worker (mantiene PostgreSQL en ejecución).
  4. Ejecuta una copia de seguridad puntual.
  5. Reinicia todos los servicios.
  6. Verifica el estado del backend.
  7. Escribe un diario de mantenimiento JSONL a nivel de host.

Si el script falla en cualquier punto, una trampa de seguridad reinicia los servicios automáticamente.

Usa --dry-run para previsualizar el plan sin ejecutarlo:

bash scripts/backup-offline-maintenance.sh --dry-run

Programación

  • Programación predeterminada: todos los días a las 2:00 (0 0 2 * * *, configurable mediante BACKUP_CRON)
  • Activar/desactivar: BACKUP_ENABLED=true|false
  • Activación manual: los administradores pueden activar una copia de seguridad inmediata desde el panel de administración o mediante la API (POST /api/v1/admin/backup/trigger). El activador responde de inmediato (HTTP 202) mientras la copia de seguridad se ejecuta de forma asíncrona.
  • Protección frente a concurrencia: un bloqueo respaldado por la base de datos garantiza que solo se ejecute una copia de seguridad a la vez en todos los workers y activaciones manuales.

Para los perfiles ONE_SHOT y OFFLINE_MAINTENANCE, el planificador interno está desactivado — las copias de seguridad se gestionan externamente.

Opciones de almacenamiento

Almacenamiento local (predeterminado)

Las copias de seguridad se almacenan en BACKUP_LOCAL_DIR (predeterminado: /app/data/backups), mapeado a un volumen de Docker.

En producción, las copias de seguridad locales deben complementarse con copias remotas.

Almacenamiento remoto mediante rclone

Si BACKUP_RCLONE_REMOTE está configurado, las copias de seguridad se suben automáticamente a un destino remoto tras su creación local. rclone admite más de 70 proveedores de almacenamiento en la nube, incluidos los compatibles con S3, Google Cloud Storage, Azure Blob, SFTP y WebDAV.

Descarga de copias de seguridad

La descarga de archivos de copia de seguridad requiere autorización reforzada (step-up):

  1. Ve a Configuración > Copia de seguridad.
  2. Haz clic en Descargar en la entrada de copia de seguridad.
  3. Introduce tu contraseña actual (y el código TOTP si la MFA está activada).
  4. El sistema emite una concesión de descarga de un solo uso y de corta duración (predeterminado: 120 segundos).
  5. El archivo se descarga automáticamente.

El token de concesión se almacena únicamente como hash SHA-256. La creación de la concesión y la descarga se auditan y se notifica a los demás administradores.

Nota: una descarga directa sin concesión devuelve 403. Las copias de seguridad en almacenamiento remoto no se pueden descargar a través del panel de administración.

Metadatos y registro de auditoría

Cada copia de seguridad se registra en la tabla backup_logs con:

  • Estado (RUNNING, SUCCESS, FAILED)
  • Iniciada por (SCHEDULER o ADMIN)
  • Hora de inicio/finalización
  • Tamaño (bytes) y suma de comprobación SHA-256
  • Destino (ruta local o URL remota)
  • Versión del manifiesto y versión de la app de origen
  • Modo de cifrado del archivo y huella
  • Modo de carga útil de la base de datos
  • Perfil de ejecución
  • Estado de verificación (NOT_RUN, PASSED, FAILED, SKIPPED_TIMEOUT)
  • Estado de la prueba de restauración y estado de compatibilidad de restauración

Restauración

Uso de scripts/restore.sh

El script de restauración canónico gestiona el ciclo de vida completo:

COMPOSE_PROJECT_NAME=quotenode \
  COMPOSE_ENV_FILE=infra/.env.prod \
  RESTORE_AGE_IDENTITY_FILE=/path/to/quotenode-age-identity.txt \
  [email protected] \
  RESTORE_ADMIN_PASSWORD_FILE=/path/to/password \
  bash scripts/restore.sh /path/to/backup.tar.age

El script:

  1. Extrae y valida el archivo (manifiesto, sumas de comprobación, seguridad de las entradas tar).
  2. Ejecuta comprobaciones previas (capacidad de disco, versión de PostgreSQL, conectividad con la base de datos).
  3. Crea una copia de seguridad de protección del destino antes de cualquier operación destructiva.
  4. Escribe un diario de restauración a nivel de host (JSONL, sobrevive al reemplazo de la base de datos).
  5. Restaura la base de datos.
  6. Prepara los archivos en un directorio temporal y los intercambia en su lugar.
  7. Conserva la identidad del destino (ID de instancia, metadatos de cifrado de copias de seguridad, estado de la licencia).
  8. Borra las sesiones, los enlaces públicos de ofertas y los tokens de notificación.
  9. Restablece o fuerza el cambio del acceso de administrador.
  10. Vuelve a cifrar los PII si la copia de seguridad es PII_DECRYPTED y el destino tiene ENCRYPT_PII=true.

Restauración entre instancias

Al restaurar una copia de seguridad de una instalación diferente, el modo predeterminado es PRESERVE_TARGET_IDENTITY:

  • El ID de instancia de destino, el destinatario age de las copias de seguridad, los metadatos del kit de recuperación y el estado de la licencia se conservan.
  • Las sesiones, los enlaces públicos de ofertas y los tokens de notificación se borran.
  • La contraseña de administrador se restablece/se fuerza su cambio.
  • Los datos de negocio se reemplazan por completo (sin fusión).

Compatibilidad de restauración

La comprobación previa de restauración valida:

  • Copia de seguridad más antigua en una app más nueva: permitido (compatible).
  • Copia de seguridad más nueva en una app más antigua: bloqueado.
  • Manifiesto ausente: bloqueado.
  • Huella de clave de cifrado incorrecta para una carga útil APP_ENCRYPTED: bloqueado antes del trabajo destructivo.
  • PII_DECRYPTED sin cifrado de archivo: bloqueado.

Garantía de acceso de administrador

La restauración garantiza el acceso de administrador mediante una de estas opciones:

  • RESTORE_ADMIN_PASSWORD_FILE — contraseña proporcionada por el operador (definida con forcePasswordChange=true)
  • RESTORE_ADMIN_PASSWORD — solo para uso local/de pruebas
  • Token de restablecimiento de un solo uso generado automáticamente — impreso en la consola y en el diario a nivel de host

Ejecución de prueba (dry run)

RESTORE_DRY_RUN=true bash scripts/restore.sh /path/to/backup.tar.age

Valida el archivo, comprueba las sumas de comprobación y el manifiesto, e informa del plan de restauración sin modificar nada.

Recuperación ante desastres — reconstrucción desde cero

Tu servidor ha desaparecido. Tienes un archivo de copia de seguridad (.tar.age o .tar) y un servidor nuevo con Docker. Así es como se restaura.

Paso 1 — Preparar el nuevo servidor

mkdir quotenode && cd quotenode

Configura docker-compose.yml y .env como se describe en la Guía de instalación.

Crítico para las copias APP_ENCRYPTED: usa la misma DB_ENCRYPTION_KEY que en tu instalación original. Sin ella, los PII cifrados quedarán permanentemente ilegibles.

Crítico para los archivos cifrados: necesitas el archivo de identidad privada del kit de recuperación (quotenode-age-identity.txt) para descifrar el archivo.

Paso 2 — Copiar la copia de seguridad y el script de restauración

scp backup.tar.age user@new-server:~/quotenode/
scp scripts/restore.sh user@new-server:~/quotenode/

Paso 3 — Ejecutar la restauración

RESTORE_AGE_IDENTITY_FILE=./quotenode-age-identity.txt \
  [email protected] \
  RESTORE_ADMIN_PASSWORD_FILE=./admin-password \
  ./restore.sh --fresh-install backup.tar.age

Paso 4 — Verificar

Abre tu dominio e inicia sesión. Todos los clientes, ofertas, productos y configuraciones deberían estar exactamente como estaban en el momento de la copia de seguridad.

Retención de copias de seguridad

  • Local: se conservan las BACKUP_RETENTION_DAILY (predeterminado: 7) copias de seguridad correctas más recientes.
  • Remoto: configura políticas de ciclo de vida en tu proveedor de almacenamiento.
  • Las copias de seguridad con PII descifradas deberían tener una retención más estricta debido a su naturaleza sensible.

Tiempo de espera de la copia de seguridad

Cada copia de seguridad tiene un tiempo de espera configurable (predeterminado: 30 minutos mediante BACKUP_TIMEOUT_MINUTES). Si se supera, la copia de seguridad se termina y se registra como FAILED.

Estrategia recomendada

  1. Activa las copias de seguridad automáticas diarias con cifrado AGE_RECIPIENT y almacenamiento remoto.
  2. Genera un kit de recuperación durante la configuración y guárdalo en una ubicación sin conexión segura.
  3. Verifica tu kit de recuperación frente a una copia de seguridad real usando los scripts de verificación incluidos.
  4. Prueba periódicamente los procedimientos de restauración — una copia de seguridad que nunca se ha probado no es una copia de seguridad.
  5. Descarga una copia de seguridad al menos una vez al mes a una ubicación sin conexión (requiere autorización reforzada).
  6. Supervisa el estado de las copias de seguridad en el panel de administración — aparecen avisos para las copias obsoletas, fallidas o sin cifrar.
  7. Conserva al menos 7 días de historial de copias de seguridad (el valor predeterminado).

Last reviewed: Recently