How to back up and encrypt data using rsync and VeraCrypt on macOS
Heads-up: when using storage devices with wear-leveling (most flash storage devices), it is not possible to securely change password once it has been set (see Wear-Leveling).
Requirements
- Computer running macOS Catalina or Big Sur
- USB flash drive or SD card formatted using FAT (4GiB file size limit) or exFAT filesystem (see Journaling File Systems)
Caveats
- When copy/pasting commands that start with
$
, strip out$
as this character is not part of the command - When copy/pasting commands that start with
cat << "EOF"
, select all lines at once (fromcat << "EOF"
toEOF
inclusively) as they are part of the same (single) command
Setup guide
Step 1: install Homebrew
$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
$ uname -m | grep arm64 && echo 'export PATH=$PATH:/opt/homebrew/bin' >> ~/.zshrc && source ~/.zshrc
Step 2: disable Homebrew analytics
brew analytics off
Step 3: install FUSE for macOS and GnuPG
Heads-up: if
brew install --cask macfuse
fails, trybrew cask install macfuse
(see issue).
brew install --cask macfuse
brew install gnupg
Step 4: import VeraCrypt’s public key
$ gpg --keyserver hkps://keys.openpgp.org --recv-keys 0x821ACD02680D16DE
gpg: key 0x821ACD02680D16DE: public key "VeraCrypt Team (2018 - Supersedes Key ID=0x54DDD393) <veracrypt@idrix.fr>" imported
gpg: Total number processed: 1
gpg: imported: 1
Step 5: download VeraCrypt
Go to https://www.veracrypt.fr/en/Downloads.html and download latest release and associated PGP signature to ~/Downloads
folder.
Step 6: verify VeraCrypt release signature (learn how here)
Heads-up: replace
VeraCrypt_1.24-Update7
with current release.
$ gpg --verify ~/Downloads/VeraCrypt_1.24-Update7.dmg.sig
gpg: assuming signed data in '/Users/sunknudsen/Downloads/VeraCrypt_1.24-Update7.dmg'
gpg: Signature made Sat 8 Aug 14:20:27 2020 EDT
gpg: using RSA key 5069A233D55A0EEB174A5FC3821ACD02680D16DE
gpg: Good signature from "VeraCrypt Team (2018 - Supersedes Key ID=0x54DDD393) <veracrypt@idrix.fr>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: 5069 A233 D55A 0EEB 174A 5FC3 821A CD02 680D 16DE
Good signature
👍
Step 7: install VeraCrypt
Step 8: create and test VeraCrypt symlink
$ ln -s /Applications/VeraCrypt.app/Contents/MacOS/VeraCrypt /usr/local/bin/veracrypt
$ veracrypt --text --version
VeraCrypt 1.24-Update7
VeraCrypt 1.24-Update7
👍
Step 9: set backup volume path environment variable
BACKUP_VOLUME_PATH="/Volumes/Samsung BAR/backup"
Step 10: create encrypted volume
Heads-up: volume size cannot be increased later.
Heads-up: Mac OS Extended filesystem required on macOS.
$ veracrypt --text --create "$BACKUP_VOLUME_PATH"
Volume type:
1) Normal
2) Hidden
Select [1]:
Enter volume size (sizeK/size[M]/sizeG): 1G
Encryption Algorithm:
1) AES
2) Serpent
3) Twofish
4) Camellia
5) Kuznyechik
6) AES(Twofish)
7) AES(Twofish(Serpent))
8) Camellia(Kuznyechik)
9) Camellia(Serpent)
10) Kuznyechik(AES)
11) Kuznyechik(Serpent(Camellia))
12) Kuznyechik(Twofish)
13) Serpent(AES)
14) Serpent(Twofish(AES))
15) Twofish(Serpent)
Select [7]:
Hash algorithm:
1) SHA-512
2) Whirlpool
3) SHA-256
4) Streebog
Select [1]:
Filesystem:
1) None
2) FAT
3) Mac OS Extended
4) exFAT
5) APFS
Select [3]:
Enter password:
Re-enter password:
Enter PIM:
Enter keyfile path [none]:
Please type at least 320 randomly chosen characters and then press Enter:
Done: 100.000% Speed: 24 MiB/s Left: 0 s
Error: mount_macfuse: the file system is not available (1)
Heads-up: given “FUSE for macOS” is a third-party extension, macOS prevents using the extension without explicit user consent (granted by clicking “Allow” in “System Preferences” / “Privacy & Security”).
$ veracrypt --text --create "$BACKUP_VOLUME_PATH"
Volume type:
1) Normal
2) Hidden
Select [1]:
Enter volume size (sizeK/size[M]/sizeG): 1G
Encryption Algorithm:
1) AES
2) Serpent
3) Twofish
4) Camellia
5) Kuznyechik
6) AES(Twofish)
7) AES(Twofish(Serpent))
8) Camellia(Kuznyechik)
9) Camellia(Serpent)
10) Kuznyechik(AES)
11) Kuznyechik(Serpent(Camellia))
12) Kuznyechik(Twofish)
13) Serpent(AES)
14) Serpent(Twofish(AES))
15) Twofish(Serpent)
Select [7]:
Hash algorithm:
1) SHA-512
2) Whirlpool
3) SHA-256
4) Streebog
Select [1]:
Filesystem:
1) None
2) FAT
3) Mac OS Extended
4) exFAT
5) APFS
Select [3]:
Enter password:
Re-enter password:
Enter PIM:
Enter keyfile path [none]:
Please type at least 320 randomly chosen characters and then press Enter:
Done: 100.000% Speed: 24 MiB/s Left: 0 s
The VeraCrypt volume has been successfully created.
The VeraCrypt volume has been successfully created.
👍
Step 11 (optional): mount, rename and dismount encrypted volume
By default, VeraCrypt encrypted volumes with Mac OS Extended filesystem are named “untitled”.
Mount encrypted volume
$ veracrypt --text --mount --pim 0 --keyfiles "" --protect-hidden no "$BACKUP_VOLUME_PATH" /Volumes/Backup
Enter password for /Volumes/Samsung BAR/backup:
Rename encrypted volume
$ diskutil rename "untitled" "Backup"
Volume on disk3 renamed to Backup
Volume on disk3 renamed to Backup
👍
Dismount encrypted volume
veracrypt --text --dismount "$BACKUP_VOLUME_PATH"
Step 12: create /usr/local/bin/backup.sh
script
cat << EOF > /usr/local/bin/backup.sh
#! /bin/sh
set -e
set -o pipefail
function dismount()
{
if [ -d "\$mount_point" ]; then
veracrypt --text --dismount "\$mount_point"
fi
}
trap dismount ERR INT
volume_path="$BACKUP_VOLUME_PATH"
mount_point="/Volumes/Backup"
veracrypt --text --mount --pim "0" --keyfiles "" --protect-hidden "no" "\$volume_path" "\$mount_point"
mkdir -p "\$mount_point/Versioning"
files=(
"$HOME/.gnupg"
"$HOME/.ssh"
"$HOME/Library/Keychains"
)
for file in "\${files[@]}"; do
rsync \\
-axRS \\
--backup \\
--backup-dir \\
"\$mount_point/Versioning" \\
--delete \\
--suffix="\$(date +".%F-%H%M%S")" \\
"\$file" \\
"\$mount_point"
done
if [ "\$(find "\$mount_point/Versioning" -type f -ctime +90)" != "" ]; then
printf "Do you wish to prune versions older than 90 days (y or n)? "
read -r answer
if [ "\$answer" = "y" ]; then
find "\$mount_point/Versioning" -type f -ctime +90 -delete
find "\$mount_point/Versioning" -type d -empty -delete
fi
fi
open "\$mount_point"
printf "Inspect backup and press enter"
read -r answer
dismount
printf "Generate hash (y or n)? "
read -r answer
if [ "\$answer" = "y" ]; then
openssl dgst -sha512 "\$volume_path"
fi
printf "%s\n" "Done"
EOF
chmod +x /usr/local/bin/backup.sh
Step 13: edit /usr/local/bin/backup.sh
script
vi /usr/local/bin/backup.sh
Press i to enter insert mode, edit backup script, press esc to exit insert mode and press shift+z+z to save and exit.
Step 14: create /usr/local/bin/check.sh
script
cat << EOF > /usr/local/bin/check.sh
#! /bin/sh
set -e
set -o pipefail
red=\$(tput setaf 1)
normal=\$(tput sgr0)
printf "Backup hash: "
read -r previous
current=\$(openssl dgst -sha512 "$BACKUP_VOLUME_PATH")
if [ "\$current" != "\$previous" ]; then
printf "\$red%s\$normal\n" "Integrity check failed"
exit 1
fi
printf "%s\n" "OK"
EOF
chmod +x /usr/local/bin/check.sh
Step 15: create /usr/local/bin/restore.sh
script
cat << EOF > /usr/local/bin/restore.sh
#! /bin/sh
set -e
function dismount()
{
if [ -d "\$mount_point" ]; then
veracrypt --text --dismount "\$mount_point"
fi
}
trap dismount ERR INT
volume_path="$BACKUP_VOLUME_PATH"
mount_point="/Volumes/Backup"
veracrypt --text --mount --mount-options "readonly" --pim "0" --keyfiles "" --protect-hidden "no" "\$volume_path" "\$mount_point"
open "\$mount_point"
printf "Restore data and press enter"
read -r answer
dismount
printf "%s\n" "Done"
EOF
chmod +x /usr/local/bin/restore.sh
👍
Usage guide
Backup
Heads-up: store hash in safe place such as password manager (not on same device as backup).
$ backup.sh
Enter password for /Volumes/Samsung BAR/backup:
Inspect backup and press enter
Generate hash (y or n)? y
SHA512(/Volumes/Samsung BAR/backup)= 281a3b0afec6708eff9566effdfa67de357933527688dfa2dfabae5dda5b7681f0fb84f6cfec6c3f7ac20246517f18f40babbd4f337b254a55de30ff67d6dd2e
Done
Done
👍
Check
$ check.sh
Backup hash: SHA512(/Volumes/Samsung BAR/backup)= 281a3b0afec6708eff9566effdfa67de357933527688dfa2dfabae5dda5b7681f0fb84f6cfec6c3f7ac20246517f18f40babbd4f337b254a55de30ff67d6dd2e
OK
OK
👍
Restore
$ restore.sh
Enter password for /Volumes/Samsung BAR/backup:
Restore data and press enter
Done
Done
👍