#!/bin/bash TITLE="Backup" DESCRIPTION="Back up your home folder daily to your Nextcloud files" CORE_CONF="$HOME/.config/coredesktop/core.conf" BACKUP_CONF="$HOME/.config/coredesktop/backup.conf" UNITS="$HOME/.config/systemd/user" source "$COREDESKTOP/configure.d/lib/nextcloud.sh" [ -f "$CORE_CONF" ] && source "$CORE_CONF" is_installed() { command -v restic > /dev/null && command -v rclone > /dev/null && command -v secret-tool > /dev/null } backups_on() { source "$COREDESKTOP/system/backup-env.sh" > /dev/null || return 1 [ -n "$RESTIC_PASSWORD_COMMAND" ] && systemctl --user is-enabled -q coredesktop-backup.timer } # Done when backing up to the main Core is_configured() { backups_on && [ "$BACKUP_WEBDAV" = "https://nextcloud.$CORE_DOMAIN/remote.php/dav/files/${CORE_EMAIL%@*}" ] } save_secret() { printf '%s' "$3" | secret-tool store --label "CoreDesktop backup $1 ($2)" \ coredesktop backup item "$1" url "$2" } # The backup gets its own app password, so revoking it stops only backups nextcloud_login() { local tries [ -n "$(backup_secret nextcloud "$WEBDAV")" ] \ && rclone lsf --max-depth 1 ":webdav:" > /dev/null 2>&1 && return 0 for tries in 1 2 3; do read -rsp "Your Core password: " PASSWORD echo nextcloud_app_password "CoreDesktop backup on $HOSTNAME" case $? in 0) break ;; 2) echo "Nextcloud says that password is wrong." ;; *) echo "Couldn't reach Nextcloud at $NEXTCLOUD."; return 1 ;; esac done unset PASSWORD [ -n "$APP_PASSWORD" ] || return 1 save_secret nextcloud "$WEBDAV" "$APP_PASSWORD" || return 1 unset APP_PASSWORD RCLONE_WEBDAV_PASS="$(backup_secret nextcloud "$WEBDAV" | rclone obscure -)" } new_passphrase() { local pass pass="$(head -c 24 /dev/urandom | base64)" echo "Your backup is encrypted with this passphrase:" echo echo " $pass" echo echo "Save it somewhere other than this computer, such as Bitwarden. Without it, the" echo "backup can't be read if this computer is lost. It's kept in your keyring so the" echo "daily backup can run." until confirm "Have you saved it?"; do :; done save_secret passphrase "$WEBDAV/$BACKUP_DIR" "$pass" } # Stop the daily backup and any running now, and remove its units stop_backups() { systemctl --user disable --now coredesktop-backup.timer 2> /dev/null systemctl --user stop coredesktop-backup.service 2> /dev/null rm -f "$UNITS"/coredesktop-backup.{service,timer} systemctl --user daemon-reload } # Delete the backup from Nextcloud, then what's on this computer for it: # without the backup, the passphrase and app password are no use delete_backups() { local app_password nextcloud_login || { echo "Couldn't sign in to Nextcloud."; return 1; } echo "Deleting $BACKUP_DIR from your Nextcloud files..." rclone purge ":webdav:$BACKUP_DIR" \ || { echo "rclone couldn't delete $BACKUP_DIR."; return 1; } echo "Deleted. Nextcloud keeps it in its trash bin, where it still uses space, until you empty it." app_password="$(backup_secret nextcloud "$WEBDAV")" [ -n "$app_password" ] && nextcloud_revoke_app_password "$app_password" secret-tool clear coredesktop backup item nextcloud url "$WEBDAV" secret-tool clear coredesktop backup item passphrase url "$WEBDAV/$BACKUP_DIR" rm -f "$BACKUP_CONF" } turn_off() { local answer WEBDAV="$BACKUP_WEBDAV" CORE_USER="$BACKUP_USER" NEXTCLOUD="${BACKUP_WEBDAV%%/remote.php/*}" stop_backups echo "Backups are off." echo echo "The backups made so far are still in $BACKUP_DIR in your Nextcloud files." if ! confirm "Delete them? They can't be restored from after that."; then echo "Kept them. To turn backups back on: configure backup" return 0 fi read -rp "To delete them, type the folder name ($BACKUP_DIR): " answer if [ "$answer" != "$BACKUP_DIR" ]; then echo "That's not the folder name, so the backups were kept." return 0 fi delete_backups } run_configure() { local pass choice if backups_on &> /dev/null; then if is_configured &> /dev/null; then echo "Your home folder is backed up daily to $BACKUP_DIR in your Nextcloud files." echo echo " 1) Set it up again" else echo "Your home folder is backed up daily to $BACKUP_DIR on ${BACKUP_WEBDAV%%/remote.php/*}," echo "which isn't your main Core." echo echo " 1) Back up to your main Core ($CORE_DOMAIN) instead; the backups so far stay where they are" fi echo " 2) Turn backups off" echo " q) Leave it as it is" echo read -rp "Choose: " choice case "$choice" in 1) ;; 2) turn_off; return ;; *) return 0 ;; esac fi if [ -z "$CORE_DOMAIN" ] || [ -z "$CORE_EMAIL" ]; then echo "Backups go to your Nextcloud files, so set up your Core first: configure core" return 1 fi CORE_USER="${CORE_EMAIL%@*}" NEXTCLOUD="https://nextcloud.$CORE_DOMAIN" WEBDAV="$NEXTCLOUD/remote.php/dav/files/$CORE_USER" BACKUP_DIR=".backup-$HOSTNAME" mkdir -p "$(dirname "$BACKUP_CONF")" { printf 'BACKUP_WEBDAV=%q\n' "$WEBDAV" printf 'BACKUP_USER=%q\n' "$CORE_USER" printf 'BACKUP_DIR=%q\n' "$BACKUP_DIR" } > "$BACKUP_CONF" source "$COREDESKTOP/system/backup-env.sh" nextcloud_login || { echo "Couldn't sign in to Nextcloud."; return 1; } if rclone lsf --files-only ":webdav:$BACKUP_DIR" 2> /dev/null | grep -qx config; then # Already there, e.g. after reinstalling: it needs the passphrase from then if [ -z "$RESTIC_PASSWORD_COMMAND" ] || ! restic cat config &> /dev/null; then echo "There's already a backup in $BACKUP_DIR in your Nextcloud files." read -rsp "Its passphrase: " pass echo RESTIC_PASSWORD_COMMAND= RESTIC_PASSWORD="$pass" restic cat config > /dev/null \ || { echo "restic couldn't open it with that passphrase."; return 1; } save_secret passphrase "$WEBDAV/$BACKUP_DIR" "$pass" || return 1 fi echo "Using the backup in $BACKUP_DIR." else new_passphrase || return 1 source "$COREDESKTOP/system/backup-env.sh" echo "Creating the backup in $BACKUP_DIR in your Nextcloud files..." restic init || return 1 fi mkdir -p "$UNITS" cp "$COREDESKTOP"/system/systemd/coredesktop-backup.{service,timer} "$UNITS/" || return 1 systemctl --user daemon-reload systemctl --user enable --now coredesktop-backup.timer || return 1 echo "Your home folder will be backed up daily to $BACKUP_DIR in your Nextcloud files." if confirm_yes "Start the first backup now? It runs in the background and can take hours."; then systemctl --user start --no-block coredesktop-backup.service echo "Started. To follow it: journalctl --user -fu coredesktop-backup" fi }