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

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.

backup.sh
#!/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.

saveState.pl
#!/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.

restoreState.pl
#!/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.

syncFromRemote.sh
#!/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.

syncFromAllRemotes.sh
#!/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.zip

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
#!/bin/sh

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

Edit rsnapshot config
#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

Create crons on remotes
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 a local user called 'snapshot'

Create user 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)

Ssh keys
sudo mkdir /home/snapshot/.ssh
sudo cp id_rsa.pub /home/snapshot/.ssh/authorized_keys
sudo chown -R snapshot:snapshot /home/snapshot/.ssh/
  1. And "that’s it". You can test the whole process before cron runs the scripts.

comments powered by Disqus