20 KiB
RF-ADM-005: Backups Automáticos y Disaster Recovery
ID: RF-ADM-005 Módulo: MAI-013 - Administración & Seguridad Tipo: Requerimiento Funcional Prioridad: P0 (Crítica) Fecha de creación: 2025-11-20 Versión: 1.0
📋 Descripción
El sistema debe proporcionar backups automáticos y un plan de Disaster Recovery (DR) que garantice:
- Backups automáticos programados sin intervención manual
- Estrategia 3-2-1: 3 copias, 2 medios diferentes, 1 copia offsite
- Restauración rápida: RTO < 4 horas, RPO < 1 hora
- Verificación de integridad: Checksums MD5/SHA-256
- Pruebas mensuales de restauración
- Alertas de backups fallidos
La estrategia de backups es fundamental para garantizar continuidad del negocio ante desastres (fallas de hardware, ataques ransomware, errores humanos).
🎯 Objetivos
Objetivos de Negocio
- Protección de datos: Cero pérdida de datos críticos
- Continuidad del negocio: Recuperación rápida ante desastres
- Cumplimiento normativo: ISO 27001, SOC 2, GDPR
- Confianza del cliente: Datos siempre disponibles y seguros
- Reducción de riesgos: Mitigación de ransomware, fallas, errores
Objetivos Técnicos
- RTO (Recovery Time Objective): < 4 horas
- RPO (Recovery Point Objective): < 1 hora
- Automatización: 100% de backups sin intervención manual
- Retención: Según criticidad (7 días, 30 días, 1 año)
- Performance: Backups no afectan operación del sistema
📊 Estrategia 3-2-1 de Backups
Regla 3-2-1
3 copias de los datos:
- 1 copia en producción (activa)
- 2 copias de respaldo (backups)
2 medios diferentes:
- Local: NAS/SAN en oficina
- Cloud: AWS S3 / Azure Blob Storage
1 copia offsite:
- Geográficamente separada (otra ciudad/país)
- Protección contra desastres físicos (incendio, inundación)
Implementación
┌─────────────────────────────────────────────────────────────┐
│ ESTRATEGIA 3-2-1 │
├─────────────────────────────────────────────────────────────┤
│ │
│ COPIA 1: Producción (Activa) │
│ └─> Servidor DB Principal (PostgreSQL) │
│ Ubicación: Culiacán, Sinaloa │
│ │
│ COPIA 2: Backup Local (Medio 1) │
│ └─> NAS/SAN en oficina │
│ Ubicación: Culiacán, Sinaloa │
│ Retención: 7 días │
│ │
│ COPIA 3: Backup Cloud (Medio 2 + Offsite) │
│ └─> AWS S3 / Azure Blob │
│ Ubicación: us-west-2 (Oregon, USA) │
│ Retención: 30 días (rolling) │
│ │
└─────────────────────────────────────────────────────────────┘
🗂️ Tipos de Backup
1. Full Backup (Completo)
Frecuencia: Diario (3:00 AM) Duración: 2-4 horas (según tamaño) Retención: 7 días
Contenido:
- Base de datos completa (PostgreSQL dump)
- Archivos subidos (documentos, evidencias, planos)
- Configuraciones del sistema
- Variables de entorno (secrets)
- Logs de aplicación (últimos 7 días)
Comando (PostgreSQL):
pg_dump -h localhost -U postgres -F c \
-f /backups/full/backup-$(date +%Y-%m-%d).dump \
erp_construccion
Tamaño estimado:
- DB pequeña (< 1 año): 5-10 GB
- DB mediana (1-3 años): 20-50 GB
- DB grande (> 3 años): 100-200 GB
Ventajas:
- ✅ Restauración más rápida (un solo archivo)
- ✅ Autónomo (no depende de backups anteriores)
Desventajas:
- ❌ Consume más espacio
- ❌ Más lento que incremental
2. Incremental Backup
Frecuencia: Cada 6 horas (00:00, 06:00, 12:00, 18:00) Duración: 15-30 minutos Retención: 48 horas
Contenido:
- Solo cambios desde el último backup (full o incremental)
- Archivos modificados/creados en últimas 6 horas
- Logs de aplicación nuevos
Herramienta: rsync o pg_basebackup con WAL archiving
Comando (rsync):
rsync -av --delete --link-dest=/backups/previous \
/var/lib/postgresql/data \
/backups/incremental/backup-$(date +%Y-%m-%d_%H-%M)
Ventajas:
- ✅ Rápido (solo cambios)
- ✅ Consume menos espacio
Desventajas:
- ❌ Restauración más lenta (requiere full + todos los incrementales)
3. Backup de Archivos Críticos
Frecuencia: Cada hora (top of the hour) Duración: 5-10 minutos Retención: 24 horas
Contenido:
- Documentos subidos (contratos, minutas)
- Evidencias fotográficas de obra
- Planos y documentación técnica
- Archivos CFDI (facturas electrónicas)
Ubicaciones:
/storage/documents/
/storage/photos/
/storage/plans/
/storage/invoices/
Método:
- Sincronización en tiempo real a AWS S3
- Versionado activado (conservar versiones anteriores)
Ventajas:
- ✅ RPO < 1 hora (baja pérdida de datos)
- ✅ Archivos versionados (recuperar versiones antiguas)
4. Snapshots de Base de Datos (PITR)
Frecuencia: Cada 30 minutos Duración: Instantáneo Retención: 6 horas
Contenido:
- Snapshot del filesystem de PostgreSQL
- WAL (Write-Ahead Logging) archiving
- Permite Point-In-Time Recovery
Configuración PostgreSQL:
# postgresql.conf
wal_level = replica
archive_mode = on
archive_command = 'rsync %p /backups/wal/%f'
Ventajas:
- ✅ RPO mínimo (< 1 hora)
- ✅ Recuperación a cualquier punto en el tiempo
Desventajas:
- ❌ Requiere configuración avanzada
📅 Calendario de Backups
Calendario Semanal
┌──────────┬────────┬────────────┬──────────┬──────────┐
│ Hora │ Lun-Dom│ Tipo │ Ubicación│ Retención│
├──────────┼────────┼────────────┼──────────┼──────────┤
│ 03:00 AM │ Diario │ Full │ Local+S3 │ 7 días │
│ 00:00 │ Diario │ Incremental│ Local │ 48 hrs │
│ 06:00 │ Diario │ Incremental│ Local │ 48 hrs │
│ 12:00 │ Diario │ Incremental│ Local │ 48 hrs │
│ 18:00 │ Diario │ Incremental│ Local │ 48 hrs │
│ Hourly │ Diario │ Archivos │ S3 │ 24 hrs │
│ :00/:30 │ Diario │ Snapshot │ Local │ 6 hrs │
└──────────┴────────┴────────────┴──────────┴──────────┘
Calendario Mensual
Primer domingo de cada mes:
- Backup full completo con retención de 1 año
- Prueba de restauración completa
- Reporte de integridad de backups
Ejemplo:
- Backup del 2025-12-01 se conserva hasta 2026-12-01
- Total: 12 backups mensuales por año
🔄 Proceso de Restauración
Escenarios de Recuperación
Escenario 1: Recuperación de Archivo Individual
Caso: Usuario borró por error un contrato PDF
RTO: 15 minutos RPO: < 1 hora
Procedimiento:
- Usuario reporta: "Borré contrato-123.pdf por error"
- Admin accede a AWS S3 console
- Busca archivo en versiones anteriores
- Descarga versión más reciente
- Sube archivo nuevamente al sistema
- Valida con usuario
Herramienta: AWS S3 Versioning
Escenario 2: Recuperación de Base de Datos a Punto Específico
Caso: Error en script SQL borró 500 registros a las 14:30
RTO: 1-2 horas RPO: < 30 minutos
Procedimiento:
- Identificar timestamp exacto del error: 2025-11-20 14:30
- Detener aplicación (modo mantenimiento)
- Crear backup de estado actual (por si acaso)
- Restaurar base de datos a 2025-11-20 14:25 (5 min antes):
pg_restore -h localhost -U postgres \ --clean --if-exists \ /backups/full/backup-2025-11-20.dump - Aplicar WAL logs hasta 14:25:
pg_wal_replay --target-time='2025-11-20 14:25:00' - Verificar integridad de datos
- Reiniciar aplicación
- Validar con usuarios clave
Resultado: Recuperados 500 registros, pérdida de datos: 5 minutos.
Escenario 3: Disaster Recovery (Servidor Completo)
Caso: Servidor físico destruido por incendio
RTO: 4-8 horas RPO: < 1 hora
Procedimiento:
Fase 1: Provisionamiento (2 horas)
- Provisionar nueva infraestructura:
- Servidor cloud (AWS EC2 / Azure VM)
- Disco de 500 GB SSD
- Configuración de red (VPC, Security Groups)
- Instalar sistema operativo (Ubuntu 22.04 LTS)
- Instalar PostgreSQL 15
- Instalar Node.js 20, npm, pm2
Fase 2: Restauración de Datos (2 horas) 5. Descargar último full backup desde AWS S3:
aws s3 cp s3://backups-empresa/backup-2025-11-20.dump .
- Restaurar base de datos:
pg_restore -h localhost -U postgres \ --clean --create \ backup-2025-11-20.dump - Sincronizar archivos desde S3:
aws s3 sync s3://storage-empresa/documents /storage/documents aws s3 sync s3://storage-empresa/photos /storage/photos - Restaurar configuraciones:
aws s3 cp s3://backups-empresa/configs/env .env
Fase 3: Validación y Reinicio (1 hora) 9. Validar integridad de datos:
SELECT COUNT(*) FROM projects; -- Debe coincidir con último reporte
SELECT COUNT(*) FROM users;
- Ejecutar smoke tests:
- Login funciona
- Proyectos se listan correctamente
- Crear registro de prueba
- Reconfigurar DNS:
app.ejemplo.com → Nueva IP: 54.123.45.67 - Reiniciar aplicación:
pm2 start ecosystem.config.js pm2 logs --lines 100 - Notificar a usuarios: "Sistema restaurado, funcionando normalmente"
Fase 4: Post-Mortem (1 hora) 14. Reunión de equipo para analizar incidente 15. Documentar lecciones aprendidas 16. Actualizar plan de DR si es necesario
Resultado: Sistema en línea en 4-8 horas, pérdida de datos < 1 hora.
🔒 Seguridad de Backups
Encriptación
En tránsito (upload a S3):
aws s3 cp backup.dump s3://backups/ \
--sse AES256 \
--storage-class STANDARD_IA
En reposo (S3):
- Server-Side Encryption (SSE-S3 o SSE-KMS)
- Archivos cifrados con AES-256
Backups locales:
# Cifrar backup antes de almacenar
gpg --symmetric --cipher-algo AES256 backup.dump
Control de Acceso
AWS S3 Bucket Policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Principal": "*",
"Action": "s3:DeleteObject",
"Resource": "arn:aws:s3:::backups-empresa/*"
}
]
}
Resultado: Backups no se pueden eliminar, solo agregar nuevos.
Acceso:
- Solo Admin de TI tiene credenciales de S3
- MFA requerido para acceder a S3 console
- Logs de acceso activados (CloudTrail)
✅ Verificación de Integridad
Checksums MD5/SHA-256
Al crear backup:
# Crear backup
pg_dump -F c -f backup.dump erp_construccion
# Calcular checksum
sha256sum backup.dump > backup.dump.sha256
Al restaurar:
# Verificar checksum antes de restaurar
sha256sum -c backup.dump.sha256
# Si OK, proceder con restauración
Validación de Contenido
Tests automáticos post-backup:
#!/bin/bash
# validate-backup.sh
# Restaurar en DB temporal
createdb backup_test
pg_restore -d backup_test backup.dump
# Ejecutar queries de validación
psql -d backup_test -c "SELECT COUNT(*) FROM users;" > users_count.txt
psql -d backup_test -c "SELECT COUNT(*) FROM projects;" > projects_count.txt
# Comparar con producción
diff users_count.txt /backups/validation/users_count_prod.txt
# Si diff vacío, backup OK
if [ $? -eq 0 ]; then
echo "✅ Backup válido"
else
echo "❌ Backup corrupto, enviar alerta"
fi
# Limpiar
dropdb backup_test
📋 Modelo de Datos de Backup
interface BackupRecord {
// Identificación
id: string; // UUID
timestamp: Date;
backupType: BackupType; // full | incremental | files | snapshot
// Ubicación
storagePath: string; // "/backups/full/backup-2025-11-20.dump"
s3Url?: string; // "s3://backups-empresa/backup-2025-11-20.dump"
storageTier: 'local' | 's3_standard' | 's3_glacier';
// Tamaño y compresión
sizeBytes: number;
sizeCompressed?: number;
compressionRatio?: number; // %
// Integridad
checksum: string; // SHA-256
checksumAlgorithm: 'md5' | 'sha256';
isVerified: boolean;
verifiedAt?: Date;
// Metadata
databaseVersion: string; // "PostgreSQL 15.3"
schemaVersion: string; // "v2.5.0"
recordCount?: {
users: number;
projects: number;
budgets: number;
// ...
};
// Estado
status: BackupStatus; // pending | in_progress | completed | failed | verified
startedAt: Date;
completedAt?: Date;
duration?: number; // Segundos
// Retención
retentionDays: number; // 7, 30, 365
expiresAt: Date;
isDeleted: boolean;
// Errores
errorMessage?: string;
errorDetails?: string;
// Restauración
lastRestoreTest?: Date;
restoreTestSuccess?: boolean;
}
enum BackupType {
FULL = 'full',
INCREMENTAL = 'incremental',
FILES = 'files',
SNAPSHOT = 'snapshot'
}
enum BackupStatus {
PENDING = 'pending',
IN_PROGRESS = 'in_progress',
COMPLETED = 'completed',
FAILED = 'failed',
VERIFIED = 'verified'
}
🚨 Alertas de Backups
Configuración de Alertas
| Condición | Severidad | Destinatarios | Canal |
|---|---|---|---|
| Backup fallido | Critical | Admin TI + Director | Email + SMS |
| Backup > 2 horas | High | Admin TI | |
| Checksum inválido | Critical | Admin TI | Email + SMS |
| Disco > 80% lleno | High | Admin TI | |
| Prueba restauración fallida | Critical | Admin TI + Director | Email + SMS |
| Backup no ejecutado | Critical | Admin TI | Email + SMS |
Ejemplo de Alerta
Asunto: 🚨 CRÍTICO: Backup Full Fallido
Estimado Administrador,
El backup full programado para hoy falló:
Tipo: Full Backup
Fecha/Hora: 2025-11-20 03:00 AM
Duración: 35 minutos (abortado)
Error: "Disk quota exceeded"
Detalles del error:
- Disco /backups al 98% de capacidad
- Backup abortado tras escribir 45 GB de 50 GB
Acciones recomendadas:
1. Liberar espacio en disco /backups
2. Ejecutar backup manualmente
3. Verificar integridad del último backup exitoso (2025-11-19)
Último backup exitoso: 2025-11-19 03:00 AM (24 horas atrás)
⚠️ Acción inmediata requerida
---
Sistema de Backups Automáticos
Constructora ABC
📋 Casos de Uso
Caso 1: Prueba Mensual de Restauración
Actor: Admin de TI (automatizado)
Flujo:
- Primer domingo del mes a las 02:00 AM
- Cron job ejecuta script:
monthly-restore-test.sh - Script:
- Descarga último full backup desde S3
- Verifica checksum
- Crea base de datos temporal
erp_restore_test - Restaura backup completo
- Ejecuta 20 queries de validación
- Compara resultados con producción
- Genera reporte HTML
- Si exitoso:
- Marca backup como
verified - Envía reporte a Admin TI y Director
- Marca backup como
- Si falla:
- Alerta crítica por email + SMS
- Reporte de error detallado
Resultado: Confianza mensual de que backups funcionan.
Caso 2: Ataque Ransomware
Contexto: Ransomware cifra toda la base de datos de producción
Flujo:
- 08:30 AM - Usuarios reportan: "Sistema no funciona, pide pago de $50K BTC"
- Admin identifica ransomware
- Decisión: No pagar, restaurar desde backup
- Admin:
- Aísla servidor infectado (desconecta red)
- Provisiona nuevo servidor limpio
- Descarga último backup (ayer 03:00 AM)
- Restaura base de datos
- Restaura archivos desde S3
- 12:00 PM - Sistema en línea
- Pérdida de datos: 5.5 horas (desde 03:00 AM backup hasta 08:30 AM ataque)
- Post-mortem:
- Identificar vector de ataque
- Actualizar firewall rules
- Capacitar usuarios en phishing
Resultado: Ataque mitigado sin pagar rescate, pérdida < 6 horas.
✅ Criterios de Aceptación
AC1: Backups Automáticos sin Falla
DADO el sistema configurado correctamente CUANDO se ejecuta cron job de backup full a las 03:00 AM ENTONCES
- ✅ Backup se ejecuta automáticamente (sin intervención)
- ✅ Backup se completa exitosamente en < 4 horas
- ✅ Archivo generado con checksum SHA-256
- ✅ Backup subido a S3 con encriptación
- ✅ Backup local guardado en NAS
- ✅ Registro creado en tabla
backup_records - ✅ Si falla, alerta enviada inmediatamente
AC2: Estrategia 3-2-1 Implementada
DADO un backup full exitoso CUANDO se valida la estrategia ENTONCES
- ✅ 3 copias existen:
- Producción (activa)
- Backup local (NAS)
- Backup cloud (S3)
- ✅ 2 medios diferentes:
- Local (NAS)
- Cloud (S3)
- ✅ 1 copia offsite:
- S3 en región diferente (us-west-2)
AC3: Restauración < RTO
DADO un desastre que requiere restauración completa CUANDO se ejecuta plan de DR ENTONCES
- ✅ Sistema completamente restaurado en < 4 horas (RTO)
- ✅ Pérdida de datos < 1 hora (RPO)
- ✅ Integridad de datos validada (checksums OK)
- ✅ Aplicación funcional y accesible
AC4: Pruebas Mensuales Exitosas
DADO el primer domingo del mes CUANDO se ejecuta prueba de restauración ENTONCES
- ✅ Backup se restaura en DB temporal exitosamente
- ✅ Queries de validación retornan resultados esperados
- ✅ Reporte HTML generado automáticamente
- ✅ Email enviado a Admin y Director
- ✅ Si falla, alerta crítica enviada
AC5: Retención Automática
DADO backups con diferentes retenciones CUANDO pasa el periodo de retención ENTONCES
- ✅ Backups diarios eliminados tras 7 días
- ✅ Backups incrementales eliminados tras 48 horas
- ✅ Backups mensuales conservados 1 año
- ✅ Proceso de limpieza ejecuta automáticamente
🧪 Escenarios de Prueba
Test 1: Backup Full Exitoso
describe('RF-ADM-005: Full Backup', () => {
it('should create full backup successfully', async () => {
const backupService = new BackupService();
const result = await backupService.createFullBackup();
expect(result.status).toBe('completed');
expect(result.sizeBytes).toBeGreaterThan(0);
expect(result.checksum).toBeDefined();
expect(result.isVerified).toBe(true);
expect(result.s3Url).toContain('s3://backups-empresa');
// Verificar archivo existe
const fileExists = await fs.pathExists(result.storagePath);
expect(fileExists).toBe(true);
});
});
Test 2: Restauración Point-in-Time
describe('RF-ADM-005: Point-in-Time Recovery', () => {
it('should restore database to specific timestamp', async () => {
// Crear datos iniciales
await createProject({ name: 'Proyecto 1' });
const snapshot1 = new Date();
await sleep(1000);
// Crear más datos
await createProject({ name: 'Proyecto 2' });
// Restaurar a snapshot1 (antes de Proyecto 2)
await restoreToPointInTime(snapshot1);
// Validar
const projects = await db.query('SELECT * FROM projects');
expect(projects.rows).toHaveLength(1);
expect(projects.rows[0].name).toBe('Proyecto 1');
});
});
🔗 Referencias
- Especificación técnica: ET-ADM-004
- Historia de usuario: US-ADM-005, US-ADM-006
- RF relacionado: RF-ADM-004
- Módulo: README.md
Generado: 2025-11-20 Versión: 1.0 Autor: Sistema de Documentación Técnica Estado: ✅ Completo