packages/super-sync-server/docs/encryption-at-rest.md
This guide covers setup and operation of LUKS-encrypted database storage for SuperSync.
What: Full-disk encryption for PostgreSQL database files using LUKS2 (Linux Unified Key Setup) Why: GDPR compliance (Article 32) and protection against physical storage compromise How: Loop device with AES-256-XTS encryption, manual passphrase unlock on boot
Status: ✅ Production-ready (reviewed by Codex AI security review, 92/100 security score)
[PostgreSQL Container]
↓
[Bind Mount: /mnt/pg-data-encrypted]
↓
[LUKS Encrypted ext4 Filesystem]
↓
[Loop Device: /dev/mapper/pg-data-encrypted]
↓
[Encrypted Image File: /var/lib/supersync-encrypted.img]
↓
[Physical Disk]
Key Features:
cd /opt/supersync/packages/super-sync-server
# 1. Verify prerequisites
sudo ./tools/verify-prerequisites.sh
# 2. Create encrypted volume (50GB, adjust as needed)
sudo ./tools/setup-encrypted-volume.sh --size 50G
# 3. Start services with encryption
docker compose -f docker-compose.yml -f docker-compose.encrypted.yaml up -d
# 4. Create backup passphrase
diceware -n 8 | sudo tee /run/secrets/backup_passphrase > /dev/null
sudo chmod 600 /run/secrets/backup_passphrase
See full migration guide in /docs/long-term-plans/supersync-encryption-at-rest.md
# Debian/Ubuntu
sudo apt install cryptsetup gnupg rsync sysstat coreutils
# Verify
./tools/verify-prerequisites.sh
Check support: grep aes /proc/cpuinfo
CRITICAL: Manual unlock required every time server reboots.
# 1. SSH into server
ssh [email protected]
# 2. Unlock encrypted volume
cd /opt/supersync/packages/super-sync-server
sudo ./tools/unlock-encrypted-volume.sh pg-data-encrypted
# Enter operational passphrase when prompted
# 3. Start Docker services
docker compose -f docker-compose.yml -f docker-compose.encrypted.yaml up -d
# 4. Verify health
docker compose ps
curl https://your-domain.com/health
Expected duration: 2-5 minutes
# Automated encrypted backup (runs daily via cron)
sudo ./tools/backup-encrypted.sh
# Manual backup
sudo POSTGRES_CONTAINER=postgres ./tools/backup-encrypted.sh
Backup location: /var/backups/supersync/supersync-YYYYMMDD-HHMMSS.sql.gz.enc
# 1. Decrypt and restore
BACKUP_FILE="/var/backups/supersync/supersync-20260123-120000.sql.gz.enc"
openssl enc -d -aes-256-cbc -pbkdf2 -iter 1000000 \
-pass file:/run/secrets/backup_passphrase \
-in "$BACKUP_FILE" | \
gunzip | \
docker exec -i postgres psql -U supersync supersync
# 2. Verify restore
docker exec postgres psql -U supersync supersync -c "SELECT COUNT(*) FROM operations;"
Operational Passphrase (Slot 0):
Recovery Passphrase (Slot 1):
Backup Passphrase (separate):
/run/secrets/backup_passphrasediceware -n 8 or 1Password passphrase generatorFrequency: Annually OR upon personnel change OR security incident
# 1. Generate new passphrase
diceware -n 8 > new-passphrase.txt
chmod 600 new-passphrase.txt
# 2. Add new key to LUKS
sudo cryptsetup luksAddKey /var/lib/supersync-encrypted.img
# Enter CURRENT passphrase, then NEW passphrase (twice)
# 3. Test new key
sudo cryptsetup luksOpen --test-passphrase /var/lib/supersync-encrypted.img
# Enter NEW passphrase - should succeed
# 4. Update 1Password with new passphrase
# 5. Remove old key (after verification)
sudo cryptsetup luksRemoveKey /var/lib/supersync-encrypted.img
# Enter OLD passphrase to remove it
# 6. Backup new LUKS header
sudo cryptsetup luksHeaderBackup /var/lib/supersync-encrypted.img \
--header-backup-file /var/backups/luks-header-$(date +%Y%m%d).img
Symptoms: Device is not a valid LUKS device error
Recovery:
# 1. Locate encrypted header backup
HEADER_BACKUP="/path/to/luks-header-backup.img.enc"
# 2. Decrypt header backup
openssl enc -d -aes-256-cbc -pbkdf2 -iter 1000000 \
-pass file:/run/secrets/backup_passphrase \
-in "$HEADER_BACKUP" \
-out luks-header.img
# 3. Restore LUKS header
sudo cryptsetup luksHeaderRestore /var/lib/supersync-encrypted.img \
--header-backup-file luks-header.img
# 4. Test unlock
sudo cryptsetup luksOpen --test-passphrase /var/lib/supersync-encrypted.img
# 5. Mount and verify data
sudo ./tools/unlock-encrypted-volume.sh pg-data-encrypted
ls -la /mnt/pg-data-encrypted/
# 1. Retrieve recovery passphrase from physical safe
# (requires 2 witnesses + photo ID)
# 2. Unlock with recovery key
sudo ./tools/unlock-encrypted-volume.sh pg-data-encrypted
# Enter RECOVERY passphrase
# 3. Rotate keys immediately
# - Generate 2 new passphrases (operational + recovery)
# - Add both to LUKS
# - Remove old recovery key
# - Store new recovery key in safe
# 4. Document incident in audit log
echo "[$(date)] Emergency access: operational key unavailable" | \
sudo tee -a /var/log/luks-audit.log
# Verify volume is encrypted and mounted
sudo cryptsetup status pg-data-encrypted
sudo mountpoint -q /mnt/pg-data-encrypted && echo "Mounted" || echo "Not mounted"
# Check disk space
df -h /mnt/pg-data-encrypted
# Review audit logs
sudo tail -20 /var/log/luks-audit.log
See /docs/long-term-plans/supersync-encryption-at-rest.md Section 5.4 for:
# Check passphrase (try both operational and recovery)
sudo cryptsetup luksOpen /var/lib/supersync-encrypted.img test-unlock
# If fails: Verify LUKS header integrity
sudo cryptsetup luksDump /var/lib/supersync-encrypted.img
# Should show: Version: 2, Cipher: aes-xts-plain64, Key Slots: 0 1
# If header corrupted: Restore from backup (see Disaster Recovery)
# Verify volume is mounted
mountpoint /mnt/pg-data-encrypted || echo "ERROR: Volume not mounted!"
# Verify data exists
ls -la /mnt/pg-data-encrypted/base/
# Check permissions (should be 700, owner 999:999)
ls -lad /mnt/pg-data-encrypted/
# Fix permissions if needed
sudo chown -R 999:999 /mnt/pg-data-encrypted
sudo chmod 700 /mnt/pg-data-encrypted
# Check Docker healthcheck
docker compose ps
docker compose logs postgres
# Check AES-NI acceleration
grep aes /proc/cpuinfo
# Monitor I/O
iostat -x 60 /dev/mapper/pg-data-encrypted
# Benchmark (compare encrypted vs unencrypted baseline)
docker exec postgres pgbench -i -s 10 supersync
docker exec postgres pgbench -c 10 -j 2 -t 1000 supersync
Article 32 Requirements Met:
Breach Notification:
With AES-NI (recommended):
Without AES-NI:
/docs/long-term-plans/supersync-encryption-at-rest.md