Plan de Recuperación ante Desastres para Servidores Linux

Un plan integral de recuperación ante desastres (DR) es esencial para cualquier organización que dependa de infraestructura Linux. Esta guía proporciona un marco completo para desarrollar, implementar y mantener una estrategia efectiva de recuperación ante desastres que minimice el tiempo de inactividad y la pérdida de datos mientras asegura la continuidad del negocio.

Tabla de Contenidos

  1. Comprensión de RTO y RPO
  2. Realización de una Evaluación de Riesgos
  3. Estrategias de Copia de Seguridad
  4. Procedimientos de Recuperación
  5. Cronograma de Pruebas
  6. Plantilla de Documentación
  7. Mejores Prácticas de Implementación
  8. Monitoreo y Alertas
  9. Conclusión

Comprensión de RTO y RPO

Recovery Time Objective (RTO) y Recovery Point Objective (RPO) son conceptos fundamentales que definen tus requisitos de recuperación ante desastres.

RTO representa el tiempo de inactividad máximo aceptable antes de que el impacto empresarial sea crítico. Por ejemplo, un RTO de 4 horas significa que tus servicios deben restaurarse dentro de ese período de tiempo.

RPO define la pérdida de datos máxima aceptable. Un RPO de 1 hora significa que puedes tolerar perder hasta 1 hora de datos.

# Calcular RTO y RPO para tu infraestructura
cat > /tmp/rto_rpo_calculator.sh << 'EOF'
#!/bin/bash

# Define los niveles de criticidad
declare -A criticality_rto
declare -A criticality_rpo

criticality_rto[critical]="1h"
criticality_rto[high]="4h"
criticality_rto[medium]="8h"
criticality_rto[low]="24h"

criticality_rpo[critical]="15min"
criticality_rpo[high]="1h"
criticality_rpo[medium]="4h"
criticality_rpo[low]="24h"

# Función para determinar la criticidad del servicio
determine_criticality() {
    local service=$1
    local business_impact=$2
    
    if [[ "$business_impact" == *"critical"* ]]; then
        echo "critical"
    elif [[ "$business_impact" == *"significant"* ]]; then
        echo "high"
    elif [[ "$business_impact" == *"moderate"* ]]; then
        echo "medium"
    else
        echo "low"
    fi
}

# Generar matriz RTO/RPO
generate_dr_matrix() {
    echo "Análisis de Criticidad del Servicio"
    echo "===================================="
    echo "Servicio | Criticidad | RTO | RPO"
    echo "--------|-------------|-----|----"
}

generate_dr_matrix
EOF

chmod +x /tmp/rto_rpo_calculator.sh

Realización de una Evaluación de Riesgos

Una evaluación exhaustiva de riesgos identifica amenazas potenciales y su impacto en tu infraestructura.

# Marco de Evaluación de Riesgos
cat > /tmp/risk_assessment.md << 'EOF'
# Plantilla de Evaluación de Riesgos

## Activos de Infraestructura
- [ ] Servidores web
- [ ] Servidores de base de datos
- [ ] Servidores de correo
- [ ] Servidores de archivos
- [ ] Infraestructura de red
- [ ] Sistemas de almacenamiento

## Amenazas Potenciales
- [ ] Fallo de hardware (disco, memoria, fuente de alimentación)
- [ ] Corrupción de datos
- [ ] Ataques cibernéticos y ransomware
- [ ] Interrupciones de red
- [ ] Desastres naturales
- [ ] Error humano
- [ ] Bugs de software
- [ ] Mala configuración

## Análisis de Impacto
Para cada activo:
1. Identificar funciones críticas
2. Estimar impacto en ingresos por hora de tiempo de inactividad
3. Calcular costo de recuperación
4. Determinar dependencias en otros sistemas
EOF

cat /tmp/risk_assessment.md

Crear una matriz de riesgos para priorizar esfuerzos de mitigación:

