diff --git a/borgmatic_setup.sh b/borgmatic_setup.sh index 9a4c8ea..449df07 100755 --- a/borgmatic_setup.sh +++ b/borgmatic_setup.sh @@ -24,7 +24,6 @@ # Planned Features: # - Add support for more distributions -# - Add support for automatic setup (non-interactive) # SETTINGS - Make sure to adjust these settings to your needs DEBUG=0 # Set to 1 to enable debug mode @@ -48,6 +47,12 @@ SSH_KEY_FILE="/root/.ssh/${SSH_KEY_NAME}_${SSH_KEY_TYPE}" # SCRIPT - DO NOT EDIT +# internal variables +AUTO=0 +NO_UPGRADE=0 +FIRST_BACKUP=0 +IS_REMOTE_SYNOLGY=0 + # constants readonly SCRIPT_NAME="Borgmatic Backup Setup Tool" readonly SCRIPT_AUTHOR="Zion Networks at admin@zion-networks.de" @@ -107,6 +112,22 @@ function errp { 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 @@ -120,6 +141,8 @@ function inf_follow { ${@: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" @@ -146,6 +169,8 @@ function wrn_follow { _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" @@ -170,6 +195,8 @@ function err_follow { _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" @@ -269,6 +296,9 @@ function run_command { status=$? echo "$output" | while read -r line; do dbg "$line"; done 1>&3 exec 3>&- + + dbg "Command exit status: $status" + return $status } @@ -302,6 +332,11 @@ function check_crontab { return $? } +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: @@ -390,13 +425,15 @@ while [[ $# -gt 0 ]]; do inf "" inf "Options:" inf " -a, --auto: Enable automatic, non-interactive mode" + inf " -n, --no-upgrade: Skip package upgrades" inf " -d, --debug: Enable debug mode" + inf " -f, --first-backup: Run the first backup after setup" 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" + 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" @@ -404,7 +441,7 @@ while [[ $# -gt 0 ]]; do 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" + 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" @@ -427,9 +464,14 @@ while [[ $# -gt 0 ]]; do shift ;; -P|--passphrase) - BACKUP_PASSPHRASE="$2" - shift - shift + if [ -z $BACKUP_PASSPHRASE ]; then + BACKUP_PASSPHRASE="$2" + shift + shift + else + BACKUP_PASSPHRASE="" + shift + fi ;; -D|--dirs) BACKUP_DIRS="$2" @@ -462,9 +504,14 @@ while [[ $# -gt 0 ]]; do shift ;; -W|--password) - SSH_PASSWORD="$2" - shift - shift + if [ -z $SSH_PASSWORD ]; then + SSH_PASSWORD="$2" + shift + shift + else + SSH_PASSWORD="" + shift + fi ;; -K|--key-name) SSH_KEY_NAME="$2" @@ -516,6 +563,13 @@ wrn "This script will install \e[1mborgbackup\e[0m, \e[1mpipx\e[0m" wrn "and \e[1msshpass\e[0m from the official repositories as well" wrn "as \e[1mborgmatic\e[0m from the official PyPI repository." +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" + # only ask for confirmation in interactive mode if [ -z $AUTO ] && ! wrnp "Do you want to continue? [y/N]"; then inf "Aborted." @@ -609,37 +663,93 @@ if ! inf_follow "Checking for default ssh key at $SSH_KEY_FILE..." "\e[1;32mYes\ fi fi -if ! inf_follow "Copying ssh key to $SSH_HOST..." "\e[1;32mOK\e[0m" "\e[1;31mFAILED\e[0m" run_command sshpass -p "$SSH_PASSWORD" ssh-copy-id -i "$SSH_KEY_FILE" -p "$SSH_PORT" -o StrictHostKeyChecking=no "$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" "$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." +# 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 -i "$SSH_KEY_FILE" -p "$SSH_PORT" "$SSH_USER@$SSH_HOST" exit; then + # ask for ssh password if none is set + if [ -z "$SSH_PASSWORD" ]; 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." + + read -s -p "Password: " SSH_PASSWORD + echo "" + fi + + if ! inf_follow "Copying ssh key to $SSH_HOST..." "\e[1;32mOK\e[0m" "\e[1;31mFAILED\e[0m" run_command sshpass -p "$SSH_PASSWORD" ssh-copy-id -i "$SSH_KEY_FILE" -p "$SSH_PORT" -o StrictHostKeyChecking=no "$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" "$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" "$SSH_USER@$SSH_HOST" borg --version; then - if 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" "$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" "$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 + 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" "$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" "$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" "$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 - err "Please make sure borg is installed on the remote end." - err "This is required in order to do backups using borgmatic." - exit 1 + if 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" "$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" "$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 @@ -692,7 +802,16 @@ if ! inf_follow "Validating borgmatic configuration..." "\e[1;32mOK\e[0m" "\e[1; exit 1 fi -if [ "$BACKUP_ENCRYPTION" != "none" ]; then +# ask for backup passphrase if none is set and encryption is enabled +if [ -z "$BACKUP_PASSPHRASE" ] && [ "$BACKUP_ENCRYPTION" != "none" ]; then + wrn "Please enter the passphrase for the backup encryption." + wrn "This is required to encrypt your backups." + + read -s -p "Passphrase: " BACKUP_PASSPHRASE + echo "" +fi + +if [ "$BACKUP_ENCRYPTION" == "none" ]; then if ! inf_follow "Setting up borgmatic repository without encryption..." "\e[1;32mOK\e[0m" "\e[1;31mFAILED\e[0m" run_command /root/.local/pipx/venvs/borgmatic/bin/borgmatic init --make-parent-dirs --encryption=none; then err "Failed to set up borgmatic repository without encryption." err "If the error persists, please contact the support at $SCRIPT_SUPPORT."