Backup

From wiki.welmers.net

Jump to: navigation, search

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
Personal tools