Tue 16 Nov 2021 11:19:16 AM EST
We want to run a weekly backup of our Linux workstation to an encrypted external USB drive.
Install the necessary encryption packages and modules, if they’re not already installed on this workstation:
🐚 ~ $ sudo apt install cryptsetup
🐚 ~ $ grep -i config_dm_crypt /boot/config-$(uname -r)
CONFIG_DM_CRYPT=m
🐚 ~ $ lsmod | grep dm_crypt
🐚 ~ $ sudo modprobe dm_crypt
🐚 ~ $ lsmod | grep dm_crypt
dm_crypt 53248 0
dm_mod 163840 1 dm_crypt
Plug in a fresh USB hard drive, and set it up.
Take pains to make sure we’re looking at the correct device before overwriting it!
🐚 ~ $ sudo dmesg -T | grep -e sd | tail
[Tue Nov 16 08:35:52 2021] sd 0:0:0:0: [sda] Starting disk
[Tue Nov 16 08:35:52 2021] sd 1:0:0:0: [sdb] Starting disk
[Tue Nov 16 12:33:38 2021] sd 8:0:0:0: Attached scsi generic sg4 type 0
[Tue Nov 16 12:33:38 2021] sd 8:0:0:0: [sdd] 1953458176 512-byte logical blocks: (1.00 TB/931 GiB)
[Tue Nov 16 12:33:38 2021] sd 8:0:0:0: [sdd] Write Protect is off
[Tue Nov 16 12:33:38 2021] sd 8:0:0:0: [sdd] Mode Sense: 47 00 10 08
[Tue Nov 16 12:33:38 2021] sd 8:0:0:0: [sdd] No Caching mode page found
[Tue Nov 16 12:33:38 2021] sd 8:0:0:0: [sdd] Assuming drive cache: write through
[Tue Nov 16 12:33:38 2021] sdd: sdd1
[Tue Nov 16 12:33:38 2021] sd 8:0:0:0: [sdd] Attached SCSI disk
🐚 ~ $ lsblk | grep sdd
sdd 8:48 0 931.5G 0 disk
└─sdd1 8:49 0 931.5G 0 part
🐚 ~ $ mount | grep sdd
Once we identify the right drive (sdd
in this case) wipe its data and encrypt it with LUKS.
🐚 ~ $ sudo wipefs /dev/sdd*
DEVICE OFFSET TYPE UUID LABEL
sdd 0x200 gpt
sdd 0xe8decffe00 gpt
sdd 0x1fe PMBR
sdd1 0x3 ntfs EC94806C94803ADA Elements
🐚 ~ $ sudo wipefs --all --backup /dev/sdd
/dev/sdd: 8 bytes were erased at offset 0x00000200 (gpt): 45 46 49 20 50 41 52 54
/dev/sdd: 8 bytes were erased at offset 0xe8decffe00 (gpt): 45 46 49 20 50 41 52 54
/dev/sdd: 2 bytes were erased at offset 0x000001fe (PMBR): 55 aa
/dev/sdd: calling ioctl to re-read partition table: Success
🐚 ~ $ sudo cryptsetup luksFormat /dev/sdd
WARNING!
========
This will overwrite data on /dev/sdd irrevocably.
Are you sure? (Type 'yes' in capital letters): YES
Enter passphrase for /dev/sdd:
Verify passphrase:
Open the now-encrypted drive, and create a partition and file system in it.
🐚 ~ $ sudo cryptsetup luksOpen /dev/sdd backup
🐚 ~ $ lsblk | grep -e sdd -e backup
sdd 8:48 0 931.5G 0 disk
└─backup 254:0 0 931.5G 0 crypt
🐚 ~ $ ls -l /dev/mapper/backup
lrwxrwxrwx 1 root root 7 Nov 23 08:08 /dev/mapper/backup -> ../dm-0
🐚 ~ $ sudo mkfs.ext4 /dev/mapper/backup
Mount, test, unmount, and close the drive (close, as in wipe cryptographic key from kernel memory):
🐚 ~ $ sudo mkdir /backup
🐚 ~ $ sudo mount /dev/mapper/backup /backup
🐚 ~ $ ls -l /backup/
total 16
drwx------ 2 root root 16384 Nov 23 08:11 lost+found
🐚 ~ $ df -h | grep backup
/dev/mapper/backup 916G 28K 870G 1% /backup
🐚 ~ $ sudo umount /backup
🐚 ~ $ sudo cryptsetup luksClose backup
Run backups with two shell scripts and a cron job.
This cron job:
0 */2 * * * /home/paulgorman/bin/backup-nag 5
…runs this script to nag us if we haven’t recently backed up:
#!/bin/sh
# Nag me via cron job if I haven't run a backup in N days.
# Install the `libnotify-bin` and `mako-notifier` packages on Debian, under Sway.
#
# m h dom mon dow command
# 0 */2 * * * /home/paulgorman/bin/backup-nag 5
#
# Paul Gorman, November 2021
days="$1"
f="$HOME"/.lastbackup
export $(cat /proc/$(pgrep -u $(whoami) -f 'dbus-daemon --session')/environ | grep -z '^DBUS_SESSION_BUS_ADDRESS=')
if [ ! -e "$f" ]; then
touch "$f"
fi
if [ $(find "$f" -mtime +"$days") ]; then
notify-send "Plug in a backup drive. Run '~/bin/backup'."
fi
Here’s the script that actually writes the backup. It requires user input, so we run it manually.
#!/bin/sh
set -uf
# Back up to an external LUKS-encrypted disk.
# Run as my user, not root.
# Paul Gorman, November 2021
echo '--- $ lsblk | grep sd'
lsblk | grep sd
echo
echo '--- $ df -h | grep sd'
df -h | grep sd
echo
echo -n 'Enter device name of backup drive (like "/dev/sdd"): '
read -r backupDevice
echo -n "Backup will be written to $backupDevice. Type 'yes' to continue: "
read -r continue
if [ "$continue" != "yes" ]; then
exit
fi
sudo mkdir -p /backup
mkdir -p "$HOME"/backups
sudo chown "$USER":root "$HOME"/backups
sudo chmod 0770 "$HOME"/backups
if [ $(find "$HOME"/backups/nanook:backup-nanook.tgz -mmin +120 -print) ] || [ ! -f "$HOME"/backups/nanook:backup-nanook.tgz ]; then
scp nanook:backup-nanook.tgz "$HOME"/backups/
fi
if [ $(find "$HOME"/backups/nanook:backup-inky.tgz -mmin +120 -print) ] || [ ! -f "$HOME"/backups/nanook:backup-inky.tgz ]; then
scp example.org:/backups/backup-inky.tgz "$HOME"/backups/
fi
sudo cryptsetup luksOpen "$backupDevice" backup
sudo mount /dev/mapper/backup /backup
cleanup () {
sudo umount /backup
sudo cryptsetup luksClose backup
exit "$1"
}
sudo mkdir -p /backup/Trash
ok=$? ; if [ ! "$ok" ]; then cleanup 1 ; fi
sudo rsync -aqh --delete --backup --backup-dir=/backup/Trash \
--exclude tmp \
--exclude Downloads \
--exclude .cache \
"$HOME" /backup
ok=$? ; if [ ! "$ok" ]; then cleanup 1 ; fi
sudo rsync -aqh --delete --backup --backup-dir=/backup/Trash /root /backup
ok=$? ; if [ ! "$ok" ]; then cleanup 1 ; fi
sudo rsync -aqh --delete --backup --backup-dir=/backup/Trash --exclude old /data /backup
ok=$? ; if [ ! "$ok" ]; then cleanup 1 ; fi
sudo rsync -aqh --delete --backup --backup-dir=/backup/Trash /etc /backup
ok=$? ; if [ ! "$ok" ]; then cleanup 1 ; fi
sudo rsync -aqh --delete --backup --backup-dir=/backup/Trash /usr/local/etc /backup
ok=$? ; if [ ! "$ok" ]; then cleanup 1 ; fi
sudo rsync -aqh --delete --backup --backup-dir=/backup/Trash /var/spool/cron /backup
ok=$? ; if [ ! "$ok" ]; then cleanup 1 ; fi
sudo rsync -aqh --delete --backup --backup-dir=/backup/Trash /var/spool/anacron /backup
cleanup 0
date +%s > "$HOME"/.lastbackup
touch "$HOME"/.lastbackup