Backup remote Linux systems without root access, using rsnapshot and rsync

New version

An easier solution can be found here

http://dev.kprod.net/?q=linux-backup-rsnapshot-no-root

Why ?

Rsnapshot is a powerfull rotating system snapshot utility.

Rotations process use hardlinks ; only the changed files are copied, the rest is hard linked to the most recent backup.

Rsnapshot can backup locally and remotly, but it requires remote root access in order to copy all system files, which is not really secure.

How ?

Consider we have several servers to backup, and a central backuping server.
Each remote will run a cron task to save local content using rsnapshot.
Then, our backuping station will access all remotes using dedicated account, copying backups using Rsync.

You'll say that the problem is not solved. We still have root files that our dedicated account could'nt access.
The trick is that a script would have saved each file permissions, before changing all files access rights in order to allow dedicated account to access it.

Using a dedicated account with restricted rights allows backuping station to access through SSH (using authorized_keys).

Backuping scripts explained

Remotes

backup.sh

This script  directories/files permissions from the state file (run restoreState.pl)

Then run Rsnapshot.

When done, saves current directories/files permissions into the state file.  (run saveState.pl)

Finally, set permissions to a special user (snapshot:snapshot) to allow secure remote copy.

#!/bin/sh
 
scriptsRoot="[CHANGEME]"
snapshotRoot="/var/cache/rsnapshot"
 
if [ $# -ne 1 ];then
    echo "Not enough arguments"
    echo "./backup.sh [rsnapshot period]"
    exit
fi
 
if [ -f $scriptsRoot/state ];then
    $scriptsRoot/restoreState.pl $scriptsRoot/state
fi
 
/usr/bin/rsnapshot $1
 
$scriptsRoot/saveState.pl $snapshotRoot > $scriptsRoot/state
 
/bin/cp $scriptsRoot/state $snapshotRoot
 
/bin/chown -R snapshot:snapshot $snapshotRoot
/bin/chmod -R 700 $snapshotRoot/*

saveState.pl

Save each file permissions to a state file.

#!/usr/bin/perl
my $tree = $ARGV[0];
 
foreach $file (`/usr/bin/find ${tree}`)
{
  chomp($file);
  (undef, $inode, $mode, undef, $uid, $gid, $undef, $size, undef, undef, undef, undef, undef) = stat($file);
  $permissions = $mode & 07777;
  printf "%i:%i:%04o:%i:%i:%s\n", $inode, $size, $permissions, $uid, $gid, $file;
}

restoreState.pl

Read state file and apply saved permissions to files.

#!/usr/bin/perl
my $stateFile = $ARGV[0];
 
open(STATE_IN, "<$stateFile");
 
foreach $line (<STATE_IN>)
{
  chomp($line);
  my ($inode, $size, $permissions, $uid, $gid, $file) = split(':', $line, 6);
  if( -f $file || -d $file)
  {
    #add inode / size check here if desired
    chmod(oct($permissions), $file);
    chown($uid, $gid, $file);
  }
}
 
close(STATE_IN);


Backuping Server

syncFromRemote.sh

Runs rsync to a remote system.

#!/bin/sh
 
if [ $# -ne 4 ];then
    echo "Not enough arguments"
    echo "./syncFromRemote [user host remoteDir backupDir]"
    exit
fi
 
remoteUser=$1
remoteHost=$2
remoteDir=$3
backupDir=$4
 
if [ -d $4/$2 ];then
    mkdir $4/$2
fi
 
rsync -avzH -e ssh $1@$2:$3 $4/$2

syncFromAllRemotes.sh

Runs all backups and send a report mail.

#!/bin/sh
 
scriptsRoot="[CHANGEME]"
backupRoot="[CHANGEME]"
snapshotRoot="/var/cache/rsnapshot"
 
echo "backup started" > $scriptsRoot/.mailtmp
date >> $scriptsRoot/.mailtmp
du -sh $backupRoot/* >> $scriptsRoot/.mailtmp
 
$scriptsRoot/syncFromRemote.sh [USER] [HOST] $snapshotRoot $backupRoot
 
echo "backup done" >> $scriptsRoot/.mailtmp
date >> $scriptsRoot/.mailtmp
du -sh $backupRoot/* >> $scriptsRoot/.mailtmp
 
mail user@domain.com -s "backup log" < $scriptsRoot/.mailtmp

Install process

Files

All scripts can be found in this archive : backupScripts.tar

Backuping server

SSH Key

We'll create a public key and push it to each remote. Using snapshot user, backuping server could connect on each remote.

Create snapshot user

> sudo adduser \
  --system \
  --shell /bin/sh \
  --group \
  --disabled-password \
  --home /home/snapshot \
  snapshot

Logon snapshot user

> sudo su snapshot

Create RSA key

> ssh-keygen -t rsa

Copy public key to all remotes using scp

> scp .ssh/id_rsa.pub user@remote_host:

Script files

Copy syncFromAllRemotes.sh and syncFromRemote.sh to /home/snapshot/scripts folder.

Setting up cron

Logon snapshot user

> sudo su snapshot

Create task (example : each day @ 6am)

> sudo crontab -e
0 6 * * *       /home/snapshot/scripts/syncFromAllRemotes.sh

Remotes

Setting up Rsnapshot

Install Rsnapshot from apt

> sudo apt-get install rsnapshot

Edit config file

> sudo vim /etc/rsnapshot.conf

Edit following parameters

#backups destination
snapshot_root   /var/cache/rsnapshot/
 
#uncomment
cmd_cp          /bin/cp
 
#set intervals (keep 3 daily, weekly and monthly backups)
interval        daily   3
interval        weekly  3
interval        monthly 3
 
#set directories to backup (trailing slash is mandatory)
#separator MUST BE TAB
backup  /etc/                   localhost/
backup  /home/                  localhost/

Test this config

> sudo rsnapshot configtest
Syntax OK

Run a backup manually

> sudo rsnapshot daily

Create cron jobs, depending on what you set in rsnapshot.conf (daily @3am, weekly @4am, monthly @4am)

> sudo crontab -e
0 3 * * *       /usr/bin/rsnapshot daily
0 4 * * 1       /usr/bin/rsnapshot weekly
0 4 1 * *       /usr/bin/rsnapshot monthly

Create remotes dedicated backup user

create local a local user called 'snapshot'

> sudo adduser \
  --system \
  --shell /bin/sh \
  --group \
  --disabled-password \
  --home /home/snapshot \
  snapshot

copy ssh key from backup server and add it to authorized_keys (We're talking about the public key we've made previously)

> sudo mkdir /home/snapshot/.ssh
> sudo cp id_rsa.pub /home/snapshot/.ssh/authorized_keys
> sudo chown -R snapshot:snapshot /home/snapshot/.ssh/

 

... And that's it. You can test the whole process before cron runs the scripts.