La prima volta che mi resi conto dell’importanza di avere almeno un backup dei miei dati è stato nel lontano 2006, quando persi quasi tutto a causa di una défaillance del mio portatile. Da allora ho sempre cercato di avere almeno una copia completa e recente dei miei dati e col tempo ho cercato di affinare e rendere più solida la procedura, fino a giungere a quella attuale che copre le mie principali necessità:
La prima e la terza necessità sono puramente personali, mentre la seconda mi serve per garantire che non vadano persi per sempre dati non particolarmente sensibili, ma indubbiamente molto importanti come foto, video o alcuni documenti personali.
È il mio sistema di backup principale, non serve praticamente nessuna configurazione perché è integrato in macOS (basta collegare l’hard disk esterno dedicato) e mi garantisce un ripristino dei dati facile e rapido. Lo utilizzo per soddisfare la prima delle mie necessità.
La seconda necessità è invece soddisfatta da una copia dei dati su un hard disk esterno con rsync tramite il comando qui sotto, a cui ho assegnato un alias
in .zshrc
.
rsync -ah --info=stats1,progress2 --delete --exclude={'Library','.Trash','.cache','.gnupg'} $HOME/ /Volumes/Backup/
La terza necessità è quella che richiede un’implementazione più articolata e per questo ho dovuto investire un po’ di tempo per trovare la soluzione che mi permettesse di raggiungere il risultato finale che mi ero prefissato.
La scelta è quella di utilizzare BorgBackup per creare backup remoti su BorgBase.
N.B.: le istruzioni qui di seguito sono al 99% la trascrizione di questa fantastica guida di Sun Knudsen, ci sono solo minime modifiche per adattarla alle mie esigenze.
borg
Generare una passphrase e memorizzarla nel proprio password manager:
openssl rand -base64 24 | pbcopy
N.B.: il comando pbcopy
copia direttamente la passphrase nella clipboard. Quando richiesta, basterà dare ⌘+V per incollarla.
Con questi comandi creare la chiave SSH e quando richiesto inserire la passphrase generata precedentemente:
mkdir ~/.ssh
cd ~/.ssh
ssh-keygen -o -a 100 -t ed25519 -f ~/.ssh/borg -C "borg"
cat borg.pub | pbcopy
borg-append-only
Per questa chiave non c’è necessità di generare una passphrase perché verrà utilizzata solo per le operazioni append-only:
ssh-keygen -o -a 100 -t ed25519 -f ~/.ssh/borg-append-only -C "borg-append-only"
cat borg-append-only.pub | pbcopy
In BorgBase andare alla pagina SSH Keys e inserire le chiavi pubbliche borg.pub
e borg-append-only.pub
.
Successivamente andare in Repositories e creare un repository per i backup.
macFUSE serve per montare il repository come fosse una archivio esterno per poter accedere ai backup.
brew install --cask macfuse
brew install borgbackup/tap/borgbackup-fuse
Generare una passphrase che utilizzeremo per la connessione a BorgBase ed aggiungerla ad Accesso Portachiavi con questo unico comando (memorizzarla anche nel proprio password manager):
security add-generic-password -D secret -U -a $USER -s borg-passphrase -w $(openssl rand -base64 24)
Esportare temporaneamente le due variabili d’ambiente prima di inizializzare il repository (quando richiesto inserire la passphrase della chiave SSH borg
):
export BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
export BORG_RSH="ssh -i ~/.ssh/borg"
borg init --encryption=keyfile-blake2 "1234abcd@1234abcd.repo.borgbase.com:repo"
nano ~/.zshrc
BORG_REPO="1234abcd@1234abcd.repo.borgbase.com:repo"
BACKUP_NAME="$USER-macbook-air”
source ~/.zshrc
/usr/local/bin/borg-backup
cat << EOF > /usr/local/bin/borg-backup
# ! /bin/sh
set -e
repo="$BORG_REPO"
prefix="$BACKUP_NAME-"
export BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
export BORG_RSH="ssh -i ~/.ssh/borg-append-only"
borg create \\
--filter "AME" \\
--list \\
--stats \\
--verbose \\
--compression auto,zstd,9 \\
--exclude "*.DS_Store" \\
--exclude "*.localized" \\
"\$repo::\$prefix{now:%F-%H%M}" \\
"$HOME/.ssh" \\
"$HOME/Library/Keychains"
printf "%s\n" "Done"
EOF
chmod +x /usr/local/bin/borg-backup
/usr/local/bin/borg-list
cat << EOF > /usr/local/bin/borg-list
# ! /bin/sh
set -e
prefix="$BACKUP_NAME-"
repo="$BORG_REPO"
export BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
export BORG_RSH="ssh -i ~/.ssh/borg-append-only"
borg list --prefix "\$prefix" "\$repo"
printf "%s\n" "Done"
EOF
chmod +x /usr/local/bin/borg-list
/usr/local/bin/borg-check
cat << EOF > /usr/local/bin/borg-check
# ! /bin/sh
set -e
prefix="$BACKUP_NAME-"
repo="$BORG_REPO"
export BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
export BORG_RSH="ssh -i ~/.ssh/borg-append-only"
borg check --prefix "\$prefix" "\$repo"
printf "%s\n" "Done"
EOF
chmod +x /usr/local/bin/borg-check
/usr/local/bin/borg-restore
cat << EOF > /usr/local/bin/borg-restore
# ! /bin/sh
set -e
function umount()
{
if [ -d "\$mount_point" ]; then
borg umount \$mount_point
fi
}
trap umount ERR INT
mount_point="\${TMPDIR}borg"
prefix="$BACKUP_NAME-"
repo="$BORG_REPO"
mkdir -p \$mount_point
export BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
export BORG_RSH="ssh -i ~/.ssh/borg-append-only"
borg mount --prefix "\$prefix" "\$repo" "\$mount_point"
open \$mount_point
printf "Restore data and press enter"
read -r answer
umount
printf "%s\n" "Done"
EOF
chmod +x /usr/local/bin/borg-restore
/usr/local/bin/borg-prune
cat << EOF > /usr/local/bin/borg-prune
# ! /bin/sh
set -e
prefix="$BACKUP_NAME-"
repo="$BORG_REPO"
export BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
export BORG_RSH="ssh -i ~/.ssh/borg"
borg prune --keep-hourly 24 --keep-daily 31 --keep-weekly 52 --keep-monthly 12 --keep-yearly 10 --list --prefix "\$prefix" --stats "\$repo"
printf "%s\n" "Done"
EOF
chmod +x /usr/local/bin/borg-prune
/usr/local/bin/borg-break-lock
cat << EOF > /usr/local/bin/borg-break-lock
# ! /bin/sh
set -e
prefix="$BACKUP_NAME-"
repo="$BORG_REPO"
export BORG_PASSCOMMAND="security find-generic-password -a $USER -s borg-passphrase -w"
export BORG_RSH="ssh -i ~/.ssh/borg-append-only"
borg break-lock "\$repo"
printf "%s\n" "Done"
EOF
chmod +x /usr/local/bin/borg-break-lock
In macOS bisogna utilizzare un’applicazione nativa per poter schedulare i backup con BorgBackup, diversamente si dovrebbe concedere a zsh
l’accesso completo al disco (oppure a singole cartelle protette) e questo potenzialmente potrebbe causare delle vulnerabilità e pregiudicare la sicurezza.
Sun Knudsen ha reso disponibile Borg Wrapper per assolvere questa funzione.
Io ho preferito crearmi un semplicissimo AppleScript con Script Editor ed esportarlo come applicazione nativa. Questo script controlla che ci sia una connessione e fa partire il backup
Questo è il codice dello script:
display notification "" & (current date) & "" with title "Backup avviato"
try
do shell script "PATH=$PATH:/opt/homebrew/bin:/usr/local/bin borg-backup"
display notification "" & (current date) & "" with title "Backup completato"
on error
display alert "Backup non completato
" & (current date) & "" as critical
end try
Creare un agente per avviare automaticamente il backup ogni ora:
mkdir -p ~/Library/LaunchAgents
cat << EOF > ~/Library/LaunchAgents/local.borgbackup-wrapper.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>local.borgbackup-wrapper</string>
<key>ProgramArguments</key>
<array>
<string>open</string>
<string>/Applications/BorgBackup Wrapper.app</string>
</array>
<key>RunAtLoad</key>
<false/>
<key>StartCalendarInterval</key>
<dict>
<key>Minute</key>
<integer>0</integer>
</dict>
</dict>
</plist>
EOF
launchctl load ~/Library/LaunchAgents/local.borgbackup-wrapper.plist
Rimane un po’ il dubbio sull’affidabilità nel lungo periodo, soprattutto per quanto riguarda l’eventuale manutenzione degli script, ma dopo qualche settimana di utilizzo posso ritenermi soddisfatto del funzionamento.