# Crear análisis de matriz de riesgos
cat > /tmp/risk_matrix.sh << 'EOF'
#!/bin/bash

# Riesgo = Probabilidad x Impacto
# Probabilidad: 1-5 (1=improbable, 5=muy probable)
# Impacto: 1-5 (1=mínimo, 5=catastrófico)

declare -A risks=(
    ["disk_failure"]="4x5=20"
    ["ransomware"]="3x5=15"
    ["network_outage"]="2x4=8"
    ["human_error"]="4x3=12"
    ["power_failure"]="2x4=8"
    ["data_corruption"]="3x4=12"
)

echo "Matriz de Evaluación de Riesgos"
echo "====================="
printf "%-25s %-15s %-10s\n" "Tipo de Riesgo" "Puntuación" "Prioridad"
echo "---------------------------------------------"

for risk in $(printf '%s\n' "${!risks[@]}" | sort); do
    score=${risks[$risk]}
    printf "%-25s %-15s %-10s\n" "$risk" "$score" "$([ ${score%=*} -gt 15 ] && echo 'Crítico' || echo 'Alto')"
done
EOF

chmod +x /tmp/risk_matrix.sh
bash /tmp/risk_matrix.sh

Estrategias de Copia de Seguridad

Implementar una estrategia de copia de seguridad en múltiples capas utilizando la regla 3-2-1: 3 copias de datos, en 2 medios diferentes, con 1 fuera del sitio.

Estrategia de Copia de Seguridad Completa

# Copia de seguridad completa de todo el servidor
backup_full() {
    local server=$1
    local backup_dir="/backup/full"
    local timestamp=$(date +%Y%m%d_%H%M%S)
    local backup_file="$backup_dir/full_${server}_${timestamp}.tar.gz"
    
    mkdir -p "$backup_dir"
    
    # Excluir archivos innecesarios
    tar --exclude='/proc' \
        --exclude='/sys' \
        --exclude='/dev' \
        --exclude='/tmp' \
        --exclude='/var/tmp' \
        --exclude='/var/log' \
        --exclude='/var/cache' \
        --exclude='/backup' \
        --exclude='*.swp' \
        -czf "$backup_file" / 2>/dev/null
    
    if [ $? -eq 0 ]; then
        echo "Copia de seguridad completa completada: $backup_file"
        ls -lh "$backup_file"
    else
        echo "Error: Copia de seguridad completa fallida"
        return 1
    fi
}

# Ejecutar copia de seguridad completa
backup_full "production-server-01"

Estrategia de Copia de Seguridad Incremental

# Copia de seguridad incremental usando find y tar
backup_incremental() {
    local backup_dir="/backup/incremental"
    local timestamp=$(date +%Y%m%d_%H%M%S)
    local backup_file="$backup_dir/incremental_${timestamp}.tar.gz"
    local last_backup="/var/lib/backup/.last_backup"
    
    mkdir -p "$backup_dir"
    
    # Crear listado de archivos para copia de seguridad incremental
    if [ -f "$last_backup" ]; then
        # Copia de seguridad solo de archivos modificados desde la última copia
        find / -type f -newer "$last_backup" \
            -not -path '/proc/*' \
            -not -path '/sys/*' \
            -not -path '/dev/*' \
            -not -path '/tmp/*' \
            -not -path '/backup/*' | \
            tar -czf "$backup_file" -T - 2>/dev/null
    fi
    
    touch "$last_backup"
    echo "Copia de seguridad incremental: $backup_file"
}

backup_incremental

Estrategia de Copia de Seguridad de Base de Datos

