## Why backup repositories deserve security engineering
Most organizations put serious effort into protecting production systems, then treat the backup repository as a simple storage target. That is backwards during a ransomware incident. Once an attacker has domain credentials, VPN access, or remote management access, the backup platform becomes one of the first things they try to disable. If they can delete restore points, encrypt backup files, or compromise the repository itself, recovery turns into negotiation.
A hardened Linux backup repository is not a magic ransomware shield, but it changes the attacker economics. The goal is to make backup deletion difficult, make unauthorized access noisy, and preserve restore points even if a backup service account is compromised. This article walks through a practical design that works well for small and midsize businesses, nonprofits, and distributed enterprises that need resilient backups without building a full secondary data center.
The examples below use a dedicated Linux server with ZFS snapshots, restricted SSH, nftables firewall rules, and a retention model where the backup application can write new data but cannot destroy protected snapshots. The same principles apply if your backup software is Veeam, Borg, Restic, Rsync-based, Nakivo, or a managed backup agent. The exact integration details vary, but the engineering pattern is consistent: restrict identity, restrict network paths, separate write access from retention control, and test restores regularly.
## The design pattern: writable landing zone, protected history
A secure backup repository should not rely on one control. It should use layers that still matter when one credential is stolen.
The model in this guide has four layers:
1. A dedicated Linux repository that is not joined to Active Directory.
2. A restricted service account used only by the backup system.
3. A filesystem dataset where the backup service can write current backup data.
4. Root-owned snapshots and retention jobs that the backup service cannot delete.
This is an important distinction. Many backup jobs require permission to update synthetic fulls, prune chains, or maintain indexes. If the same account can also delete every historical copy, the backup chain is only as safe as that account. By placing snapshots outside the control of the backup account, you create a recovery point that survives many common compromises.
This does not replace offline backups or cloud object lock. For high-risk environments, you should still maintain a 3-2-1-1-0 strategy: three copies of data, on two media types, one offsite, one immutable or offline, and zero restore verification errors. A hardened Linux repository is often the fastest local recovery layer in that strategy.
## Baseline assumptions
The commands below assume a modern Debian or Ubuntu server, but the approach works on most Linux distributions with minor package and service differences. The repository should be dedicated to backups. Do not run general file shares, web applications, remote desktop gateways, or monitoring dashboards on the same host.
Example network assumptions:
– Backup server IP: 10.20.30.40
– Repository IP: 10.20.30.50
– Management workstation subnet: 10.20.10.0/24
– Backup storage mount point: /repo/backups
– Linux group allowed to write backups: backupwriters
– Backup service user: backupsvc
Adjust addresses and device names before running commands. Storage commands can destroy data if used incorrectly.
## Step 1: Keep the repository out of the domain
Do not join the backup repository to Active Directory or Entra Domain Services unless you have a very specific requirement. Domain membership is convenient, but convenience is not the objective for a ransomware-resistant target. If a domain admin account is compromised, domain-joined infrastructure is often within reach.
Use local Linux accounts, SSH keys, and a separate administrative process. Store the root password or break-glass credentials in a controlled password vault with MFA and access logging. If your IT team uses a privileged access workstation model, require it for repository administration.
Create a local group and service account:
“`bash
sudo groupadd backupwriters
sudo adduser –disabled-password –gecos ” backupsvc
sudo usermod -aG backupwriters backupsvc
“`
The backup service account should not have sudo access. It should not be allowed to log in from arbitrary networks. It should not be reused for monitoring, administration, or vendor support.
## Step 2: Create a ZFS-backed repository dataset
ZFS is useful for backup repositories because it provides checksumming, compression, quotas, and fast snapshots. It also gives administrators a clean way to preserve historical versions outside the backup application.
Install ZFS tools:
“`bash
sudo apt update
sudo apt install -y zfsutils-linux
“`
Create a mirrored pool. Replace the disk identifiers with your actual devices from /dev/disk/by-id.
“`bash
ls -l /dev/disk/by-id/
sudo zpool create -o ashift=12 backup_tank mirror \
/dev/disk/by-id/scsi-REPLACE_DISK_1 \
/dev/disk/by-id/scsi-REPLACE_DISK_2
“`
Create a dataset for backups:
“`bash
sudo zfs create \
-o mountpoint=/repo/backups \
-o compression=zstd \
-o atime=off \
-o acltype=posixacl \
backup_tank/backups
sudo chown root:backupwriters /repo/backups
sudo chmod 0770 /repo/backups
“`
The backup service can write because it belongs to backupwriters. It cannot administer ZFS unless you explicitly delegate that ability, which you should not do for this design.
You may also set a quota to prevent one runaway job from consuming the entire pool:
“`bash
sudo zfs set quota=8T backup_tank/backups
“`
Quotas are not a security control by themselves, but they reduce operational surprises. A repository that silently fills up may stop protecting new data at exactly the wrong time.
## Step 3: Lock down SSH for the backup account
Most backup tools use SSH, SFTP, SMB, NFS, or an agent protocol. SSH is a good default for Linux repositories because it supports key-based authentication and network restrictions without adding a large service footprint.
Create an SSH directory for the service account:
“`bash
sudo install -d -m 0700 -o backupsvc -g backupsvc /home/backupsvc/.ssh
sudo touch /home/backupsvc/.ssh/authorized_keys
sudo chown backupsvc:backupsvc /home/backupsvc/.ssh/authorized_keys
sudo chmod 0600 /home/backupsvc/.ssh/authorized_keys
“`
Generate a key pair on the backup server or backup application host, not on the repository:
“`bash
ssh-keygen -t ed25519 -f ./backup_repo_ed25519 -C backup-repository-access
“`
Copy the public key into /home/backupsvc/.ssh/authorized_keys on the repository.
Then create a restrictive SSH configuration file:
“`bash
sudo nano /etc/ssh/sshd_config.d/20-backup-repo.conf
“`
Example configuration:
“`text
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
AllowUsers backupsvc
Match User backupsvc
X11Forwarding no
AllowTcpForwarding no
PermitTunnel no
GatewayPorts no
PermitTTY no
“`
Restart SSH after validating the configuration:
“`bash
sudo sshd -t
sudo systemctl restart ssh
“`
Before closing your existing administrative session, test a new connection from the backup server. A common mistake is hardening SSH before confirming out-of-band console access. Always keep a rollback path.
For stricter environments, use the from option in authorized_keys to limit the key to the backup server IP address. If your backup tool supports a forced command or restricted shell, use it. Be careful, though: some enterprise backup applications require a normal SSH session for repository maintenance, so test restrictions before production cutover.
## Step 4: Use nftables to permit only required traffic
A backup repository should not be reachable from every workstation VLAN. Permit SSH only from the backup server and administrative network. Drop everything else by default.
Install nftables if it is not already present:
“`bash
sudo apt install -y nftables
sudo systemctl enable –now nftables
“`
Create /etc/nftables.conf:
“`text
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0;
policy drop;
iif lo accept
ct state established,related accept
ip saddr { 10.20.30.40 } tcp dport 22 accept
ip saddr 10.20.10.0/24 tcp dport 22 accept
ip protocol icmp icmp type echo-request limit rate 5/second accept
}
chain forward {
type filter hook forward priority 0;
policy drop;
}
chain output {
type filter hook output priority 0;
policy accept;
}
}
“`
Apply and verify:
“`bash
sudo nft -f /etc/nftables.conf
sudo nft list ruleset
“`
This example leaves outbound traffic open for package updates, monitoring, and time synchronization. In higher-security environments, restrict outbound traffic as well. At minimum, the repository should not be able to initiate connections to domain controllers, file servers, hypervisors, or user subnets unless there is a documented reason.
## Step 5: Create root-owned snapshots on a schedule
This is the core control. The backup account can write backup files to /repo/backups, but snapshots are created and deleted by root-owned scheduled tasks. If ransomware uses the backup service credential to delete or encrypt files in the live dataset, snapshots still contain earlier versions.
Create a snapshot script:
“`bash
sudo nano /usr/local/sbin/backup-zfs-snapshot.sh
“`
Script contents:
“`bash
#!/bin/sh
set -eu
DATASET=backup_tank/backups
STAMP=$(date -u +%Y%m%dT%H%M%SZ)
zfs snapshot ${DATASET}@auto-${STAMP}
zfs list -H -t snapshot -o name -S creation -r ${DATASET} \
| grep ‘@auto-‘ \
| tail -n +97 \
| xargs -r -n 1 zfs destroy
“`
Make it executable:
“`bash
sudo chmod 0750 /usr/local/sbin/backup-zfs-snapshot.sh
sudo chown root:root /usr/local/sbin/backup-zfs-snapshot.sh
“`
This example keeps the newest 96 automatic snapshots. If you run the job hourly, that is roughly four days of rollback. Many organizations will want a longer pattern, such as hourly for 48 hours, daily for 30 days, and weekly for 12 weeks. The right retention depends on storage capacity, backup frequency, and how long it may take to detect compromise.
Create a systemd service:
“`bash
sudo nano /etc/systemd/system/backup-zfs-snapshot.service
“`
“`text
[Unit]
Description=Create ZFS backup repository snapshot
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/backup-zfs-snapshot.sh
“`
Create a timer:
“`bash
sudo nano /etc/systemd/system/backup-zfs-snapshot.timer
“`
“`text
[Unit]
Description=Run backup repository snapshot schedule
[Timer]
OnCalendar=hourly
Persistent=true
[Install]
WantedBy=timers.target
“`
Enable it:
“`bash
sudo systemctl daemon-reload
sudo systemctl enable –now backup-zfs-snapshot.timer
sudo systemctl list-timers | grep backup-zfs
“`
Take one manual snapshot to confirm everything works:
“`bash
sudo systemctl start backup-zfs-snapshot.service
zfs list -t snapshot -r backup_tank/backups
“`
## Step 6: Prevent administrators from becoming the weak point
Technical controls fail when every administrator has unrestricted standing access. Use these practices for the repository:
– No shared admin accounts for routine work.
– No password-based SSH.
– No domain admin login path.
– MFA on the password vault storing break-glass credentials.
– Console access limited to trusted administrators.
– Change control for retention scripts and firewall rules.
– Logging forwarded to a system the repository administrators cannot modify.
If a managed service provider supports the environment, this is especially important. MSP tooling is powerful by design. Do not let a compromised remote management platform automatically imply the ability to destroy every client backup repository.
## Step 7: Add monitoring that checks recoverability, not just job success
A backup job success message is not the same as a recoverable backup. Monitor the repository from three angles: capacity, snapshot freshness, and restore testing.
Useful commands for local health checks:
“`bash
zpool status backup_tank
zfs list backup_tank/backups
zfs list -t snapshot -r backup_tank/backups | tail
systemctl status backup-zfs-snapshot.timer
“`
A simple snapshot age check can be integrated into a monitoring platform:
“`bash
#!/bin/sh
set -eu
LATEST=$(zfs list -H -t snapshot -o creation -S creation -r backup_tank/backups | head -n 1)
test -n “$LATEST”
“`
In production, use your monitoring system to alert when:
– ZFS pool health is degraded.
– Free space drops below a defined threshold.
– No snapshot has been created recently.
– SSH logins occur from unexpected addresses.
– The backup service account logs in outside the backup window.
– Restore verification has not completed within the required interval.
The restore test is the control executives usually care about after an incident. Schedule a recurring restore of a representative VM, database, or file set to an isolated network. Document the time required and the steps followed. That evidence supports cyber insurance discussions, audit requests, and internal risk reviews.
## Optional: pair local hardening with cloud object lock
Local hardened storage is valuable because it restores quickly. Offsite immutable storage is valuable because it survives site loss and some administrator mistakes. If your backup platform supports S3-compatible object storage with object lock, consider copying backups to a bucket with retention enabled.
The important detail is that object lock must be enabled when the bucket is created for many S3-compatible systems. You cannot always add it later. Use separate credentials for writing backup objects and administering retention. Do not store the object storage administrator credential inside the backup application.
For AWS-style tooling, the workflow usually looks like this:
“`bash
aws s3api create-bucket \
–bucket company-backup-lock \
–region us-east-1 \
–object-lock-enabled-for-bucket
aws s3api put-object-lock-configuration \
–bucket company-backup-lock \
–object-lock-configuration file://object-lock.json
“`
Test this carefully in a nonproduction bucket first. Retention modes and legal holds can prevent deletion even by administrators, which is exactly the point, but mistakes can also create unnecessary storage cost.
## Common mistakes to avoid
### Using the same credential for backup and administration
If the backup application credential can SSH into the repository, run sudo, and delete snapshots, the design has failed. Separate write access from retention control.
### Mounting the repository over SMB from domain computers
SMB can be appropriate for some backup products, but exposing the repository as a broad Windows file share increases the blast radius of a domain compromise. If SMB is required, use dedicated accounts, host firewalls, share-level restrictions, and snapshot protection outside the SMB administrator role.
### Assuming RAID is a backup control
RAID protects availability after disk failure. It does not protect against ransomware, accidental deletion, malicious administrators, or corrupted backup chains. ZFS mirrors are useful, but snapshots and offsite copies are what improve recovery options.
### Never testing snapshot rollback
A snapshot you have never restored from is an assumption. Practice cloning or rolling back a dataset in a controlled maintenance window so the team knows the process before an emergency.
Example clone for testing:
“`bash
sudo zfs clone backup_tank/backups@auto-REPLACE_TIMESTAMP backup_tank/restore-test
sudo zfs set mountpoint=/repo/restore-test backup_tank/restore-test
ls -lah /repo/restore-test
“`
After testing:
“`bash
sudo zfs destroy backup_tank/restore-test
“`
## Practical summary and key takeaways
A ransomware-resistant backup repository is engineered, not merely installed. The most important shift is to stop treating the repository as ordinary storage. It is a security boundary and should be designed with the same care as a firewall, identity provider, or hypervisor cluster.
Key takeaways:
– Keep the repository dedicated and avoid joining it to the Windows domain.
– Use a dedicated backup service account with SSH keys and no sudo access.
– Restrict network access so only backup and management systems can reach it.
– Store backups on a filesystem that supports reliable snapshots and integrity checks.
– Create and prune snapshots with root-owned scheduled tasks, not the backup account.
– Monitor pool health, snapshot freshness, login activity, and restore verification.
– Pair fast local recovery with offsite immutable storage when risk and budget justify it.
For SMBs and nonprofits, this approach offers a strong balance of cost, performance, and resilience. For enterprise IT teams, it provides a repeatable pattern that can be standardized across sites. In both cases, the business benefit is clear: when an incident happens, the organization has more than a backup job report. It has protected recovery points and a tested path back to operations.