#!/bin/bash ##### ##### This file is part of Mail-in-a-Box-LDAP which is released under the ##### terms of the GNU Affero General Public License as published by the ##### Free Software Foundation, either version 3 of the License, or (at ##### your option) any later version. See file LICENSE or go to ##### https://github.com/downtownallday/mailinabox-ldap for full license ##### details. ##### usage() { echo "" echo "Restore a Mail-In-A-Box or Cloud-In-A-Box user-data directory from a local backup" echo "" echo "usage: $0 [path-to-restore-to]" echo " storage-user:" echo " the user account that owns the miab files. eg 'user-data'" echo " path-to-encrypted-dir:" echo " a directory containing a copy of duplicity files to restore. These were in" echo " /home/user-data/backup/encrypted on the system." echo "" echo " path-secret-key.txt:" echo " a copy of the encryption key file 'secret-key.txt' that was kept in" echo " /home/user-data/backup/secret-key.txt." echo "" echo " path-to-restore-to:" echo " the directory where the restored files are placed. the default location is" echo " /home/. FILES IN THIS DIRECTORY WILL BE REPLACED. IF THIS IS A MOUNT POINT ENTER A SUBDIRECTORY OF THE MOUNT POINT THEN MANUALLY MOVE THE FILES BACK ONE LEVEL BECAUSE DUPLICITY AUTOMATICALLY UNMOUNTS IT!" echo "" echo "If you're using encryption-at-rest, make sure it's mounted before restoring" echo "eg: run ehdd/mount.sh" echo "" exit 1 } if [ $# -lt 3 ]; then usage fi if [ $EUID -ne 0 ]; then echo "Must be run as root" 1>&2 exit 1 fi storage_user="$1" backup_files_dir="$(realpath "$2")" secret_key_file="$3" restore_to_dir="$(realpath "${4:-/home/$storage_user}")" PASSPHRASE="$(cat "$secret_key_file")" if [ $? -ne 0 ]; then echo "unable to access $secret_key_file" 1>&2 exit 1 fi export PASSPHRASE if [ ! -d "$backup_files_dir" ]; then echo "Does not exist or not a directory: $backup_files_dir" 1>&2 exit 1 fi echo "Shutting down services" ehdd/shutdown.sh --no-umount || exit 1 if [ ! -x /usr/bin/duplicity ]; then echo "Installing duplicity" tmpf=$(mktemp) apt-get install -y duplicity &> "$tmpf" if [ $? -ne 0 ]; then echo "Failed: " 1>&2 cat "$tmpf" 1>&2 rm -f "$tmpf" exit 1 fi rm -f "$tmpf" fi # Ensure users and groups are created so that duplicity properly # restores permissions. # # system_users format: # user:group:comment:homedir:shell # # if the user name and group of `system_users` are identical, the # group is created, otherwise the group must already exist (see # system_groups below, which are created before users) if [ -e "setup/ldap.sh" ]; then # Mail-In-A-Box system_users=( "openldap:openldap:OpenLDAP Server Account:/var/lib/ldap:/bin/false" "opendkim:opendkim::/run/opendkim:/usr/sbin/nologin" "spampd:spampd::/nonexistent:/usr/sbin/nologin" "www-data:www-data:www-data:/var/www:/usr/sbin/nologin" "postgrey:postgrey::/var/lib/postgrey:/usr/sbin/nologin" ) else # Cloud-In-A-Box system_users=( "mysql:mysql:MySQL Server:/nonexistent:/bin/false" "www-data:www-data:www-data:/var/www:/usr/sbin/nologin" ) fi system_groups=( "ssl-cert" ) # add system groups idx=0 while [ $idx -lt ${#system_groups[*]} ]; do group="${system_groups[$idx]}" groupadd -fr "$group" let idx+=1 done # add system users idx=0 while [ $idx -lt ${#system_users[*]} ]; do user=$(awk -F: '{print $1}' <<<"${system_users[$idx]}") group=$(awk -F: '{print $2}' <<<"${system_users[$idx]}") comment=$(awk -F: '{print $3}' <<<"${system_users[$idx]}") homedir=$(awk -F: '{print $4}' <<<"${system_users[$idx]}") shellpath=$(awk -F: '{print $5}' <<<"${system_users[$idx]}") if ! id "$user" >/dev/null 2>&1; then opts="-g $group" if [ "$group" = "$user" ]; then opts="-U" fi echo "Add user $user" useradd --shell "$shellpath" -r -M $opts -c "$comment" -d "$homedir" "$user" fi let idx+=1 done # add regular user STORAGE_USER if ! id "$storage_user" >/dev/null 2>&1; then # ensure the storage user exists echo "Add user $storage_user" useradd -m $storage_user chmod o+x /home/$storage_user fi echo "Restoring with duplicity" opts="" if [ -e "$restore_to_dir" ]; then opts="--force" fi duplicity restore $opts "file://$backup_files_dir" "$restore_to_dir" 2>&1 | ( code=0 while read line; do echo "$line" case "$line" in Error\ * ) code=1 ;; esac done; exit $code) codes="${PIPESTATUS[0]}${PIPESTATUS[1]}" [ "$codes" -ne "00" ] && exit 1 # # check that filesystem uid's/gid's mapped to actual users/groups # files_with_nouser="$(find "$restore_to_dir" -nouser)" files_with_nogroup="$(find "$restore_to_dir" -nogroup)" if [ "${files_with_nouser}${files_with_nogroup}" != "" ]; then echo "" echo "WARNING: some restored file/directory ownerships are unmatched" echo "They are:" (find "$restore_to_dir" -nouser; find "$restore_to_dir" -nogroup) | sort | uniq fi echo "" echo "Restore successful" echo "" exit 0