# Copia de seguridad de MySQL con compresión
backup_mysql() {
    local db_user="backup_user"
    local db_password="secure_password"
    local backup_dir="/backup/mysql"
    local timestamp=$(date +%Y%m%d_%H%M%S)
    local backup_file="$backup_dir/mysql_${timestamp}.sql.gz"
    
    mkdir -p "$backup_dir"
    
    mysqldump \
        -u "$db_user" \
        -p"$db_password" \
        --all-databases \
        --single-transaction \
        --quick \
        --lock-tables=false | \
        gzip > "$backup_file"
    
    if [ $? -eq 0 ]; then
        echo "Copia de seguridad de MySQL: $backup_file ($(ls -lh "$backup_file" | awk '{print $5}'))"
    fi
}

backup_mysql

# Copia de seguridad de PostgreSQL
backup_postgresql() {
    local backup_dir="/backup/postgresql"
    local timestamp=$(date +%Y%m%d_%H%M%S)
    local backup_file="$backup_dir/postgresql_${timestamp}.sql.gz"
    
    mkdir -p "$backup_dir"
    
    pg_dumpall | gzip > "$backup_file"
    
    echo "Copia de seguridad de PostgreSQL: $backup_file ($(ls -lh "$backup_file" | awk '{print $5}'))"
}

