Backup
From wiki.welmers.net
2008-07-26
- There are some issues with fakeroot, due to bugs / limitations in this software. First of all, it uses inode numbers to identify files. So when you copy your backup data to another file system, all inode information will change and the fakeroot database information has become bogus. Same happens if you edit or delete files outside the fakeroot wrapper. Even when not doing this things, I ended up with corrupt database several times. I now use virtual servers / chroot solutions with direct root access on the backupserver instead of fakeroot.
- I fixed the script so you can specify another ssh port if you have to
In August 2007 I needed to develop a new backup system and scripts for my servers.
Actual purpose is to copy (parts of) a system to one user on a backup machine. Important requirements were:
- backup script will run on the machine with dat main data which need to be backed up (so not from the backup machine, to avoid vulnarability issues when access to the main machine is needed)
- preserve file owning/permissions
- all necessary backup activities on the backup machine should run under one user, so no root access or running root daemons on the backup machine is allowed
- we only want to waste data traffic for data changed since the previous backup
- incremental backups; daily for one week, 4 days a month for 1 months thereafter, one a month 4 months thereafter, and only one left a year
- only disk space in use for unique data, so for files changed since the previous backup, by using hardlinks to files which are not changed
Contents |
Required software
The software solutions choosen which match these requirements:
- rsync for creating backups on the backup machine, by checking the already present data on the backup machine and only transfer those which are new or changed. Files which are not changed are hardlinked from the previous backup.
- ssh with authorizing keys for access to the backup machine without passwords.
- fakeroot to preserve file owning when only have one-user access on the backup machine.
Procedure
- Create a backup group on the backup machine if you see fit:
backuphost# groupadd backup
- Create a backup user bumainhost on the backup machine:
backuphost# useradd -d /home/bumainhost -m -g backup -s /usr/pkg/bin/bash bumainhost
- Create a rsa public key for user bumainhost on the backup machine to be able to log on from the main machine without password:
backuphost# su - bumainhost bumainhost@backuphost$ ssh-keygen -t rsa Generating public/private rsa key pair. Enter file in which to save the key (/home/bumainhost/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/bumainhost/.ssh/id_rsa. Your public key has been saved in /home/bumainhost/.ssh/id_rsa.pub. bumainhost@backuphost$ exit
- Create a rsa public key under root on the main machine:
mainhost# ssh-keygen -t rsa Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /root/.ssh/id_rsa. Your public key has been saved in /root/.ssh/id_rsa.pub.
- Set each others public rsa key found in ~/.ssh/id_rsa.pub of the first host in ~/.ssh/authorized_keys of the other host
mainhost# echo "ssh-rsa AAAA[...]== bumainhost@backuphost.example.com" >> /root/.ssh/authorized_keys
bumainhost@backuphost$ echo "ssh-rsa AAAA[...]== root@mainhost.example.com" >> /home/bumainhost/.ssh/authorized_keys
Now you should be able to logon from root@mainhost to bumainhost@backuphost without password prompts etcetera:
mainhost# ssh backuphost.example.com -l bumainhost ....... bumainhost@backuphost$
- Give the user bumainhost on the backuphost a (secure!) password if you want to be able to log in to the machine besides via the rsa key of the mainhost:
backuphost# passwd bumainhost
- Create a seperate tree where the backup file will be stored if you see fit (useful for excluding if you want to backup the backuphost itself):
backuphost# mkdir /home/backup backuphost# chown root:backup /home/backup backuphost# chmod 750 /home/backup/mainhost backuphost# chown buremoteuser:bacup /home/backup/mainhost backuphost# chmod 700 /home/backup/mainhost backuphost# su - bumainhost -c ln -s /home/backup/mainhost ~/backup
- Place the contents of the backup script on the mainhost some place like /usr/local/sbin/backup.sh or /usr/contrib/sbin/backup.sh
- Make these script executable, readable and writable only for root:
mainhost# chmod 700 /usr/local/sbin/backup.sh mainhost# chown root /usr/local/bin/backup.sh
- Adapt the scripts for your needs. Especially the following is important to check and change if needed
REMOTEUSER=buthishost REMOTEHOST=backuphost.example.com REMOTEBACKUPDIR=/home/backup/thishost/thishost.files LOGDIR=/var/log/backup LOCKFILE=/var/run/backup.lock
With this settings you should create the log location /var/log/backup and make it only accessible for root:
mainhost# mkdir /var/log/backup mainhost# chmod 700 /var/log/backup
- Of course you need to specify the directories you want to backup:
backup /home "--exclude music --exclude backups" backup /etc backup /usr/local backup /var/tmp/mysqldump
The first argument is the directory you want to be backup, recursivly. You can exclude subdirectories you don't want (for example large binary database files or other large data already backed up somewhere else) with the second argument.
If you wish to preserve file owning
File owning etcetera can't be saved under a regular user on the backuphost. If you wish to preserve them, fakeroot on the backuphost can help. It will fake a root environment under a ordinary user and with a different LD_PRELOAD'ed libary it will use alterative versions of setuid() etc. and will save them in a seperate database file if wanted. In our situation we want to run the ssh subprocess for the backup to run in a fakeroot environment. Therefor we install a wrapper on the backup host:
- Install the wrapper script in /home/bumainhost/bin/fakerootwrapper.sh or something like that, and make it executable:
bumainhost@backuphost$ chmod 750 ~/bin/fakerootwrapper.sh
- Make ssh on the backuphost use this script as command when logging in from the mainhost, by setting it in front of the rsa key of the mainhost in authorized_keys of the backuphost:
bumainhost@backuphost$ vim ~/.ssh/authorized_keys command="/home/bumainhost/bin/fakerootwrapper.sh" ssh-rsa AAAA[...]== root@mainhost.example.com
On this way when logging from root@mainhost to bumainhost@backuphost with the rsa key, everything will be executed via te fakeroot wrapper.
Installation complete!
Try and test your installation, to be sure it will make proper backups.
If you found errors or have comments, send an email to haasje@welmers.net or set something in this wiki page, preferably in the discussion section. (Registration required)
Backup script
#!/bin/sh
#
# Backup script
# (C) 2007 Bastiaan Welmers
#
# http://wiki.welmers.net/en/Backup
#
# !!!!!!!!!! CONFIGURE THIS VARIABLES TO YOUR LOCAL NEEDS !!!!!!!!!!
REMOTEUSER=buthishost
REMOTEHOST=backuphost.example.com
REMOTEPORT=22
REMOTEBACKUPDIR=/home/backup/thishost/thishost.files
LOGDIR=/var/log/backup
LOCKFILE=/var/run/backup.lock
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
TODAY=`date +%Y_%m_%d`
touch $LOGDIR/$TODAY
if [ -e $LOCKFILE ]; then
echo "`date +%Y-%m-%d\ %H:%M:%S` ERROR: backup locked by lockfile $LOCKFILE; aborting" >> $LOGDIR/$TODAY
exit
fi
touch $LOCKFILE
echo "`date +%Y-%m-%d\ %H:%M:%S` Initializing backup" >> $LOGDIR/$TODAY
case `uname` in
NetBSD | OpenBSD | FreeBSD )
echo "`date +%Y-%m-%d\ %H:%M:%S` Using BSD style date for finding history files" >> $LOGDIR/$TODAY
WEEKAGO=`date -r $((\`date "+%s"\` - 604800)) +%Y_%m_%d`
WEEKAGO_DAY=`date -r $((\`date "+%s"\` - 604800)) +%d`
MONTHAGO=`date -r $((\`date "+%s"\` - 2764800)) +%Y_%m_%d`
MONTHAGO_DAY=`date -r $((\`date "+%s"\` - 2764800)) +%d`
FOURMONTHAGO=`date -r $((\`date "+%s"\` - 11059200)) +%Y+%m+%d`
FOURMONTHAGO_MONTHDAY=`date -r $((\`date "+%s"\` - 11059200)) +%m_%d`
;;
Linux )
echo "`date +%Y-%m-%d\ %H:%M:%S` Using Linux style date for finding history files" >> $LOGDIR/$TODAY
WEEKAGO=`date -d "1 weeks ago" +%Y_%m_%d`
WEEKAGO_DAY=`date -d "1 weeks ago" +%d`
MONTHAGO=`date -d "1 months ago" +%Y_%m_%d`
MONTHAGO_DAY=`date -d "1 months ago" +%d`
FOURMONTHAGO=`date -d "4 months ago" +%Y+%m+%d`
FOURMONTHAGO_MONTHDAY=`date -d "4 months ago" +%m_%d`
;;
* )
echo "`date +%Y-%m-%d\ %H:%M:%S` Error: system 'uname' does not return either NetBSD, OpenBSD, FreeBSD or Linux. Please check the /bin/date of your system before using this script and adapt this script $0 if necessary" >> $LOGDIR/$TODAY
rm $LOCKFILE
exit
;;
esac
backup() {
if [ -z "$2" ]; then
echo "`date +%Y-%m-%d\ %H:%M:%S` backup $1 ..." >> $LOGDIR/$TODAY
else
echo "`date +%Y-%m-%d\ %H:%M:%S` backup $1 with excluding string "$2"..." >> $LOGDIR/$TODAY
fi
# create directory on the backup host
echo "`date +%Y-%m-%d\ %H:%M:%S` Creating directory $REMOTEBACKUPDIR/$TODAY$1 on the backup host" >> $LOGDIR/$TODAY
ssh -p $REMOTEPORT $REMOTEUSER@$REMOTEHOST "mkdir -p $REMOTEBACKUPDIR/$TODAY$1" >> $LOGDIR/$TODAY 2>&1
#-avHxz -> all files recursively
# -> verbose for logging
# -> preserver Hardlinks
# -> stay on one file system
# -> gzip compression
#
#--delete -> remove files on de backup if not locally existing
#--numberic-ids -> copy permissions numeric to the backup host so they will be preserved
#--link-dest -> use previous copy CURRENT to hardlink files to if not changed to save disk space
# $1/ -> local dir to be backuped
# $2 -> exclutions if given with this function
#
echo `date +%Y-%m-%d\ %H:%M:%S` Rsyncing $1/ to $REMOTEUSER@$REMOTEHOST:$REMOTEBACKUPDIR/$TODAY/$1 >> $LOGDIR/$TODAY
rsync -avHxz -e "ssh -p $REMOTEPORT" --delete --numeric-ids --link-dest=$REMOTEBACKUPDIR/CURRENT/$1 $1/ $2 >> $LOGDIR/$TODAY $REMOTEUSER@$REMOTEHOST:$REMOTEBACKUPDIR/$TODAY/$1 2>&1
}
# Declare here your directories you wish to backup
backup /home "--exclude music --exclude backups"
backup /etc
backup /usr/local
backup /var/tmp/mysqldump
# Point symlink CURRENT to new backup for next increment
echo "`date +%Y-%m-%d\ %H:%M:%S` Point symlink CURRENT to new backup for next increment ..." >> $LOGDIR/$TODAY
ssh -p $REMOTEPORT $REMOTEUSER@$REMOTEHOST "ln -nsf $TODAY $REMOTEBACKUPDIR/CURRENT" >> $LOGDIR/$TODAY 2>&1
# Remove old increments
echo "`date +%Y-%m-%d\ %H:%M:%S` Removing old increments ..." >> $LOGDIR/$TODAY
case $WEEKAGO_DAY in
01 | 07 | 14 | 21 )
# preserve every backup this month on 1st, 7th, 14th and 21st day
echo "`date +%Y-%m-%d\ %H:%M:%S` Backup of one week ago ($WEEKAGO) needs to be preserved for archiving so not removed" >> $LOGDIR/$TODAY
;;
* )
echo "`date +%Y-%m-%d\ %H:%M:%S` Removing backup and logfile of one week ago ($WEEKAGO) if exists" >> $LOGDIR/$TODAY
ssh -p $REMOTEPORT $REMOTEUSER@$REMOTEHOST "rm -rf $REMOTEBACKUPDIR/$WEEKAGO" >> $LOGDIR/$TODAY 2>&1
rm $LOGDIR/$WEEKAGO* >> $LOGDIR/$TODAY 2>&1
;;
esac
case $MONTHAGO_DAY in
01 )
# preserve every backup on the 1st of the month
echo "`date +%Y-%m-%d\ %H:%M:%S` Backup of one month ago ($MONTHAGO) needs to be preserved for archiving so not removed" >> $LOGDIR/$TODAY
;;
07 | 14 | 21 )
# remove (possible) remaining archive backups
echo "`date +%Y-%m-%d\ %H:%M:%S` Removing backup and logfile of one month ago ($MONTHAGO) if exists" >> $LOGDIR/$TODAY
ssh -p $REMOTEPORT $REMOTEUSER@$REMOTEHOST "rm -rf $REMOTEBACKUPDIR/$MONTHAGO" >> $LOGDIR/$TODAY 2>&1
rm $LOGDIR/$MONTHAGO* >> $LOGDIR/$TODAY 2>&1
;;
esac
case $FOURMONTHAGO_MONTHDAY in
01_01 )
# preserve every backup on january 1st
echo "`date +%Y-%m-%d\ %H:%M:%S` Backup of 4 months ago ($FOURMONTHAGO) needs to be preserved for archiving so not removed" >> $LOGDIR/$TODAY
;;
02_01 | 03_01 | 04_01 | 05_01 | 06_01 | 07_01 | 08_01 | 09_01 | 10_01 | 11_01 | 12_01 )
# remove (possible) remaining archive backups
echo "`date +%Y-%m-%d\ %H:%M:%S` Removing backup and logfile of 4 months ago ($FOURMONTHAGO) if exists" >> $LOGDIR/$TODAY
ssh -p $REMOTEPORT $REMOTEUSER@$REMOTEHOST "rm -rf $REMOTEBACKUPDIR/$FOURMONTHAGO" >> $LOGDIR/$TODAY 2>&1
rm $LOGDIR/$FOURMONTHAGO* >> $LOGDIR/$TODAY 2>&1
;;
esac
echo "`date +%Y-%m-%d\ %H:%M:%S` Backup done." >> $LOGDIR/$TODAY
gzip $LOGDIR/$TODAY
rm $LOCKFILE
Wrapper script
#!/bin/sh COMMAND=$SSH_ORIGINAL_COMMAND BACKUPDIR=/home/backup/mainhost/mainhost.files ENVIRONDB=/home/backup/mainhost/mainhost.environ fakeroot -i $ENVIRONDB -s $ENVIRONDB $COMMAND # wait some time to make sure the database is entirely written and faked daemon is closed: sleep 10
Single-system backup script patch
Patch for the main backup script for use on one local system without ssh (so the backup media must be mounted somewhere in the BACKUPDIR)
11,13c11 < REMOTEUSER=buthishost < REMOTEHOST=backuphost.example.com < REMOTEBACKUPDIR=/home/backup/thishost/thishost.files --- > BACKUPDIR=/home/backup/thishost/thishost.files 69,70c67,68 < echo "`date +%Y-%m-%d\ %H:%M:%S` Creating directory $REMOTEBACKUPDIR/$TODAY$1 on the backup host" >> $LOGDIR/$TODAY < ssh $REMOTEUSER@$REMOTEHOST "mkdir -p $REMOTEBACKUPDIR/$TODAY$1" >> $LOGDIR/$TODAY 2>&1 --- > echo "`date +%Y-%m-%d\ %H:%M:%S` Creating directory $BACKUPDIR/$TODAY$1" >> $LOGDIR/$TODAY > mkdir -p $BACKUPDIR/$TODAY$1 >> $LOGDIR/$TODAY 2>&1 84,85c82,83 < echo `date +%Y-%m-%d\ %H:%M:%S` Rsyncing $1/ to $REMOTEUSER@$REMOTEHOST:$REMOTEBACKUPDIR/$TODAY/$1 >> $LOGDIR/$TODAY < rsync -avHxz --delete --numeric-ids --link-dest=$REMOTEBACKUPDIR/CURRENT/$1 $1/ $2 >> $LOGDIR/$TODAY $REMOTEUSER@$REMOTEHOST:$REMOTEBACKUPDIR/$TODAY/$1 2>&1 --- > echo `date +%Y-%m-%d\ %H:%M:%S` Rsyncing $1/ to $BACKUPDIR/$TODAY/$1 >> $LOGDIR/$TODAY > rsync -avHxz --delete --numeric-ids --link-dest=$BACKUPDIR/CURRENT/$1 $1/ $2 >> $LOGDIR/$TODAY $BACKUPDIR/$TODAY/$1 2>&1 99c97 < ssh $REMOTEUSER@$REMOTEHOST "ln -nsf $TODAY $REMOTEBACKUPDIR/CURRENT" >> $LOGDIR/$TODAY 2>&1 --- > ln -nsf $TODAY $BACKUPDIR/CURRENT >> $LOGDIR/$TODAY 2>&1 112c110 < ssh $REMOTEUSER@$REMOTEHOST "rm -rf $REMOTEBACKUPDIR/$WEEKAGO" >> $LOGDIR/$TODAY 2>&1 --- > rm -rf $BACKUPDIR/$WEEKAGO >> $LOGDIR/$TODAY 2>&1 125c123 < ssh $REMOTEUSER@$REMOTEHOST "rm -rf $REMOTEBACKUPDIR/$MONTHAGO" >> $LOGDIR/$TODAY 2>&1 --- > rm -rf $BACKUPDIR/$MONTHAGO >> $LOGDIR/$TODAY 2>&1 138c136 < ssh $REMOTEUSER@$REMOTEHOST "rm -rf $REMOTEBACKUPDIR/$FOURMONTHAGO" >> $LOGDIR/$TODAY 2>&1 --- > rm -rf $BACKUPDIR/$FOURMONTHAGO >> $LOGDIR/$TODAY 2>&1
Backup script patch using sftp
Patch for the main backup script when ssh access is limited to stuff like quota, uses sftp commands to replace the ssh commands
WATCH OUT, STILL BROKEN, only toplevel directories will be backupped
8a9,19 > # > # Adapted to remove the need for full SSH access on the remote backup host by: > # Finke Lamein, December 2009 > # uses SFTP for remote directory creation > # > > # > # replace all ssh -p commands for sftp equivalents > # > > 59c70,79 < --- > # > # Create today's directory on remote server > # > echo "`date +%Y-%m-%d\ %H:%M:%S` Creating directory $REMOTEBACKUPDIR/$TODAY on the backup host" >> $LOGDIR/$TODAY > sftp -oPort=$REMOTEPORT $REMOTEUSER@$REMOTEHOST <<< "mkdir $REMOTEBACKUPDIR/$TODAY" >> $LOGDIR/$TODAY 2>&1 > > # > # Backup starts here with every directory declared under this function > # > 71,72c91,93 < ssh -p $REMOTEPORT $REMOTEUSER@$REMOTEHOST "mkdir -p $REMOTEBACKUPDIR/$TODAY$1" >> $LOGDIR/$TODAY 2>&1 < --- > #ssh -p $REMOTEPORT $REMOTEUSER@$REMOTEHOST "mkdir -p $REMOTEBACKUPDIR/$TODAY$1" >> $LOGDIR/$TODAY 2>&1 > sftp -oPORT=$REMOTEPORT $REMOTEUSER@$REMOTEHOST <<< "mkdir $REMOTEBACKUPDIR/$TODAY$1" >> $LOGDIR/$TODAY 2>&1 > 103c124,126 < ssh -p $EMOTEPORT $REMOTEUSER@$REMOTEHOST "ln -nsf $TODAY $REMOTEBACKUPDIR/CURRENT" >> $LOGDIR/$TODAY 2>&1 --- > #ssh -p $REMOTEPORT $REMOTEUSER@$REMOTEHOST "ln -nsf $TODAY $REMOTEBACKUPDIR/CURRENT" >> $LOGDIR/$TODAY 2>&1 > sftp -oPORT=$REMOTEPORT $REMOTEUSER@$REMOTEHOST <<< "rm $REMOTEBACKUPDIR/CURRENT" >> $LOGDIR/$TODAY 2>&1 > sftp -oPORT=$REMOTEPORT $REMOTEUSER@$REMOTEHOST <<< "ln $TODAY $REMOTEBACKUPDIR/CURRENT" >> $LOGDIR/$TODAY 2>&1 116c139,140 < ssh -p $REMOTEHOST $REMOTEUSER@$REMOTEHOST "rm -rf $REMOTEBACKUPDIR/$WEEKAGO" >> $LOGDIR/$TODAY 2>&1 --- > #ssh -p $REMOTEPORT $REMOTEUSER@$REMOTEHOST "rm -rf $REMOTEBACKUPDIR/$WEEKAGO" >> $LOGDIR/$TODAY 2>&1 > sftp -oPORT=$REMOTEPORT $REMOTEUSER@$REMOTEHOST <<< "rm $REMOTEBACKUPDIR/$WEEKAGO" >> $LOGDIR/$TODAY 2>&1 130c154,155 < ssh -p $REMOTEHOST $REMOTEUSER@$REMOTEHOST "rm -rf $REMOTEBACKUPDIR/$MONTHAGO" >> $LOGDIR/$TODAY 2>&1 --- > #ssh -p $REMOTEPORT $REMOTEUSER@$REMOTEHOST "rm -rf $REMOTEBACKUPDIR/$MONTHAGO" >> $LOGDIR/$TODAY 2>&1 > sftp -oPORT=$REMOTEPORT $REMOTEUSER@$REMOTEHOST <<< "rm $REMOTEBACKUPDIR/$MONTHAGO" >> $LOGDIR/$TODAY 2>&1 144c169,170 < ssh -p $REMOTEHOST $REMOTEUSER@$REMOTEHOST "rm -rf $REMOTEBACKUPDIR/$FOURMONTHAGO" >> $LOGDIR/$TODAY 2>&1 --- > #ssh -p $REMOTEPORT $REMOTEUSER@$REMOTEHOST "rm -rf $REMOTEBACKUPDIR/$FOURMONTHAGO" >> $LOGDIR/$TODAY 2>&1 > sftp -oPORT=$REMOTEPORT $REMOTEUSER@$REMOTEHOST <<< "rm $REMOTEBACKUPDIR/$FOURMONTHAGO" >> $LOGDIR/$TODAY 2>&1

