1. Introducción
Una copia de seguridad solo vale lo que vale su capacidad de sobrevivir al incidente que pretende cubrir. El ransomware moderno lo sabe: antes de cifrar los datos busca los respaldos, y si el agente de copia tiene permiso para borrar en el destino, ese permiso es exactamente el que se utiliza para destruirlos. De poco sirve entonces una copia diaria impecable si el atacante puede eliminarla con las mismas credenciales que la crearon.
La respuesta a ese problema se llama inmutabilidad, y en el mundo del almacenamiento de objetos tiene un nombre concreto: Object Lock. Es un mecanismo WORM (Write Once, Read Many): una vez escrito, un objeto no se puede modificar ni borrar hasta que expire el plazo de retención fijado, y esa regla la impone el propio almacenamiento, no la aplicación que escribe. Esta guía monta ese almacenamiento con MinIO, un servidor de objetos compatible con la API de S3 que se despliega con Docker, y respalda contra él dos equipos reales con Kopia, un motor de copias con cifrado extremo a extremo, deduplicación y soporte nativo de Object Lock.
2. Variables de esta guía
Todos los bloques usan los siguientes marcadores en MAYÚSCULAS. Sustitúyelos por los valores reales del entorno donde se aplique esta guía.
| Marcador | Qué es | Dónde se obtiene |
|---|---|---|
IP_HOST_DOCKER | IP del servidor Docker por la red LAN, que es por donde los equipos a respaldar alcanzan el almacenamiento | La interfaz LAN de ese host (ip a) |
IP_HOST_DOCKER_2 | Segunda IP del servidor, la de la red WAN o de administración, desde la que se accede a la consola con el navegador | La segunda interfaz de ese host (ip a). Si el servidor solo tiene una IP, este marcador se ignora |
MINIO_ROOT_USER | Usuario administrador de MinIO | Se define en /docker/minio/.env (sección 5) |
MINIO_ROOT_PASSWORD | Contraseña del administrador de MinIO, mínimo 8 caracteres | Se define en /docker/minio/.env (sección 5) |
KOPIA_ACCESS_KEY | Clave de acceso dedicada que usarán los agentes de copia, distinta de la del administrador | Se crea en la sección 10 |
KOPIA_SECRET_KEY | Clave secreta asociada a la anterior | Se crea en la sección 10 |
KOPIA_REPO_PASSWORD | Contraseña de cifrado del repositorio Kopia. Sin ella los datos no se recuperan, ni siquiera teniendo acceso completo al almacenamiento | La eliges tú en la sección 11 |
USUARIO_SERVIDOR | Usuario con acceso SSH al servidor Docker, para traerse el certificado al equipo Windows | El que uses habitualmente para administrar ese servidor |
ID_DE_LA_COPIA | Identificador de una copia concreta, necesario para restaurarla | Lo devuelve kopia snapshot list (sección 14) |
KOPIA_REPO_PASSWORD no es una contraseña más. Kopia cifra los datos en el cliente antes de subirlos, de modo que el servidor de almacenamiento nunca ve el contenido en claro; ese es justamente su punto fuerte. La consecuencia es directa: si se pierde esa contraseña, el repositorio es irrecuperable por diseño y no hay procedimiento de rescate. Guárdala fuera de los equipos que estás respaldando, por ejemplo en el gestor de contraseñas del propio laboratorio.ss -tlnp | grep 8447 y elige otros libres si hiciera falta, cambiándolos en las secciones 5, 6, 7, 8, 11 y 16.3. Arquitectura y flujo
Dos contenedores en el servidor central, y un agente de copia nativo instalado en cada equipo a respaldar. Igual que con los agentes de inventario, Kopia no vive en un contenedor: se instala sobre el sistema operativo de cada máquina, que es como se hace en un despliegue real.
[UBUNTU SERVER] [WINDOWS 11]
kopia kopia
| |
| copia cifrada por HTTPS |
| (el agente inicia la conexion)
+------------------+---------------+
|
red LAN
|
v
+--------------------------------------------+
| SERVIDOR DOCKER |
| LAN: IP_HOST_DOCKER |
| WAN: IP_HOST_DOCKER_2 |
| |
| [minio-proxy] :8447 (S3) :8448 (web) |
| | |
| v |
| [minio] :9000 (API S3) :9001 (consola) |
| | |
| v |
| volumen minio-data |
| deposito "backups" |
| Object Lock = COMPLIANCE 30d |
+--------------------------------------------+
4. Requisitos
- Un host Linux con Docker Engine y el plugin Docker Compose (
docker compose, sin guion) ya instalados, y preferiblemente con dos interfaces de red. - Un Ubuntu Server y un Windows 11 en la misma red, que serán los equipos respaldados.
- Salida a Internet desde los tres equipos, para descargar las imágenes de Docker y los paquetes de Kopia.
- Espacio libre en el servidor para el volumen de copias. Ten presente que con Object Lock nada se borra antes de tiempo, así que el consumo solo crece durante el periodo de retención.
- Hora sincronizada en los tres equipos. La retención se calcula sobre fechas, y un reloj desviado desajusta la protección.
5. Levantar MinIO con Docker
Misma organización de siempre: cada stack en su carpeta dentro de /docker, y las credenciales en un .env aparte, nunca escritas dentro del docker-compose.yml.
sudo mkdir -p /docker/minio && cd /docker/minio
sudo nano .env
MINIO_ROOT_USER=clockworkadmin MINIO_ROOT_PASSWORD=minio_root_pwd
sudo chmod 600 .env para que solo root pueda leer el archivo.Ahora el archivo del stack, en la misma carpeta:
sudo nano docker-compose.yml
services:
minio:
image: minio/minio:latest
container_name: minio
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
TZ: Europe/Madrid
volumes:
- minio-data:/data
healthcheck:
test: ["CMD", "mc", "ready", "local"]
interval: 5s
timeout: 5s
retries: 20
start_period: 15s
restart: unless-stopped
proxy:
image: nginx:alpine
container_name: minio-proxy
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./proxy-selfsigned.crt:/etc/nginx/certs/proxy-selfsigned.crt:ro
- ./proxy-selfsigned.key:/etc/nginx/certs/proxy-selfsigned.key:ro
ports:
- "8447:8447"
- "8448:8448"
depends_on:
minio:
condition: service_healthy
restart: unless-stopped
volumes:
minio-data:
MINIO_BROWSER_REDIRECT_URL para indicarle a la consola cuál es su dirección pública detrás de un proxy inverso. Aquí no se usa a propósito: MinIO valida esa variable al arrancar y solo admite un nombre de host, nunca una dirección IP. Si se la pasas con una IP, el contenedor no arranca y se queda en un bucle de reinicio con el mensaje FATAL Invalid MINIO_BROWSER_REDIRECT_URL value in environment variable: invalid hostname. Como todo este montaje trabaja por IP, la variable sencillamente se omite: la consola funciona igual a través del proxy.minio.clockwork.lan— sí puedes añadirla con ese nombre, y entonces ese mismo nombre debe constar también en el subjectAltName del certificado, con el prefijo DNS: en lugar de IP: (sección 6).minio-data monta /data, que es donde MinIO guarda tanto los objetos como sus metadatos y las versiones. Persistir esa ruta en un volumen con nombre es lo que marca la diferencia entre poder recrear el contenedor sin consecuencias y perder todas las copias al hacerlo.6. Configurar el proxy inverso con HTTPS propio
Genera un certificado autofirmado con clave EC, válido para las dos direcciones del servidor: la de la LAN por la que llegan los agentes y la de la WAN desde la que administras.
cd /docker/minio sudo openssl ecparam -name prime256v1 -genkey -noout -out proxy-selfsigned.key sudo openssl req -new -x509 -key proxy-selfsigned.key -out proxy-selfsigned.crt -days 825 \ -subj "/CN=IP_HOST_DOCKER" \ -addext "subjectAltName=IP:IP_HOST_DOCKER,IP:IP_HOST_DOCKER_2"
subjectAltName, y no vuelvas a tocarlo.Ahora la configuración de Nginx, con un bloque por cada servicio:
sudo nano nginx.conf
resolver 127.0.0.11 valid=10s;
# ---------- API S3: por aqui hablan los agentes de copia ----------
server {
listen 8447 ssl;
server_name IP_HOST_DOCKER;
ssl_certificate /etc/nginx/certs/proxy-selfsigned.crt;
ssl_certificate_key /etc/nginx/certs/proxy-selfsigned.key;
# Sin limite de tamano: los bloques de una copia superan el limite por defecto
client_max_body_size 0;
ignore_invalid_headers off;
proxy_buffering off;
proxy_request_buffering off;
chunked_transfer_encoding off;
location / {
set $upstream_minio http://minio:9000;
proxy_pass $upstream_minio;
# La firma de S3 incluye la cabecera Host: debe llegar intacta
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_connect_timeout 300;
proxy_send_timeout 300;
proxy_read_timeout 300;
}
}
# ---------- Consola web de administracion ----------
server {
listen 8448 ssl;
server_name IP_HOST_DOCKER_2;
ssl_certificate /etc/nginx/certs/proxy-selfsigned.crt;
ssl_certificate_key /etc/nginx/certs/proxy-selfsigned.key;
location / {
set $upstream_console http://minio:9001;
proxy_pass $upstream_console;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# La consola usa WebSockets para el estado en tiempo real
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
chunked_transfer_encoding off;
}
}
proxy_set_header Host $http_host;. El protocolo S3 firma cada petición con la Signature V4, y esa firma incluye la cabecera Host tal y como la envió el cliente. Si el proxy la reescribe —por ejemplo con el habitual $host, que descarta el puerto—, la firma que calcula el servidor deja de coincidir con la del cliente y toda petición se rechaza con SignatureDoesNotMatch. Es un error desconcertante porque parece un problema de credenciales cuando en realidad las credenciales son correctas.client_max_body_size 0 desactiva el límite de tamaño de subida. El valor por defecto de Nginx es 1 MB, muy por debajo de los bloques que sube Kopia, así que sin esta línea las copias fallan con 413 Request Entity Too Large en cuanto el primer bloque supera ese umbral. Junto a ella, proxy_request_buffering off evita que Nginx acumule en disco cada bloque antes de reenviarlo.resolver 127.0.0.11 (el DNS interno de Docker) junto con set $upstream_minio; proxy_pass $upstream_minio; no es cosmético: sin la variable, Nginx intenta resolver el nombre minio una sola vez al arrancar, y si en ese instante el contenedor todavía no está listo, se niega a arrancar con un error de host not found in upstream. Con la variable, la resolución se hace en cada petición.7. Arrancar el stack y verificar
cd /docker/minio docker compose up -d docker compose ps
Comprueba el registro de MinIO, donde debe aparecer que la API y la consola están escuchando:
docker logs -f minio
Sal con Ctrl+C y comprueba que el proxy responde por las dos interfaces:
curl -kI https://IP_HOST_DOCKER:8447/minio/health/live curl -kI https://IP_HOST_DOCKER_2:8447/minio/health/live
HTTP/1.1 200 OK en ambas confirma que el TLS funciona, que Nginx alcanza el contenedor y que el puerto está publicado correctamente en las dos interfaces. Si una responde y la otra no, el problema es de red o de enrutado, no del despliegue.8. La consola web y el cliente mc
Abre en el navegador, desde la red de administración:
https://IP_HOST_DOCKER_2:8448
MINIO_ROOT_USER y MINIO_ROOT_PASSWORD.Object Browser. La propia MinIO remite a los usuarios de esta edición al cliente de línea de comandos mc para todo lo demás, y reserva la consola completa para su producto de pago. No es un fallo de tu instalación: es así desde entonces.mc en la sección siguiente, y no desde el navegador.Instalar el cliente mc en el servidor Docker
El cliente se instala en el host donde corre Docker, que es la máquina desde la que se administra el almacenamiento y donde ya está el certificado generado en la sección 6. Los equipos que se respaldan no lo necesitan: allí solo va Kopia.
cd /tmp curl -LO https://dl.min.io/client/mc/release/linux-amd64/mc ls -lh mc file mc
-L no es opcional. La dirección de descarga responde con una redirección hacia el servidor de distribución real, y curl no sigue redirecciones por defecto: sin esa opción se descarga el cuerpo de la redirección —un fichero de texto de apenas un centenar de bytes— con el nombre mc. Al no ser un ejecutable, el sistema intenta interpretarlo como un guion de consola y devuelve errores desconcertantes del tipo mc: line 32: a: No such file or directory, que no tienen nada que ver con MinIO. Por eso se comprueba el fichero antes de instalarlo.ls -lh debe mostrar un fichero de decenas de megabytes y file debe identificarlo como ELF 64-bit LSB executable. Si ves unos pocos bytes o la palabra HTML, la descarga no ha traído el binario y no tiene sentido seguir.Con el fichero verificado, ya se puede instalar:
chmod +x mc sudo mv mc /usr/local/bin/ mc --version
Antes de crear el alias, enseña a mc a confiar en el certificado autofirmado copiándolo a su almacén propio. Es más limpio que ir arrastrando la opción --insecure en cada comando:
mkdir -p ~/.mc/certs/CAs sudo cp /docker/minio/proxy-selfsigned.crt ~/.mc/certs/CAs/minio-clockwork.crt sudo chown $USER: ~/.mc/certs/CAs/minio-clockwork.crt
mc alias set clockwork https://IP_HOST_DOCKER:8447 MINIO_ROOT_USER MINIO_ROOT_PASSWORD mc admin info clockwork
mc admin info devuelve el estado del servidor, la cadena completa funciona: TLS válido, cabecera Host intacta y credenciales correctas. Este comando es la mejor prueba de humo antes de seguir.9. Crear el depósito inmutable
Aquí está el corazón de la práctica. El depósito se crea con el bloqueo de objetos activado desde el primer momento, y acto seguido se le fija una retención por defecto en modo Compliance. Se hace desde mc, en el servidor Docker:
mc mb --with-lock clockwork/backups
mc retention set --default COMPLIANCE 30d clockwork/backups
mc retention info --default clockwork/backups mc version info clockwork/backups
COMPLIANCE con validez de 30 días, y el versionado como habilitado. El versionado no se activa a mano: el bloqueo de objetos lo enciende automáticamente y ya no puede desactivarse, porque sin versiones no habría forma de conservar el estado anterior de un objeto sobrescrito.Governance y Compliance: la diferencia que lo cambia todo
| Governance | Compliance (el usado aquí) | |
|---|---|---|
| Quién puede borrar antes de tiempo | Un usuario con el permiso s3:BypassGovernanceRetention | Nadie, ni el administrador del propio MinIO |
| Se puede acortar el plazo de un objeto ya escrito | Sí, con el permiso de excepción | No. Solo se puede ampliar |
| Protege frente a credenciales robadas | Solo si la cuenta robada no tiene la excepción | Sí, en cualquier caso |
| Para qué sirve | Evitar borrados accidentales | Cumplimiento normativo y defensa real ante ransomware |
3d en lugar de 30d— hasta tener el montaje rodado, y subirlo después.10. Credenciales dedicadas para los agentes
Los agentes no deben usar la cuenta administradora. Se crea un usuario propio y se le concede acceso únicamente al depósito de copias, siguiendo el principio de mínimo privilegio:
mc admin user add clockwork KOPIA_ACCESS_KEY KOPIA_SECRET_KEY
sudo nano /docker/minio/kopia-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:ListBucketVersions",
"s3:GetBucketLocation",
"s3:GetBucketObjectLockConfiguration",
"s3:GetObject",
"s3:GetObjectVersion",
"s3:PutObject",
"s3:PutObjectRetention",
"s3:GetObjectRetention",
"s3:DeleteObject"
],
"Resource": [
"arn:aws:s3:::backups",
"arn:aws:s3:::backups/*"
]
}
]
}
mc admin policy create clockwork kopia-backups /docker/minio/kopia-policy.json mc admin policy attach clockwork kopia-backups --user KOPIA_ACCESS_KEY
s3:BypassGovernanceRetention ni s3:DeleteBucket. El agente puede escribir sus copias y hacer su mantenimiento, pero no puede saltarse retenciones ni destruir el depósito. En modo Compliance la primera ni siquiera existiría como opción, pero conviene que la política lo diga explícitamente: si algún día bajas a Governance, esa línea es la que marca la diferencia.s3:DeleteObject sí es necesario, y no contradice la inmutabilidad. Kopia necesita ese permiso para su mantenimiento interno, pero en un depósito con versionado y bloqueo una petición de borrado no destruye nada: se limita a crear un marcador de borrado sobre la versión actual, mientras el dato original sigue guardado e intacto. Esta distinción es la clave de la sección 13 y conviene tenerla presente desde ya.11. Kopia en Ubuntu Server
Kopia se instala desde su repositorio oficial de paquetes. Registra primero la clave de firma y el origen:
sudo mkdir -p /etc/apt/keyrings curl -s https://kopia.io/signing-key | sudo gpg --dearmor -o /etc/apt/keyrings/kopia-keyring.gpg echo "deb [signed-by=/etc/apt/keyrings/kopia-keyring.gpg] http://packages.kopia.io/apt/ stable main" | sudo tee /etc/apt/sources.list.d/kopia.list sudo apt update
sudo apt install kopia -y kopia --version
Confiar en el certificado del servidor
Kopia usa el almacén de certificados del sistema. En lugar de copiar el fichero a mano desde el servidor, se puede pedir directamente al propio servicio y añadirlo al almacén:
openssl s_client -connect IP_HOST_DOCKER:8447 -showcerts </dev/null 2>/dev/null \ | openssl x509 -outform PEM \ | sudo tee /usr/local/share/ca-certificates/minio-clockwork.crt sudo update-ca-certificates
1 added). A partir de ese momento cualquier herramienta del sistema que use el almacén estándar —Kopia incluido— confía en el servidor sin necesidad de desactivar comprobaciones.--disable-tls-verification en los comandos de Kopia, que desactiva por completo la validación del certificado. Sirve para salir del paso en una prueba, pero deja al agente enviando sus copias a cualquiera que suplante la dirección del servidor, así que no debería quedarse así. Es el mismo criterio que se aplica a cualquier agente que valide un certificado privado.Crear los datos de prueba
sudo mkdir -p /datos/pruebas echo "Informe trimestral - Clockwork Computer" | sudo tee /datos/pruebas/informe.txt echo "Listado de clientes confidencial" | sudo tee /datos/pruebas/clientes.txt sudo dd if=/dev/urandom of=/datos/pruebas/base-datos.bin bs=1M count=20 ls -lh /datos/pruebas
Crear el repositorio con retención
Este es el comando que enlaza Kopia con el bloqueo de objetos de MinIO. Los parámetros --retention-mode y --retention-period son los que hacen que cada bloque subido nazca ya protegido:
sudo kopia repository create s3 \ --bucket=backups \ --endpoint=IP_HOST_DOCKER:8447 \ --access-key=KOPIA_ACCESS_KEY \ --secret-access-key=KOPIA_SECRET_KEY \ --region=us-east-1 \ --retention-mode=COMPLIANCE \ --retention-period=30d \ --password=KOPIA_REPO_PASSWORD
--endpoint se escribe sin https://: solo dirección y puerto. Kopia usa TLS por defecto y añade el esquema por su cuenta; si se incluye, el comando falla con un error de resolución que despista bastante.us-east-1 no significa que nada viaje a Estados Unidos ni tiene la menor implicación geográfica. Los datos van únicamente al servidor de esta misma red y no salen de ahí. La región es un artefacto del protocolo: la firma Signature V4 de S3 incorpora el nombre de la región al derivar la clave con la que se firma cada petición, de modo que el cliente está obligado a enviar alguna, y us-east-1 es el valor que todos usan por convenio cuando no hay ninguna configurada. Un MinIO sin región definida acepta la que le llegue sin comprobarla, así que en la práctica funciona como una etiqueta.eu-south-2, que es el código de la región de Madrid— definiéndola en el servidor y usando ese mismo valor en el --region de todos los clientes. Pero ambos lados deben coincidir exactamente: si no lo hacen, las peticiones se rechazan con un AuthorizationHeaderMalformed porque la firma se calcula con una clave distinta. Como no aporta nada funcional y añade una forma más de romper el montaje, lo razonable es dejar us-east-1 y explicar por qué.AES256-GCM-HMAC-SHA256), la política de conservación por defecto y, al final, quién queda como propietario del mantenimiento, que será este mismo equipo. Anota ese dato: es la pieza de la que depende que los bloqueos se sigan renovando, y se explica en la sección 15.Validar el proveedor de almacenamiento
El propio Kopia sugiere al terminar una comprobación que conviene no saltarse. No se limita a probar que hay conexión: escribe bloques reales y verifica que el almacenamiento se comporta como Kopia necesita —creaciones condicionales, listados coherentes, lecturas parciales y acceso concurrente—, que es justo donde suelen fallar las implementaciones de S3 de terceros:
sudo kopia repository validate-provider
All good. seguido de la limpieza de los datos temporales. Con eso queda demostrado que MinIO detrás del proxy inverso cumple todo lo que Kopia le va a exigir, y cualquier problema posterior ya no será del almacenamiento. Es una comprobación excelente para dejar grabada, porque valida de una sola vez el certificado, la cabecera Host, el límite de subida de Nginx y los tiempos de espera.sudo kopia maintenance set --extend-object-locks true sudo kopia maintenance set --full-interval=24h sudo kopia maintenance info
--extend-object-locks la protección se evapora al cumplirse el plazo. El intervalo de 24 horas coincide con el valor que Kopia trae de fábrica, pero se fija de forma explícita a propósito: es un parámetro del que depende la protección y conviene que quede declarado, no heredado.KOPIA_CHECK_FOR_UPDATES=false, que en la sección 12 ya queda recogida en el fichero de entorno del servicio.Primera copia
sudo kopia snapshot create /datos/pruebas sudo kopia snapshot list
backups, verás aparecer las carpetas internas de Kopia (p, q, x, kopia.repository): los datos ya están arriba, cifrados y con su bloqueo puesto.Conviene rematar comprobando que la protección quedó grabada en el repositorio, y no solo en la línea de comandos que lo creó:
sudo kopia repository status
Blob retention mode: COMPLIANCE y Blob retention period: 720h0m0s. Esas 720 horas son los 30 días expresados en la unidad que usa Kopia internamente, así que si ves ese par de líneas, cada bloque que suba el agente nacerá protegido. Si faltasen, el repositorio se creó sin retención y habría que rehacerlo.+ 2 identical snapshots until .... No son copias duplicadas ocupando espacio: Kopia detecta que el contenido es idéntico y agrupa esas entradas, guardando los datos una sola vez. Es la deduplicación haciendo su trabajo, y viene bien enseñarlo para que no parezca que cada ejecución multiplica el consumo.12. Programar las copias en Ubuntu Server
Una copia manual demuestra que el montaje funciona; lo que protege de verdad es la que se ejecuta sola todos los días, sin que nadie se acuerde de lanzarla. En el Ubuntu Server se resuelve con las herramientas nativas del sistema: un servicio y un temporizador de systemd, que sustituyen con ventaja al viejo cron porque dejan registro consultable de cada ejecución y sobreviven a los apagados.
El temporizador de systemd
Primero, el fichero con la contraseña del repositorio, con permisos restringidos para que solo root pueda leerlo:
sudo mkdir -p /etc/kopia sudo tee /etc/kopia/kopia.env >/dev/null <<'EOF' KOPIA_PASSWORD=KOPIA_REPO_PASSWORD KOPIA_CHECK_FOR_UPDATES=false EOF sudo chmod 600 /etc/kopia/kopia.env
sudo nano /etc/systemd/system/kopia-backup.service
[Unit] Description=Copia de seguridad con Kopia hacia MinIO After=network-online.target Wants=network-online.target [Service] Type=oneshot EnvironmentFile=/etc/kopia/kopia.env Environment=HOME=/root ExecStart=/usr/bin/kopia --config-file=/root/.config/kopia/repository.config snapshot create /datos/pruebas ExecStartPost=/usr/bin/kopia --config-file=/root/.config/kopia/repository.config maintenance run --full
Environment=HOME=/root y el --config-file explícito. El motivo: la configuración de Kopia vive en $HOME/.config/kopia/repository.config, y systemd no define $HOME en los servicios del sistema aunque se ejecuten como root. Sin ellas, la copia funciona a mano con sudo pero el servicio falla con open repository: repository is not connected, que hace pensar en un problema de red o de credenciales cuando en realidad Kopia sencillamente no encuentra su propio fichero de configuración. Definir HOME importa además por la caché, que también cuelga de ahí.sudo nano /etc/systemd/system/kopia-backup.timer
[Unit] Description=Copia diaria con Kopia [Timer] OnCalendar=*-*-* 02:00:00 Persistent=true [Install] WantedBy=timers.target
sudo systemctl daemon-reload sudo systemctl enable --now kopia-backup.timer systemctl list-timers kopia-backup.timer sudo systemctl start kopia-backup.service journalctl -u kopia-backup.service -n 30 --no-pager
Persistent=true es un detalle que marca la diferencia en equipos que no están encendidos las 24 horas: si el servidor estaba apagado a las 02:00, la copia se lanza en cuanto arranca en lugar de perderse hasta el día siguiente. El último comando ejecuta el servicio a mano para validarlo sin esperar a la hora programada.13. Simulacro de ransomware
Con las copias en su sitio, toca comprobar que la inmutabilidad no es una casilla marcada en una configuración sino una defensa real. El simulacro tiene tres fases, en orden de agresividad creciente.
Fase 1: el ransomware ataca los datos originales
sudo bash -c 'for f in /datos/pruebas/*; do \ openssl enc -aes-256-cbc -pbkdf2 -in "$f" -out "$f.locked" -k "rescate123" && rm -f "$f"; \ done' ls -l /datos/pruebas
.locked. Es exactamente el estado en el que un usuario descubre que ha sido víctima de un ataque.Fase 2: el atacante intenta borrar las copias
El siguiente paso de cualquier ransomware serio es ir a por el respaldo, usando las credenciales que encuentre en el equipo comprometido. Y ahí está la gracia de hacerlo desde el propio equipo atacado, no desde el servidor: es donde estarían realmente esas credenciales, dentro de la configuración del agente de copia. Como mc se instaló en el servidor Docker, hay que ponerlo también aquí:
cd /tmp curl -LO https://dl.min.io/client/mc/release/linux-amd64/mc file mc chmod +x mc sudo mv mc /usr/local/bin/ mc --version
update-ca-certificates, y mc lo respeta. Y recuerda el -L del curl, por el mismo motivo que en la sección 8.--endpoint al crear el repositorio, no la de administración. Si el servidor tiene dos interfaces y aquí pones la equivocada, la conexión no llegará —o llegará por una ruta que no toca— y parecerá un fallo de la práctica cuando solo es un error de direccionamiento.mc alias set atacante https://IP_HOST_DOCKER:8447 KOPIA_ACCESS_KEY KOPIA_SECRET_KEY mc ls atacante/backups/
mc ya está. El resultado técnico es idéntico; solo se pierde algo de verosimilitud en el relato, porque el ataque real parte del equipo comprometido y no del servidor de copias.mc rm --recursive --force atacante/backups/ mc ls atacante/backups/
Created delete marker ... (versionId=...) en lugar de un borrado. Se reconocen ahí, además, las piezas internas del repositorio de Kopia: kopia.repository con los metadatos, kopia.blobcfg y kopia.maintenance con su configuración, y los bloques de datos e índices con prefijos p, q y xn0_. Todo cifrado: ni los nombres revelan qué se ha copiado.kopia snapshot list fallará, porque para él los objetos han desaparecido. Es justo la sensación que tendría el administrador al descubrir el incidente, y por eso la restauración de la sección 14 empieza retirando estos marcadores.mc ls --versions atacante/backups/
Un atacante con conocimientos lo sabría e iría a por las versiones. Aquí es donde entra el bloqueo:
mc rm --recursive --force --versions atacante/backups/
Removed ... que parece indicar que el ataque está teniendo éxito, y solo al final un único error. No cunda el pánico: lo que se ha borrado no son los datos.La prueba está en los identificadores de versión. Si comparas las líneas Removed con los Created delete marker del paso anterior, verás que coinciden uno a uno: lo que el comando ha eliminado son los propios marcadores de borrado, que no llevan retención porque no contienen datos. Y borrar un marcador no destruye nada; al contrario, devuelve la visibilidad al objeto que ocultaba. El resto de líneas corresponde a blobs temporales que dejó la validación del proveedor de la sección 11, que tampoco estaban protegidos.
Object, '..." is WORM protected and cannot be overwritten. En cuanto el comando llega a la primera versión real de un dato —fíjate en que su identificador de versión ya no coincide con ningún marcador—, MinIO lo rechaza y la operación se detiene. Los datos son indestructibles y el ataque ha fracasado.mc aborta en el primer objeto protegido en lugar de continuar y devolver un error por cada uno. Por eso se ve una sola línea de error y no un muro de rechazos. Y como efecto colateral curioso, al haber eliminado los marcadores, el depósito vuelve a mostrar su contenido en un listado normal: el atacante, intentando afinar, ha deshecho sin querer su propio borrado.Fase 3: el atacante consigue las credenciales de administrador
Vamos al peor escenario posible dentro del almacenamiento: que el atacante llegue a la cuenta administradora de MinIO. Se usa el alias clockwork, el de la cuenta root.
mc son locales a cada equipo: viven en ~/.mc/config.json. El alias clockwork se creó en la sección 8 en el servidor Docker, así que en el equipo atacado no existe. Si lanzas ahí estos comandos sin más, mc no reconoce clockwork como un alias, lo interpreta como una ruta del sistema de ficheros local y devuelve errores tan desconcertantes como Requested path /tmp/clockwork/backups not found o SetObjectLockConfig is not supported for 'filesystem'. No es un fallo del bloqueo: es que está mirando en el disco local.Hay dos formas de resolverlo, y ambas valen. La primera es ejecutar esta fase desde el servidor Docker, donde el alias ya existe. La segunda, más coherente con el relato —el atacante ha escalado privilegios y ahora tiene las credenciales del administrador—, es darlo de alta también en el equipo atacado:
mc alias set clockwork https://IP_HOST_DOCKER:8447 MINIO_ROOT_USER MINIO_ROOT_PASSWORD mc ls clockwork/backups/
Con el alias resuelto, los intentos con la cuenta administradora:
mc rm --recursive --force --versions clockwork/backups/ mc rb --force clockwork/backups
is WORM protected and cannot be overwritten. El modo Compliance no admite excepciones ni siquiera para la cuenta administradora, así que no se pueden borrar las versiones protegidas y tampoco eliminar el depósito mientras las contenga. Esta es exactamente la propiedad que convierte una copia en una copia inmutable.Lo que el administrador sí puede hacer, y por qué no le sirve de nada
Queda un último intento, el más sutil: si no puede borrar, que rebaje el plazo de retención y espere a que caduque. Este comando sí se ejecuta correctamente:
mc retention set --default COMPLIANCE 1d clockwork/backups
La demostración es inmediata: pregunta por el valor por defecto del depósito y por el de un objeto concreto ya almacenado, y compáralos:
mc retention info --default clockwork/backups mc retention info clockwork/backups/kopia.repository
--retention-period de la sección 11). Aunque alguien rebaje el defecto del depósito, las copias que siga haciendo el agente nacerán igualmente con sus 30 días. Son dos capas independientes, y hace falta comprometer las dos para debilitar la protección futura.mc retention set --default COMPLIANCE 30d clockwork/backups.14. Restauración
Primero: comprobar si hace falta deshacer algo
La recuperación no empieza restaurando, sino mirando en qué estado quedó el repositorio. Dependiendo de hasta dónde llegara el atacante, el punto de partida es distinto, así que lo primero es preguntárselo al propio agente:
sudo kopia repository status
Blob retention mode: COMPLIANCE y Blob retention period: 720h0m0s —las 720 horas son los 30 días—, es decir que el agente sigue escribiendo con protección activa incluso después del incidente.Si el repositorio sigue oculto: retirar los marcadores
Solo en el primer caso. Los datos nunca se fueron, únicamente quedaron tapados, y mc lo revierte deshaciendo la última operación sobre los objetos versionados:
mc undo --recursive --force clockwork/backups/ mc ls clockwork/backups/
Undo command works only with S3 versioned-enabled buckets junto a una ruta del sistema de ficheros local, no es que falte el versionado: es que el alias no existe en ese equipo y mc está mirando en el disco. Revisa el aviso sobre alias de la sección 13.Ahora la restauración propiamente dicha. Localiza la copia y recupérala en una carpeta nueva, para poder comparar antes de sobrescribir nada:
sudo kopia repository status sudo kopia snapshot list /datos/pruebas sudo kopia snapshot list --all
sudo kopia snapshot restore ID_DE_LA_COPIA /datos/restaurado ls -lh /datos/restaurado cat /datos/restaurado/informe.txt
/datos/pruebas siguen los .locked del ataque. La restauración de extremo a extremo está demostrada: ese es el cero de la regla 3-2-1-1-0.ID_DE_LA_COPIA por el identificador que devuelve snapshot list (una cadena hexadecimal larga). Si prefieres explorar antes de restaurar, sudo kopia mount all /mnt/kopia monta todas las copias como un sistema de ficheros de solo lectura y permite navegarlas con ls y cp como si fueran carpetas normales.15. Vigencia de los bloqueos y mantenimiento
Este es el punto que más instalaciones tiene mal configuradas, y el que decide si la protección sigue viva dentro de seis meses o se apagó sin que nadie se diera cuenta.
El bloqueo de un objeto no es perpetuo: tiene una fecha de expiración, la que se fijó al subirlo. Pasada esa fecha, el objeto vuelve a ser borrable. Como una copia útil necesita seguir protegida indefinidamente mientras se conserve, Kopia va renovando la fecha de los bloques que siguen formando parte de alguna copia activa. Esa renovación ocurre durante el mantenimiento completo, y solo si está activada la opción correspondiente:
sudo kopia maintenance info
Object Lock Extension: enabled, que confirma que la renovación está activada; el Owner, que identifica al equipo responsable de ejecutarla; y sobre todo el histórico de la tarea extend-blob-retention-time, donde cada ejecución deja constancia del trabajo real con mensajes del tipo Blob retention extension found 15 blobs and extended for 15 blobs, retention period 720h0m0s. Ese contador creciendo copia tras copia es la inmutabilidad renovándose sola.snapshot-gc distingue los contenidos en uso de los huérfanos, cleanup-logs aplica la retención a los registros, y las tareas de épocas (advance-epoch, compact-single-epoch) mantienen compactado el índice. Todas deben aparecer como SUCCESS.kopia maintenance info: si algún día ese equipo desaparece, hay que traspasar la propiedad con kopia maintenance set --owner=usuario@equipo o el mantenimiento sencillamente dejará de ejecutarse, en silencio.sudo kopia maintenance run --full
El riesgo del depósito lleno
mc quota set clockwork/backups --size 100gi mc quota info clockwork/backups
Política de conservación de copias
Independientemente de la retención del almacenamiento, Kopia decide cuántas copias conserva. Ya viene con unos valores por defecto que la propia creación del repositorio mostró en pantalla —10 últimas, 48 horarias, 7 diarias, 4 semanales, 24 mensuales y 3 anuales—, generosos y pensados para conservar mucho historial. Conviene revisarlos y ajustarlos de forma consciente, sobre todo si el espacio es limitado y no se puede liberar antes de que expire el bloqueo:
sudo kopia policy set --global \ --keep-latest=10 \ --keep-daily=14 \ --keep-weekly=4 \ --keep-monthly=6 sudo kopia policy show --global
16. Kopia en Windows 11
Con el ciclo completo demostrado en Linux —copia, programación, ataque y recuperación—, toca comprobar que el mismo repositorio da servicio a un sistema operativo distinto sin cambiar nada del servidor. Este apartado repite el recorrido entero en un Windows 11: instalación del agente, confianza en el certificado, conexión al repositorio ya existente, copia programada y restauración, incluida la de los datos que respaldó la otra máquina.
La instalación se resuelve con el gestor de paquetes integrado. Ejecuta PowerShell como administrador:
winget install Kopia.KopiaCLI --accept-package-agreements --accept-source-agreements kopia --version
kopia no se reconoce inmediatamente después de instalar, cierra y vuelve a abrir PowerShell: la variable PATH se actualiza al iniciar una sesión nueva. También existe el paquete Kopia.KopiaUI, que añade una interfaz gráfica de escritorio sobre el mismo motor, útil si prefieres enseñar el proceso visualmente en lugar de por línea de comandos.Confiar en el certificado del servidor
En Linux el certificado se descargaba del propio servicio con openssl; en Windows es más cómodo traerlo por SCP, porque Windows 10 y 11 incluyen el cliente de OpenSSH de serie y scp funciona directamente desde PowerShell sin instalar nada. Pedirá la contraseña del usuario del servidor:
scp USUARIO_SERVIDOR@IP_HOST_DOCKER:/docker/minio/proxy-selfsigned.crt "$env:USERPROFILE\Downloads\proxy-selfsigned.crt"
Permission denied al leer el fichero, es porque el certificado pertenece a root y el usuario con el que entras no puede leerlo. Se resuelve dejando una copia accesible en el servidor antes de repetir el scp: sudo cp /docker/minio/proxy-selfsigned.crt ~ && sudo chown $USER: ~/proxy-selfsigned.crt, y apuntando entonces a ~/proxy-selfsigned.crt. Ten en cuenta también que muchos servidores tienen deshabilitado el acceso SSH directo como root, así que usa tu usuario habitual.Antes de instalarlo, conviene mirar qué se ha traído. Este paso confirma de un vistazo que el fichero es el certificado correcto y no otra cosa:
Get-PfxCertificate -FilePath "$env:USERPROFILE\Downloads\proxy-selfsigned.crt" | Format-List Subject, Issuer, NotAfter, Thumbprint
Subject y el Issuer deben coincidir —es autofirmado, así que se emite a sí mismo— y mostrar la IP del servidor. La NotAfter debe estar a algo más de dos años vista, que son los 825 días con los que se generó.Ya se puede añadir al almacén de entidades de certificación de confianza del equipo:
Import-Certificate -FilePath "$env:USERPROFILE\Downloads\proxy-selfsigned.crt" -CertStoreLocation Cert:\LocalMachine\Root
LocalMachine\Root y no a CurrentUser, porque más adelante la tarea programada puede ejecutarse en un contexto distinto al de tu sesión interactiva. Importarlo a nivel de equipo evita un fallo que solo aparecería de madrugada, cuando la tarea se lanza sola. Y por ese mismo motivo, esta consola de PowerShell debe estar abierta como administrador: escribir en el almacén del equipo requiere elevación, y sin ella el comando falla con un acceso denegado.Conectar al repositorio existente
connect, no create. El repositorio ya existe: lo creó el Ubuntu Server en la sección 11, y volver a crearlo sobre el mismo depósito daría error o, peor, dejaría dos estructuras conviviendo. Los parámetros de retención tampoco se repiten: quedaron grabados en el repositorio y los hereda cualquier cliente que se conecte.kopia repository connect s3 --bucket=backups --endpoint=IP_HOST_DOCKER:8447 --access-key=KOPIA_ACCESS_KEY --secret-access-key=KOPIA_SECRET_KEY --region=us-east-1 --password=KOPIA_REPO_PASSWORD
New-Item -ItemType Directory -Force -Path C:\datos\pruebas "Contrato de mantenimiento - Clockwork Computer" | Out-File C:\datos\pruebas\contrato.txt "Presupuesto 2026" | Out-File C:\datos\pruebas\presupuesto.txt kopia snapshot create C:\datos\pruebas kopia snapshot list
kopia snapshot list filtra por defecto por el usuario y la máquina actuales. Las copias del Ubuntu Server están en el mismo repositorio, pero no se muestran salvo que se pidan expresamente.kopia snapshot list --all
usuario@maquina:ruta, conviviendo en el mismo repositorio. Kopia deduplica entre ellas: si ambos equipos guardaran el mismo fichero, se almacenaría una sola vez, aunque procedan de sistemas operativos distintos.Programar la copia diaria con el Programador de tareas
$kopia = (Get-Command kopia).Source $accion = New-ScheduledTaskAction -Execute $kopia -Argument "snapshot create C:\datos\pruebas" $disparo = New-ScheduledTaskTrigger -Daily -At 02:00 $opciones = New-ScheduledTaskSettingsSet -StartWhenAvailable -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries Register-ScheduledTask -TaskName "Kopia - Copia diaria" -Action $accion -Trigger $disparo -Settings $opciones -User $env:USERNAME -RunLevel Highest
Start-ScheduledTask -TaskName "Kopia - Copia diaria" Get-ScheduledTaskInfo -TaskName "Kopia - Copia diaria"
repository connect. Por eso la tarea se registra con -User $env:USERNAME y no como SYSTEM. Si la registras como SYSTEM sin más, se ejecutará sin encontrar ninguna conexión al repositorio y fallará con un escueto repository is not connected. Si necesitas que corra sin sesión iniciada, hay que conectar el repositorio también en el contexto de esa cuenta.kopia policy set --enable-volume-shadow-copy=when-available C:\datos\pruebas y requiere ejecutar con privilegios elevados.Restaurar en Windows y desde Windows
El procedimiento es idéntico al de Linux: se localiza la copia por su identificador y se recupera en una carpeta nueva.
kopia snapshot list C:\datos\pruebas kopia snapshot restore ID_DE_LA_COPIA C:\datos\restaurado Get-ChildItem C:\datos\restaurado
kopia snapshot list --all, ya que por defecto cada equipo solo ve las suyas, y después restaurar por identificador con normalidad. Es una demostración muy visual de que la copia sobrevive incluso a la pérdida total del equipo de origen, que es el escenario que de verdad importa.17. Tabla de puertos y componentes
| Puerto | Componente | Sentido de la conexión | Para qué sirve |
|---|---|---|---|
| 8447/tcp | minio-proxy (Nginx) | Agentes → contenedor | API compatible con S3: por aquí suben y bajan las copias, por HTTPS |
| 8448/tcp | minio-proxy (Nginx) | Navegador → contenedor | Consola web de MinIO, para consultar objetos y versiones |
| — | minio :9000 (Docker, interno) | proxy → minio | API S3 real; no publica ningún puerto al host |
| — | minio :9001 (Docker, interno) | proxy → minio | Consola web real; no publica ningún puerto al host |
| — | Kopia en cada endpoint | Agente → servidor | No escucha en ningún puerto: solo abre conexiones salientes |
18. Errores comunes y cómo resolverlos
| Problema | Causa probable | Solución |
|---|---|---|
Todas las peticiones fallan con SignatureDoesNotMatch aunque las credenciales sean correctas |
El proxy está reescribiendo la cabecera Host, que forma parte de la firma S3 |
Usar proxy_set_header Host $http_host; y no $host ni $host:$server_port (sección 6) |
Las peticiones se rechazan con AuthorizationHeaderMalformed tras cambiar la región |
La región configurada en el servidor y la que envía el cliente no coinciden, y la firma se deriva con ella | Usar el mismo valor en ambos lados, o volver a us-east-1 en el --region y dejar el servidor sin región definida (sección 11) |
La copia empieza y se corta con 413 Request Entity Too Large |
Los bloques que sube Kopia superan el límite por defecto de Nginx, que es de 1 MB | Confirmar client_max_body_size 0; en el bloque del puerto 8447 (sección 6) |
Kopia falla con x509: certificate signed by unknown authority |
El certificado autofirmado no está en el almacén de confianza del cliente | Añadirlo con update-ca-certificates en Linux o Import-Certificate en Windows (secciones 11 y 16) |
| Al crear el repositorio, error de resolución de nombre en el endpoint | Se ha escrito --endpoint=https://IP:8447 con el esquema incluido |
Indicar solo dirección y puerto: --endpoint=IP_HOST_DOCKER:8447 (sección 11) |
| No se puede activar el bloqueo sobre un depósito que ya existe | Object Lock solo se habilita en el momento de crear el depósito | Crear uno nuevo con mc mb --with-lock y migrar las copias (sección 9) |
| El depósito se creó desde el botón Create Bucket de la consola web y no admite retención | El formulario de la edición Community solo pide el nombre y crea el depósito sin bloqueo de objetos | Borrarlo y volver a crearlo con mc mb --with-lock, que es la única vía que activa el bloqueo (secciones 8 y 9) |
mc rm parece borrar las copias sin ningún error |
Comportamiento normal del versionado: solo se han creado marcadores de borrado, el dato sigue intacto | Verificarlo con mc ls --versions y revertirlo con mc undo (secciones 13 y 14) |
Tras instalar mc, cualquier comando responde mc: line 32: a: No such file or directory |
La descarga se hizo sin -L: lo que se guardó como mc es el cuerpo de una redirección, no el binario |
Borrarlo con sudo rm /usr/local/bin/mc y repetir la descarga con curl -LO, verificándola con file mc (sección 8) |
mc admin policy create no se reconoce como subcomando |
Versión antigua del cliente, que usaba otra nomenclatura | Actualizar mc, o usar mc admin policy add y mc admin policy set en su lugar |
| La consola web no ofrece gestión de usuarios, políticas ni depósitos | MinIO retiró esas funciones de la edición Community en 2025 | Administrar con mc, que es la vía soportada para esta edición (sección 8) |
mc responde Requested path /tmp/ALIAS/... not found o ... is not supported for 'filesystem' |
Ese alias no existe en el equipo desde el que lanzas el comando: los alias son locales, viven en ~/.mc/config.json. Al no reconocerlo, mc lo trata como una ruta del disco local |
Dar de alta el alias en ese equipo con mc alias set, o ejecutar el comando desde el servidor donde ya existe (secciones 8 y 13) |
| Un equipo no ve las copias del otro aunque compartan repositorio | Comportamiento normal: kopia snapshot list filtra por el usuario y la máquina actuales |
Añadir --all para ver las copias de todos los equipos (secciones 14 y 16) |
La copia funciona a mano con sudo pero el servicio de systemd falla con open repository: repository is not connected |
systemd no define $HOME en los servicios del sistema, así que Kopia no encuentra su configuración en /root/.config/kopia/ |
Añadir Environment=HOME=/root y el parámetro --config-file a la unidad (sección 12) |
La tarea programada de Windows falla con repository is not connected |
La tarea corre bajo una cuenta distinta a la que conectó el repositorio, y la configuración de Kopia es por usuario | Registrar la tarea con la misma cuenta, o conectar el repositorio en el contexto de la cuenta que la ejecuta (sección 16) |
| Pasado un tiempo, los objetos vuelven a poder borrarse | El mantenimiento completo no se está ejecutando y los bloqueos han caducado sin renovarse | Revisar kopia maintenance info, el propietario del mantenimiento y el temporizador (sección 15) |
| El almacenamiento se llena y no se puede liberar espacio | Con Object Lock nada se borra antes de expirar la retención | Poner cuota al depósito y ajustar retención y política de conservación (sección 15) |
El contenedor minio no arranca y se queda en Restarting (1), con FATAL Invalid MINIO_BROWSER_REDIRECT_URL value in environment variable: invalid hostname |
Se ha definido MINIO_BROWSER_REDIRECT_URL con una dirección IP. Esa variable solo admite nombres de host |
Quitarla del docker-compose.yml, o sustituir la IP por un nombre DNS que además conste en el certificado (sección 5) |
El contenedor minio arranca y muere al instante por credenciales |
La contraseña de MINIO_ROOT_PASSWORD tiene menos de 8 caracteres, o el .env no se está leyendo |
Comprobar los valores ya resueltos con docker compose config y revisar el .env (sección 5) |
19. Notas de ampliación
El montaje de esta guía cubre lo necesario para tener copias inmutables funcionando y verificadas en dos sistemas operativos. Para un uso continuado conviene ampliar con:
- Un segundo destino fuera del emplazamiento, que es lo que completa la regla 3-2-1-1-0. Kopia admite varios repositorios, y MinIO puede replicar un depósito hacia otra instalación en otra ubicación.
- Alertas de copia fallida, porque el peor fallo de un sistema de respaldo es el que no avisa. Kopia puede lanzar acciones tras cada ejecución, y un temporizador que falla es un evento que el sistema de monitorización debería recoger.
- Integración con el SIEM, enviando el resultado de cada copia y los intentos de borrado rechazados por MinIO. Un
rmdenegado sobre el depósito de copias es, en sí mismo, un indicador de compromiso de primer nivel. - Apertura automática de tickets en la mesa de ayuda cuando una copia falle dos días seguidos, para que el aviso no se quede en un correo que nadie lee.
- Cifrado y custodia de la contraseña del repositorio en un gestor de contraseñas propio, fuera de los equipos respaldados. Sin ella no hay restauración posible, y guardarla en la máquina que se respalda es un contrasentido.
- Endurecimiento del servidor de copias, que a estas alturas es el activo más valioso de la infraestructura: acceso administrativo restringido, sin servicios adicionales, y a poder ser en una red separada del resto.
- Pruebas de restauración periódicas y documentadas. Una copia que nunca se ha restaurado es una hipótesis, no una copia de seguridad.