# Habilitar archivado de WAL para recuperación en un momento específico
postgresql_setup_wal_archiving() {
    local wal_archive_dir="/backup/postgres_wal"
    mkdir -p "$wal_archive_dir"
    
    # Actualizar postgresql.conf
    sed -i "s/#wal_level = .*/wal_level = replica/" /etc/postgresql/*/main/postgresql.conf
    sed -i "s|#archive_mode = .*|archive_mode = on|" /etc/postgresql/*/main/postgresql.conf
    sed -i "s|#archive_command = .*|archive_command = 'test ! -f $wal_archive_dir/%f \&\& cp %p $wal_archive_dir/%f'|" /etc/postgresql/*/main/postgresql.conf
    
    systemctl restart postgresql
}

Verificación de Copia de Seguridad

# Verificar integridad de copia de seguridad
verify_backup() {
    local backup_file=$1
    
    if [ ! -f "$backup_file" ]; then
        echo "Error: Archivo de copia de seguridad no encontrado: $backup_file"
        return 1
    fi
    
    # Verificar que el tamaño del archivo no sea cero
    if [ ! -s "$backup_file" ]; then
        echo "Error: El archivo de copia de seguridad está vacío"
        return 1
    fi
    
    # Verificar si el archivo gzip es válido
    if [[ "$backup_file" == *.gz ]]; then
        gzip -t "$backup_file" 2>/dev/null
        if [ $? -ne 0 ]; then
            echo "Error: Corrupción de copia de seguridad detectada"
            return 1
        fi
    fi
    
    # Verificar integridad del archivo tar
    if [[ "$backup_file" == *.tar.gz ]]; then
        tar -tzf "$backup_file" > /dev/null 2>&1
        if [ $? -ne 0 ]; then
            echo "Error: El archivo TAR está corrupto"
            return 1
        fi
    fi
    
    echo "Verificación de copia de seguridad aprobada: $backup_file"
    return 0
}

# Verificación de prueba
verify_backup "/backup/full/full_production-server-01_20240101_120000.tar.gz"

Procedimientos de Recuperación

Establecer procedimientos claros y probados para recuperarse de varios escenarios de fallo.

Recuperación Completa del Servidor

# Procedimiento de recuperación completa del servidor
recover_full_server() {
    local backup_file=$1
    local recovery_mount="/mnt/recovery"
    
    echo "Iniciando recuperación completa del servidor"
    echo "==============================="
    
    # 1. Arrancar en modo de recuperación (paso manual)
    echo "Paso 1: Arrancar en entorno de recuperación/rescate"
    
    # 2. Preparar disco
    echo "Paso 2: Preparar disco"
    # parted /dev/sda mklabel gpt
    # parted /dev/sda mkpart primary ext4 1MiB 100%
    
    # 3. Crear sistema de archivos
    echo "Paso 3: Crear sistema de archivos"
    # mkfs.ext4 /dev/sda1
    
    # 4. Montar sistema de archivos
    mkdir -p "$recovery_mount"
    # mount /dev/sda1 "$recovery_mount"
    
    # 5. Extraer copia de seguridad
    echo "Paso 4: Extrayendo copia de seguridad (esto puede tardar un tiempo)"
    tar -xzf "$backup_file" -C "$recovery_mount" --exclude='./dev' --exclude='./proc' --exclude='./sys'
    
    # 6. Crear directorios esenciales
    mkdir -p "$recovery_mount"/{dev,proc,sys,run}
    
    # 7. Reinstalar bootloader
    echo "Paso 5: Reinstalando bootloader"
    # mount -B /dev "$recovery_mount/dev"
    # mount -t proc proc "$recovery_mount/proc"
    # mount -t sysfs sys "$recovery_mount/sys"
    # chroot "$recovery_mount" grub-install /dev/sda
    # chroot "$recovery_mount" update-grub
    
    echo "Recuperación completa del servidor completada"
}

# Recuperación parcial de archivos
recover_files() {
    local backup_file=$1
    local file_path=$2
    local recovery_dir="/tmp/recovery"
    
    mkdir -p "$recovery_dir"
    
    # Extraer archivos específicos de copia de seguridad
    tar -xzf "$backup_file" -C "$recovery_dir" "$file_path" 2>/dev/null
    
    if [ $? -eq 0 ]; then
        echo "Archivo recuperado a: $recovery_dir/$file_path"
    fi
}

Recuperación de Base de Datos

# Recuperación de MySQL desde copia de seguridad
recover_mysql() {
    local backup_file=$1
    local db_user="root"
    local db_password="secure_password"
    
    # Restaurar desde copia de seguridad
    if [[ "$backup_file" == *.gz ]]; then
        gunzip < "$backup_file" | mysql -u "$db_user" -p"$db_password"
    else
        mysql -u "$db_user" -p"$db_password" < "$backup_file"
    fi
    
    echo "Recuperación de MySQL completada"
}

# Recuperación de PostgreSQL
recover_postgresql() {
    local backup_file=$1
    
    if [[ "$backup_file" == *.gz ]]; then
        gunzip < "$backup_file" | psql -U postgres
    else
        psql -U postgres -f "$backup_file"
    fi
    
    echo "Recuperación de PostgreSQL completada"
}

Cronograma de Pruebas

Las pruebas regulares aseguran que tus copias de seguridad funcionen cuando sea necesario y tus procedimientos estén actualizados.

# Crear cronograma automatizado de pruebas de copia de seguridad
cat > /etc/cron.d/backup-testing << 'EOF'
# Cronograma de Pruebas de Copia de Seguridad
# Prueba de copia de seguridad completa semanal el domingo a las 2 AM
0 2 * * 0 root /usr/local/bin/test-backup-restoration.sh >> /var/log/backup-test.log 2>&1

# Verificación diaria de integridad de copia de seguridad a las 3 AM
0 3 * * * root /usr/local/bin/verify-backup-integrity.sh >> /var/log/backup-verify.log 2>&1

# Simulacro mensual de recuperación ante desastres el primer domingo a las 4 AM
0 4 1 * 0 root /usr/local/bin/dr-drill.sh >> /var/log/dr-drill.log 2>&1
EOF

# Crear script de pruebas de copia de seguridad
cat > /usr/local/bin/test-backup-restoration.sh << 'EOF'
#!/bin/bash

BACKUP_DIR="/backup/full"
TEST_DIR="/tmp/backup-test"
LOG_FILE="/var/log/backup-restoration-test.log"

echo "[$(date)]Iniciando prueba de restauración de copia de seguridad" >> "$LOG_FILE"

# Buscar la copia de seguridad más reciente
LATEST_BACKUP=$(ls -t "$BACKUP_DIR"/*.tar.gz 2>/dev/null | head -1)

if [ -z "$LATEST_BACKUP" ]; then
    echo "[$(date)] ERROR: No se encontró copia de seguridad" >> "$LOG_FILE"
    exit 1
fi

# Crear directorio de prueba
rm -rf "$TEST_DIR"
mkdir -p "$TEST_DIR"

# Extraer y verificar
tar -tzf "$LATEST_BACKUP" > /dev/null 2>&1
if [ $? -eq 0 ]; then
    echo "[$(date)] APROBADA: Integridad de copia de seguridad verificada" >> "$LOG_FILE"
else
    echo "[$(date)] FALLIDA: Copia de seguridad corrupta" >> "$LOG_FILE"
    exit 1
fi

# Probar extracción parcial
tar -xzf "$LATEST_BACKUP" -C "$TEST_DIR" ./etc --warning=no-file-changed 2>/dev/null
if [ $? -eq 0 ] && [ -d "$TEST_DIR/etc" ]; then
    echo "[$(date)] APROBADA: Restauración parcial exitosa" >> "$LOG_FILE"
else
    echo "[$(date)] FALLIDA: Restauración parcial fallida" >> "$LOG_FILE"
fi

# Limpieza
rm -rf "$TEST_DIR"
echo "[$(date)] Prueba de copia de seguridad completada" >> "$LOG_FILE"
EOF

chmod +x /usr/local/bin/test-backup-restoration.sh

Plantilla de Documentación

Mantener documentación integral para tu plan de recuperación ante desastres.

# Plan de Recuperación ante Desastres - [Organización]

## Resumen Ejecutivo
- RTO: [Tiempo]
- RPO: [Tiempo]
- Última Revisión: [Fecha]
- Próxima Revisión: [Fecha]

## Sistemas Críticos
| Sistema | RTO | RPO | Ubicación | Método de Copia de Seguridad |
|--------|-----|-----|----------|---------------|
| Servidor Web | 4h | 1h | Centro de Datos Primario | Copia de seguridad completa diaria + incremental cada hora |
| Base de Datos | 2h | 15min | Centro de Datos Primario | Replicación continua + archivado WAL |
| Servidor de Archivos | 8h | 4h | Centro de Datos Primario | Incremental diario |

## Procedimientos de Recuperación
1. Pasos de Recuperación de [Sistema]
   - Requisitos previos
   - Pasos detallados
   - Verificación
   - Tiempo de recuperación estimado

## Información de Contacto
- Coordinador de DR: [Nombre] - [Contacto]
- Responsable Técnico: [Nombre] - [Contacto]
- Patrocinador Ejecutivo: [Nombre] - [Contacto]

## Resultados de Pruebas
- Fecha de Última Prueba: [Fecha]
- Tipo de Prueba: [Completa/Parcial]
- Resultado: [Aprobada/Fallida]
- Problemas Encontrados: [Lista]

## Historial de Versiones
| Fecha | Versión | Cambios | Revisado Por |
|------|---------|---------|-------------|
EOF

Mejores Prácticas de Implementación

Gestión Automatizada de Copias de Seguridad

# Rotación automatizada de copias de seguridad (mantener 30 días)
backup_rotation() {
    local backup_dir=$1
    local retention_days=30
    
    find "$backup_dir" -type f -mtime +$retention_days -delete
    echo "Rotación de copia de seguridad completada. Últimos $retention_days días retenidos."
}

# Programar rotación de copia de seguridad
(crontab -l 2>/dev/null; echo "0 5 * * * /usr/local/bin/backup-rotation.sh /backup/full") | crontab -

# Monitoreo de copias de seguridad
monitor_backups() {
    local backup_dir="/backup/full"
    local alert_threshold=24  # horas
    
    for backup in "$backup_dir"/*.tar.gz; do
        local age=$(($(date +%s) - $(stat -f%m "$backup" 2>/dev/null || stat -c%Y "$backup" 2>/dev/null)))
        local age_hours=$((age / 3600))
        
        if [ $age_hours -gt $alert_threshold ]; then
            echo "ALERTA: La copia de seguridad tiene $age_hours horas: $backup"
        fi
    done
}

monitor_backups

Replicación de Copia de Seguridad Fuera del Sitio

# Replicar copias de seguridad a ubicación remota
replicate_backups_offsite() {
    local source_dir="/backup/full"
    local remote_host="backup.remote-dc.com"
    local remote_user="backup"
    local remote_dir="/backups/offsite"
    
    # Usar rsync para transferencia eficiente
    rsync -avz \
        --delete \
        --bwlimit=10240 \
        "$source_dir/" \
        "$remote_user@$remote_host:$remote_dir"
    
    if [ $? -eq 0 ]; then
        echo "Replicación de copia de seguridad fuera del sitio completada"
    else
        echo "ERROR: Replicación de copia de seguridad fuera del sitio fallida"
    fi
}

# Programar replicación diaria fuera del sitio
(crontab -l 2>/dev/null; echo "0 22 * * * /usr/local/bin/replicate-backups-offsite.sh") | crontab -

Monitoreo y Alertas

# Crear alertas de monitoreo de copia de seguridad
cat > /usr/local/bin/backup-health-check.sh << 'EOF'
#!/bin/bash

BACKUP_DIR="/backup/full"
ALERT_EMAIL="[email protected]"
CRITICAL_THRESHOLD=48  # horas

check_backup_currency() {
    local latest_backup=$(ls -t "$BACKUP_DIR"/*.tar.gz 2>/dev/null | head -1)
    
    if [ -z "$latest_backup" ]; then
        send_alert "CRÍTICO: No se encontraron copias de seguridad en $BACKUP_DIR"
        return 1
    fi
    
    local backup_age=$(($(date +%s) - $(stat -c%Y "$latest_backup" 2>/dev/null)))
    local backup_age_hours=$((backup_age / 3600))
    
    if [ $backup_age_hours -gt $CRITICAL_THRESHOLD ]; then
        send_alert "CRÍTICO: La copia de seguridad tiene $backup_age_hours horas (umbral: $CRITICAL_THRESHOLD horas)"
        return 1
    fi
}

check_backup_size() {
    local latest_backup=$(ls -t "$BACKUP_DIR"/*.tar.gz 2>/dev/null | head -1)
    local size=$(stat -c%s "$latest_backup" 2>/dev/null)
    local size_mb=$((size / 1048576))
    
    if [ $size_mb -lt 100 ]; then
        send_alert "ADVERTENCIA: La copia de seguridad es sospechosamente pequeña: ${size_mb}MB"
    fi
}

send_alert() {
    local message=$1
    echo "$message" | mail -s "Alerta de Copia de Seguridad" "$ALERT_EMAIL"
}

check_backup_currency
check_backup_size
EOF

chmod +x /usr/local/bin/backup-health-check.sh

# Programar verificaciones de salud
(crontab -l 2>/dev/null; echo "0 * * * * /usr/local/bin/backup-health-check.sh") | crontab -

Conclusión

Un plan robusto de recuperación ante desastres requiere:

  1. Objetivos Claros: Definir RTO y RPO para cada sistema crítico
  2. Copias de Seguridad Regulares: Implementar estrategia 3-2-1 con múltiples tipos de copia de seguridad
  3. Pruebas: Pruebas de restauración mensuales aseguran que los procedimientos funcionen
  4. Documentación: Mantener procedimientos de recuperación actuales y detallados
  5. Monitoreo: Verificaciones de salud automatizadas y alertas
  6. Revisión: Actualizaciones trimestrales conforme la infraestructura cambia

Comienza con sistemas críticos, luego expande la cobertura. Las pruebas regulares son la única forma de asegurar que tu plan de DR funcione cuando el desastre ocurra. Recuerda: las copias de seguridad sin pruebas son solo almacenamiento caro.