Purpose: Secure long-term backup storage isolated from network threats
Key Requirements: Full disk encryption, physical security, validated data transfer
Target Environment: FreeBSD with ZFS and GELI encryption
Status: Powered off when not actively receiving updates
An Air Gap Server is a server that operates without network connectivity to protect critical backup data from remote attacks. A modified approach allows temporary network access for system updates while maintaining security boundaries.
The primary purpose is to store long-term backups with significantly reduced attack surface. Physical isolation combined with encryption provides defense-in-depth against:
Critical: If the server must be stored in an unsecured location, full disk encryption is mandatory, not optional.
Ideal Configuration:
Fallback for Insecure Locations:
When secure facilities are unavailable:
GELI or equivalent)Store encryption keys in a different physical location than the server. Consider splitting keys across multiple secure locations.
This implementation uses FreeBSD with GELI disk encryption backing a ZFS filesystem.
At Rest Protection:
GELI (minimum requirement)Split-Key Architecture:
For enhanced security, consider using split-key encryption where the final encryption key is derived from combining two separate key components. This enhances security by allowing the actual GELI key to be stored securely off-site, as it cannot be reconstructed without both components:
In Transit Protection:
Example GELI Setup:
# Generate a random key file (4096 bits = 512 bytes) openssl rand 512 > /secure/path/geli.key chmod 400 /secure/path/geli.key # Initialize GELI encryption on disk using the key file geli init -s 4096 -K /secure/path/geli.key /dev/ada0 # Attach encrypted device geli attach -k /secure/path/geli.key /dev/ada0 # Create ZFS pool on encrypted device zpool create backup /dev/ada0.eli
Key size of 4096 bits provides strong encryption. The key file should be stored securely and backed up to a separate location. Use -P flag to add passphrase protection in addition to key file.
Transport Media Requirements:
GELI, LUKS, or BitLocker) or Encryption of individual files in transitDelta Monitoring:
Monitor transfer sizes to detect anomalies:
Data Integrity Verification:
# Generate checksum on source zfs send pool/dataset@snapshot | tee >(sha256) > /mnt/transport/delta.zfs # Verify checksum on air gap server sha256 /mnt/transport/delta.zfs
Data Validation:
Air gap servers require special consideration for maintenance since they lack network access for updates.
Validated Script Execution:
Scripts may be deployed to perform maintenance tasks:
ZFS scrubs and pool health checksScript Deployment Process:
Example Script Encryption/Decryption:
# On source server: encrypt script openssl enc -aes-256-cbc -salt -in cleanup_script.sh \ -out cleanup_script.sh.enc -pass file:/secure/transport.key # On air gap server: decrypt and execute openssl enc -aes-256-cbc -d -in cleanup_script.sh.enc \ -out cleanup_script.sh -pass file:/secure/transport.key && \ sh cleanup_script.sh || { echo "Decryption failed - aborting"; exit 1; }
Security through decryption: Scripts that cannot be decrypted with the correct symmetric key are rejected. Any decryption failure terminates the entire process to prevent execution of potentially tampered scripts.
Reporting Challenges:
Solution — Report Drive:
Report Contents:
Example Report Structure:
=== Air Gap Backup Report ===
Date: 2026-01-18 03:00:00
Operation: Incremental Backup
Source: production.example.com
Target: airgap-backup01
Datasets Processed:
- pool/data: 45.2 GB transferred
Latest: pool/data@2026-01-18_02:00:00
- pool/databases: 12.8 GB transferred
Latest: pool/databases@2026-01-18_02:00:00
Pool Health: ONLINE
Disk Status: All disks PASSED SMART checks
Maintenance Scripts Executed:
- snapshot_cleanup.sh: SUCCESS (removed 3 old snapshots)
- zfs_scrub.sh: SUCCESS (no errors found)
System Shutdown: 2026-01-18 03:45:00
Next Expected Update: 2026-01-25
Default State: Powered Off
The air gap server should remain powered off except during:
Benefits of Power-Off Strategy:
Automated Shutdown:
Final script in maintenance chain should power off the system:
#!/bin/sh # Final maintenance script - shutdown system # Verify all operations completed successfully if [ -f /var/run/backup_complete ]; then # Write final report echo "Backup completed successfully at $(date)" >> /mnt/report/status.log # Sync all filesystem buffers sync # Unmount transport media umount /mnt/transport umount /mnt/report # Power off system shutdown -p now else echo "ERROR: Backup did not complete. Manual intervention required." >> /mnt/report/error.log # Do NOT shutdown - leave powered on for troubleshooting fi
Do not configure automatic shutdown if backups fail. A powered-on system indicates problems requiring manual investigation.
A typical weekly backup cycle:
Day 1 (Monday) — Source Server:
Day 2 (Tuesday) — Physical Transport:
Day 3 (Wednesday) — Air Gap Server:
Day 4 (Thursday) — Report Processing:
Day 8 (Next Monday):
[ ] Physical Security
[ ] Secure location identified and documented
[ ] Access procedures established
[ ] Key storage locations determined
[ ] Hardware
[ ] Air gap server procured and tested
[ ] Transport drives procured (minimum 2 for rotation)
[ ] Report drive procured
[ ] All drives labeled appropriately
[ ] Encryption
[ ] GELI encryption configured and tested
[ ] Encryption keys generated and stored securely
[ ] Key recovery procedures documented
[ ] Transport drives encrypted
[ ] Software
[ ] FreeBSD installed and hardened
[ ] ZFS pools created and tested
[ ] Replication scripts developed and tested
[ ] Maintenance scripts developed and tested
[ ] Symmetric transport keys generated and deployed
[ ] Procedures
[ ] Backup schedule documented
[ ] Transport procedures documented
[ ] Report review procedures documented
[ ] Key rotation schedule established
[ ] Disaster recovery plan created
[ ] Testing
[ ] Full backup cycle tested end-to-end
[ ] Recovery procedures tested
[ ] Failure scenarios tested
[ ] Report generation verified
[ ] Automated shutdown verified
Threat Model:
This design protects against:
This design does NOT fully protect against:
Best Practices:
Common Issues:
| Problem | Symptom | Solution |
|---|---|---|
| Transport drive not mounting | Server unable to find /dev/gpt/label | Verify GPT label, check dmesg for device detection |
| Decryption fails | OpenSSL reports bad decrypt error | Verify correct symmetric key in use, check file integrity, investigate potential tampering or corruption |
| Large delta size | Delta exceeds baseline by 200%+ | Do not import — investigate source system for compromise or legitimate growth |
| Server won't shutdown | Remains powered on after backup | Check /var/run/backup_complete flag, review error logs on report drive |
| ZFS pool won't import | Import command fails | Verify encryption key, check pool status with zpool import -F |