#!/usr/bin/env bash ############################################################################### # Copyright (c) 2024 Zion Networks UG # # # # Permission is hereby granted, free of charge, to any person obtaining a # # copy of this software and associated documentation files (the "Software"), # # to deal in the Software without restriction, including without limitation # # the rights to use, copy, modify, merge, publish, distribute, sublicense, # # and/or sell copies of the Software, and to permit persons to whom the # # Software is furnished to do so, subject to the following conditions: # # # # The above copyright notice and this permission notice shall be included in # # all copies or substantial portions of the Software. # # # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, # # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # # DEALINGS IN THE SOFTWARE. # ############################################################################### # Planned Features: # - Add support for more distributions # - Add support for databases # - Pre-Backup commands # - Post-Backup commands # - Add support to run, restore, prune, check, delete and list backups # - Add support for multiple backup repositories # - Add support for multiple backup configurations # - Add support for multiple backup servers # SETTINGS - Make sure to adjust these settings to your needs DEBUG=0 # Set to 1 to enable debug mode BACKUP_REPO="Backups/$(hostname | tr '[:lower:]' '[:upper:]')" # Repository path on the backup server BACKUP_ENCRYPTION="none" # Encryption method for the backup repository (default: none) BACKUP_PASSPHRASE='example' # Make sure to change this to a secure passphrase (will be ignored if encryption is set to none) BACKUP_DIRS="/etc /var/log /home" # Directories to backup, separated by space BACKUP_LABEL_PREFIX="BACKUP-" # Prefix for the backup label BACKUP_CRON_SCHEDULE="0 3 * * *" # Cron schedule for backups (default: daily at 3:00 AM) BORGMATIC_CONFIG_FILE="/etc/borgmatic/config.yaml" # Path to the borgmatic configuration file - DO NOT CHANGE UNLESS YOU KNOW WHAT YOU ARE DOING SSH_HOST="127.0.0.1" # Hostname or IP address of the backup server SSH_PORT="22" # SSH port of the backup server (default: 22) SSH_USER="root" # SSH user on the backup server SSH_PASSWORD='12345678' # Only required for copying the ssh key to the backup server SSH_KEY_NAME="borgmatic" SSH_KEY_TYPE="ed25519" # SSH key type (e.g. rsa, dsa, ecdsa, ed25519), default: ed25519 SSH_KEY_FILE="/root/.ssh/${SSH_KEY_NAME}_${SSH_KEY_TYPE}" # SCRIPT - DO NOT EDIT # internal variables AUTO=0 # Set to 1 to enable automatic, non-interactive mode DO_UPGRADE_ALL=0 # Set to 1 to upgrade all installed packages DO_UPGRADE_REQUIRED=0 # Set to 1 to upgrade only required packages FIRST_BACKUP=0 # Set to 1 to run the first backup after setup IS_REMOTE_SYNOLGY=0 # Set to 1 if the remote end is a Synology NAS OVERRIDE_CRONTAB=0 # Set to 1 to override the existing crontab file OVERRIDE_CONFIG=0 # Set to 1 to override the existing borgmatic configuration file OVERRIDE_REPOSITORY=0 # Set to 1 to override the existing backup repository SKIP_CONFIG=0 # Set to 1 to skip the borgmatic configuration # constants readonly SCRIPT_NAME="Borgmatic Backup Setup Tool" readonly SCRIPT_AUTHOR="Zion Networks at admin@zion-networks.de" readonly SCRIPT_SUPPORT="admin@zion-networks.de" readonly VERSION="1.2.4" # logging functions function inf { echo -e "\e[97m[$(date +%H:%M:%S)] \e[1mINF\e[0m $1" } function wrn { echo -e "\e[93m[$(date +%H:%M:%S)] \e[1mWRN\e[0m $1" } function err { echo -e "\e[91m[$(date +%H:%M:%S)] \e[1mERR\e[0m $1" } function dbg { if [ $DEBUG -eq 1 ]; then echo -e "\e[94m[$(date +%H:%M:%S)] \e[1mDBG\e[0m \e[3m$1\e[0m" fi } # logging prompt functions function infp { echo -e -n "\e[97m[$(date +%H:%M:%S)] \e[1mINF\e[0m $1 " read -p "" -r if [[ ! $REPLY =~ ^[Yy]$ ]]; then return 1 else return 0 fi } function wrnp { echo -e -n "\e[93m[$(date +%H:%M:%S)] \e[1mWRN\e[0m $1 " read -p "" -r if [[ ! $REPLY =~ ^[Yy]$ ]]; then return 1 else return 0 fi } function errp { echo -e -n "\e[91m[$(date +%H:%M:%S)] \e[1mERR\e[0m $1 " read -p "" -r if [[ ! $REPLY =~ ^[Yy]$ ]]; then return 1 else return 0 fi } # logging functions that wait for user input (single key) function infw { echo -e -n "\e[97m[$(date +%H:%M:%S)] \e[1mINF\e[0m $1 " read -n 1 -r } function wrnw { echo -e -n "\e[93m[$(date +%H:%M:%S)] \e[1mWRN\e[0m $1 " read -n 1 -r } function errw { echo -e -n "\e[91m[$(date +%H:%M:%S)] \e[1mERR\e[0m $1 " read -n 1 -r } # logging functions with follow up (like "[HH:MM:SS] INF Installig package ... OK" where OK is a follow up) function inf_follow { if [ $DEBUG -eq 1 ]; then echo -e "\e[97m[$(date +%H:%M:%S)] \e[1mINF\e[0m $1" else echo -e -n "\e[97m[$(date +%H:%M:%S)] \e[1mINF\e[0m $1" fi dbg "Running ${@:4}" ${@:4} _r=$? dbg "Follow exit status: $_r" if [ $_r -eq 0 ]; then if [ $DEBUG -eq 1 ]; then echo -e "\e[97m[$(date +%H:%M:%S)] \e[1mINF\e[0m $1: $2" else echo -e " $2" fi else if [ $DEBUG -eq 1 ]; then echo -e "\e[97m[$(date +%H:%M:%S)] \e[1mINF\e[0m $1: $3" else echo -e " $3" fi fi return $_r } function wrn_follow { if [ $DEBUG -eq 1 ]; then echo -e "\e[93m[$(date +%H:%M:%S)] \e[1mWRN\e[0m $1" else echo -e -n "\e[93m[$(date +%H:%M:%S)] \e[1mWRN\e[0m $1" fi _r=$(${@:4}) dbg "Follow exit status: $_r" if [ $_r -eq 0 ]; then if [ $DEBUG -eq 1 ]; then echo -e "\e[93m[$(date +%H:%M:%S)] \e[1mWRN\e[0m $1: $2" else echo -e " $2" fi else if [ $DEBUG -eq 1 ]; then echo -e "\e[93m[$(date +%H:%M:%S)] \e[1mWRN\e[0m $1: $3" else echo -e " $3" fi fi } function err_follow { if [ $DEBUG -eq 1 ]; then echo -e "\e[91m[$(date +%H:%M:%S)] \e[1mERR\e[0m $1" else echo -e -n "\e[91m[$(date +%H:%M:%S)] \e[1mERR\e[0m $1" fi _r=$(${@:4}) dbg "Follow exit status: $_r" if [ $_r -eq 0 ]; then if [ $DEBUG -eq 1 ]; then echo -e "\e[91m[$(date +%H:%M:%S)] \e[1mERR\e[0m $1: $2" else echo -e " $2" fi else if [ $DEBUG -eq 1 ]; then echo -e "\e[91m[$(date +%H:%M:%S)] \e[1mERR\e[0m $1: $3" else echo -e " $3" fi fi } # setup functions function apt_update { exec 3>&1 status=$(apt-get update 2>&1 | while read -r line; do dbg "$line"; done 1>&3) exec 3>&- return $status } function apt_upgrade { exec 3>&1 status=$(apt-get upgrade -y 2>&1 | while read -r line; do dbg "$line"; done 1>&3) exec 3>&- return $status } function apt_upgrade_selected { exec 3>&1 status=$(apt-get install -y $* 2>&1 | while read -r line; do dbg "$line"; done 1>&3) exec 3>&- return $status } function apt_install { exec 3>&1 status=$(apt-get install -y $1 2>&1 | while read -r line; do dbg "$line"; done 1>&3) exec 3>&- return $status } function pipx_install { exec 3>&1 status=$(pipx install $1 2>&1 | while read -r line; do dbg "$line"; done 1>&3) exec 3>&- return $status } function pipx_upgrade { exec 3>&1 status=$(pipx upgrade $1 2>&1 | while read -r line; do dbg "$line"; done 1>&3) exec 3>&- return $status } function apt_is_installed { status=$(dpkg -l $1 2>&1 | tee >(while read -r line; do dbg "$line"; done)) dbg "Status: $status" # Check if package is installed based on the output of dpkg -l if echo "$status" | grep -q "^ii"; then return 0 else return 1 fi } function pipx_is_installed { if ! check_command pipx; then return 1 fi status=$(pipx list 2>&1 | tee >(while read -r line; do dbg "$line"; done) | grep -q $1) return $? } function check_command { exec 3>&1 command -v $1 >/dev/null 2>&1 status=$? if [ $status -eq 0 ]; then echo "$1 exists" | while read -r line; do dbg "$line"; done 1>&3 else echo "$1 does not exist" | while read -r line; do dbg "$line"; done 1>&3 fi exec 3>&- return $status } function run_command { dbg "Running command: sudo $*" exec 3>&1 output=$(sudo "$@" 2>&1) status=$? echo "$output" | while read -r line; do dbg "$line"; done 1>&3 exec 3>&- dbg "Command exit status: $status" return $status } function run_command_logged { # run command and log output using inf function for each line dbg "Running command: sudo $*" exec 3>&1 sudo "$@" 2>&1 | while read -r line; do inf "$line"; done 1>&3 status=$? exec 3>&- dbg "Command exit status: $status" return $status } function file_exists { if [ -f $1 ]; then return 0 else return 1 fi } function dir_exists { if [ -d $1 ]; then return 0 else return 1 fi } function add_crontab { if check_crontab; then if [ $OVERRIDE_CRONTAB -eq 1 ]; then wrn "Crontab file already exists. Overriding existing crontab file." else err "Crontab file already exists. Please remove the existing crontab file or use the --override-crontab option." return 1 fi fi echo "$BACKUP_CRON_SCHEDULE root PATH=\$PATH:/usr/bin:/usr/local/bin /root/.local/bin/borgmatic --verbosity -1 --syslog-verbosity 1" > /etc/cron.d/borgmatic return $? } function check_crontab { # check for file /etc/cron.d/borgmatic if [ -f /etc/cron.d/borgmatic ]; then return 0 else return 1 fi } function is_remote_synology { run_command ssh -i "$SSH_KEY_FILE" -p "$SSH_PORT" "$SSH_USER@$SSH_HOST" test -f /etc/synoinfo.conf return $? } # main script # Available arguments: # -a, --auto: Enable automatic, non-interactive mode # -d, --debug: Enable debug mode # -f, --first-backup: Run the first backup after setup # -o, --override-crontab: Override the existing crontab file # -c, --override-config: Override the existing borgmatic configuration file # -r, --override-repository: Override the existing backup repository # -s, --upgrade-required: Upgrade only required packages # -u, --upgrade-all: Upgrade all installed packages # -v, --version: Show script version # -h, --help: Show help message # # -R, --repo: Set the backup repository path # -E, --encryption: Set the encryption method # -P, --passphrase: Set the backup passphrase # -D, --dirs: Set the backup directories (separated by space as a string, for example: --dirs "/etc /var/log /home") # -L, --label: Set the backup label prefix # -S, --schedule: Set the cron schedule # # -H, --host: Set the backup server hostname or IP address # -P, --port: Set the backup server SSH port # -U, --user: Set the backup server SSH user # -W, --password: Set the backup server SSH password # -K, --key-name: Set the SSH key name # -T, --key-type: Set the SSH key type # -F, --key-file: Set the SSH key file path # # Example usage: # ./borgmatic_setup.sh -R "Backups/MyServer" \ # -E "none" -P "example" \ # -D "/etc /var/log /home" \ # -L "BACKUP-" \ # -S "0 3 * * *" \ # -H "192.168.1.200" \ # -P "22" \ # -U "root" \ # -W "12345678" \ # -K "borgmatic" \ # -T "ed25519" \ # -F "/root/.ssh/borgmatic_ed25519" # # or with long options: # ./borgmatic_setup.sh --repo "Backups/MyServer" \ # --encryption "none" --passphrase "example" \ # --dirs "/etc /var/log /home" \ # --label "BACKUP-" \ # --schedule "0 3 * * *" \ # --host "192.168.1.200" \ # --port "22" \ # --user "root" \ # --password "12345678" # --key-name "borgmatic" \ # --key-type "ed25519" \ # --key-file "/root/.ssh/borgmatic_ed25519" while [[ $# -gt 0 ]]; do key="$1" case $key in -a|--auto) AUTO=1 shift ;; -d|--debug) DEBUG=1 shift ;; -v|--version) echo "$SCRIPT_NAME v$VERSION" exit 0 ;; -s|--upgrade-required) DO_UPGRADE_REQUIRED=1 shift ;; -u|--upgrade-all) DO_UPGRADE_ALL=1 shift ;; -f|--first-backup) FIRST_BACKUP=1 shift ;; -o|--override-crontab) OVERRIDE_CRONTAB=1 shift ;; -c|--override-config) OVERRIDE_CONFIG=1 shift ;; -k|--skip-config) SKIP_CONFIG=1 shift ;; -r|--override-repository) OVERRIDE_REPOSITORY=1 shift ;; -h|--help) inf "Usage: borgmatic_setup.sh [options]" inf "" inf "Options will override the default settings set in the script." inf "If no options are provided, the script will run in interactive mode." inf "All options are optional." inf "" if [ "$EUID" -ne 0 ]; then inf "\e[1m\e[31mImportant: The script must be run as root to work properly.\e[0m" inf "" fi inf "Options:" inf " -a, --auto: Enable automatic, non-interactive mode" inf " -d, --debug: Enable debug mode" inf " -f, --first-backup: Run the first backup after setup" inf " -o, --override-crontab: Override the existing crontab file" inf " -c, --override-config: Override the existing borgmatic configuration file" inf " -k, --skip-config: Skip the borgmatic configuration" inf " -r, --override-repository: Override the existing backup repository" inf " -s, --upgrade-required: Upgrade only required packages" inf " -u, --upgrade-all: Upgrade all installed packages" inf " -v, --version: Show script version" inf " -h, --help: Show help message" inf "" inf " -R, --repo: Set the backup repository path" inf " -E, --encryption: Set the encryption method" inf " -P, --passphrase: Set the backup passphrase (if set but empty, the script will ask for the passphrase)" inf " -D, --dirs: Set the backup directories (separated by space as a string, for example: --dirs \"/etc /var/log /home\")" inf " -L, --label: Set the backup label prefix" inf " -S, --schedule: Set the cron schedule" inf "" inf " -H, --host: Set the backup server hostname or IP address" inf " -P, --port: Set the backup server SSH port" inf " -U, --user: Set the backup server SSH user" inf " -W, --password: Set the backup server SSH password (if set but empty, the script will ask for the password)" inf " -K, --key-name: Set the SSH key name" inf " -T, --key-type: Set the SSH key type" inf " -F, --key-file: Set the SSH key file path" inf "" inf "Example usage:" inf "./borgmatic_setup.sh --dirs \"/etc /var/log /home\" --host \"192.168.1.200\" --encryption \"none\"" inf "" inf "or for an encrypted backup:" inf "./borgmatic_setup.sh --dirs \"/etc /var/log /home\" --host \"192.168.1.200\" --encryption \"repokey\" --passphrase \"example\"" exit 0 ;; -R|--repo) BACKUP_REPO="$2" shift shift ;; -E|--encryption) BACKUP_ENCRYPTION="$2" shift shift ;; -P|--passphrase) if [ -z $BACKUP_PASSPHRASE ]; then BACKUP_PASSPHRASE="$2" shift shift else BACKUP_PASSPHRASE="" shift fi ;; -D|--dirs) BACKUP_DIRS="$2" shift shift ;; -L|--label) BACKUP_LABEL_PREFIX="$2" shift shift ;; -S|--schedule) BACKUP_CRON_SCHEDULE="$2" shift shift ;; -H|--host) SSH_HOST="$2" shift shift ;; -P|--port) SSH_PORT="$2" shift shift ;; -U|--user) SSH_USER="$2" shift shift ;; -W|--password) if [ -z $SSH_PASSWORD ]; then SSH_PASSWORD="$2" shift shift else SSH_PASSWORD="" shift fi ;; -K|--key-name) SSH_KEY_NAME="$2" shift shift ;; -T|--key-type) SSH_KEY_TYPE="$2" shift shift ;; -F|--key-file) SSH_KEY_FILE="$2" shift shift ;; *) shift ;; esac done echo -e "\e[97m╭─────────────────────────────────────────────────────────────────────────────╮" echo -e "│ │" echo -e "│ \e[1m$SCRIPT_NAME\e[0m │" echo -e "│ \e[1mv$VERSION\e[0m │" echo -e "│ │" echo -e "╰─────────────────────────────────────────────────────────────────────────────╯\e[0m" inf "For support please contact $SCRIPT_AUTHOR" if [ $DO_UPGRADE_ALL -eq 1 ]; then DO_UPGRADE_REQUIRED=1 fi if [ $DEBUG -eq 1 ]; then dbg "Debug mode is enabled. This will print additional information." fi # check if script is running as root if [ "$EUID" -ne 0 ]; then err "Please run this script as root" exit 1 fi # check if script is running on a supported OS (Ubuntu or Debian) if [ ! -f /etc/os-release ]; then err "This script currently only supports Ubuntu and Debian" exit 1 fi wrn "This script will install \e[1mborgbackup\e[0m, \e[1mpipx\e[0m, python3-venv" wrn "and \e[1msshpass\e[0m from the official repositories and \e[1mborgmatic\e[0m" wrn "from the official PyPI repository." inf "" inf "The following settings will be used for the backup setup:" inf "Backup repository: $BACKUP_REPO" inf "Backup encryption: $BACKUP_ENCRYPTION" inf "Backup directories: $BACKUP_DIRS" inf "Backup cron schedule: $BACKUP_CRON_SCHEDULE" inf "Backup server host: $SSH_USER@$SSH_HOST:$SSH_PORT" inf "" if [ $DO_UPGRADE_ALL -eq 1 ]; then wrn "All installed packages will be upgraded!" fi if [ $OVERRIDE_CRONTAB -eq 1 ]; then wrn "Existing cron job at /etc/cron.d/borgmatic will be overridden!" fi if [ $OVERRIDE_CONFIG -eq 1 ]; then wrn "Existing borgmatic configuration file at $BORGMATIC_CONFIG_FILE will be overridden!" fi # only ask for confirmation in interactive mode if [ $AUTO -eq 0 ] && ! wrnp "Do you want to continue? [y/N]"; then inf "Aborted." exit 1 fi if ! check_command apt-get; then err "apt-get is not installed. This script currently only supports Ubuntu and Debian." exit 1 fi if ! check_command dpkg; then err "dpkg is not installed. This script only currently supports Ubuntu and Debian." exit 1 fi if ! inf_follow "Updating package lists..." "\e[1;32mOK\e[0m" "\e[1;31mFAILED\e[0m" apt_update; then exit 1 fi if ! check_command sshpass; then if ! inf_follow "Installing sshpass..." "\e[1;32mOK\e[0m" "\e[1;31mFAILED\e[0m" apt_install sshpass; then err "Installation of sshpass failed!" exit 1 fi else if ! inf_follow "Upgrading sshpass..." "\e[1;32mOK\e[0m" "\e[1;31mFAILED\e[0m" apt_upgrade sshpass; then err "Upgrade of sshpass failed!" exit 1 fi fi if ! inf_follow "Checking if python3-venv is installed..." "\e[1;32mYes\e[0m" "\e[1;31mNo\e[0m" apt_is_installed python3-venv; then if ! inf_follow "Installing python3-venv..." "\e[1;32mOK\e[0m" "\e[1;31mFAILED\e[0m" apt_install python3-venv; then err "Installation of python3-venv failed!" exit 1 fi fi if [ $DO_UPGRADE_ALL -eq 1 ]; then if [ $AUTO -eq 1 ] || wrnp "Do you want to upgrade all installed packages? [y/N]"; then if ! inf_follow "Upgrading packages..." "\e[1;32mOK\e[0m" "\e[1;31mFAILED\e[0m" apt_upgrade; then exit 1 fi fi fi if [ $DO_UPGRADE_REQUIRED -eq 1 ]; then if [ $AUTO -eq 1 ] || wrnp "Do you want to upgrade only required packages? [y/N]"; then inf "Upgrading required packages..." if ! inf_follow "Upgrading borgbackup..." "\e[1;32mOK\e[0m" "\e[1;31mFAILED\e[0m" apt_upgrade_selected borgbackup; then err "Upgrade of borgbackup failed!" exit 1 fi if ! inf_follow "Upgrading pipx..." "\e[1;32mOK\e[0m" "\e[1;31mFAILED\e[0m" apt_upgrade_selected pipx; then err "Upgrade of pipx failed!" exit 1 fi if ! inf_follow "Upgrading borgmatic..." "\e[1;32mOK\e[0m" "\e[1;31mFAILED\e[0m" apt_upgrade_selected borgmatic; then err "Upgrade of borgmatic failed!" exit 1 fi if ! inf_follow "Upgrading python3-venv..." "\e[1;32mOK\e[0m" "\e[1;31mFAILED\e[0m" apt_upgrade_selected python3-venv; then err "Upgrade of python3-venv failed!" exit 1 fi if ! inf_follow "Upgrading sshpass..." "\e[1;32mOK\e[0m" "\e[1;31mFAILED\e[0m" apt_upgrade_selected sshpass; then err "Upgrade of sshpass failed!" exit 1 fi fi fi if ! inf_follow "Checking if borgbackup is installed..." "\e[1;32mYes\e[0m" "\e[1;31mNo\e[0m" apt_is_installed borgbackup; then if ! inf_follow "Installing borgbackup..." "\e[1;32mOK\e[0m" "\e[1;31mFAILED\e[0m" apt_install borgbackup; then err "Installation of borgbackup failed!" exit 1 fi if ! inf_follow "Validating ..." "\e[1;32mOK\e[0m" "\e[1;31mFAILED\e[0m" check_command borg; then err "Installation of borgbackup failed!" exit 1 fi fi if ! inf_follow "Checking if pipx is installed..." "\e[1;32mYes\e[0m" "\e[1;31mNo\e[0m" apt_is_installed pipx; then if ! inf_follow "Installing pipx..." "\e[1;32mOK\e[0m" "\e[1;31mFAILED\e[0m" apt_install pipx; then err "Installation of pipx failed!" exit 1 fi fi if ! inf_follow "Checking if borgmatic is installed..." "\e[1;32mYes\e[0m" "\e[1;31mNo\e[0m" pipx_is_installed borgmatic; then if ! inf_follow "Installing borgmatic..." "\e[1;32mOK\e[0m" "\e[1;31mFAILED\e[0m" pipx_install borgmatic; then err "Installation of borgmatic failed!" exit 1 fi else if [ $DO_UPGRADE_REQUIRED -eq 1 ]; then if [ $AUTO -eq 1 ] || wrnp "Do you want to upgrade borgmatic? [y/N]"; then if ! inf_follow "Upgrading borgmatic..." "\e[1;32mOK\e[0m" "\e[1;31mFAILED\e[0m" pipx_upgrade borgmatic; then err "Upgrade of borgmatic failed!" exit 1 fi fi fi fi if ! inf_follow "Checking for ~/.ssh directory..." "\e[1;32mYes\e[0m" "\e[1;31mNo\e[0m" dir_exists ~/.ssh; then if ! inf_follow "Creating ~/.ssh directory..." "\e[1;32mOK\e[0m" "\e[1;31mFAILED\e[0m" mkdir -p ~/.ssh; then err "Failed to create ~/.ssh directory." err "Please make sure you have the required permissions." exit 1 fi fi if ! inf_follow "Checking for default ssh key at $SSH_KEY_FILE..." "\e[1;32mYes\e[0m" "\e[1;31mNo\e[0m" file_exists $SSH_KEY_FILE; then inf "Generating a new ssh key pair..." ssh-keygen -t $SSH_KEY_TYPE -C "borgmatic_backup_$(hostname)" -f $SSH_KEY_FILE -N "" 2>&1 | while read -r line; do dbg "$line"; done if [ $? -eq 0 ]; then inf "Successfully generated a new ssh key pair." else err "Failed to generate a new ssh key pair." err "If the error persists, please contact the support at $SCRIPT_SUPPORT." exit 1 fi fi # check first if ssh key is already present on the remote end if ! inf_follow "Checking if ssh key is already present on $SSH_HOST..." "\e[1;32mYes\e[0m" "\e[1;31mNo\e[0m" run_command ssh -oStrictHostKeyChecking=no -oBatchMode=yes -i "$SSH_KEY_FILE" -p "$SSH_PORT" -l "$SSH_USER" "$SSH_HOST" exit; then wrn "Please enter the password for the ssh key to copy it to the backup server." wrn "This is required to enable passwordless ssh login for backups." if ! inf_follow "Copying ssh key to $SSH_HOST..." "\e[1;32mOK\e[0m" "\e[1;31mFAILED\e[0m" run_command ssh-copy-id -oStrictHostKeyChecking=no -oBatchMode=no -i "$SSH_KEY_FILE.pub" -p "$SSH_PORT" "$SSH_USER@$SSH_HOST"; then err "Failed to copy ssh key to $SSH_HOST." err "Please validate your ssh password and and host settings and try again." err "If the error persists, please contact the support at $SCRIPT_SUPPORT." exit 1 else if ! inf_follow "Checking if ssh key is working..." "\e[1;32mOK\e[0m" "\e[1;31mFAILED\e[0m" run_command ssh -i "$SSH_KEY_FILE" -p "$SSH_PORT" -oStrictHostKeyChecking=no -oBatchMode=yes -l "$SSH_USER" "$SSH_HOST" exit; then err "Failed to connect to $SSH_HOST using the ssh key." err "Please validate you have the correct ssh key and host settings." err "If the error persists, please contact the support at $SCRIPT_SUPPORT." exit 1 fi fi fi if ! inf_follow "Checking if remote end has borg installed..." "\e[1;32mYes\e[0m" "\e[1;31mNo\e[0m" run_command ssh -i "$SSH_KEY_FILE" -p "$SSH_PORT" -oStrictHostKeyChecking=no -oBatchMode=yes -l "$SSH_USER" "$SSH_HOST" borg --version; then if inf_follow "Checking if remote end is a Synology NAS..." "\e[1;32mYes\e[0m" "\e[1;31mNo\e[0m" is_remote_synology; then IS_REMOTE_SYNOLGY=1 fi if [ $IS_REMOTE_SYNOLGY -eq 1 ]; then wrn "The remote end appears to be a Synology NAS." wrn "Please make sure you have installed borg on the remote end." wrn "To install borg on a Synology NAS you can use the Synology Package Center:" wrn "- Open the Package Center" wrn "- Go to Settings > Package Sources" wrn "- Add a package source named 'Community' with the Location http://packages.synocommunity.com" wrn "- Go to the Community section and search for 'Borg'" wrn "- Install the 'Borg' package" wrn "" wrn "You also need to enable user homes in the Synology Control Panel:" wrn "- Open the Control Panel" wrn "- Go to User & Group > Advanced" wrn "- At the bottom, check 'Enable user home service'" wrn "- Apply the changes" wrn "" wrn "PLEASE DO NOT CONTINUE UNTIL YOU HAVE INSTALLED BORG ON THE REMOTE END." wrn "" wrnw "Press any key to continue..." if ! inf_follow "Checking if remote end has borg installed..." "\e[1;32mYes\e[0m" "\e[1;31mNo\e[0m" run_command ssh -i "$SSH_KEY_FILE" -p "$SSH_PORT" -oStrictHostKeyChecking=no -oBatchMode=yes -l "$SSH_USER" "$SSH_HOST" borg --version; then if inf_follow "Checking if borg was installed at /usr/local/bin..." "\e[1;32mYes\e[0m" "\e[1;31mNo\e[0m" run_command ssh -i "$SSH_KEY_FILE" -p "$SSH_PORT" -oStrictHostKeyChecking=no -oBatchMode=yes -l "$SSH_USER" "$SSH_HOST" test -f /usr/local/bin/borg; then if inf_follow "Creating symlink to /bin/borg..." "\e[1;32mOK\e[0m" "\e[1;31mFAILED\e[0m" run_command ssh -i "$SSH_KEY_FILE" -p "$SSH_PORT" -oStrictHostKeyChecking=no -oBatchMode=yes -l "$SSH_USER" "$SSH_HOST" ln -s /usr/local/bin/borg /bin/borg; then inf "Successfully created symlink to /bin/borg." else err "Failed to create symlink to /bin/borg." err "Please login to the remote end via ssh at $SSH_USER@$SSH_HOST and create the symlink manually." err "The required command is: sudo ln -s /usr/local/bin/borg /bin/borg" err "Please to not continue until you have created the symlink." err "" errw "Press any key to continue..." fi else err "Borg could not be found on the remote end at /usr/local/bin (which is the default installation path for Synology NAS)." err "Please make sure you have installed borg on the remote end." exit 1 fi fi else if [ $AUTO -eq 1 ] || wrnp "The remote end does not have borg installed. Do you want to install it now? [y/N]"; then if ! inf_follow "Updating package repositories on remote end" "\e[1;32mOK\e[0m" "\e[1;31mFAILED\e[0m" run_command ssh -i "$SSH_KEY_FILE" -p "$SSH_PORT" -oStrictHostKeyChecking=no -oBatchMode=yes -l "$SSH_USER" "$SSH_HOST" apt-get update; then err "Failed to update package repositories on remote end." err "Please make sure you have the required access rights." exit 1 fi if ! inf_follow "Installing borg on remote end..." "\e[1;32mOK\e[0m" "\e[1;31mFAILED\e[0m" run_command ssh -i "$SSH_KEY_FILE" -p "$SSH_PORT" -oStrictHostKeyChecking=no -oBatchMode=yes -l "$SSH_USER" "$SSH_HOST" apt-get install -y borgbackup; then err "Failed to install borg on remote end." err "Please make sure you have the required access rights" exit 1 fi else err "Please make sure borg is installed on the remote end." err "This is required in order to do backups using borgmatic." exit 1 fi fi fi inf "Setting up borgmatic configuration..." # check if directory path exists if ! inf_follow "Checking for borgmatic configuration directory..." "\e[1;32mYes\e[0m" "\e[1;31mNo\e[0m" dir_exists $(dirname $BORGMATIC_CONFIG_FILE); then if ! inf_follow "Creating borgmatic configuration directory..." "\e[1;32mOK\e[0m" "\e[1;31mFAILED\e[0m" mkdir -p $(dirname $BORGMATIC_CONFIG_FILE); then err "Failed to create borgmatic configuration directory." err "If the error persists, please contact the support at $SCRIPT_SUPPORT." exit 1 fi fi if [ "${BACKUP_REPO:0:1}" == "/" ]; then wrn "Your backup repository path starts with a '/', which is not recommended." wrn "It will be removed automatically, but please make sure you have set the correct" wrn "path. Leading slashes will cause the config validation to fail." BACKUP_REPO=${BACKUP_REPO:1} fi # check if borgmatic configuration file already exists and abort if $OVERRIDE_CONFIG is not set if [ -f $BORGMATIC_CONFIG_FILE ] && [ $OVERRIDE_CONFIG -ne 1 ]; then if [ $SKIP_CONFIG -eq 1 ]; then wrn "Skipping borgmatic configuration setup." wrn "Please make sure you have set up the configuration file manually." else err "A borgmatic configuration file already exists at $BORGMATIC_CONFIG_FILE." err "Please remove the existing configuration file or use the --override-config option." exit 1 fi elif [ -f $BORGMATIC_CONFIG_FILE ] && [ $OVERRIDE_CONFIG -eq 1 ]; then wrn "Existing borgmatic configuration file will be overridden!" fi if [ $SKIP_CONFIG -eq 0 ]; then cat > $BORGMATIC_CONFIG_FILE <