How to backup and encrypt data privately and securely 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 Mojave or Catalina
- 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: download and install FUSE for macOS
Go to https://osxfuse.github.io/, download and install latest release.
Step 2: install Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
Step 3: disable Homebrew analytics
brew analytics off
Step 4: install GnuPG
brew install gnupg
Step 5: 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 6: download VeraCrypt
Go to https://www.veracrypt.fr/en/Downloads.html and download latest release and its associated PGP signature to ~/Downloads
folder.
Step 7: verify VeraCrypt release signature using GnuPG
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 8: install VeraCrypt
Step 9: 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 10: set temporary environment variable
Heads up: using
b
as encrypted volume file name to make things inconspicuous.
BACKUP_VOLUME_PATH
path to VeraCrypt volume
BACKUP_VOLUME_PATH="/Volumes/Samsung BAR/b"
Step 11: 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
The VeraCrypt volume has been successfully created.
Step 12 (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/b:
Rename encrypted volume
$ diskutil rename "untitled" "Backup"
Volume on disk3 renamed to Backup
Dismount encrypted volume
veracrypt --text --dismount "$BACKUP_VOLUME_PATH"
Step 13: create /usr/local/bin/backup.sh
script
cat << EOF > /usr/local/bin/backup.sh
#! /bin/sh
set -e
function cleanup()
{
if [ -d "/Volumes/Backup" ]; then
veracrypt --text --dismount "$BACKUP_VOLUME_PATH"
fi
}
trap cleanup ERR INT
veracrypt --text --mount --pim 0 --keyfiles "" --protect-hidden no "$BACKUP_VOLUME_PATH" /Volumes/Backup
mkdir -p /Volumes/Backup/Versioning
declare -a files=(
"/Users/$(whoami)/.gnupg"
"/Users/$(whoami)/.ssh"
"/Users/$(whoami)/Library/Keychains"
)
for file in "\${files[@]}"; do
rsync -axRS --delete --backup --backup-dir /Volumes/Backup/Versioning --suffix=\$(date +'.%F_%H-%M') "\$file" /Volumes/Backup
done
if [ "\$(find /Volumes/Backup/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 /Volumes/Backup/Versioning -type f -ctime +90 -delete
find /Volumes/Backup/Versioning -type d -empty -delete
fi
fi
open /Volumes/Backup
printf "Inspect backup and press enter"
read -r answer
veracrypt --text --dismount "$BACKUP_VOLUME_PATH"
printf "Generate hash (y or n)? "
read -r answer
if [ "\$answer" = "y" ]; then
openssl dgst -sha512 "$BACKUP_VOLUME_PATH"
fi
printf "%s\n" "Done"
EOF
chmod +x /usr/local/bin/backup.sh
Step 14: edit backup 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 15: create /usr/local/bin/check.sh
script
cat << EOF > /usr/local/bin/check.sh
#! /bin/sh
set -e
red=$'\e[1;31m'
nc=$'\e[0m'
printf "Backup hash: "
read -r previous
current=\$(openssl dgst -sha512 "$BACKUP_VOLUME_PATH")
if [ "\$current" != "\$previous" ]; then
printf "\${red}%s\${nc}\n" "Integrity check failed"
exit 1
fi
printf "%s\n" "OK"
EOF
chmod +x /usr/local/bin/check.sh
Usage guide
Backup
$ backup.sh
Enter password for /Volumes/Samsung BAR/b:
Inspect backup and press enter
Generate hash (y or n)? y
SHA512(/Volumes/Samsung BAR/b)= 281a3b0afec6708eff9566effdfa67de357933527688dfa2dfabae5dda5b7681f0fb84f6cfec6c3f7ac20246517f18f40babbd4f337b254a55de30ff67d6dd2e
Done
Done
👍
Check integrity of backup
$ check.sh
Backup hash: SHA512(/Volumes/Samsung BAR/b)= 281a3b0afec6708eff9566effdfa67de357933527688dfa2dfabae5dda5b7681f0fb84f6cfec6c3f7ac20246517f18f40babbd4f337b254a55de30ff67d6dd2e
OK
OK
👍
You should be careful to ensure the hash is not modified by an attacker, since this would allow tampering with the backup. One good way to do this is to use an HMAC hash instead of just hashing the backup, or to simply store a copy of the hash securely.
Putting the hash in another VeraCrypt container would be enough, assuming you use the standard XTS mode.