From 2bfe1c62e03bc37f722bd757f77d5df876a16074 Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Thu, 18 Aug 2022 10:53:52 +0200 Subject: [PATCH 01/40] renamed and resorted configuration options - there is now only one identity and one remote host for all protocols - added more configuration option descriptions --- remote-backup/config.yaml | 68 +++++++++---------- remote-backup/run.sh | 103 ++++++++++++++--------------- remote-backup/translations/en.yaml | 51 +++++++++++--- 3 files changed, 122 insertions(+), 100 deletions(-) diff --git a/remote-backup/config.yaml b/remote-backup/config.yaml index 4f15198..a71a922 100644 --- a/remote-backup/config.yaml +++ b/remote-backup/config.yaml @@ -23,63 +23,59 @@ map: - share - ssl - backup:rw + options: debug: false - ssh_enabled: true - friendly_name: true - custom_prefix: Automated backup - ssh_host: "" - ssh_port: 22 - ssh_user: "" - ssh_key: "" - ssh_host_key_algorithms: "" - exclude_folders: [] - exclude_addons: [] - remote_directory: "" + remote_host: "" + remote_port: 22 + remote_user: "" + remote_password: "" + remote_key: "" + remote_host_key_algorithms: "" + backup_friendly_name: true + backup_custom_prefix: Automated backup + backup_exclude_folders: [] + backup_exclude_addons: [] + backup_keep_local: "all" backup_password: "" - keep_local_backup: "all" + ssh_enabled: true + ssh_remote_directory: "" rsync_enabled: false - rsync_host: "" rsync_rootfolder: hassio-sync rsync_exclude: - "/config/*.db-shm" - "/config/*.db-wal" - "/config/*.db" - rsync_user: "" - rsync_password: "" rclone_enabled: false + rclone_remote_directory: "" rclone_copy: false rclone_sync: false rclone_restore: false - rclone_remote: "" - rclone_remote_directory: "" + schema: debug: bool? - ssh_enabled: bool? - friendly_name: bool? - custom_prefix: str? - ssh_host: str? - ssh_port: port? - ssh_user: str? - ssh_key: str? - ssh_host_key_algorithms: str? - exclude_folders: + remote_host: str + remote_port: port + remote_user: str + remote_password: str? + remote_key: str? + remote_host_key_algorithms: str? + backup_friendly_name: bool? + backup_custom_prefix: str? + backup_exclude_folders: - match(^[A-Za-z0-9_\-\.\*\/\?\+\\]*$)? - exclude_addons: + backup_exclude_addons: - str? - remote_directory: str? + backup_keep_local: match(^(all|[+]?\d*)$) backup_password: str? - keep_local_backup: match(^(all|[+]?\d*)$) - rsync_enabled: bool? - rsync_host: str? + ssh_enabled: bool + ssh_remote_directory: str? + rsync_enabled: bool rsync_rootfolder: str? rsync_exclude: - match(^[A-Za-z0-9_\-\.\*\/\?\+\\]+$) - rsync_user: str? - rsync_password: str? - rclone_enabled: bool? + rclone_enabled: bool + rclone_remote_directory: str? rclone_copy: bool? rclone_sync: bool? rclone_restore: bool? - rclone_remote: str? - rclone_remote_directory: str? diff --git a/remote-backup/run.sh b/remote-backup/run.sh index e57a970..025f2c0 100755 --- a/remote-backup/run.sh +++ b/remote-backup/run.sh @@ -1,41 +1,38 @@ #!/command/with-contenv bashio # shellcheck shell=bash # parse inputs from options -DEBUG=$(bashio::config 'debug') -SSH_ENABLED=$(bashio::config "ssh_enabled") -FRIENDLY_NAME=$(bashio::config "friendly_name") -CUSTOM_PREFIX=$(bashio::config "custom_prefix") -SSH_HOST=$(bashio::config "ssh_host") -SSH_PORT=$(bashio::config "ssh_port") -SSH_USER=$(bashio::config "ssh_user") -SSH_KEY=$(bashio::config "ssh_key") -SSH_HOST_KEY_ALGORITHMS=$(bashio::config "ssh_host_key_algorithms") -EXCLUDE_FOLDERS=$(bashio::config "exclude_folders") -EXCLUDE_ADDONS=$(bashio::config "exclude_addons") -REMOTE_DIRECTORY=$(bashio::config "remote_directory") +DEBUG=$(bashio::config "debug") +REMOTE_HOST=$(bashio::config "remote_host") +REMOTE_PORT=$(bashio::config "remote_port") +REMOTE_USER=$(bashio::config "remote_user") +REMOTE_PASSWORD=$(bashio::config "remote_password") +REMOTE_KEY=$(bashio::config "remote_key") +REMOTE_HOST_KEY_ALGORITHMS=$(bashio::config "remote_host_key_algorithms") + +BACKUP_FRIENDLY_NAME=$(bashio::config "backup_friendly_name") +BACKUP_CUSTOM_PREFIX=$(bashio::config "backup_custom_prefix") +BACKUP_EXCLUDE_FOLDERS=$(bashio::config "backup_exclude_folders") +BACKUP_EXCLUDE_ADDONS=$(bashio::config "backup_exclude_addons") +BACKUP_KEEP_LOCAL=$(bashio::config 'backup_keep_local') BACKUP_PASSWORD=$(bashio::config 'backup_password') -KEEP_LOCAL_BACKUP=$(bashio::config 'keep_local_backup') + +SSH_ENABLED=$(bashio::config "ssh_enabled") +SSH_REMOTE_DIRECTORY=$(bashio::config "ssh_remote_directory") RSYNC_ENABLED=$(bashio::config "rsync_enabled") -RSYNC_HOST=$(bashio::config "rsync_host") RSYNC_ROOTFOLDER=$(bashio::config "rsync_rootfolder") -RSYNC_USER=$(bashio::config "rsync_user") RSYNC_EXCLUDE=$(bashio::config "rsync_exclude") -RSYNC_PASSWORD=$(bashio::config "rsync_password") + RCLONE_ENABLED=$(bashio::config "rclone_enabled") +RCLONE_REMOTE_DIRECTORY=$(bashio::config "rclone_remote_directory") RCLONE_COPY=$(bashio::config "rclone_copy") RCLONE_SYNC=$(bashio::config "rclone_sync") RCLONE_RESTORE=$(bashio::config "rclone_restore") -RCLONE_REMOTE=$(bashio::config "rclone_remote") -RCLONE_REMOTE_DIRECTORY=$(bashio::config "rclone_remote_directory") -# create variables -SSH_ID="/ssl/${SSH_KEY}" -SSH_ID=$(echo -n "${SSH_ID}") function set-debug-level { # default log level according to bashio const.sh is INFO - if [ "${DEBUG}" = true ] ; then + if bashio::var.true "${DEBUG}"; then bashio::log.level "debug" fi } @@ -44,18 +41,18 @@ function add-ssh-key { if [ "${SSH_ENABLED}" = true ] || [ "${RSYNC_ENABLED}" = true ] ; then bashio::log.info "Adding SSH key" mkdir -p ~/.ssh - cp "${SSH_ID}" "${HOME}"/.ssh/id_rsa + cp "/ssl/${REMOTE_KEY}" "${HOME}"/.ssh/id_rsa chmod 600 "${HOME}/.ssh/id_rsa" ssh-keygen -y -f ~/.ssh/id_rsa > ~/.ssh/id_rsa.pub ( echo "Host remote" echo " IdentityFile ${HOME}/.ssh/id_rsa" - echo " HostName ${SSH_HOST}" - echo " User ${SSH_USER}" - echo " Port ${SSH_PORT}" + echo " HostName ${REMOTE_HOST}" + echo " User ${REMOTE_USER}" + echo " Port ${REMOTE_PORT}" echo " StrictHostKeyChecking no" - if [ -n "${SSH_HOST_KEY_ALGORITHMS}" ] ; then - echo " HostKeyAlgorithms ${SSH_HOST_KEY_ALGORITHMS}" + if [ -n "${REMOTE_HOST_KEY_ALGORITHMS}" ] ; then + echo " HostKeyAlgorithms ${REMOTE_HOST_KEY_ALGORITHMS}" fi ) > "${HOME}/.ssh/config" @@ -71,11 +68,11 @@ function create-local-backup { ADDONS="" BASE_FOLDERS="addons/local homeassistant media share ssl" INSTALLED_ADDONS=$(bashio::supervisor.addons) - name="${CUSTOM_PREFIX} $(date +'%Y-%m-%d %H-%M')" + name="${BACKUP_CUSTOM_PREFIX} $(date +'%Y-%m-%d %H-%M')" bashio::log.info "Creating local backup: \"${name}\"" - if [ -n "${EXCLUDE_ADDONS}" ] || [ -n "${EXCLUDE_FOLDERS}" ] ; then - EXCLUDED_FOLDERS=$(echo "${EXCLUDE_FOLDERS}") - EXCLUDED_ADDONS=$(echo "${EXCLUDE_ADDONS}") + if [ -n "${BACKUP_EXCLUDE_ADDONS}" ] || [ -n "${BACKUP_EXCLUDE_FOLDERS}" ] ; then + EXCLUDED_FOLDERS=$(echo "${BACKUP_EXCLUDE_FOLDERS}") + EXCLUDED_ADDONS=$(echo "${BACKUP_EXCLUDE_ADDONS}") UNFORMATTED_FOLDERS="${BASE_FOLDERS}" UNFORMATTED_ADDONS="${INSTALLED_ADDONS}" if [ -n "${EXCLUDED_FOLDERS}" ] ; then @@ -112,14 +109,14 @@ function copy-backup-to-remote { if [ "${SSH_ENABLED}" = true ] ; then cd /backup/ || exit - bashio::log.info "Copying ${slug}.tar to ${REMOTE_DIRECTORY} on ${SSH_HOST} using SCP" - scp -F "${HOME}/.ssh/config" "${slug}.tar" remote:"${REMOTE_DIRECTORY}" - bashio::log.info "Backup copied to ${REMOTE_DIRECTORY}/${slug}.tar on ${SSH_HOST}" - - if [ "${FRIENDLY_NAME}" = true ] ; then - bashio::log.notice "Renaming ${slug}.tar to ${name}.tar" - ssh remote "mv \"${REMOTE_DIRECTORY}/${slug}.tar\" \"${REMOTE_DIRECTORY}/${name}.tar\"" - bashio::log.info "Backup renamed to ${REMOTE_DIRECTORY}/${name}.tar on ${SSH_HOST}" + bashio::log.info "Copying ${slug}.tar to ${SSH_REMOTE_DIRECTORY} on ${REMOTE_HOST} using SCP" + scp -F "${HOME}/.ssh/config" "${slug}.tar" remote:"${SSH_REMOTE_DIRECTORY}" + bashio::log.info "Backup copied to ${SSH_REMOTE_DIRECTORY}/${slug}.tar on ${REMOTE_HOST}" + + if [ "${BACKUP_FRIENDLY_NAME}" = true ] ; then + bashio::log.notice "Renaming ${slug}.tar to ${name}.tar" + ssh remote "mv \"${SSH_REMOTE_DIRECTORY}/${slug}.tar\" \"${SSH_REMOTE_DIRECTORY}/${name}.tar\"" + bashio::log.info "Backup renamed to ${SSH_REMOTE_DIRECTORY}/${name}.tar on ${REMOTE_HOST}" fi bashio::log.info "SCP complete" fi @@ -131,8 +128,8 @@ function rsync_folders { return fi - local FOLDERS="/config /addons /backup /share /ssl" - local RSYNC_URL="${RSYNC_USER}@${RSYNC_HOST}:${RSYNC_ROOTFOLDER}" + local FOLDERS="/config /addons /backup /share /ssl" # put directories without trailing slash + local RSYNC_URL="${REMOTE_USER}@${REMOTE_HOST}:${RSYNC_ROOTFOLDER}" bashio::log.info "Starting rsync" if bashio::var.true "${DEBUG}"; then @@ -149,8 +146,8 @@ function rsync_folders { fi bashio::log.debug "Syncing ${FOLDERS}" - sshpass -p "${RSYNC_PASSWORD}" \ - rsync ${FLAGS} --exclude-from='/tmp/rsync_exclude.txt' ${FOLDERS} "${RSYNC_URL}/" --delete \ + sshpass -p "${REMOTE_PASSWORD}" \ + rsync ${FLAGS} --port ${REMOTE_PORT} --exclude-from='/tmp/rsync_exclude.txt' ${FOLDERS} "${RSYNC_URL}/" --delete \ || bashio::log.fatal "Error syncing folder(s) ${FOLDERS}" bashio::log.info "Finished rsync" @@ -163,19 +160,19 @@ function rclone_backups { cp -a /ssl/rclone.conf ~/.config/rclone/rclone.conf bashio::log.info "Starting rclone" if [ "$RCLONE_COPY" = true ] ; then - if [ "$FRIENDLY_NAME" = true ] ; then + if [ "$BACKUP_FRIENDLY_NAME" = true ] ; then bashio::log.debug "Copying ${slug}.tar to ${RCLONE_REMOTE_DIRECTORY}/${name}.tar" - rclone copyto "${slug}.tar" "${RCLONE_REMOTE}:${RCLONE_REMOTE_DIRECTORY}/${name}".tar + rclone copyto "${slug}.tar" "${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY}/${name}".tar bashio::log.debug "Finished rclone copy" else bashio::log.debug "Copying ${slug}.tar to ${RCLONE_REMOTE_DIRECTORY}/${slug}.tar" - rclone copy "${slug}.tar" "${RCLONE_REMOTE}:${RCLONE_REMOTE_DIRECTORY}" + rclone copy "${slug}.tar" "${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY}" bashio::log.debug "Finished rclone copy" fi fi if [ "${RCLONE_SYNC}" = true ] ; then bashio::log.info "Syncing Backups" - rclone sync . "${RCLONE_REMOTE}:${RCLONE_REMOTE_DIRECTORY}" + rclone sync . "${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY}" bashio::log.info "Finished rclone sync" fi if [ "${RCLONE_RESTORE}" = true ] ; then @@ -183,7 +180,7 @@ function rclone_backups { RESTORENAME="restore-${DATEFORMAT}" mkdir -p "${RESTORENAME}" bashio::log.info "Restoring Backups to ${RESTORENAME}" - rclone copyto "${RCLONE_REMOTE}:${RCLONE_REMOTE_DIRECTORY} ${RESTORENAME}/" + rclone copyto "${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY} ${RESTORENAME}/" bashio::log.info "Finished rclone restore" fi fi @@ -194,15 +191,15 @@ function delete-local-backup { ha backups reload - if [[ "${KEEP_LOCAL_BACKUP}" == "all" ]]; then + if [[ "${BACKUP_KEEP_LOCAL}" == "all" ]]; then : - elif [[ -z "${KEEP_LOCAL_BACKUP}" ]]; then + elif [[ -z "${BACKUP_KEEP_LOCAL}" ]]; then bashio::log.warning "Deleting local backup: ${slug}" ha backups remove "${slug}" else last_date_to_keep=$(ha backups list --raw-json | jq .data.backups[].date | sort -r | \ - head -n "${KEEP_LOCAL_BACKUP}" | tail -n 1 | xargs date -D "%Y-%m-%dT%T" +%s --date ) + head -n "${BACKUP_KEEP_LOCAL}" | tail -n 1 | xargs date -D "%Y-%m-%dT%T" +%s --date ) ha backups list --raw-json | jq -c .data.backups[] | while read -r backup; do if [[ $(echo "${backup}" | jq .date | xargs date -D "%Y-%m-%dT%T" +%s --date ) -lt ${last_date_to_keep} ]]; then @@ -224,4 +221,4 @@ rclone_backups delete-local-backup bashio::log.info "Backup process done!" -exit 0 +bashio::exit.ok diff --git a/remote-backup/translations/en.yaml b/remote-backup/translations/en.yaml index cb503ad..1e5006b 100644 --- a/remote-backup/translations/en.yaml +++ b/remote-backup/translations/en.yaml @@ -1,25 +1,54 @@ configuration: debug: name: Enable debugging - ssh_enabled: - name: Enable SSH - description: Copies Home Assistant backups to the remote server - friendly_name: + remote_host: + name: Remote host + description: The hostname or IP address of the remote server + remote_port: + name: Remote port + description: Port number of the remote server + remote_user: + name: Username + description: Username to be used for authentication with remote server + remote_password: + name: Password + description: Password to be used for authentication with remote server + remote_key: + name: SSH private key + description: SSH private key file to be used for authentication with remote server. This be located in the directory 'ssl' of Home Assistant. + remote_host_key_algorithms: + name: Host key algorithms + description: Can be used to enable further (legacy) algorithms for authentication + backup_friendly_name: name: Friendly name description: Rename the backup on the destination server to match the name in the Home Assistant UI - custom_prefix: + backup_custom_prefix: name: Custom backup name prefix - ssh_key: - name: SSH key - description: The filename of the SSH key, which must be located in the directory 'ssl' of Home Assistant. - exclude_folders: + backup_exclude_folders: name: Folder to exclude from backup - exclude_addons: + backup_exclude_addons: name: Addon to exclude from backup description: Give the addons slug which equals the addon hostname using '_' instead of '-', e.g. core-mariadb + backup_keep_local: + name: Local backups to keep + description: default is 'all', give a number for the last x backups to keep or empty to immediately remove created backups after copying. + backup_password: + name: Password for protected backup + ssh_enabled: + name: Enable SSH + description: Copies Home Assistant backups to the remote server + ssh_remote_directory: + name: SSH remote directory + description: Remote directory the backups are copied to rsync_enabled: name: Enable rsync description: Clones local folders to remote server (including backups) + rsync_rootfolder: + name: rsync root folder + description: Root folder to which Home Assistant directories are synchronized rsync_exclude: - name: Path patterns to exclude from sync + name: rsync path patterns to exclude from sync description: This feature uses the rsync --exclude scheme + rclone_enabled: + name: Enable rclone + description: Experimental! From b32491dafe4a0b81cbae31195580e63f9c36aecf Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Thu, 18 Aug 2022 10:57:34 +0200 Subject: [PATCH 02/40] moved host key retrieval to add-ssh-key function --- remote-backup/run.sh | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/remote-backup/run.sh b/remote-backup/run.sh index 025f2c0..85d1e89 100755 --- a/remote-backup/run.sh +++ b/remote-backup/run.sh @@ -43,6 +43,9 @@ function add-ssh-key { mkdir -p ~/.ssh cp "/ssl/${REMOTE_KEY}" "${HOME}"/.ssh/id_rsa chmod 600 "${HOME}/.ssh/id_rsa" + bashio::log.debug "Adding key of remote host ${REMOTE_HOST} to known hosts." + ssh-keyscan -t rsa ${REMOTE_HOST} >> ${HOME}/.ssh/known_hosts \ + || bashio::log.error "Failed to add ${REMOTE_HOST} host key" ssh-keygen -y -f ~/.ssh/id_rsa > ~/.ssh/id_rsa.pub ( echo "Host remote" @@ -137,9 +140,7 @@ function rsync_folders { else local FLAGS='-a' fi - bashio::log.debug "Adding key of remote host ${RSYNC_HOST} to known hosts." - ssh-keyscan -t rsa ${RSYNC_HOST} >> ~/.ssh/known_hosts \ - || bashio::log.error "Failed to add ${RSYNC_HOST} host key" + echo "${RSYNC_EXCLUDE}" > /tmp/rsync_exclude.txt if bashio::var.has_value "${RSYNC_EXCLUDE}"; then bashio::log.warning "File patterns that have been excluded:\n${RSYNC_EXCLUDE}" @@ -161,13 +162,13 @@ function rclone_backups { bashio::log.info "Starting rclone" if [ "$RCLONE_COPY" = true ] ; then if [ "$BACKUP_FRIENDLY_NAME" = true ] ; then - bashio::log.debug "Copying ${slug}.tar to ${RCLONE_REMOTE_DIRECTORY}/${name}.tar" - rclone copyto "${slug}.tar" "${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY}/${name}".tar - bashio::log.debug "Finished rclone copy" + bashio::log.debug "Copying ${slug}.tar to ${RCLONE_REMOTE_DIRECTORY}/${name}.tar" + rclone copyto "${slug}.tar" "${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY}/${name}".tar + bashio::log.debug "Finished rclone copy" else - bashio::log.debug "Copying ${slug}.tar to ${RCLONE_REMOTE_DIRECTORY}/${slug}.tar" - rclone copy "${slug}.tar" "${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY}" - bashio::log.debug "Finished rclone copy" + bashio::log.debug "Copying ${slug}.tar to ${RCLONE_REMOTE_DIRECTORY}/${slug}.tar" + rclone copy "${slug}.tar" "${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY}" + bashio::log.debug "Finished rclone copy" fi fi if [ "${RCLONE_SYNC}" = true ] ; then From ea9a6c59c3906ee5dc200c04f5b5e16fb1d990c5 Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Thu, 18 Aug 2022 10:58:15 +0200 Subject: [PATCH 03/40] added trailing directory slash to avoid file renaming --- remote-backup/run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/remote-backup/run.sh b/remote-backup/run.sh index 85d1e89..1d6ff22 100755 --- a/remote-backup/run.sh +++ b/remote-backup/run.sh @@ -113,7 +113,7 @@ function copy-backup-to-remote { if [ "${SSH_ENABLED}" = true ] ; then cd /backup/ || exit bashio::log.info "Copying ${slug}.tar to ${SSH_REMOTE_DIRECTORY} on ${REMOTE_HOST} using SCP" - scp -F "${HOME}/.ssh/config" "${slug}.tar" remote:"${SSH_REMOTE_DIRECTORY}" + scp -F "${HOME}/.ssh/config" "${slug}.tar" remote:"${SSH_REMOTE_DIRECTORY}/" bashio::log.info "Backup copied to ${SSH_REMOTE_DIRECTORY}/${slug}.tar on ${REMOTE_HOST}" if [ "${BACKUP_FRIENDLY_NAME}" = true ] ; then From 71f2e1047acd3934af7d002b1b8c8fe39ef39f37 Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Thu, 18 Aug 2022 14:17:38 +0200 Subject: [PATCH 04/40] config description enhancements --- remote-backup/translations/en.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/remote-backup/translations/en.yaml b/remote-backup/translations/en.yaml index 1e5006b..9649712 100644 --- a/remote-backup/translations/en.yaml +++ b/remote-backup/translations/en.yaml @@ -28,7 +28,7 @@ configuration: name: Folder to exclude from backup backup_exclude_addons: name: Addon to exclude from backup - description: Give the addons slug which equals the addon hostname using '_' instead of '-', e.g. core-mariadb + description: Give the addons slug which equals the addon hostname using '_' instead of '-', e.g. core_mariadb backup_keep_local: name: Local backups to keep description: default is 'all', give a number for the last x backups to keep or empty to immediately remove created backups after copying. @@ -39,7 +39,7 @@ configuration: description: Copies Home Assistant backups to the remote server ssh_remote_directory: name: SSH remote directory - description: Remote directory the backups are copied to + description: Remote directory the backups are copied to (path must exist) rsync_enabled: name: Enable rsync description: Clones local folders to remote server (including backups) From 27a201e8890250cab06337e581754b0d12cf167f Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Thu, 18 Aug 2022 14:17:38 +0200 Subject: [PATCH 05/40] config description enhancements --- remote-backup/translations/en.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/remote-backup/translations/en.yaml b/remote-backup/translations/en.yaml index 1e5006b..acaa84c 100644 --- a/remote-backup/translations/en.yaml +++ b/remote-backup/translations/en.yaml @@ -25,10 +25,10 @@ configuration: backup_custom_prefix: name: Custom backup name prefix backup_exclude_folders: - name: Folder to exclude from backup + name: Folder to exclude from backup (valid options are addons/local, homeassistant, media, share, ssl)" backup_exclude_addons: name: Addon to exclude from backup - description: Give the addons slug which equals the addon hostname using '_' instead of '-', e.g. core-mariadb + description: Give the addons slug which equals the addon hostname using '_' instead of '-', e.g. core_mariadb backup_keep_local: name: Local backups to keep description: default is 'all', give a number for the last x backups to keep or empty to immediately remove created backups after copying. @@ -39,7 +39,7 @@ configuration: description: Copies Home Assistant backups to the remote server ssh_remote_directory: name: SSH remote directory - description: Remote directory the backups are copied to + description: Remote directory the backups are copied to (path must exist) rsync_enabled: name: Enable rsync description: Clones local folders to remote server (including backups) From 48eb5d25f29919d5f1807c1df4a48956a79170e1 Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Thu, 18 Aug 2022 15:05:08 +0200 Subject: [PATCH 06/40] cleanup backup creation --- remote-backup/run.sh | 102 +++++++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 48 deletions(-) diff --git a/remote-backup/run.sh b/remote-backup/run.sh index 1d6ff22..d0a1362 100755 --- a/remote-backup/run.sh +++ b/remote-backup/run.sh @@ -29,7 +29,6 @@ RCLONE_COPY=$(bashio::config "rclone_copy") RCLONE_SYNC=$(bashio::config "rclone_sync") RCLONE_RESTORE=$(bashio::config "rclone_restore") - function set-debug-level { # default log level according to bashio const.sh is INFO if bashio::var.true "${DEBUG}"; then @@ -67,58 +66,65 @@ function add-ssh-key { function create-local-backup { # Bind variables - FOLDERS="" - ADDONS="" - BASE_FOLDERS="addons/local homeassistant media share ssl" - INSTALLED_ADDONS=$(bashio::supervisor.addons) - name="${BACKUP_CUSTOM_PREFIX} $(date +'%Y-%m-%d %H-%M')" - bashio::log.info "Creating local backup: \"${name}\"" - if [ -n "${BACKUP_EXCLUDE_ADDONS}" ] || [ -n "${BACKUP_EXCLUDE_FOLDERS}" ] ; then - EXCLUDED_FOLDERS=$(echo "${BACKUP_EXCLUDE_FOLDERS}") - EXCLUDED_ADDONS=$(echo "${BACKUP_EXCLUDE_ADDONS}") - UNFORMATTED_FOLDERS="${BASE_FOLDERS}" - UNFORMATTED_ADDONS="${INSTALLED_ADDONS}" - if [ -n "${EXCLUDED_FOLDERS}" ] ; then - bashio::log.warning "Excluded folders:\n ${EXCLUDED_FOLDERS}" - for folder in ${EXCLUDED_FOLDERS} ; do - UNFORMATTED_FOLDERS=$(echo "${UNFORMATTED_FOLDERS}" | sed -e "s/${folder}//g") - done - fi - if [ -n "${EXCLUDED_ADDONS}" ] ; then - bashio::log.warning "Excluded addons:\n${EXCLUDED_ADDONS}" - for addon in ${EXCLUDED_ADDONS} ; do - UNFORMATTED_ADDONS="$(echo "${UNFORMATTED_ADDONS}" | sed -e "s/${addon}//g")" - done - fi - if [ -n "${UNFORMATTED_ADDONS}" ] && [ -n "${UNFORMATTED_FOLDERS}" ] ; then - for addon in ${UNFORMATTED_ADDONS} ; do - ADDONS="${ADDONS}--addons ${addon} " - done - for folder in ${UNFORMATTED_FOLDERS} ; do - FOLDERS="${FOLDERS}--folders ${folder} " - done + local base_folders="addons/local homeassistant media share ssl" + local installed_addons=$(bashio::supervisor.addons) + local name="${BACKUP_CUSTOM_PREFIX} $(date +'%Y-%m-%d %H-%M')" + local data="{\"name\":\"${name}\" \"password\": \"${BACKUP_PASSWORD}\"}" + + if bashio::var.has_value "${BACKUP_EXCLUDE_ADDONS}" ] || bashio::var.has_value "${BACKUP_EXCLUDE_FOLDERS}"; then + bashio::log.info "Creating partial backup: \"${name}\"" + + local excluded_folders=$(echo "${BACKUP_EXCLUDE_FOLDERS}") + local excluded_addons=$(echo "${BACKUP_EXCLUDE_ADDONS}") + local unformatted_folders="${base_folders}" + local unformatted_addons="${installed_addons}" + + if bashio::var.has_value "${excluded_folders}"; then + bashio::log.warning "Excluded folder(s):\n ${excluded_folders}" + for folder in ${excluded_folders} ; do + unformatted_folders=$(echo "${unformatted_folders}" | sed -e "s/${folder}//g") + done + fi + if bashio::var.has_value "${excluded_addons}"; then + bashio::log.warning "Excluded addon(s):\n${excluded_addons}" + for addon in ${excluded_addons} ; do + unformatted_addons="$(echo "${unformatted_addons}" | sed -e "s/${addon}//g")" + done + fi + + local addons=$(echo ${unformatted_addons} | sed "s/ /\", \"/g") + local folders=$(echo "${unformatted_folders}" | sed "s/ /\", \"/g" | sed "s/, \"\"//g") + bashio::log.debug "Including folder(s) \"${folders}\"" + bashio::log.debug "Including addon(s) \"${addons}\"" + + local data="$(echo $data | tr -d '}'), \"addons\": [\"${addons}\"], \"folders\": [\"${folders}\"]}" # append addon and folder set + if ! SLUG=$(bashio::api.supervisor "POST" "/backups/new/partial" "${data}" ".slug"); then + bashio::exit.nok "Error creating partial backup!" fi - bashio::log.info "Creating partial backup" - bashio::log.debug "Including ${FOLDERS} and ${ADDONS}" - slug=$(ha backups new --raw-json --name="${name}" ${ADDONS} ${FOLDERS} --password="${BACKUP_PASSWORD}" | jq --raw-output '.data.slug') else - bashio::log.info "Creating full backup" - slug=$(ha backups new --raw-json --name="${name}" --password="${BACKUP_PASSWORD}" | jq --raw-output '.data.slug') + bashio::log.info "Creating full backup: \"${name}\"" + + if ! SLUG=$(bashio::api.supervisor "POST" "/backups/new/full" "${data}" ".slug"); then + bashio::exit.nok "Error creating full backup!" + fi + fi - bashio::log.info "Backup created: ${slug}" + + bashio::log.info "Backup created: ${SLUG}" + return "${__BASHIO_EXIT_OK}" } function copy-backup-to-remote { if [ "${SSH_ENABLED}" = true ] ; then cd /backup/ || exit - bashio::log.info "Copying ${slug}.tar to ${SSH_REMOTE_DIRECTORY} on ${REMOTE_HOST} using SCP" - scp -F "${HOME}/.ssh/config" "${slug}.tar" remote:"${SSH_REMOTE_DIRECTORY}/" - bashio::log.info "Backup copied to ${SSH_REMOTE_DIRECTORY}/${slug}.tar on ${REMOTE_HOST}" + bashio::log.info "Copying ${SLUG}.tar to ${SSH_REMOTE_DIRECTORY} on ${REMOTE_HOST} using SCP" + scp -F "${HOME}/.ssh/config" "${SLUG}.tar" remote:"${SSH_REMOTE_DIRECTORY}/" + bashio::log.info "Backup copied to ${SSH_REMOTE_DIRECTORY}/${SLUG}.tar on ${REMOTE_HOST}" if [ "${BACKUP_FRIENDLY_NAME}" = true ] ; then - bashio::log.notice "Renaming ${slug}.tar to ${name}.tar" - ssh remote "mv \"${SSH_REMOTE_DIRECTORY}/${slug}.tar\" \"${SSH_REMOTE_DIRECTORY}/${name}.tar\"" + bashio::log.notice "Renaming ${SLUG}.tar to ${name}.tar" + ssh remote "mv \"${SSH_REMOTE_DIRECTORY}/${SLUG}.tar\" \"${SSH_REMOTE_DIRECTORY}/${name}.tar\"" bashio::log.info "Backup renamed to ${SSH_REMOTE_DIRECTORY}/${name}.tar on ${REMOTE_HOST}" fi bashio::log.info "SCP complete" @@ -162,12 +168,12 @@ function rclone_backups { bashio::log.info "Starting rclone" if [ "$RCLONE_COPY" = true ] ; then if [ "$BACKUP_FRIENDLY_NAME" = true ] ; then - bashio::log.debug "Copying ${slug}.tar to ${RCLONE_REMOTE_DIRECTORY}/${name}.tar" - rclone copyto "${slug}.tar" "${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY}/${name}".tar + bashio::log.debug "Copying ${SLUG}.tar to ${RCLONE_REMOTE_DIRECTORY}/${name}.tar" + rclone copyto "${SLUG}.tar" "${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY}/${name}".tar bashio::log.debug "Finished rclone copy" else - bashio::log.debug "Copying ${slug}.tar to ${RCLONE_REMOTE_DIRECTORY}/${slug}.tar" - rclone copy "${slug}.tar" "${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY}" + bashio::log.debug "Copying ${SLUG}.tar to ${RCLONE_REMOTE_DIRECTORY}/${SLUG}.tar" + rclone copy "${SLUG}.tar" "${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY}" bashio::log.debug "Finished rclone copy" fi fi @@ -195,8 +201,8 @@ function delete-local-backup { if [[ "${BACKUP_KEEP_LOCAL}" == "all" ]]; then : elif [[ -z "${BACKUP_KEEP_LOCAL}" ]]; then - bashio::log.warning "Deleting local backup: ${slug}" - ha backups remove "${slug}" + bashio::log.warning "Deleting local backup: ${SLUG}" + ha backups remove "${SLUG}" else last_date_to_keep=$(ha backups list --raw-json | jq .data.backups[].date | sort -r | \ From 0ddcda973b81fbc5c9d6736bb9ac3dd3bf91d7a1 Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Thu, 18 Aug 2022 18:16:42 +0200 Subject: [PATCH 07/40] enhanced error handling for SCP and rsync - removed rw permission for backup folder (unnecessary) - added permission for core API - added function to die with persistent notification --- remote-backup/config.yaml | 3 +- remote-backup/run.sh | 83 ++++++++++++++++++++++++--------------- 2 files changed, 54 insertions(+), 32 deletions(-) diff --git a/remote-backup/config.yaml b/remote-backup/config.yaml index a71a922..a68b008 100644 --- a/remote-backup/config.yaml +++ b/remote-backup/config.yaml @@ -17,12 +17,13 @@ arch: homeassistant: "2021.9.0" hassio_api: true hassio_role: manager +homeassistant_api: true map: - config - addons - share - ssl - - backup:rw + - backup options: debug: false diff --git a/remote-backup/run.sh b/remote-backup/run.sh index d0a1362..27302e5 100755 --- a/remote-backup/run.sh +++ b/remote-backup/run.sh @@ -29,6 +29,8 @@ RCLONE_COPY=$(bashio::config "rclone_copy") RCLONE_SYNC=$(bashio::config "rclone_sync") RCLONE_RESTORE=$(bashio::config "rclone_restore") +BACKUP_NAME="${BACKUP_CUSTOM_PREFIX} $(date +'%Y-%m-%d %H-%M')" + function set-debug-level { # default log level according to bashio const.sh is INFO if bashio::var.true "${DEBUG}"; then @@ -36,6 +38,14 @@ function set-debug-level { fi } +function die { + local message=${1:-'no message'} + local title=${2:-'Addon: Remote Backup'} + + bashio::api.supervisor POST /core/api/services/persistent_notification/create "{\"message\":\"${message}\", \"title\":\"${title}\"}" + bashio::exit.nok "${message}" +} + function add-ssh-key { if [ "${SSH_ENABLED}" = true ] || [ "${RSYNC_ENABLED}" = true ] ; then bashio::log.info "Adding SSH key" @@ -60,7 +70,6 @@ function add-ssh-key { chmod 600 "${HOME}/.ssh/config" chmod 644 "${HOME}/.ssh/id_rsa.pub" - bashio::log.info "SSH key added" fi } @@ -68,11 +77,10 @@ function create-local-backup { # Bind variables local base_folders="addons/local homeassistant media share ssl" local installed_addons=$(bashio::supervisor.addons) - local name="${BACKUP_CUSTOM_PREFIX} $(date +'%Y-%m-%d %H-%M')" - local data="{\"name\":\"${name}\" \"password\": \"${BACKUP_PASSWORD}\"}" + local data="{\"name\":\"${BACKUP_NAME}\", \"password\": \"${BACKUP_PASSWORD}\"}" - if bashio::var.has_value "${BACKUP_EXCLUDE_ADDONS}" ] || bashio::var.has_value "${BACKUP_EXCLUDE_FOLDERS}"; then - bashio::log.info "Creating partial backup: \"${name}\"" + if bashio::var.has_value "${BACKUP_EXCLUDE_ADDONS}" || bashio::var.has_value "${BACKUP_EXCLUDE_FOLDERS}"; then + bashio::log.info "Creating partial backup: \"${BACKUP_NAME}\"" local excluded_folders=$(echo "${BACKUP_EXCLUDE_FOLDERS}") local excluded_addons=$(echo "${BACKUP_EXCLUDE_ADDONS}") @@ -98,14 +106,14 @@ function create-local-backup { bashio::log.debug "Including addon(s) \"${addons}\"" local data="$(echo $data | tr -d '}'), \"addons\": [\"${addons}\"], \"folders\": [\"${folders}\"]}" # append addon and folder set - if ! SLUG=$(bashio::api.supervisor "POST" "/backups/new/partial" "${data}" ".slug"); then - bashio::exit.nok "Error creating partial backup!" + if ! SLUG=$(bashio::api.supervisor POST /backups/new/partial "${data}" .slug); then + die "Error creating partial backup!" fi else - bashio::log.info "Creating full backup: \"${name}\"" + bashio::log.info "Creating full backup: \"${BACKUP_NAME}\"" - if ! SLUG=$(bashio::api.supervisor "POST" "/backups/new/full" "${data}" ".slug"); then - bashio::exit.nok "Error creating full backup!" + if ! SLUG=$(bashio::api.supervisor POST /backups/new/full "${data}" .slug); then + die "Error creating full backup!" fi fi @@ -115,26 +123,34 @@ function create-local-backup { } function copy-backup-to-remote { + if bashio::var.false "${SSH_ENABLED}"; then + bashio::log.debug "SCP disabled." + return "${__BASHIO_EXIT_OK}" + fi - if [ "${SSH_ENABLED}" = true ] ; then - cd /backup/ || exit - bashio::log.info "Copying ${SLUG}.tar to ${SSH_REMOTE_DIRECTORY} on ${REMOTE_HOST} using SCP" - scp -F "${HOME}/.ssh/config" "${SLUG}.tar" remote:"${SSH_REMOTE_DIRECTORY}/" - bashio::log.info "Backup copied to ${SSH_REMOTE_DIRECTORY}/${SLUG}.tar on ${REMOTE_HOST}" - - if [ "${BACKUP_FRIENDLY_NAME}" = true ] ; then - bashio::log.notice "Renaming ${SLUG}.tar to ${name}.tar" - ssh remote "mv \"${SSH_REMOTE_DIRECTORY}/${SLUG}.tar\" \"${SSH_REMOTE_DIRECTORY}/${name}.tar\"" - bashio::log.info "Backup renamed to ${SSH_REMOTE_DIRECTORY}/${name}.tar on ${REMOTE_HOST}" + cd /backup/ \ + || bashio::log.fatal "Error accessing directory /backup" \ + && return "${__BASHIO_EXIT_NOK}" + bashio::log.info "Copying backup using SCP" + if ! scp -F "${HOME}/.ssh/config" "/backup/${SLUG}.tar" remote:"${SSH_REMOTE_DIRECTORY}/"; then + bashio::log.error "Error copying backup ${SLUG}.tar to ${SSH_REMOTE_DIRECTORY} on ${REMOTE_HOST}" + return "${__BASHIO_EXIT_NOK}" + fi + + if bashio::var.true "${BACKUP_FRIENDLY_NAME}"; then + bashio::log.info "Renaming ${SLUG}.tar to ${BACKUP_NAME}.tar" + if ! ssh remote "mv \"${SSH_REMOTE_DIRECTORY}/${SLUG}.tar\" \"${SSH_REMOTE_DIRECTORY}/${BACKUP_NAME}.tar\""; then + bashio::log.error "Error renaming backup to ${SSH_REMOTE_DIRECTORY}/${BACKUP_NAME}.tar on ${REMOTE_HOST}" + return "${__BASHIO_EXIT_NOK}" fi - bashio::log.info "SCP complete" fi + return "${__BASHIO_EXIT_OK}" } function rsync_folders { if bashio::var.false "${RSYNC_ENABLED}"; then bashio::log.debug "rsync disabled." - return + return "${__BASHIO_EXIT_OK}" fi local FOLDERS="/config /addons /backup /share /ssl" # put directories without trailing slash @@ -153,11 +169,12 @@ function rsync_folders { fi bashio::log.debug "Syncing ${FOLDERS}" - sshpass -p "${REMOTE_PASSWORD}" \ - rsync ${FLAGS} --port ${REMOTE_PORT} --exclude-from='/tmp/rsync_exclude.txt' ${FOLDERS} "${RSYNC_URL}/" --delete \ - || bashio::log.fatal "Error syncing folder(s) ${FOLDERS}" + if ! sshpass -p "${REMOTE_PASSWORD}" rsync ${FLAGS} --port ${REMOTE_PORT} --exclude-from='/tmp/rsync_exclude.txt' ${FOLDERS} "${RSYNC_URL}/" --delete; then + bashio::log.error "Error syncing folder(s) ${FOLDERS}" + return "${__BASHIO_EXIT_NOK}" + fi - bashio::log.info "Finished rsync" + return "${__BASHIO_EXIT_OK}" } function rclone_backups { @@ -168,8 +185,8 @@ function rclone_backups { bashio::log.info "Starting rclone" if [ "$RCLONE_COPY" = true ] ; then if [ "$BACKUP_FRIENDLY_NAME" = true ] ; then - bashio::log.debug "Copying ${SLUG}.tar to ${RCLONE_REMOTE_DIRECTORY}/${name}.tar" - rclone copyto "${SLUG}.tar" "${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY}/${name}".tar + bashio::log.debug "Copying ${SLUG}.tar to ${RCLONE_REMOTE_DIRECTORY}/${BACKUP_NAME}.tar" + rclone copyto "${SLUG}.tar" "${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY}/${BACKUP_NAME}.tar" bashio::log.debug "Finished rclone copy" else bashio::log.debug "Copying ${SLUG}.tar to ${RCLONE_REMOTE_DIRECTORY}/${SLUG}.tar" @@ -193,7 +210,6 @@ function rclone_backups { fi } - function delete-local-backup { ha backups reload @@ -219,12 +235,17 @@ function delete-local-backup { fi } +# general setup and backup set-debug-level add-ssh-key create-local-backup -copy-backup-to-remote -rsync_folders + +# different remote cloning modes +copy-backup-to-remote || bashio::log.fatal "SCP failed." +rsync_folders || bashio::log.fatal "rsync failed." rclone_backups + +# cleanup delete-local-backup bashio::log.info "Backup process done!" From 27504c146cb3cebf94a64a0ac10c94d07145e559 Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Fri, 19 Aug 2022 13:01:10 +0200 Subject: [PATCH 08/40] adaoted folder exclude description --- remote-backup/translations/en.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/remote-backup/translations/en.yaml b/remote-backup/translations/en.yaml index acaa84c..10512c8 100644 --- a/remote-backup/translations/en.yaml +++ b/remote-backup/translations/en.yaml @@ -25,7 +25,8 @@ configuration: backup_custom_prefix: name: Custom backup name prefix backup_exclude_folders: - name: Folder to exclude from backup (valid options are addons/local, homeassistant, media, share, ssl)" + name: Folder to exclude from backup + description: valid options are addons/local, homeassistant, media, share, ssl backup_exclude_addons: name: Addon to exclude from backup description: Give the addons slug which equals the addon hostname using '_' instead of '-', e.g. core_mariadb From a3e8f3ea2d61412f3d4a9443593b4a8f02a01570 Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Sat, 20 Aug 2022 11:02:22 +0200 Subject: [PATCH 09/40] set empty config values to null Empty string is a valid imput, thus mandatory parameters are accepted as if they were filled out. --- remote-backup/config.yaml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/remote-backup/config.yaml b/remote-backup/config.yaml index a68b008..cdcd76f 100644 --- a/remote-backup/config.yaml +++ b/remote-backup/config.yaml @@ -24,23 +24,23 @@ map: - share - ssl - backup - + options: debug: false - remote_host: "" + remote_host: null remote_port: 22 - remote_user: "" - remote_password: "" - remote_key: "" - remote_host_key_algorithms: "" + remote_user: null + remote_password: null + remote_key: null + remote_host_key_algorithms: null backup_friendly_name: true backup_custom_prefix: Automated backup backup_exclude_folders: [] backup_exclude_addons: [] backup_keep_local: "all" - backup_password: "" + backup_password: null ssh_enabled: true - ssh_remote_directory: "" + ssh_remote_directory: null rsync_enabled: false rsync_rootfolder: hassio-sync rsync_exclude: @@ -48,7 +48,7 @@ options: - "/config/*.db-wal" - "/config/*.db" rclone_enabled: false - rclone_remote_directory: "" + rclone_remote_directory: null rclone_copy: false rclone_sync: false rclone_restore: false @@ -67,7 +67,7 @@ schema: - match(^[A-Za-z0-9_\-\.\*\/\?\+\\]*$)? backup_exclude_addons: - str? - backup_keep_local: match(^(all|[+]?\d*)$) + backup_keep_local: match(^(all|[+]?\d*)$)? backup_password: str? ssh_enabled: bool ssh_remote_directory: str? From 86d63486161f5bd80138e32408fdbece31eecf24 Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Sat, 20 Aug 2022 11:21:05 +0200 Subject: [PATCH 10/40] detailed error handling - fixed die function (remaining issue in comment) - error handling of add-ssh-key function - added clone-to-remote function to try multiple methods in sequence - changed delete-local-backup to supervisor API and added error handling --- remote-backup/run.sh | 154 ++++++++++++++++++++++++++----------------- 1 file changed, 92 insertions(+), 62 deletions(-) diff --git a/remote-backup/run.sh b/remote-backup/run.sh index 27302e5..e0af5c4 100755 --- a/remote-backup/run.sh +++ b/remote-backup/run.sh @@ -40,22 +40,27 @@ function set-debug-level { function die { local message=${1:-'no message'} - local title=${2:-'Addon: Remote Backup'} + local title=${2:-'Addon: Remote Backup Failed!'} - bashio::api.supervisor POST /core/api/services/persistent_notification/create "{\"message\":\"${message}\", \"title\":\"${title}\"}" + # catch return code which is always false, see https://github.com/hassio-addons/bashio/issues/31 + local ret=$(bashio::api.supervisor POST /core/api/services/persistent_notification/create "{\"message\":\"${message}\", \"title\":\"${title}\"}") bashio::exit.nok "${message}" } +# prepare SSH environment/configuration +# function does never fail to continue with further commands function add-ssh-key { - if [ "${SSH_ENABLED}" = true ] || [ "${RSYNC_ENABLED}" = true ] ; then - bashio::log.info "Adding SSH key" - mkdir -p ~/.ssh - cp "/ssl/${REMOTE_KEY}" "${HOME}"/.ssh/id_rsa - chmod 600 "${HOME}/.ssh/id_rsa" - bashio::log.debug "Adding key of remote host ${REMOTE_HOST} to known hosts." + if bashio::var.true "${SSH_ENABLED}" || bashio::var.true "${RSYNC_ENABLED}"; then + bashio::log.info "Adding SSH key." + ( + mkdir -p ${HOME}/.ssh + cp "/ssl/${REMOTE_KEY}" "${HOME}/.ssh/id_rsa" + ssh-keygen -y -f ${HOME}/.ssh/id_rsa > ${HOME}/.ssh/id_rsa.pub + ) || bashio::log.error "Failed to create SSH key pair!" + + bashio::log.debug "Adding public key of remote host ${REMOTE_HOST} to known hosts." ssh-keyscan -t rsa ${REMOTE_HOST} >> ${HOME}/.ssh/known_hosts \ - || bashio::log.error "Failed to add ${REMOTE_HOST} host key" - ssh-keygen -y -f ~/.ssh/id_rsa > ~/.ssh/id_rsa.pub + || bashio::log.error "Failed to add public key for remote host ${REMOTE_HOST}!" ( echo "Host remote" echo " IdentityFile ${HOME}/.ssh/id_rsa" @@ -63,18 +68,23 @@ function add-ssh-key { echo " User ${REMOTE_USER}" echo " Port ${REMOTE_PORT}" echo " StrictHostKeyChecking no" - if [ -n "${REMOTE_HOST_KEY_ALGORITHMS}" ] ; then + if bashio::var.has_value "${REMOTE_HOST_KEY_ALGORITHMS}"; then echo " HostKeyAlgorithms ${REMOTE_HOST_KEY_ALGORITHMS}" fi ) > "${HOME}/.ssh/config" - chmod 600 "${HOME}/.ssh/config" - chmod 644 "${HOME}/.ssh/id_rsa.pub" + ( + chmod 600 "${HOME}/.ssh/id_rsa" + chmod 600 "${HOME}/.ssh/config" + chmod 644 "${HOME}/.ssh/id_rsa.pub" + ) || bashio::log.error "Failed to set SSH file permissions!" fi } +# call Home Assistant to create a local backup +# function fails in case local backup is not created function create-local-backup { - # Bind variables + # Bind local variables local base_folders="addons/local homeassistant media share ssl" local installed_addons=$(bashio::supervisor.addons) local data="{\"name\":\"${BACKUP_NAME}\", \"password\": \"${BACKUP_PASSWORD}\"}" @@ -88,16 +98,16 @@ function create-local-backup { local unformatted_addons="${installed_addons}" if bashio::var.has_value "${excluded_folders}"; then - bashio::log.warning "Excluded folder(s):\n ${excluded_folders}" - for folder in ${excluded_folders} ; do - unformatted_folders=$(echo "${unformatted_folders}" | sed -e "s/${folder}//g") - done + bashio::log.warning "Excluded folder(s):\n ${excluded_folders}" + for folder in ${excluded_folders} ; do + unformatted_folders=$(echo "${unformatted_folders}" | sed -e "s/${folder}//g") + done fi if bashio::var.has_value "${excluded_addons}"; then - bashio::log.warning "Excluded addon(s):\n${excluded_addons}" - for addon in ${excluded_addons} ; do - unformatted_addons="$(echo "${unformatted_addons}" | sed -e "s/${addon}//g")" - done + bashio::log.warning "Excluded addon(s):\n${excluded_addons}" + for addon in ${excluded_addons} ; do + unformatted_addons="$(echo "${unformatted_addons}" | sed -e "s/${addon}//g")" + done fi local addons=$(echo ${unformatted_addons} | sed "s/ /\", \"/g") @@ -107,13 +117,15 @@ function create-local-backup { local data="$(echo $data | tr -d '}'), \"addons\": [\"${addons}\"], \"folders\": [\"${folders}\"]}" # append addon and folder set if ! SLUG=$(bashio::api.supervisor POST /backups/new/partial "${data}" .slug); then - die "Error creating partial backup!" + bashio::log.fatal "Error creating partial backup!" + return "${__BASHIO_EXIT_NOK}" fi else bashio::log.info "Creating full backup: \"${BACKUP_NAME}\"" if ! SLUG=$(bashio::api.supervisor POST /backups/new/full "${data}" .slug); then - die "Error creating full backup!" + bashio::log.fatal "Error creating full backup!" + return "${__BASHIO_EXIT_NOK}" fi fi @@ -128,12 +140,9 @@ function copy-backup-to-remote { return "${__BASHIO_EXIT_OK}" fi - cd /backup/ \ - || bashio::log.fatal "Error accessing directory /backup" \ - && return "${__BASHIO_EXIT_NOK}" - bashio::log.info "Copying backup using SCP" + bashio::log.info "Copying backup using SCP." if ! scp -F "${HOME}/.ssh/config" "/backup/${SLUG}.tar" remote:"${SSH_REMOTE_DIRECTORY}/"; then - bashio::log.error "Error copying backup ${SLUG}.tar to ${SSH_REMOTE_DIRECTORY} on ${REMOTE_HOST}" + bashio::log.error "Error copying backup ${SLUG}.tar to ${SSH_REMOTE_DIRECTORY} on ${REMOTE_HOST}." return "${__BASHIO_EXIT_NOK}" fi @@ -147,20 +156,19 @@ function copy-backup-to-remote { return "${__BASHIO_EXIT_OK}" } -function rsync_folders { +function rsync-folders { if bashio::var.false "${RSYNC_ENABLED}"; then - bashio::log.debug "rsync disabled." + bashio::log.debug "Rsync disabled." return "${__BASHIO_EXIT_OK}" fi - local FOLDERS="/config /addons /backup /share /ssl" # put directories without trailing slash - local RSYNC_URL="${REMOTE_USER}@${REMOTE_HOST}:${RSYNC_ROOTFOLDER}" + local folders="/config /addons /backup /share /ssl" # put directories without trailing slash + local rsync_url="${REMOTE_USER}@${REMOTE_HOST}:${RSYNC_ROOTFOLDER}" + local flags='-a -r' bashio::log.info "Starting rsync" if bashio::var.true "${DEBUG}"; then - local FLAGS='-av' - else - local FLAGS='-a' + local flags="${flags} -v" fi echo "${RSYNC_EXCLUDE}" > /tmp/rsync_exclude.txt @@ -168,16 +176,16 @@ function rsync_folders { bashio::log.warning "File patterns that have been excluded:\n${RSYNC_EXCLUDE}" fi - bashio::log.debug "Syncing ${FOLDERS}" - if ! sshpass -p "${REMOTE_PASSWORD}" rsync ${FLAGS} --port ${REMOTE_PORT} --exclude-from='/tmp/rsync_exclude.txt' ${FOLDERS} "${RSYNC_URL}/" --delete; then - bashio::log.error "Error syncing folder(s) ${FOLDERS}" + bashio::log.debug "Syncing ${folders}" + if ! sshpass -p "${REMOTE_PASSWORD}" rsync ${flags} --port ${REMOTE_PORT} --exclude-from='/tmp/rsync_exclude.txt' ${folders} "${rsync_url}/" --delete; then + bashio::log.error "Error syncing folder(s) ${folders}." return "${__BASHIO_EXIT_NOK}" fi return "${__BASHIO_EXIT_OK}" } -function rclone_backups { +function rclone-backups { if [ "${RCLONE_ENABLED}" = true ] ; then cd /backup/ || exit mkdir -p ~/.config/rclone/ @@ -210,43 +218,65 @@ function rclone_backups { fi } +function clone-to-remote { + local ret="${__BASHIO_EXIT_OK}" + + copy-backup-to-remote || ret="${__BASHIO_EXIT_NOK}" + rsync-folders || ret="${__BASHIO_EXIT_NOK}" + rclone-backups || ret="${__BASHIO_EXIT_NOK}" + + return "${ret}" +} + function delete-local-backup { + if bashio::var.equals "${BACKUP_KEEP_LOCAL}" "all"; then + bashio::log.debug "Keep all backups." + return "${__BASHIO_EXIT_OK}" + fi - ha backups reload + if ! bashio::api.supervisor POST /backups/reload; then + bashio::log.warning "Failed to reload backups!" + fi - if [[ "${BACKUP_KEEP_LOCAL}" == "all" ]]; then - : - elif [[ -z "${BACKUP_KEEP_LOCAL}" ]]; then - bashio::log.warning "Deleting local backup: ${SLUG}" - ha backups remove "${SLUG}" + if bashio::var.is_empty "${BACKUP_KEEP_LOCAL}"; then + if bashio::var.has_value "$SLUG"; then + bashio::log.warning "Deleting local backup: ${SLUG}" + if ! bashio::api.supervisor DELETE /backups/${SLUG}; then + bashio::log.error "Failed to delete backup: ${SLUG}" + return "${__BASHIO_EXIT_NOK}" + fi + else + bashio::log.debug "No current backup to delete." + fi else - - last_date_to_keep=$(ha backups list --raw-json | jq .data.backups[].date | sort -r | \ + local ret="${__BASHIO_EXIT_OK}" + local backup_list=$(bashio::api.supervisor GET /backups) + local last_date_to_keep=$(echo "${backup_list}" | jq ".backups[].date" | sort -r | \ head -n "${BACKUP_KEEP_LOCAL}" | tail -n 1 | xargs date -D "%Y-%m-%dT%T" +%s --date ) - ha backups list --raw-json | jq -c .data.backups[] | while read -r backup; do - if [[ $(echo "${backup}" | jq .date | xargs date -D "%Y-%m-%dT%T" +%s --date ) -lt ${last_date_to_keep} ]]; then - bashio::log.warning "Deleting local backup: $(echo "${backup}" | jq -r .slug)" - ha backups remove "$(echo "${backup}" | jq -r .slug)" - bashio::log.info "Finished deleting local backup: $(echo "${backup}" | jq -r .slug)" + echo "${backup_list}" | jq -c ".backups[]" | while read -r backup; do + if [[ $(echo "${backup}" | jq ".date" | xargs date -D "%Y-%m-%dT%T" +%s --date ) -lt ${last_date_to_keep} ]]; then + local backup_slug=$(echo "${backup}" | jq -r .slug) + bashio::log.warning "Deleting local backup: ${backup_slug}" + if ! bashio::api.supervisor DELETE /backups/${backup_slug}; then + bashio::log.error "Failed to delete backup: ${backup_slug}" + ret="${__BASHIO_EXIT_NOK}" + fi fi done - + return "${ret}" fi + + return "${__BASHIO_EXIT_OK}" } # general setup and backup set-debug-level add-ssh-key -create-local-backup - -# different remote cloning modes -copy-backup-to-remote || bashio::log.fatal "SCP failed." -rsync_folders || bashio::log.fatal "rsync failed." -rclone_backups -# cleanup -delete-local-backup +create-local-backup || die "Local backup process failed! See log for details." +clone-to-remote || die "Cloning backup(s) to remote host ${REMOTE_HOST} failed! See log for details." +delete-local-backup || die "Removing local backup(s) failed! See log for details." bashio::log.info "Backup process done!" bashio::exit.ok From fb70041dfa21e43aa41525824a3131f3bb7ad744 Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Sat, 20 Aug 2022 11:40:20 +0200 Subject: [PATCH 11/40] removed unneeded variables --- remote-backup/run.sh | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/remote-backup/run.sh b/remote-backup/run.sh index e0af5c4..2a3cf75 100755 --- a/remote-backup/run.sh +++ b/remote-backup/run.sh @@ -92,20 +92,18 @@ function create-local-backup { if bashio::var.has_value "${BACKUP_EXCLUDE_ADDONS}" || bashio::var.has_value "${BACKUP_EXCLUDE_FOLDERS}"; then bashio::log.info "Creating partial backup: \"${BACKUP_NAME}\"" - local excluded_folders=$(echo "${BACKUP_EXCLUDE_FOLDERS}") - local excluded_addons=$(echo "${BACKUP_EXCLUDE_ADDONS}") local unformatted_folders="${base_folders}" local unformatted_addons="${installed_addons}" - if bashio::var.has_value "${excluded_folders}"; then - bashio::log.warning "Excluded folder(s):\n ${excluded_folders}" - for folder in ${excluded_folders} ; do + if bashio::var.has_value "${BACKUP_EXCLUDE_FOLDERS}"; then + bashio::log.notice "Excluded folder(s):\n${BACKUP_EXCLUDE_FOLDERS}" + for folder in ${BACKUP_EXCLUDE_FOLDERS} ; do unformatted_folders=$(echo "${unformatted_folders}" | sed -e "s/${folder}//g") done fi - if bashio::var.has_value "${excluded_addons}"; then - bashio::log.warning "Excluded addon(s):\n${excluded_addons}" - for addon in ${excluded_addons} ; do + if bashio::var.has_value "${BACKUP_EXCLUDE_ADDONS}"; then + bashio::log.notice "Excluded addon(s):\n${BACKUP_EXCLUDE_ADDONS}" + for addon in ${BACKUP_EXCLUDE_ADDONS} ; do unformatted_addons="$(echo "${unformatted_addons}" | sed -e "s/${addon}//g")" done fi From b85d393910ae6c8d42e628107b13f76f87a99678 Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Sat, 20 Aug 2022 11:47:26 +0200 Subject: [PATCH 12/40] changed log level, cleaned messages --- remote-backup/run.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/remote-backup/run.sh b/remote-backup/run.sh index 2a3cf75..a73858e 100755 --- a/remote-backup/run.sh +++ b/remote-backup/run.sh @@ -164,19 +164,19 @@ function rsync-folders { local rsync_url="${REMOTE_USER}@${REMOTE_HOST}:${RSYNC_ROOTFOLDER}" local flags='-a -r' - bashio::log.info "Starting rsync" + bashio::log.info "Copying backup using rsync." if bashio::var.true "${DEBUG}"; then local flags="${flags} -v" fi echo "${RSYNC_EXCLUDE}" > /tmp/rsync_exclude.txt if bashio::var.has_value "${RSYNC_EXCLUDE}"; then - bashio::log.warning "File patterns that have been excluded:\n${RSYNC_EXCLUDE}" + bashio::log.notice "Excluded rsync file patterns:\n${RSYNC_EXCLUDE}" fi bashio::log.debug "Syncing ${folders}" if ! sshpass -p "${REMOTE_PASSWORD}" rsync ${flags} --port ${REMOTE_PORT} --exclude-from='/tmp/rsync_exclude.txt' ${folders} "${rsync_url}/" --delete; then - bashio::log.error "Error syncing folder(s) ${folders}." + bashio::log.error "Error rsyncing folder(s) ${folders} to ${rsync_url}!" return "${__BASHIO_EXIT_NOK}" fi @@ -188,7 +188,7 @@ function rclone-backups { cd /backup/ || exit mkdir -p ~/.config/rclone/ cp -a /ssl/rclone.conf ~/.config/rclone/rclone.conf - bashio::log.info "Starting rclone" + bashio::log.info "Copying backup using rclone." if [ "$RCLONE_COPY" = true ] ; then if [ "$BACKUP_FRIENDLY_NAME" = true ] ; then bashio::log.debug "Copying ${SLUG}.tar to ${RCLONE_REMOTE_DIRECTORY}/${BACKUP_NAME}.tar" @@ -238,7 +238,7 @@ function delete-local-backup { if bashio::var.is_empty "${BACKUP_KEEP_LOCAL}"; then if bashio::var.has_value "$SLUG"; then - bashio::log.warning "Deleting local backup: ${SLUG}" + bashio::log.notice "Deleting local backup: ${SLUG}" if ! bashio::api.supervisor DELETE /backups/${SLUG}; then bashio::log.error "Failed to delete backup: ${SLUG}" return "${__BASHIO_EXIT_NOK}" @@ -255,7 +255,7 @@ function delete-local-backup { echo "${backup_list}" | jq -c ".backups[]" | while read -r backup; do if [[ $(echo "${backup}" | jq ".date" | xargs date -D "%Y-%m-%dT%T" +%s --date ) -lt ${last_date_to_keep} ]]; then local backup_slug=$(echo "${backup}" | jq -r .slug) - bashio::log.warning "Deleting local backup: ${backup_slug}" + bashio::log.notice "Deleting local backup: ${backup_slug}" if ! bashio::api.supervisor DELETE /backups/${backup_slug}; then bashio::log.error "Failed to delete backup: ${backup_slug}" ret="${__BASHIO_EXIT_NOK}" From c477cdaa68494e17dfda8c80df4ac68e33691b39 Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Sat, 20 Aug 2022 12:20:32 +0200 Subject: [PATCH 13/40] added port to ssh-keyscan --- remote-backup/run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/remote-backup/run.sh b/remote-backup/run.sh index a73858e..2c02d99 100755 --- a/remote-backup/run.sh +++ b/remote-backup/run.sh @@ -59,7 +59,7 @@ function add-ssh-key { ) || bashio::log.error "Failed to create SSH key pair!" bashio::log.debug "Adding public key of remote host ${REMOTE_HOST} to known hosts." - ssh-keyscan -t rsa ${REMOTE_HOST} >> ${HOME}/.ssh/known_hosts \ + ssh-keyscan -t rsa -p ${REMOTE_PORT} ${REMOTE_HOST} >> ${HOME}/.ssh/known_hosts \ || bashio::log.error "Failed to add public key for remote host ${REMOTE_HOST}!" ( echo "Host remote" From e6502e613bbc1e489838cf7da6cd5422314ab25e Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Sat, 20 Aug 2022 16:40:34 +0200 Subject: [PATCH 14/40] added status event --- remote-backup/DOCS.md | 29 +++++++++++++++++++++++++++++ remote-backup/run.sh | 31 ++++++++++++++++++++++++------- 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/remote-backup/DOCS.md b/remote-backup/DOCS.md index 50cfaed..e090aac 100644 --- a/remote-backup/DOCS.md +++ b/remote-backup/DOCS.md @@ -2,6 +2,35 @@ Please visit the documentation at [addons.mathesonsteplock.ca](https://addons.mathesonsteplock.ca/docs/addons/remote-backup/basic-config) +# Persistent Notification + +In case of an error, a persistent notification with the error message is created. Please see the logs in to find out what happend (you might also want to enable debugging in the configuration). + +# Using Events + +The add-on creates an event each time it is has been executed. + +| Field | Description | +| ------------ | -------------------------------------------- | +| `event_type` | `remote_backup_status` | +| `result` | Backup result status, can be `ok` or `error` | +| `message` | Human readable message for the notification | + +Here is an example automation on how to use it: +
+alias: Backup check
+description: This automation creates an persistent notification in case the backup fails.
+trigger:
+  - platform: event
+    event_type: remote_backup_status
+    event_data:
+      result: error
+action:
+  - service: persistent_notification.create
+    data:
+      message: Backup failed
+mode: single
+
# Support Me [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/mathesonsteplock) diff --git a/remote-backup/run.sh b/remote-backup/run.sh index 2c02d99..9eb9299 100755 --- a/remote-backup/run.sh +++ b/remote-backup/run.sh @@ -33,18 +33,34 @@ BACKUP_NAME="${BACKUP_CUSTOM_PREFIX} $(date +'%Y-%m-%d %H-%M')" function set-debug-level { # default log level according to bashio const.sh is INFO - if bashio::var.true "${DEBUG}"; then + if bashio::var.true "${DEBUG}"; then bashio::log.level "debug" fi } -function die { - local message=${1:-'no message'} - local title=${2:-'Addon: Remote Backup Failed!'} +# Arguments: +# $1 result should be ok or error +# $2 message message to send with the event +function fire_event { + local result=${1} + local message=${2:-} + + if bashio::var.has_value "${message}"; then + message=",\"message:\":\"${message}\"" + fi - # catch return code which is always false, see https://github.com/hassio-addons/bashio/issues/31 - local ret=$(bashio::api.supervisor POST /core/api/services/persistent_notification/create "{\"message\":\"${message}\", \"title\":\"${title}\"}") - bashio::exit.nok "${message}" + # catch return code which is always false, see https://github.com/hassio-addons/bashio/issues/31 + local ret=$(bashio::api.supervisor POST /core/api/events/remote_backup_status "{\"result\":\"${result}\"${message}}") +} +function die { + local message=${1:-'no message'} + local title=${2:-'Addon: Remote Backup Failed!'} + + # catch return code which is always false, see https://github.com/hassio-addons/bashio/issues/31 + local ret=$(bashio::api.supervisor POST /core/api/services/persistent_notification/create \ + "{\"message\":\"${message}\", \"title\":\"${title}\", \"notification_id\":\"addon-remote-backup\"}") + fire_event "error" "${message}" + bashio::exit.nok "${message}" } # prepare SSH environment/configuration @@ -277,4 +293,5 @@ clone-to-remote || die "Cloning backup(s) to remote host ${REMOTE_HOST} failed! delete-local-backup || die "Removing local backup(s) failed! See log for details." bashio::log.info "Backup process done!" +fire_event "ok" "Backup ${BACKUP_NAME} created." bashio::exit.ok From fd68747d0ea3b8ae84a07207af842849d6af6afb Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Sun, 21 Aug 2022 10:09:57 +0200 Subject: [PATCH 15/40] cleaned up rclone-backups --- remote-backup/run.sh | 55 ++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/remote-backup/run.sh b/remote-backup/run.sh index 9eb9299..3a1d547 100755 --- a/remote-backup/run.sh +++ b/remote-backup/run.sh @@ -200,36 +200,45 @@ function rsync-folders { } function rclone-backups { - if [ "${RCLONE_ENABLED}" = true ] ; then - cd /backup/ || exit + if bashio::var.false "${RCLONE_ENABLED}"; then + bashio::log.debug "Rclone disabled." + return "${__BASHIO_EXIT_OK}" + fi + + ( + cd /backup/ mkdir -p ~/.config/rclone/ cp -a /ssl/rclone.conf ~/.config/rclone/rclone.conf + ) || bashio::log.error "Failed to prepare rclone configuration!" + + if bashio::var.true "${RCLONE_COPY}"; then + local remote_name=$SLUG + if bashio::var.true "${BACKUP_FRIENDLY_NAME}"; then + remote_name=$BACKUP_NAME + fi bashio::log.info "Copying backup using rclone." - if [ "$RCLONE_COPY" = true ] ; then - if [ "$BACKUP_FRIENDLY_NAME" = true ] ; then - bashio::log.debug "Copying ${SLUG}.tar to ${RCLONE_REMOTE_DIRECTORY}/${BACKUP_NAME}.tar" - rclone copyto "${SLUG}.tar" "${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY}/${BACKUP_NAME}.tar" - bashio::log.debug "Finished rclone copy" - else - bashio::log.debug "Copying ${SLUG}.tar to ${RCLONE_REMOTE_DIRECTORY}/${SLUG}.tar" - rclone copy "${SLUG}.tar" "${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY}" - bashio::log.debug "Finished rclone copy" - fi + if ! rclone copyto "${SLUG}.tar" "${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY}/${remote_name}.tar"; then + bashio::log.error "Error rclone ${SLUG}.tar to ${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY}/${remote_name}.tar!" + return "${__BASHIO_EXIT_NOK}" fi - if [ "${RCLONE_SYNC}" = true ] ; then - bashio::log.info "Syncing Backups" - rclone sync . "${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY}" - bashio::log.info "Finished rclone sync" + fi + if bashio::var.true "${RCLONE_SYNC}"; then + bashio::log.info "Syncing backups using rclone" + if ! rclone sync . "${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY}"; then + bashio::log.error "Error syncing backups by rclone!" + return "${__BASHIO_EXIT_NOK}" fi - if [ "${RCLONE_RESTORE}" = true ] ; then - DATEFORMAT=$(date +%F) - RESTORENAME="restore-${DATEFORMAT}" - mkdir -p "${RESTORENAME}" - bashio::log.info "Restoring Backups to ${RESTORENAME}" - rclone copyto "${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY} ${RESTORENAME}/" - bashio::log.info "Finished rclone restore" + fi + if bashio::var.true "${RCLONE_RESTORE}"; then + local restore_name="restore-$(date +%F)" + mkdir -p "${restore_name}" + bashio::log.info "Restoring backups to ${restore_name} using rclone" + if ! rclone copyto "${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY} ${restore_name}/"; then + bashio::log.error "Error restoring backups from ${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY}!" + return "${__BASHIO_EXIT_NOK}" fi fi + return "${__BASHIO_EXIT_OK}" } function clone-to-remote { From f7188cede364d08fc25880a5a9a16fb1492aa4ff Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Sun, 21 Aug 2022 10:19:40 +0200 Subject: [PATCH 16/40] minor fixes to documentation --- remote-backup/DOCS.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/remote-backup/DOCS.md b/remote-backup/DOCS.md index e090aac..b30fae1 100644 --- a/remote-backup/DOCS.md +++ b/remote-backup/DOCS.md @@ -2,11 +2,11 @@ Please visit the documentation at [addons.mathesonsteplock.ca](https://addons.mathesonsteplock.ca/docs/addons/remote-backup/basic-config) -# Persistent Notification +## Persistent Notification -In case of an error, a persistent notification with the error message is created. Please see the logs in to find out what happend (you might also want to enable debugging in the configuration). +In case of an error, a persistent notification with the error message is created. Please see the logs to find out what happend (you might also want to enable debugging in the configuration). -# Using Events +## Using Events The add-on creates an event each time it is has been executed. From 74da87ee8e26f24f6725889d9801cb1084c1fe7d Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Sun, 21 Aug 2022 10:26:35 +0200 Subject: [PATCH 17/40] allow spaces in paths --- remote-backup/config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/remote-backup/config.yaml b/remote-backup/config.yaml index cdcd76f..8df716a 100644 --- a/remote-backup/config.yaml +++ b/remote-backup/config.yaml @@ -64,7 +64,7 @@ schema: backup_friendly_name: bool? backup_custom_prefix: str? backup_exclude_folders: - - match(^[A-Za-z0-9_\-\.\*\/\?\+\\]*$)? + - match(^[A-Za-z0-9_\-\.\*\/\?\+\\ ]*$)? backup_exclude_addons: - str? backup_keep_local: match(^(all|[+]?\d*)$)? @@ -74,7 +74,7 @@ schema: rsync_enabled: bool rsync_rootfolder: str? rsync_exclude: - - match(^[A-Za-z0-9_\-\.\*\/\?\+\\]+$) + - match(^[A-Za-z0-9_\-\.\*\/\?\+\\ ]+$) rclone_enabled: bool rclone_remote_directory: str? rclone_copy: bool? From a31d17f6fcc4469483cc3c1e064ed42ad11f94b8 Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Sun, 21 Aug 2022 17:52:10 +0200 Subject: [PATCH 18/40] cleanup scp - simplified remote file renaming - fixed spaces in path issue --- remote-backup/run.sh | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/remote-backup/run.sh b/remote-backup/run.sh index 3a1d547..b850581 100755 --- a/remote-backup/run.sh +++ b/remote-backup/run.sh @@ -154,19 +154,17 @@ function copy-backup-to-remote { return "${__BASHIO_EXIT_OK}" fi + local remote_name=$SLUG + if bashio::var.true "${BACKUP_FRIENDLY_NAME}"; then + remote_name=$BACKUP_NAME + fi + bashio::log.info "Copying backup using SCP." - if ! scp -F "${HOME}/.ssh/config" "/backup/${SLUG}.tar" remote:"${SSH_REMOTE_DIRECTORY}/"; then + if ! scp -F "${HOME}/.ssh/config" "/backup/${SLUG}.tar" remote:"'${SSH_REMOTE_DIRECTORY}/${remote_name}.tar'"; then bashio::log.error "Error copying backup ${SLUG}.tar to ${SSH_REMOTE_DIRECTORY} on ${REMOTE_HOST}." return "${__BASHIO_EXIT_NOK}" fi - if bashio::var.true "${BACKUP_FRIENDLY_NAME}"; then - bashio::log.info "Renaming ${SLUG}.tar to ${BACKUP_NAME}.tar" - if ! ssh remote "mv \"${SSH_REMOTE_DIRECTORY}/${SLUG}.tar\" \"${SSH_REMOTE_DIRECTORY}/${BACKUP_NAME}.tar\""; then - bashio::log.error "Error renaming backup to ${SSH_REMOTE_DIRECTORY}/${BACKUP_NAME}.tar on ${REMOTE_HOST}" - return "${__BASHIO_EXIT_NOK}" - fi - fi return "${__BASHIO_EXIT_OK}" } From 43e3bf0923e6f3cabcbc2cf11813746ecc17aa65 Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Sun, 21 Aug 2022 18:44:47 +0200 Subject: [PATCH 19/40] added apparmor profile --- remote-backup/apparmor.txt | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 remote-backup/apparmor.txt diff --git a/remote-backup/apparmor.txt b/remote-backup/apparmor.txt new file mode 100644 index 0000000..38ab13d --- /dev/null +++ b/remote-backup/apparmor.txt @@ -0,0 +1,31 @@ +#include + +profile ADDON_SLUG flags=(attach_disconnected,mediate_deleted) { + #include + + # Capabilities + file, + signal (send) set=(kill,term,int,hup,cont), + + # S6-Overlay + /init ix, + /bin/** ix, + /usr/bin/rsync ix, + /usr/bin/scp ix, + /usr/bin/rclone ix, + /run/{s6,s6-rc*,service}/** ix, + /package/** ix, + /command/** ix, + /etc/services.d/** rwix, + /etc/cont-init.d/** rwix, + /etc/cont-finish.d/** rwix, + /run/{,**} rwk, + /dev/tty rw, + + # Bashio + /usr/lib/bashio/** ix, + /tmp/** rwk, + + # Access to options.json and other files within your addon + /data/** rw, +} From 8849dfcea8ed6f35e8f4c3aba29037d92ecfd704 Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Mon, 22 Aug 2022 11:01:08 +0200 Subject: [PATCH 20/40] cleanup of SSH environment - check whether private key is available - enable static known-hosts file for security - minor improvements - added documentation for security --- remote-backup/DOCS.md | 2 ++ remote-backup/apparmor.txt | 2 +- remote-backup/config.yaml | 2 +- remote-backup/run.sh | 63 +++++++++++++++++++++++--------------- 4 files changed, 42 insertions(+), 27 deletions(-) diff --git a/remote-backup/DOCS.md b/remote-backup/DOCS.md index b30fae1..952bb4a 100644 --- a/remote-backup/DOCS.md +++ b/remote-backup/DOCS.md @@ -2,6 +2,8 @@ Please visit the documentation at [addons.mathesonsteplock.ca](https://addons.mathesonsteplock.ca/docs/addons/remote-backup/basic-config) +## Security +For SSH and rsync operation it is recommend to add the public key of the remote host to the file `/ssl/known_hosts`. If you see a warning `Missing known_hosts file!` then you have not done so and the addon automatically does it for you each time it is called. **Note that this is a security risk** which can be fixed by executing `ssh-keyscan -t rsa >> /ssl/known_hosts` from a terminal, e.g. [SSH & Web Terminal](https://github.com/hassio-addons/addon-ssh). ## Persistent Notification In case of an error, a persistent notification with the error message is created. Please see the logs to find out what happend (you might also want to enable debugging in the configuration). diff --git a/remote-backup/apparmor.txt b/remote-backup/apparmor.txt index 38ab13d..b940eb3 100644 --- a/remote-backup/apparmor.txt +++ b/remote-backup/apparmor.txt @@ -12,7 +12,7 @@ profile ADDON_SLUG flags=(attach_disconnected,mediate_deleted) { /bin/** ix, /usr/bin/rsync ix, /usr/bin/scp ix, - /usr/bin/rclone ix, + /usr/bin/rclone ix, /run/{s6,s6-rc*,service}/** ix, /package/** ix, /command/** ix, diff --git a/remote-backup/config.yaml b/remote-backup/config.yaml index 8df716a..b5ec95c 100644 --- a/remote-backup/config.yaml +++ b/remote-backup/config.yaml @@ -1,7 +1,7 @@ name: Remote Backup version: "2022.7.2" slug: remote_backup -description: Automatically create and backup HA backups using SCP +description: Automatically create and backup HA backups using SFTP (SCP), rsync, or rclone (experimental) image: ikifar/remote-backup-{arch} url: https://addons.mathesonsteplock.ca/docs/addons/remote-backup/basic-config codenotary: cas@mathesonsteplock.ca diff --git a/remote-backup/run.sh b/remote-backup/run.sh index b850581..02d5919 100755 --- a/remote-backup/run.sh +++ b/remote-backup/run.sh @@ -29,7 +29,9 @@ RCLONE_COPY=$(bashio::config "rclone_copy") RCLONE_SYNC=$(bashio::config "rclone_sync") RCLONE_RESTORE=$(bashio::config "rclone_restore") +# script global shortcuts BACKUP_NAME="${BACKUP_CUSTOM_PREFIX} $(date +'%Y-%m-%d %H-%M')" +SSH_HOME="${HOME}/.ssh" function set-debug-level { # default log level according to bashio const.sh is INFO @@ -66,41 +68,52 @@ function die { # prepare SSH environment/configuration # function does never fail to continue with further commands function add-ssh-key { - if bashio::var.true "${SSH_ENABLED}" || bashio::var.true "${RSYNC_ENABLED}"; then - bashio::log.info "Adding SSH key." + if bashio::var.false "${SSH_ENABLED}" && bashio::var.false "${RSYNC_ENABLED}"; then + bashio::log.debug "Not creating configuration, SSH/RSYNC disabled." + return + fi + + bashio::log.info "Adding SSH configuration." + # prepare SSH key pair + mkdir -p ${SSH_HOME} || bashio::log.error "Failed to create .ssh directory!" + if bashio::var.has_value "${REMOTE_KEY}"; then ( - mkdir -p ${HOME}/.ssh - cp "/ssl/${REMOTE_KEY}" "${HOME}/.ssh/id_rsa" - ssh-keygen -y -f ${HOME}/.ssh/id_rsa > ${HOME}/.ssh/id_rsa.pub + cp "/ssl/${REMOTE_KEY}" "${SSH_HOME}/id_rsa" + ssh-keygen -y -f ${SSH_HOME}/id_rsa > ${SSH_HOME}/id_rsa.pub + chmod 600 "${SSH_HOME}/id_rsa" + chmod 644 "${SSH_HOME}/id_rsa.pub" ) || bashio::log.error "Failed to create SSH key pair!" + fi - bashio::log.debug "Adding public key of remote host ${REMOTE_HOST} to known hosts." - ssh-keyscan -t rsa -p ${REMOTE_PORT} ${REMOTE_HOST} >> ${HOME}/.ssh/known_hosts \ - || bashio::log.error "Failed to add public key for remote host ${REMOTE_HOST}!" - ( - echo "Host remote" - echo " IdentityFile ${HOME}/.ssh/id_rsa" - echo " HostName ${REMOTE_HOST}" - echo " User ${REMOTE_USER}" - echo " Port ${REMOTE_PORT}" - echo " StrictHostKeyChecking no" + # copy known_hosts if available + if bashio::fs.file_exists "/ssl/known_hosts"; then + cp "/ssl/known_hosts" "${SSH_HOME}/known_hosts" \ + || bashio::log.error "Failed to copy known_hosts file!" + else + bashio::log.warning "Missing known_hosts file! Retrieving public key of remote host ${REMOTE_HOST}." + ssh-keyscan -t rsa -p ${REMOTE_PORT} ${REMOTE_HOST} >> ${SSH_HOME}/known_hosts \ + || bashio::log.error "Failed to add public key for remote host ${REMOTE_HOST}!" + fi + + # generate configuration file + ( + echo "Host remote" + if bashio::fs.file_exists "${SSH_HOME}/id_rsa"; then + echo " IdentityFile ${SSH_HOME}/id_rsa" + fi + echo " HostName ${REMOTE_HOST}" + echo " User ${REMOTE_USER}" + echo " Port ${REMOTE_PORT}" if bashio::var.has_value "${REMOTE_HOST_KEY_ALGORITHMS}"; then echo " HostKeyAlgorithms ${REMOTE_HOST_KEY_ALGORITHMS}" fi - ) > "${HOME}/.ssh/config" - - ( - chmod 600 "${HOME}/.ssh/id_rsa" - chmod 600 "${HOME}/.ssh/config" - chmod 644 "${HOME}/.ssh/id_rsa.pub" - ) || bashio::log.error "Failed to set SSH file permissions!" - fi + ) > "${SSH_HOME}/config" + chmod 600 "${SSH_HOME}/config" || bashio::log.error "Failed to set SSH configuration file permissions!" } # call Home Assistant to create a local backup # function fails in case local backup is not created function create-local-backup { - # Bind local variables local base_folders="addons/local homeassistant media share ssl" local installed_addons=$(bashio::supervisor.addons) local data="{\"name\":\"${BACKUP_NAME}\", \"password\": \"${BACKUP_PASSWORD}\"}" @@ -160,7 +173,7 @@ function copy-backup-to-remote { fi bashio::log.info "Copying backup using SCP." - if ! scp -F "${HOME}/.ssh/config" "/backup/${SLUG}.tar" remote:"'${SSH_REMOTE_DIRECTORY}/${remote_name}.tar'"; then + if ! scp -F "${SSH_HOME}/config" "/backup/${SLUG}.tar" remote:"'${SSH_REMOTE_DIRECTORY}/${remote_name}.tar'"; then bashio::log.error "Error copying backup ${SLUG}.tar to ${SSH_REMOTE_DIRECTORY} on ${REMOTE_HOST}." return "${__BASHIO_EXIT_NOK}" fi From e52184dd753ca0750407bd39059cc10ebc3766d6 Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Mon, 22 Aug 2022 14:37:37 +0200 Subject: [PATCH 21/40] coding style fixes --- remote-backup/run.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/remote-backup/run.sh b/remote-backup/run.sh index 02d5919..eead163 100755 --- a/remote-backup/run.sh +++ b/remote-backup/run.sh @@ -34,16 +34,16 @@ BACKUP_NAME="${BACKUP_CUSTOM_PREFIX} $(date +'%Y-%m-%d %H-%M')" SSH_HOME="${HOME}/.ssh" function set-debug-level { - # default log level according to bashio const.sh is INFO - if bashio::var.true "${DEBUG}"; then - bashio::log.level "debug" - fi + # default log level according to bashio const.sh is INFO + if bashio::var.true "${DEBUG}"; then + bashio::log.level "debug" + fi } # Arguments: # $1 result should be ok or error # $2 message message to send with the event -function fire_event { +function fire-event { local result=${1} local message=${2:-} @@ -61,7 +61,7 @@ function die { # catch return code which is always false, see https://github.com/hassio-addons/bashio/issues/31 local ret=$(bashio::api.supervisor POST /core/api/services/persistent_notification/create \ "{\"message\":\"${message}\", \"title\":\"${title}\", \"notification_id\":\"addon-remote-backup\"}") - fire_event "error" "${message}" + fire-event "error" "${message}" bashio::exit.nok "${message}" } @@ -313,5 +313,5 @@ clone-to-remote || die "Cloning backup(s) to remote host ${REMOTE_HOST} failed! delete-local-backup || die "Removing local backup(s) failed! See log for details." bashio::log.info "Backup process done!" -fire_event "ok" "Backup ${BACKUP_NAME} created." +fire-event "ok" "Backup ${BACKUP_NAME} created." bashio::exit.ok From 264bfc9d00bab1cccdd5aaec0531dc29d79143d8 Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Mon, 22 Aug 2022 18:09:34 +0200 Subject: [PATCH 22/40] fixed default configuration settings --- remote-backup/config.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/remote-backup/config.yaml b/remote-backup/config.yaml index b5ec95c..4c66756 100644 --- a/remote-backup/config.yaml +++ b/remote-backup/config.yaml @@ -27,20 +27,20 @@ map: options: debug: false - remote_host: null + remote_host: "" remote_port: 22 - remote_user: null - remote_password: null - remote_key: null + remote_user: "" + remote_password: "" + remote_key: "" remote_host_key_algorithms: null backup_friendly_name: true backup_custom_prefix: Automated backup backup_exclude_folders: [] backup_exclude_addons: [] backup_keep_local: "all" - backup_password: null + backup_password: "" ssh_enabled: true - ssh_remote_directory: null + ssh_remote_directory: "hassio-backup" rsync_enabled: false rsync_rootfolder: hassio-sync rsync_exclude: @@ -48,7 +48,7 @@ options: - "/config/*.db-wal" - "/config/*.db" rclone_enabled: false - rclone_remote_directory: null + rclone_remote_directory: "" rclone_copy: false rclone_sync: false rclone_restore: false @@ -59,7 +59,7 @@ schema: remote_port: port remote_user: str remote_password: str? - remote_key: str? + remote_key: str? remote_host_key_algorithms: str? backup_friendly_name: bool? backup_custom_prefix: str? @@ -74,7 +74,7 @@ schema: rsync_enabled: bool rsync_rootfolder: str? rsync_exclude: - - match(^[A-Za-z0-9_\-\.\*\/\?\+\\ ]+$) + - match(^[A-Za-z0-9_\-\.\*\/\?\+\\ ]+$)? rclone_enabled: bool rclone_remote_directory: str? rclone_copy: bool? From 83fdc326169c2235a77215f57d1d37e55b19369f Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Mon, 22 Aug 2022 18:44:11 +0200 Subject: [PATCH 23/40] cleaned up configuration - set 'null' for required parameters - remove optional parameters empty defaults to hide in GUI --- remote-backup/config.yaml | 14 ++------------ remote-backup/translations/en.yaml | 5 ++--- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/remote-backup/config.yaml b/remote-backup/config.yaml index 4c66756..56f9567 100644 --- a/remote-backup/config.yaml +++ b/remote-backup/config.yaml @@ -26,19 +26,13 @@ map: - backup options: - debug: false - remote_host: "" + remote_host: null remote_port: 22 - remote_user: "" - remote_password: "" + remote_user: null remote_key: "" - remote_host_key_algorithms: null backup_friendly_name: true backup_custom_prefix: Automated backup - backup_exclude_folders: [] - backup_exclude_addons: [] backup_keep_local: "all" - backup_password: "" ssh_enabled: true ssh_remote_directory: "hassio-backup" rsync_enabled: false @@ -48,10 +42,6 @@ options: - "/config/*.db-wal" - "/config/*.db" rclone_enabled: false - rclone_remote_directory: "" - rclone_copy: false - rclone_sync: false - rclone_restore: false schema: debug: bool? diff --git a/remote-backup/translations/en.yaml b/remote-backup/translations/en.yaml index 10512c8..b7c6bea 100644 --- a/remote-backup/translations/en.yaml +++ b/remote-backup/translations/en.yaml @@ -15,7 +15,7 @@ configuration: description: Password to be used for authentication with remote server remote_key: name: SSH private key - description: SSH private key file to be used for authentication with remote server. This be located in the directory 'ssl' of Home Assistant. + description: SSH private key file to be used for authentication with remote server. The key must be stored in the directory 'ssl' of Home Assistant. remote_host_key_algorithms: name: Host key algorithms description: Can be used to enable further (legacy) algorithms for authentication @@ -51,5 +51,4 @@ configuration: name: rsync path patterns to exclude from sync description: This feature uses the rsync --exclude scheme rclone_enabled: - name: Enable rclone - description: Experimental! + name: Enable rclone (experimental) From d50489c0698236b843da552bf3c3b19830d145a9 Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Tue, 23 Aug 2022 09:26:13 +0200 Subject: [PATCH 24/40] reduced variable scope, stricter config checks --- remote-backup/config.yaml | 3 +- remote-backup/run.sh | 145 +++++++++++++++++--------------------- 2 files changed, 66 insertions(+), 82 deletions(-) diff --git a/remote-backup/config.yaml b/remote-backup/config.yaml index 56f9567..2a8015d 100644 --- a/remote-backup/config.yaml +++ b/remote-backup/config.yaml @@ -41,7 +41,6 @@ options: - "/config/*.db-shm" - "/config/*.db-wal" - "/config/*.db" - rclone_enabled: false schema: debug: bool? @@ -65,7 +64,7 @@ schema: rsync_rootfolder: str? rsync_exclude: - match(^[A-Za-z0-9_\-\.\*\/\?\+\\ ]+$)? - rclone_enabled: bool + rclone_enabled: bool? rclone_remote_directory: str? rclone_copy: bool? rclone_sync: bool? diff --git a/remote-backup/run.sh b/remote-backup/run.sh index eead163..c1b87d5 100755 --- a/remote-backup/run.sh +++ b/remote-backup/run.sh @@ -1,41 +1,22 @@ #!/command/with-contenv bashio # shellcheck shell=bash -# parse inputs from options -DEBUG=$(bashio::config "debug") -REMOTE_HOST=$(bashio::config "remote_host") -REMOTE_PORT=$(bashio::config "remote_port") -REMOTE_USER=$(bashio::config "remote_user") -REMOTE_PASSWORD=$(bashio::config "remote_password") -REMOTE_KEY=$(bashio::config "remote_key") -REMOTE_HOST_KEY_ALGORITHMS=$(bashio::config "remote_host_key_algorithms") - -BACKUP_FRIENDLY_NAME=$(bashio::config "backup_friendly_name") -BACKUP_CUSTOM_PREFIX=$(bashio::config "backup_custom_prefix") -BACKUP_EXCLUDE_FOLDERS=$(bashio::config "backup_exclude_folders") -BACKUP_EXCLUDE_ADDONS=$(bashio::config "backup_exclude_addons") -BACKUP_KEEP_LOCAL=$(bashio::config 'backup_keep_local') -BACKUP_PASSWORD=$(bashio::config 'backup_password') - -SSH_ENABLED=$(bashio::config "ssh_enabled") -SSH_REMOTE_DIRECTORY=$(bashio::config "ssh_remote_directory") - -RSYNC_ENABLED=$(bashio::config "rsync_enabled") -RSYNC_ROOTFOLDER=$(bashio::config "rsync_rootfolder") -RSYNC_EXCLUDE=$(bashio::config "rsync_exclude") - -RCLONE_ENABLED=$(bashio::config "rclone_enabled") -RCLONE_REMOTE_DIRECTORY=$(bashio::config "rclone_remote_directory") -RCLONE_COPY=$(bashio::config "rclone_copy") -RCLONE_SYNC=$(bashio::config "rclone_sync") -RCLONE_RESTORE=$(bashio::config "rclone_restore") +# parse global options from configuration + +bashio::config.require "remote_host" "A target host for copying backups is necessary." +bashio::config.require "remote_port" "A target host port for communication is necessary." +bashio::config.require.username "remote_user" +declare -r REMOTE_HOST=$(bashio::config "remote_host") +declare -r REMOTE_PORT=$(bashio::config "remote_port") +declare -r REMOTE_USER=$(bashio::config "remote_user") +declare -r REMOTE_PASSWORD=$(bashio::config "remote_password" "") # script global shortcuts -BACKUP_NAME="${BACKUP_CUSTOM_PREFIX} $(date +'%Y-%m-%d %H-%M')" -SSH_HOME="${HOME}/.ssh" +declare -r BACKUP_NAME="$(bashio::config 'backup_custom_prefix' '') $(date +'%Y-%m-%d %H-%M')" +declare -r SSH_HOME="${HOME}/.ssh" function set-debug-level { # default log level according to bashio const.sh is INFO - if bashio::var.true "${DEBUG}"; then + if bashio::config.true "debug"; then bashio::log.level "debug" fi } @@ -44,7 +25,7 @@ function set-debug-level { # $1 result should be ok or error # $2 message message to send with the event function fire-event { - local result=${1} + local -r result=${1} local message=${2:-} if bashio::var.has_value "${message}"; then @@ -55,8 +36,8 @@ function fire-event { local ret=$(bashio::api.supervisor POST /core/api/events/remote_backup_status "{\"result\":\"${result}\"${message}}") } function die { - local message=${1:-'no message'} - local title=${2:-'Addon: Remote Backup Failed!'} + local -r message=${1:-'no message'} + local -r title=${2:-'Addon: Remote Backup Failed!'} # catch return code which is always false, see https://github.com/hassio-addons/bashio/issues/31 local ret=$(bashio::api.supervisor POST /core/api/services/persistent_notification/create \ @@ -68,7 +49,7 @@ function die { # prepare SSH environment/configuration # function does never fail to continue with further commands function add-ssh-key { - if bashio::var.false "${SSH_ENABLED}" && bashio::var.false "${RSYNC_ENABLED}"; then + if ! bashio::config.true "ssh_enabled" && ! bashio::config.true "rsync_enabled"; then bashio::log.debug "Not creating configuration, SSH/RSYNC disabled." return fi @@ -76,9 +57,9 @@ function add-ssh-key { bashio::log.info "Adding SSH configuration." # prepare SSH key pair mkdir -p ${SSH_HOME} || bashio::log.error "Failed to create .ssh directory!" - if bashio::var.has_value "${REMOTE_KEY}"; then + if bashio::config.has_value "remote_key"; then ( - cp "/ssl/${REMOTE_KEY}" "${SSH_HOME}/id_rsa" + cp "/ssl/$(bashio::config 'remote_key')" "${SSH_HOME}/id_rsa" ssh-keygen -y -f ${SSH_HOME}/id_rsa > ${SSH_HOME}/id_rsa.pub chmod 600 "${SSH_HOME}/id_rsa" chmod 644 "${SSH_HOME}/id_rsa.pub" @@ -104,8 +85,8 @@ function add-ssh-key { echo " HostName ${REMOTE_HOST}" echo " User ${REMOTE_USER}" echo " Port ${REMOTE_PORT}" - if bashio::var.has_value "${REMOTE_HOST_KEY_ALGORITHMS}"; then - echo " HostKeyAlgorithms ${REMOTE_HOST_KEY_ALGORITHMS}" + if bashio::config.has_value "remote_host_key_algorithms"; then + echo " HostKeyAlgorithms $(bashio::config 'remote_host_key_algorithms')" fi ) > "${SSH_HOME}/config" chmod 600 "${SSH_HOME}/config" || bashio::log.error "Failed to set SSH configuration file permissions!" @@ -114,35 +95,36 @@ function add-ssh-key { # call Home Assistant to create a local backup # function fails in case local backup is not created function create-local-backup { - local base_folders="addons/local homeassistant media share ssl" - local installed_addons=$(bashio::supervisor.addons) - local data="{\"name\":\"${BACKUP_NAME}\", \"password\": \"${BACKUP_PASSWORD}\"}" + local -r backup_exclude_folders=$(bashio::config "backup_exclude_folders") + local -r backup_exclude_addons=$(bashio::config "backup_exclude_addons") + local -r base_folders="addons/local homeassistant media share ssl" + local data="{\"name\":\"${BACKUP_NAME}\", \"password\": \"$(bashio::config 'backup_password' '')\"}" - if bashio::var.has_value "${BACKUP_EXCLUDE_ADDONS}" || bashio::var.has_value "${BACKUP_EXCLUDE_FOLDERS}"; then + if bashio::var.has_value "${backup_exclude_addons}" || bashio::var.has_value "${backup_exclude_folders}"; then bashio::log.info "Creating partial backup: \"${BACKUP_NAME}\"" local unformatted_folders="${base_folders}" - local unformatted_addons="${installed_addons}" + local unformatted_addons=$(bashio::supervisor.addons) - if bashio::var.has_value "${BACKUP_EXCLUDE_FOLDERS}"; then - bashio::log.notice "Excluded folder(s):\n${BACKUP_EXCLUDE_FOLDERS}" - for folder in ${BACKUP_EXCLUDE_FOLDERS} ; do + if bashio::var.has_value "${backup_exclude_folders}"; then + bashio::log.notice "Excluded folder(s):\n${backup_exclude_folders}" + for folder in ${backup_exclude_folders} ; do unformatted_folders=$(echo "${unformatted_folders}" | sed -e "s/${folder}//g") done fi - if bashio::var.has_value "${BACKUP_EXCLUDE_ADDONS}"; then - bashio::log.notice "Excluded addon(s):\n${BACKUP_EXCLUDE_ADDONS}" - for addon in ${BACKUP_EXCLUDE_ADDONS} ; do + if bashio::var.has_value "${backup_exclude_addons}"; then + bashio::log.notice "Excluded addon(s):\n${backup_exclude_addons}" + for addon in ${backup_exclude_addons} ; do unformatted_addons="$(echo "${unformatted_addons}" | sed -e "s/${addon}//g")" done fi - local addons=$(echo ${unformatted_addons} | sed "s/ /\", \"/g") - local folders=$(echo "${unformatted_folders}" | sed "s/ /\", \"/g" | sed "s/, \"\"//g") + local -r addons=$(echo ${unformatted_addons} | sed "s/ /\", \"/g") + local -r folders=$(echo "${unformatted_folders}" | sed "s/ /\", \"/g" | sed "s/, \"\"//g") bashio::log.debug "Including folder(s) \"${folders}\"" bashio::log.debug "Including addon(s) \"${addons}\"" - local data="$(echo $data | tr -d '}'), \"addons\": [\"${addons}\"], \"folders\": [\"${folders}\"]}" # append addon and folder set + data="$(echo $data | tr -d '}'), \"addons\": [\"${addons}\"], \"folders\": [\"${folders}\"]}" # append addon and folder set if ! SLUG=$(bashio::api.supervisor POST /backups/new/partial "${data}" .slug); then bashio::log.fatal "Error creating partial backup!" return "${__BASHIO_EXIT_NOK}" @@ -162,19 +144,20 @@ function create-local-backup { } function copy-backup-to-remote { - if bashio::var.false "${SSH_ENABLED}"; then + if ! bashio::config.true "ssh_enabled"; then bashio::log.debug "SCP disabled." return "${__BASHIO_EXIT_OK}" fi + local -r remote_directory=$(bashio::config "ssh_remote_directory" "") local remote_name=$SLUG - if bashio::var.true "${BACKUP_FRIENDLY_NAME}"; then + if bashio::config.true "backup_friendly_name"; then remote_name=$BACKUP_NAME fi bashio::log.info "Copying backup using SCP." - if ! scp -F "${SSH_HOME}/config" "/backup/${SLUG}.tar" remote:"'${SSH_REMOTE_DIRECTORY}/${remote_name}.tar'"; then - bashio::log.error "Error copying backup ${SLUG}.tar to ${SSH_REMOTE_DIRECTORY} on ${REMOTE_HOST}." + if ! scp -F "${SSH_HOME}/config" "/backup/${SLUG}.tar" remote:"'${remote_directory}/${remote_name}.tar'"; then + bashio::log.error "Error copying backup ${SLUG}.tar to ${remote_directory} on ${REMOTE_HOST}." return "${__BASHIO_EXIT_NOK}" fi @@ -182,23 +165,24 @@ function copy-backup-to-remote { } function rsync-folders { - if bashio::var.false "${RSYNC_ENABLED}"; then + if ! bashio::config.true "rsync_enabled"; then bashio::log.debug "Rsync disabled." return "${__BASHIO_EXIT_OK}" fi - local folders="/config /addons /backup /share /ssl" # put directories without trailing slash - local rsync_url="${REMOTE_USER}@${REMOTE_HOST}:${RSYNC_ROOTFOLDER}" + local -r folders="/config /addons /backup /share /ssl" # put directories without trailing slash + local -r rsync_url="${REMOTE_USER}@${REMOTE_HOST}:$(bashio::config 'rsync_rootfolder')" local flags='-a -r' bashio::log.info "Copying backup using rsync." - if bashio::var.true "${DEBUG}"; then - local flags="${flags} -v" + if bashio::config.true "debug"; then + flags="${flags} -v" fi - echo "${RSYNC_EXCLUDE}" > /tmp/rsync_exclude.txt - if bashio::var.has_value "${RSYNC_EXCLUDE}"; then - bashio::log.notice "Excluded rsync file patterns:\n${RSYNC_EXCLUDE}" + local -r rsync_exclude=$(bashio::config "rsync_exclude" "") + echo "${rsync_exclude}" > /tmp/rsync_exclude.txt + if bashio::var.has_value "rsync_exclude"; then + bashio::log.notice "Excluded rsync file patterns:\n${rsync_exclude}" fi bashio::log.debug "Syncing ${folders}" @@ -211,41 +195,42 @@ function rsync-folders { } function rclone-backups { - if bashio::var.false "${RCLONE_ENABLED}"; then + if ! bashio::config.true "rclone_enabled"; then bashio::log.debug "Rclone disabled." return "${__BASHIO_EXIT_OK}" fi + local -r remote_directory=$(bashio::config "rclone_remote_directory" "") ( cd /backup/ mkdir -p ~/.config/rclone/ cp -a /ssl/rclone.conf ~/.config/rclone/rclone.conf ) || bashio::log.error "Failed to prepare rclone configuration!" - if bashio::var.true "${RCLONE_COPY}"; then + if bashio::config.true "rclone_copy"; then local remote_name=$SLUG - if bashio::var.true "${BACKUP_FRIENDLY_NAME}"; then + if bashio::config.true "backup_friendly_name"; then remote_name=$BACKUP_NAME fi bashio::log.info "Copying backup using rclone." - if ! rclone copyto "${SLUG}.tar" "${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY}/${remote_name}.tar"; then - bashio::log.error "Error rclone ${SLUG}.tar to ${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY}/${remote_name}.tar!" + if ! rclone copyto "${SLUG}.tar" "${REMOTE_HOST}:${remote_directory}/${remote_name}.tar"; then + bashio::log.error "Error rclone ${SLUG}.tar to ${REMOTE_HOST}:${remote_directory}/${remote_name}.tar!" return "${__BASHIO_EXIT_NOK}" fi fi - if bashio::var.true "${RCLONE_SYNC}"; then + if bashio::config.true "rclone_sync"; then bashio::log.info "Syncing backups using rclone" - if ! rclone sync . "${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY}"; then + if ! rclone sync . "${REMOTE_HOST}:${remote_directory}"; then bashio::log.error "Error syncing backups by rclone!" return "${__BASHIO_EXIT_NOK}" fi fi - if bashio::var.true "${RCLONE_RESTORE}"; then + if bashio::config.true "rclone_restore"; then local restore_name="restore-$(date +%F)" mkdir -p "${restore_name}" bashio::log.info "Restoring backups to ${restore_name} using rclone" - if ! rclone copyto "${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY} ${restore_name}/"; then - bashio::log.error "Error restoring backups from ${REMOTE_HOST}:${RCLONE_REMOTE_DIRECTORY}!" + if ! rclone copyto "${REMOTE_HOST}:${remote_directory} ${restore_name}/"; then + bashio::log.error "Error restoring backups from ${REMOTE_HOST}:${remote_directory}!" return "${__BASHIO_EXIT_NOK}" fi fi @@ -263,7 +248,7 @@ function clone-to-remote { } function delete-local-backup { - if bashio::var.equals "${BACKUP_KEEP_LOCAL}" "all"; then + if bashio::config.equals "backup_keep_local" "all"; then bashio::log.debug "Keep all backups." return "${__BASHIO_EXIT_OK}" fi @@ -272,7 +257,7 @@ function delete-local-backup { bashio::log.warning "Failed to reload backups!" fi - if bashio::var.is_empty "${BACKUP_KEEP_LOCAL}"; then + if bashio::config.is_empty "backup_keep_local"; then if bashio::var.has_value "$SLUG"; then bashio::log.notice "Deleting local backup: ${SLUG}" if ! bashio::api.supervisor DELETE /backups/${SLUG}; then @@ -284,9 +269,9 @@ function delete-local-backup { fi else local ret="${__BASHIO_EXIT_OK}" - local backup_list=$(bashio::api.supervisor GET /backups) - local last_date_to_keep=$(echo "${backup_list}" | jq ".backups[].date" | sort -r | \ - head -n "${BACKUP_KEEP_LOCAL}" | tail -n 1 | xargs date -D "%Y-%m-%dT%T" +%s --date ) + local -r backup_list=$(bashio::api.supervisor GET /backups) + local -r last_date_to_keep=$(echo "${backup_list}" | jq ".backups[].date" | sort -r | \ + head -n $(bashio::config "backup_keep_local") | tail -n 1 | xargs date -D "%Y-%m-%dT%T" +%s --date ) echo "${backup_list}" | jq -c ".backups[]" | while read -r backup; do if [[ $(echo "${backup}" | jq ".date" | xargs date -D "%Y-%m-%dT%T" +%s --date ) -lt ${last_date_to_keep} ]]; then From c03d6fabb4422845fbb281b4f0c009ac58cfb248 Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Wed, 24 Aug 2022 08:41:48 +0200 Subject: [PATCH 25/40] removed unnecessary commands from image - hass.io cli removed due to use of API - zip not required, since backup encryption done via API - wget removed since hass.io cli not installed --- remote-backup/Dockerfile | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/remote-backup/Dockerfile b/remote-backup/Dockerfile index 40261ab..4e6ee20 100644 --- a/remote-backup/Dockerfile +++ b/remote-backup/Dockerfile @@ -7,19 +7,12 @@ SHELL ["/bin/bash", "-o", "pipefail", "-c"] ENV LANG C.UTF-8 # Setup base -RUN apk add --no-cache jq openssh-client zip sshpass rsync wget curl unzip +RUN apk add --no-cache jq openssh-client sshpass rsync curl unzip # Rclone CLI ARG BUILD_ARCH RUN curl https://rclone.org/install.sh | bash - -# Hass.io CLI -ARG BUILD_ARCH -ARG CLI_VERSION -RUN wget -O /usr/bin/ha "https://github.com/home-assistant/cli/releases/download/4.18.0/ha_${BUILD_ARCH}" \ - && chmod a+x /usr/bin/ha - # Copy data COPY run.sh / RUN chmod a+x run.sh @@ -33,12 +26,12 @@ ARG BUILD_VERSION # Labels LABEL \ io.hass.name="Remote Backup" \ - io.hass.description="Automatically create and backup HA snapshots using SCP" \ + io.hass.description="Automatically create and backup HA backups using SFTP (SCP), rsync" \ io.hass.arch="${BUILD_ARCH}" \ io.hass.type="addon" \ io.hass.version=${BUILD_VERSION} \ maintainer="Matheson Steplock " \ - org.label-schema.description="Automatically create and backup HA snapshots using SCP" \ + org.label-schema.description="Automatically create and backup HA backups using SFTP (SCP), rsync" \ org.label-schema.build-date=${BUILD_DATE} \ org.label-schema.name="Remote Backup" \ org.label-schema.schema-version="1.0" \ From 5d005142dfe3bd76844fa1d53a80f9c68cdc9196 Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Wed, 24 Aug 2022 16:54:46 +0200 Subject: [PATCH 26/40] minimized local documentation, fixed typos --- remote-backup/DOCS.md | 31 ++----------------------------- remote-backup/Dockerfile | 4 ++-- remote-backup/config.yaml | 2 +- 3 files changed, 5 insertions(+), 32 deletions(-) diff --git a/remote-backup/DOCS.md b/remote-backup/DOCS.md index 952bb4a..3423124 100644 --- a/remote-backup/DOCS.md +++ b/remote-backup/DOCS.md @@ -3,36 +3,9 @@ Please visit the documentation at [addons.mathesonsteplock.ca](https://addons.mathesonsteplock.ca/docs/addons/remote-backup/basic-config) ## Security -For SSH and rsync operation it is recommend to add the public key of the remote host to the file `/ssl/known_hosts`. If you see a warning `Missing known_hosts file!` then you have not done so and the addon automatically does it for you each time it is called. **Note that this is a security risk** which can be fixed by executing `ssh-keyscan -t rsa >> /ssl/known_hosts` from a terminal, e.g. [SSH & Web Terminal](https://github.com/hassio-addons/addon-ssh). -## Persistent Notification +For SSH and rsync operation it is recommend to add the public key of the remote host to the file `/ssl/known_hosts`. If you see a warning `Missing known_hosts file!` then you have not done so and the add-on automatically does it for you each time it is called. +**Note that this is a security risk** which can be fixed by executing `ssh-keyscan -t rsa >> /ssl/known_hosts` from a terminal, e.g. [SSH & Web Terminal](https://github.com/hassio-addons/addon-ssh). -In case of an error, a persistent notification with the error message is created. Please see the logs to find out what happend (you might also want to enable debugging in the configuration). - -## Using Events - -The add-on creates an event each time it is has been executed. - -| Field | Description | -| ------------ | -------------------------------------------- | -| `event_type` | `remote_backup_status` | -| `result` | Backup result status, can be `ok` or `error` | -| `message` | Human readable message for the notification | - -Here is an example automation on how to use it: -
-alias: Backup check
-description: This automation creates an persistent notification in case the backup fails.
-trigger:
-  - platform: event
-    event_type: remote_backup_status
-    event_data:
-      result: error
-action:
-  - service: persistent_notification.create
-    data:
-      message: Backup failed
-mode: single
-
# Support Me [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/mathesonsteplock) diff --git a/remote-backup/Dockerfile b/remote-backup/Dockerfile index 4e6ee20..d45569b 100644 --- a/remote-backup/Dockerfile +++ b/remote-backup/Dockerfile @@ -26,12 +26,12 @@ ARG BUILD_VERSION # Labels LABEL \ io.hass.name="Remote Backup" \ - io.hass.description="Automatically create and backup HA backups using SFTP (SCP), rsync" \ + io.hass.description="Automatically create and transfer HA backups using SFTP (SCP), rsync." \ io.hass.arch="${BUILD_ARCH}" \ io.hass.type="addon" \ io.hass.version=${BUILD_VERSION} \ maintainer="Matheson Steplock " \ - org.label-schema.description="Automatically create and backup HA backups using SFTP (SCP), rsync" \ + org.label-schema.description="Automatically create and transfer HA backups using SFTP (SCP), rsync." \ org.label-schema.build-date=${BUILD_DATE} \ org.label-schema.name="Remote Backup" \ org.label-schema.schema-version="1.0" \ diff --git a/remote-backup/config.yaml b/remote-backup/config.yaml index 2a8015d..91b4570 100644 --- a/remote-backup/config.yaml +++ b/remote-backup/config.yaml @@ -1,7 +1,7 @@ name: Remote Backup version: "2022.7.2" slug: remote_backup -description: Automatically create and backup HA backups using SFTP (SCP), rsync, or rclone (experimental) +description: Automatically create and transfer HA backups using SFTP (SCP), rsync, or rclone (experimental) image: ikifar/remote-backup-{arch} url: https://addons.mathesonsteplock.ca/docs/addons/remote-backup/basic-config codenotary: cas@mathesonsteplock.ca From d691099d50d19e8448d71ed614e0257e3247cb48 Mon Sep 17 00:00:00 2001 From: Matheson Steplock Date: Mon, 29 Aug 2022 22:15:29 -0400 Subject: [PATCH 27/40] Bump base image, version and update changelog --- remote-backup/CHANGELOG.md | 16 ++++++++++++++++ remote-backup/build.yaml | 10 +++++----- remote-backup/config.yaml | 2 +- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/remote-backup/CHANGELOG.md b/remote-backup/CHANGELOG.md index 16a1f87..c093146 100644 --- a/remote-backup/CHANGELOG.md +++ b/remote-backup/CHANGELOG.md @@ -1,3 +1,19 @@ +# 2022.8.0 + +- enable rsync key-based authentication #51 +- changed logging to bashio logger #52 +- Rsync cleanup #54 +- Configuration documentation #56 +- Replace zip password with built in backup password #57 +- renamed and resorted configuration #58 +- Improve error handling #59 +- Security enhancements #60 +- Bump base image to 12.2.3 + +Special thanks to [@patman15](https://github.com/patman15) for all of his work this release! + +**Full Changelog**: https://github.com/ikifar2012/remote-backup-addon/compare/2022.7.2...2022.8.0 + # 2022.7.2 - Add init to config.yml to solve `s6-overlay-suexec: fatal: can only run as pid 1` diff --git a/remote-backup/build.yaml b/remote-backup/build.yaml index 89faee9..20551d5 100644 --- a/remote-backup/build.yaml +++ b/remote-backup/build.yaml @@ -1,10 +1,10 @@ squash: false build_from: - aarch64: ghcr.io/hassio-addons/base/aarch64:12.2.1 - amd64: ghcr.io/hassio-addons/base/amd64:12.2.1 - armhf: ghcr.io/hassio-addons/base/armhf:12.2.1 - armv7: ghcr.io/hassio-addons/base/armv7:12.2.1 - i386: ghcr.io/hassio-addons/base/i386:12.2.1 + aarch64: ghcr.io/hassio-addons/base/aarch64:12.2.3 + amd64: ghcr.io/hassio-addons/base/amd64:12.2.3 + armhf: ghcr.io/hassio-addons/base/armhf:12.2.3 + armv7: ghcr.io/hassio-addons/base/armv7:12.2.3 + i386: ghcr.io/hassio-addons/base/i386:12.2.3 codenotary: signer: cas@mathesonsteplock.ca base_image: codenotary@frenck.dev diff --git a/remote-backup/config.yaml b/remote-backup/config.yaml index 91b4570..b8b23f5 100644 --- a/remote-backup/config.yaml +++ b/remote-backup/config.yaml @@ -1,5 +1,5 @@ name: Remote Backup -version: "2022.7.2" +version: "2022.8.0" slug: remote_backup description: Automatically create and transfer HA backups using SFTP (SCP), rsync, or rclone (experimental) image: ikifar/remote-backup-{arch} From 6eca7679bb33874aa2a7941a112994454b7150ad Mon Sep 17 00:00:00 2001 From: Matheson Steplock Date: Mon, 29 Aug 2022 22:17:40 -0400 Subject: [PATCH 28/40] cleanup spaces --- remote-backup/apparmor.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/remote-backup/apparmor.txt b/remote-backup/apparmor.txt index b940eb3..38ab13d 100644 --- a/remote-backup/apparmor.txt +++ b/remote-backup/apparmor.txt @@ -12,7 +12,7 @@ profile ADDON_SLUG flags=(attach_disconnected,mediate_deleted) { /bin/** ix, /usr/bin/rsync ix, /usr/bin/scp ix, - /usr/bin/rclone ix, + /usr/bin/rclone ix, /run/{s6,s6-rc*,service}/** ix, /package/** ix, /command/** ix, From 5b4662101765fa4a2c2b23439112d463171094a9 Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Tue, 30 Aug 2022 11:15:16 +0200 Subject: [PATCH 29/40] fixed issue with slashes in excluded folder names --- remote-backup/run.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/remote-backup/run.sh b/remote-backup/run.sh index c1b87d5..7f5e032 100755 --- a/remote-backup/run.sh +++ b/remote-backup/run.sh @@ -109,22 +109,22 @@ function create-local-backup { if bashio::var.has_value "${backup_exclude_folders}"; then bashio::log.notice "Excluded folder(s):\n${backup_exclude_folders}" for folder in ${backup_exclude_folders} ; do - unformatted_folders=$(echo "${unformatted_folders}" | sed -e "s/${folder}//g") + unformatted_folders="${unformatted_folders[@]/$folder}" done fi if bashio::var.has_value "${backup_exclude_addons}"; then bashio::log.notice "Excluded addon(s):\n${backup_exclude_addons}" for addon in ${backup_exclude_addons} ; do - unformatted_addons="$(echo "${unformatted_addons}" | sed -e "s/${addon}//g")" + unformatted_addons="${unformatted_addons[@]/$addon}" done fi - local -r addons=$(echo ${unformatted_addons} | sed "s/ /\", \"/g") - local -r folders=$(echo "${unformatted_folders}" | sed "s/ /\", \"/g" | sed "s/, \"\"//g") - bashio::log.debug "Including folder(s) \"${folders}\"" - bashio::log.debug "Including addon(s) \"${addons}\"" + local -r addons=$(jq -nc '$ARGS.positional' --args ${unformatted_addons[@]}) + local -r folders=$(jq -nc '$ARGS.positional' --args ${unformatted_folders[@]}) + bashio::log.debug "Including folder(s) ${folders}" + bashio::log.debug "Including addon(s) ${addons}" - data="$(echo $data | tr -d '}'), \"addons\": [\"${addons}\"], \"folders\": [\"${folders}\"]}" # append addon and folder set + data="$(echo $data | tr -d '}'), \"addons\": ${addons}, \"folders\": ${folders}}" # append addon and folder set if ! SLUG=$(bashio::api.supervisor POST /backups/new/partial "${data}" .slug); then bashio::log.fatal "Error creating partial backup!" return "${__BASHIO_EXIT_NOK}" From 20a88d7d044e95e15e5e9783b61f8d8045df680d Mon Sep 17 00:00:00 2001 From: patman15 <14628713+patman15@users.noreply.github.com> Date: Thu, 1 Sep 2022 11:56:02 +0200 Subject: [PATCH 30/40] added SFTP/SCP fallback and password auth --- remote-backup/run.sh | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/remote-backup/run.sh b/remote-backup/run.sh index 7f5e032..d18850e 100755 --- a/remote-backup/run.sh +++ b/remote-backup/run.sh @@ -145,7 +145,7 @@ function create-local-backup { function copy-backup-to-remote { if ! bashio::config.true "ssh_enabled"; then - bashio::log.debug "SCP disabled." + bashio::log.debug "SFTP/SCP disabled." return "${__BASHIO_EXIT_OK}" fi @@ -155,10 +155,13 @@ function copy-backup-to-remote { remote_name=$BACKUP_NAME fi - bashio::log.info "Copying backup using SCP." - if ! scp -F "${SSH_HOME}/config" "/backup/${SLUG}.tar" remote:"'${remote_directory}/${remote_name}.tar'"; then - bashio::log.error "Error copying backup ${SLUG}.tar to ${remote_directory} on ${REMOTE_HOST}." - return "${__BASHIO_EXIT_NOK}" + bashio::log.info "Copying backup using SFTP/SCP." + if ! sshpass -p "${REMOTE_PASSWORD}" scp -s -F "${SSH_HOME}/config" "/backup/${SLUG}.tar" remote:"${remote_directory}/${remote_name}.tar"; then + bashio::log.warning "SFTP transfer failed, falling back to SCP." + if ! sshpass -p "${REMOTE_PASSWORD}" scp -O -F "${SSH_HOME}/config" "/backup/${SLUG}.tar" remote:"\"${remote_directory}/${remote_name}.tar\""; then + bashio::log.error "Error copying backup ${SLUG}.tar to ${remote_directory} on ${REMOTE_HOST}." + return "${__BASHIO_EXIT_NOK}" + fi fi return "${__BASHIO_EXIT_OK}" From 82fe174e61081ade69908b567ca31d9eb0c38e6b Mon Sep 17 00:00:00 2001 From: Matheson Steplock Date: Fri, 2 Sep 2022 16:53:07 -0400 Subject: [PATCH 31/40] Bump version number --- remote-backup/config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/remote-backup/config.yaml b/remote-backup/config.yaml index b8b23f5..84241d9 100644 --- a/remote-backup/config.yaml +++ b/remote-backup/config.yaml @@ -1,5 +1,5 @@ name: Remote Backup -version: "2022.8.0" +version: "2022.9.0" slug: remote_backup description: Automatically create and transfer HA backups using SFTP (SCP), rsync, or rclone (experimental) image: ikifar/remote-backup-{arch} From 95b609e315ec09258e9574c707fe6ab532b28d9f Mon Sep 17 00:00:00 2001 From: Matheson Steplock Date: Fri, 2 Sep 2022 16:54:07 -0400 Subject: [PATCH 32/40] Update changelog --- remote-backup/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/remote-backup/CHANGELOG.md b/remote-backup/CHANGELOG.md index c093146..f41284f 100644 --- a/remote-backup/CHANGELOG.md +++ b/remote-backup/CHANGELOG.md @@ -9,6 +9,7 @@ - Improve error handling #59 - Security enhancements #60 - Bump base image to 12.2.3 +- added SFTP/SCP fallback and password auth #64 Special thanks to [@patman15](https://github.com/patman15) for all of his work this release! From 22c1be97733bf3375cff4db436acee0aec1b2207 Mon Sep 17 00:00:00 2001 From: Matheson Steplock Date: Fri, 2 Sep 2022 16:55:26 -0400 Subject: [PATCH 33/40] Bump version number --- remote-backup/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/remote-backup/CHANGELOG.md b/remote-backup/CHANGELOG.md index f41284f..a04245f 100644 --- a/remote-backup/CHANGELOG.md +++ b/remote-backup/CHANGELOG.md @@ -1,4 +1,4 @@ -# 2022.8.0 +# 2022.9.0 - enable rsync key-based authentication #51 - changed logging to bashio logger #52 From 99a752154c6a9b1683519822b790928cb3179330 Mon Sep 17 00:00:00 2001 From: Matheson Steplock Date: Fri, 2 Sep 2022 16:55:53 -0400 Subject: [PATCH 34/40] change wording --- remote-backup/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/remote-backup/CHANGELOG.md b/remote-backup/CHANGELOG.md index a04245f..2345d04 100644 --- a/remote-backup/CHANGELOG.md +++ b/remote-backup/CHANGELOG.md @@ -11,7 +11,7 @@ - Bump base image to 12.2.3 - added SFTP/SCP fallback and password auth #64 -Special thanks to [@patman15](https://github.com/patman15) for all of his work this release! +Huge thanks to [@patman15](https://github.com/patman15) for all of his work this release! **Full Changelog**: https://github.com/ikifar2012/remote-backup-addon/compare/2022.7.2...2022.8.0 From b2bfabb1e78f89d930684a5e44b9bbe45ebd83ef Mon Sep 17 00:00:00 2001 From: Matheson Steplock Date: Fri, 2 Sep 2022 16:56:20 -0400 Subject: [PATCH 35/40] Bump version number --- remote-backup/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/remote-backup/CHANGELOG.md b/remote-backup/CHANGELOG.md index 2345d04..5de1475 100644 --- a/remote-backup/CHANGELOG.md +++ b/remote-backup/CHANGELOG.md @@ -13,7 +13,7 @@ Huge thanks to [@patman15](https://github.com/patman15) for all of his work this release! -**Full Changelog**: https://github.com/ikifar2012/remote-backup-addon/compare/2022.7.2...2022.8.0 +**Full Changelog**: https://github.com/ikifar2012/remote-backup-addon/compare/2022.7.2...2022.9.0 # 2022.7.2 From b133058b21d8c02b8a27ef8ee968a08deace7720 Mon Sep 17 00:00:00 2001 From: Matheson Steplock Date: Fri, 2 Sep 2022 21:50:55 -0400 Subject: [PATCH 36/40] fix rclone as broken by #58 --- remote-backup/run.sh | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/remote-backup/run.sh b/remote-backup/run.sh index d18850e..e6446ed 100755 --- a/remote-backup/run.sh +++ b/remote-backup/run.sh @@ -204,6 +204,7 @@ function rclone-backups { fi local -r remote_directory=$(bashio::config "rclone_remote_directory" "") + local -r rclone_remote_host=$(bashio::config "rclone_remote_host" "") ( cd /backup/ mkdir -p ~/.config/rclone/ @@ -216,14 +217,14 @@ function rclone-backups { remote_name=$BACKUP_NAME fi bashio::log.info "Copying backup using rclone." - if ! rclone copyto "${SLUG}.tar" "${REMOTE_HOST}:${remote_directory}/${remote_name}.tar"; then - bashio::log.error "Error rclone ${SLUG}.tar to ${REMOTE_HOST}:${remote_directory}/${remote_name}.tar!" + if ! rclone copyto "/backup/${SLUG}.tar" "${rclone_remote_host}:${remote_directory}/${remote_name}.tar"; then + bashio::log.error "Error rclone ${SLUG}.tar to ${rclone_remote_host}:${remote_directory}/${remote_name}.tar!" return "${__BASHIO_EXIT_NOK}" fi fi if bashio::config.true "rclone_sync"; then bashio::log.info "Syncing backups using rclone" - if ! rclone sync . "${REMOTE_HOST}:${remote_directory}"; then + if ! rclone sync . "${rclone_remote_host}:${remote_directory}"; then bashio::log.error "Error syncing backups by rclone!" return "${__BASHIO_EXIT_NOK}" fi @@ -232,8 +233,8 @@ function rclone-backups { local restore_name="restore-$(date +%F)" mkdir -p "${restore_name}" bashio::log.info "Restoring backups to ${restore_name} using rclone" - if ! rclone copyto "${REMOTE_HOST}:${remote_directory} ${restore_name}/"; then - bashio::log.error "Error restoring backups from ${REMOTE_HOST}:${remote_directory}!" + if ! rclone copyto "${rclone_remote_host}:${remote_directory} ${restore_name}/"; then + bashio::log.error "Error restoring backups from ${rclone_remote_host}:${remote_directory}!" return "${__BASHIO_EXIT_NOK}" fi fi From 3eb51c2123214eaefaba778042d14dce8f0da252 Mon Sep 17 00:00:00 2001 From: Matheson Steplock Date: Sat, 3 Sep 2022 00:06:48 -0400 Subject: [PATCH 37/40] fix paths --- remote-backup/run.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/remote-backup/run.sh b/remote-backup/run.sh index e6446ed..6cc7a33 100755 --- a/remote-backup/run.sh +++ b/remote-backup/run.sh @@ -224,7 +224,7 @@ function rclone-backups { fi if bashio::config.true "rclone_sync"; then bashio::log.info "Syncing backups using rclone" - if ! rclone sync . "${rclone_remote_host}:${remote_directory}"; then + if ! rclone sync "/backup" "${rclone_remote_host}:${remote_directory}"; then bashio::log.error "Error syncing backups by rclone!" return "${__BASHIO_EXIT_NOK}" fi @@ -233,7 +233,7 @@ function rclone-backups { local restore_name="restore-$(date +%F)" mkdir -p "${restore_name}" bashio::log.info "Restoring backups to ${restore_name} using rclone" - if ! rclone copyto "${rclone_remote_host}:${remote_directory} ${restore_name}/"; then + if ! rclone copyto "${rclone_remote_host}:${remote_directory} /backup/${restore_name}/"; then bashio::log.error "Error restoring backups from ${rclone_remote_host}:${remote_directory}!" return "${__BASHIO_EXIT_NOK}" fi From aa6415bc4140aaf024e24c8d0bd0fd1dc6d1acbd Mon Sep 17 00:00:00 2001 From: Matheson Steplock Date: Sat, 3 Sep 2022 00:29:31 -0400 Subject: [PATCH 38/40] Add breaking changes warning --- remote-backup/CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/remote-backup/CHANGELOG.md b/remote-backup/CHANGELOG.md index 5de1475..81e5869 100644 --- a/remote-backup/CHANGELOG.md +++ b/remote-backup/CHANGELOG.md @@ -1,5 +1,15 @@ # 2022.9.0 +## Please read before upgrading + +**This release includes a ton of breaking changes** +**Please read the documentation carefully before upgrading** +**Be aware that some of the configuration options have been renamed and may overwrite your current settings** + +Please backup your configuration before upgrading by clicking the vertical dots in the top right corner of the add-on configuration page +and click "Edit in YAML", you can then copy that to a text file and map those settings to their new config options as per the +[documentation](https://addons.mathesonsteplock.ca/docs/addons/remote-backup/basic-config). + - enable rsync key-based authentication #51 - changed logging to bashio logger #52 - Rsync cleanup #54 From 54a8612f8ae5b9fda92b2664466eb01ac4e0f8ac Mon Sep 17 00:00:00 2001 From: Matheson Steplock Date: Sat, 3 Sep 2022 00:30:55 -0400 Subject: [PATCH 39/40] Add (hopefully) final PR --- remote-backup/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/remote-backup/CHANGELOG.md b/remote-backup/CHANGELOG.md index 81e5869..46c3e3d 100644 --- a/remote-backup/CHANGELOG.md +++ b/remote-backup/CHANGELOG.md @@ -20,6 +20,7 @@ and click "Edit in YAML", you can then copy that to a text file and map those se - Security enhancements #60 - Bump base image to 12.2.3 - added SFTP/SCP fallback and password auth #64 +- Restore rclone config option #66 Huge thanks to [@patman15](https://github.com/patman15) for all of his work this release! From 9a0432e05daff8707b4b4fadc82d5bd1c7a414e3 Mon Sep 17 00:00:00 2001 From: Matheson Steplock Date: Sat, 3 Sep 2022 01:02:05 -0400 Subject: [PATCH 40/40] Add rclone remote host --- remote-backup/config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/remote-backup/config.yaml b/remote-backup/config.yaml index 84241d9..1042848 100644 --- a/remote-backup/config.yaml +++ b/remote-backup/config.yaml @@ -66,6 +66,7 @@ schema: - match(^[A-Za-z0-9_\-\.\*\/\?\+\\ ]+$)? rclone_enabled: bool? rclone_remote_directory: str? + rclone_remote_host: str? rclone_copy: bool? rclone_sync: bool? rclone_restore: bool?