Disinteressato

La mia strategia di backup (anche se di mio c'è quasi niente)

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.

Time Machine

È 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à.

rsync

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/

BorgBackup

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.

1 - Creare la chiave SSH 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

2 - Creare la chiave SSH 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

3 - Configurare le chiavi SSH e creare il repository in BorgBase

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.

4 - Installare macFUSE e BorgBackup

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

5 - Configurare BorgBackup

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)

6 - Inizializzare il repository

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"

7 - Impostare le variabili d’ambiente

nano ~/.zshrc
BORG_REPO="1234abcd@1234abcd.repo.borgbase.com:repo"
BACKUP_NAME="$USER-macbook-air”

source ~/.zshrc

8 - Creare lo script /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

9 - Creare lo script /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

10 - Creare lo script /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

11 - Creare lo script /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

12 - Creare lo script /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

13 - Creare lo script /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

14 - Creare un wrapper con AppleScript

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

15 - Schedulare i backup

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.