#!/bin/bash # # Borg backup procedure for Backupninja # Adapted from https://borgbackup.readthedocs.io/en/stable/quickstart.html#automating-backups # # Parameters export SSH_SERVER="<%= @user %>@<%= @host %>" export SSH_PORT="<%= @port %>" export HOSTNAME=`cat /etc/hostname` # Set the repository # Setting this, so the repo does not need to be given on the commandline: export BORG_REPO=ssh://$SSH_SERVER:$SSH_PORT//var/backups/remote/$HOSTNAME/borg # Set the passphrase # Setting this, so you won't be asked for your repository passphrase: export BORG_PASSPHRASE='<%= @password %>' # Optional OpenPGP encryption for the keyfile GPG_KEY="<%= @gpgkey %>" GPG_PASS='<%= @gpgpass %>' # Setting the password command # This allows to ask an external program to supply the passphrase: #export BORG_PASSCOMMAND='pass show backup' # Custom keyfile support if [ "<%= @encryption %>" == "keyfile" ] && [ ! -z "<%= @keyfile %>" ]; then # Borg does not support providing a pre-generate key file anymore # Details at https://github.com/borgbackup/borg/issues/7047 #if [ ! -e "<%= @keyfile %>" ]; then # fatal "Keyfile not found: <%= @keyfile %>. Please create it manually." #fi export BORG_KEY_FILE="<%= @keyfile %>" fi # Error handling #info() { printf "\n%s %s\n\n" "$( date )" "$*" >&2; } trap 'info $( date ) Backup interrupted >&2; exit 2' INT TERM # Check if [ "$1" == "--check" ]; then borg list exit $? fi # Initialize if ! ssh $SSH_SERVER -p $SSH_PORT test -f /var/backups/remote/$HOSTNAME/borg/config; then info "Initializing borg repository at ssh://$SSH_SERVER:$SSH_PORT//var/backups/remote/$HOSTNAME/borg..." borg init --encryption=<%= @encryption %> ssh://$SSH_SERVER:$SSH_PORT//var/backups/remote/$HOSTNAME/borg init_exit=$? if [ "$init_exit" != "0" ]; then fatal "Error initializing repository" fi fi # Backup the most important directories into an archive named after # the machine this script is currently running on: info "Starting backup..." borg create \ --verbose \ --filter AME \ --list \ --stats \ --show-rc \ --compression lz4 \ --exclude-caches \ --exclude '/home/*/.cache/*' \ --exclude '/var/cache/*' \ --exclude '/var/tmp/*' \ <% @exclude_unencrypted.each do |del| -%> --exclude <%= del %> \ <% end -%> ::'{hostname}-{now}' \ <% @include_unencrypted.each do |add| -%> <%= add %> \ <% end -%> backup_exit=$? #if [ "$backup_exit" != "0" ]; then # fatal "Error creating snapshot" #fi info "Pruning repository..." # Use the `prune` subcommand to maintain 7 daily, 4 weekly and 6 monthly # archives of THIS machine. The '{hostname}-' prefix is very important to # limit prune's operation to this machine's archives and not apply to # other machines' archives also: # Warning: "--prefix" has been deprecated. Use "--glob-archives 'yourprefix*'" (-a) instead. #--prefix '{hostname}-' \ borg prune \ --list \ --glob-archives '{hostname}-*' \ --show-rc \ --keep-daily <%= @keepdaily %> \ --keep-weekly <%= @keepweekly %> \ --keep-monthly <%= @keepmonthly %> \ prune_exit=$? #if [ "$prune_exit" != "0" ]; then # fatal "Error pruning repository" #fi # Have an OpenPGP-encrypted copy of the keyfile # # This is not ideal, but it's the workaround for configuration management. # # Borg does not support using pre-generated keys anymore (as of 2024-05-16), so # we need an alternative way to access the repository keys if the original # system becomes unavailable. # # The solution is to OpenPGP-encrypt the key file and upload it to the server, # assuming that the operators have pre-generated the OpenPGP key and have a copy # of it. They can use the same OpenPGP key used for Duplicity backups. # # The level of protection for the key will then be as strong as the OpenPGP key. # # Check also https://github.com/borgbackup/borg/issues/7047 # https://borgbackup.readthedocs.io/en/latest/faq.html#how-important-is-the-home-config-borg-directory if [ "<%= @encryption %>" == "keyfile" ] && [ ! -z "<%= @keyfile %>" ]; then if [ ! -z "$GPG_KEY" ] && [ ! -z "$GPG_PASS" ]; then info "Backing up the OpenPGP-encrypted repository key into the remote destination..." echo $GPG_PASS | gpg --passphrase-fd 0 --armor --encrypt \ --recipient $GPG_KEY --default-key \ --output $BORG_KEY_FILE.asc \ --yes \ $GPG_KEY $BORG_KEY_FILE gpg_exit=$? chmod 600 $BORG_KEY_FILE.asc scp -o Port=$SSH_PORT $BORG_KEY_FILE.asc \ $SSH_SERVER:/var/backups/remote/$HOSTNAME/borg/keyfile.asc gpg_copy_exit=$? fi fi # Use highest exit code as global exit code global_exit=$(( backup_exit > prune_exit ? backup_exit : prune_exit )) if [ ${global_exit} -eq 1 ]; then info "Backup and/or Prune finished with a warning" fi if [ ${global_exit} -gt 1 ]; then info "Backup and/or Prune finished with an error" fi if [ "${global_exit}" != "0" ]; then fatal "Error completing borg action: exit code ${global_exit}" fi if [ "${gpg_exit}" != "0" ] || [ "${gpg_copy_exit}" != "0" ]; then fatal "Error backing the GPG-encrypted copy of the keyfile into the remote repository" fi