Automate backups with a Raspberry Pi

6 minute read

In this post I describe a setup where you use a Raspberry Pi to automate the backup of your databases.

We will have to install and configure the Raspberry Pi first. Then we will create a user with limited rights on our server. We will use this user to connect to the server and perform the backups.

Download raspbian

You can download Raspbian on the official website. Unzip the image:

cd Downloads
unzip 2017-04-10-raspbian-jessie-lite.zip
# Output
Archive:  2017-04-10-raspbian-jessie-lite.zip
  inflating: 2017-04-10-raspbian-jessie-lite.img  

Find the SD card

Plug the SD card and use dmesg to find it:

dmesg | tail
# Output
[  138.753330] CIFS VFS: Error connecting to socket. Aborting operation.
[  138.753386] CIFS VFS: Error connecting to socket. Aborting operation.
[  138.753690] CIFS VFS: cifs_mount failed w/return code = -113
[  138.753717] CIFS VFS: cifs_mount failed w/return code = -113
[  138.753725] CIFS VFS: cifs_mount failed w/return code = -113
[  138.753769] CIFS VFS: cifs_mount failed w/return code = -113
[ 4170.871943] sd 6:0:0:0: [sde] 125042688 512-byte logical blocks: (64.0 GB/59.6 GiB)
[ 4170.876516]  sde: sde1

The card is sde1.

Copy the image to the SD card

cd ~/Downloads
sudo dd bs=4M if=2017-04-10-raspbian-jessie-lite.img of=/dev/sde status=progress conv=fsync

Check the image

We copy the image on the SD card on our computer:

sudo dd bs=4M if=/dev/sde of=from-sd-card.img status=progress

Then we truncate it to the size of the original image since our SD card is probably larger than the original image:

sudo truncate --reference 2017-04-10-raspbian-jessie.img from-sd-card.img
sudo diff -s from-sd-card.img 2017-04-10-raspbian-jessie.img

There should be no difference between the truncated image and the original.

Finally we run sync. This will ensure the write cache is flushed and that it is safe to unmount your SD card.

Authorize SSH

For security reasons, SSH is disabled by default on raspbian now. We can enable it by adding an empty file named ssh in /mnt/rasp.

sudo mkdir /mnt/rasp
sudo mount /dev/sde1 /mnt/rasp
sudo touch /mnt/rasp/ssh
umount /dev/sde1 /dev/sde1

Updating the Raspberry Pi

Place the card in the raspberry pi and plug the power.

We connect to the Raspberry Pi with:

ssh pi@raspberrypi
# password is raspberrypi

We start by changing the password:

passwd

Then we change the hostname:

sudo hostnamectl set-hostname backuppi

We perform updates and install useful software

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install git tree vim zsh cifs-utils rpi-update
sudo rpi-update

Remove password authentication

We create a SSH key:

ssh-keygen

This might be useful at some point but we will not use it right now.

We add our computer public SSH key to the file ~/.ssh/authorized_keys. And we test the authentication with the public SSH key.

Then we remove password authentication by modifying /etc/ssh/sshd_config

sudo vim /etc/ssh/sshd_config
/etc/ssh/sshd_config
PasswordAuthentication no

Add a new user

We add a new user backupuser with:

sudo adduser backupuser

We enter a strong password. And we impersonate the user to perform the configuration:

sudo su - backupuser

We should see that we are now the user backupuser and that our home directory is coherent with this.

Add Public Key Authentication

We generate a new key pair with

ssh-keygen

We leave the passphrase blank: this user will have to connect automatically to the servers.

We can display the created public key with:

cat ~/.ssh/id_rsa.pub

backupuser on the server

We create the user backupuser on our server and configure it so that the user backupuser on our Raspberry Pi can SSH to our server.

Add a user backupuser

sudo adduser backupuser

Add the public key

We create the directory /home/backupuser/.ssh and the file authorized_keys:

sudo su - backupuser
mkdir ~/.ssh
touch ~/.ssh/authorized_keys

We add the public key generated on our Raspberry Pi to the file /home/backupuser/.ssh/authorized_keys.

sudo vim ~/.ssh/authorized_keys

And we can stop impersonating backupser on our server.

exit

Now we can go back to our Raspberry Pi and test the connection to the server:

ssh myserver.net

We should be logged in as backupuser on our server.

Configure chroot on the server

Install chroot

On the server, as the normal user we install chroot with:

sudo apt-get install dchroot debootstrap

Add the follwing line in /etc/ssh/sshd_config. Note that you can have only one configuration line with Subsystem sftp ..., otherwise your ssh server will fail starting.

/etc/ssh/sshd_config
Subsystem sftp internal-sftp

Create a chroot jail

Create a chroot jail by adding the follwing lines at the end of the file /etc/ssh/sshd_config:

/etc/ssh/sshd_config
Match User backupuser
  ChrootDirectory /home/backupuser
  AllowTcpForwarding no

The directory /home/backupuser must be owned by ROOT

To make the user ROOT the owner of /home/backupuser we can use:

chown -R root:root /home/backupuser

Now save an restart the SSH server.

sudo service ssh restart

If we try to connect from the Raspberry Pi to the server using SSH the connection will be closed. This is normal.

Copying necessary files

The way chroot works is that the user is confined to /home/backupuser/, which means that the user cannot access /bin/bash, /bin/ls, and so on…

To allow the use of bash we have to :

  1. copy /bin/bash to /home/backupuser/
  2. copy /bin/bash dependencies to /home/backupuser

Copying /bin/bash

We create a directory /home/backupuser/bin

sudo mkdir /home/backupuser/bin

We copy the file to this new directory:

sudo cp /bin/bash /home/backupuser/bin

Copying dependencies

We list the /bin/bash dependencies with ldd:

ldd /bin/bash
#Output
linux-vdso.so.1 =>  (0x00007fff02b45000)
libtinfo.so.5 => /lib/x86_64-linux-gnu/libtinfo.so.5 (0x00007f793909a000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f7938e96000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f7938acc000)
/lib64/ld-linux-x86-64.so.2 (0x00007f79392c3000)

So we create the necessary directories:

sudo mkdir -p /home/backupuser/lib/x86_64-linux-gnu/
sudo mkdir /home/backupuser/lib64

And we copy the libraries to the newly created directories

sudo cp  /lib/x86_64-linux-gnu/libtinfo.so.5 /home/backupuser /lib/x86_64-linux-gnu/libtinfo.so.5
sudo cp /lib/x86_64-linux-gnu/libdl.so.2 /home/backpuser/lib/x86_64-linux-gnu/libdl.so.2
sudo cp /lib/x86_64-linux-gnu/libc.so.6 /home/backpuser/lib/x86_64-linux-gnu/libc.so.6
sudo cp /lib64/ld-linux-x86-64.so.2 /home/backpuser/lib64/ld-linux-x86-64.so.2

Now we can try this from our Raspberry Pi:

ssh myserver.net bash --version

We should see the following output:

output
GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Copying rsync dependencies

We repeat the operation done with /bin/bash for /usr/bin/rsync.

sudo mkdir -p /home/backupser/usr/bin
sudo cp /usr/bin/rsync /home/backupuser/usr/bin/

We list the rsync dependencies:

ldd /usr/bin/rsync
# Output
linux-vdso.so.1 =>  (0x00007ffd507b3000)
libattr.so.1 => /lib/x86_64-linux-gnu/libattr.so.1 (0x00007f1b11409000)
libacl.so.1 => /lib/x86_64-linux-gnu/libacl.so.1 (0x00007f1b11201000)
libpopt.so.0 => /lib/x86_64-linux-gnu/libpopt.so.0 (0x00007f1b10ff5000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f1b10c2b000)
/lib64/ld-linux-x86-64.so.2 (0x00007f1b11896000)

And we copy them:

sudo cp /lib/x86_64-linux-gnu/libattr.so.1 /home/backupuser/lib/x86_64-linux-gnu/libattr.so.1
sudo cp /lib/x86_64-linux-gnu/libacl.so.1 /home/backupuser/lib/x86_64-linux-gnu/libacl.so.1
sudo cp /lib/x86_64-linux-gnu/libpopt.so.0  /home/backupuser/lib/x86_64-linux-gnu/libpopt.so.0
sudo cp /lib/x86_64-linux-gnu/libc.so.6 /home/backupuser/lib/x86_64-linux-gnu/libc.so.6

Testing the chroot jail

Now to test the chroot jail we will create a file /home/backupuser/test.txt on our server, with the following content:

/home/backpuser/test.txt
Hooray it works!

Now from our Raspberry Pi, and as the user backupuser, we run the following command:

rsync myserver.net:/test.txt .

We should have the file test.txt copied on our Raspberry Pi with the correct contents.

Note that when we connect to the server, the root directory / for the user backupuser is actually the directory /home/backupuser. This is why in the rsync command we use myserver.net:/test.txt.

If we try to ssh into our server, we will have a bash terminal (since we copied /bin/bash). But we will not have access to commands to given to the user such as ls, mkdir, cat etc… Also, the user will only have access to /home/backupuser which will be its root directory.

Backing up databases

To backup our database :

  1. We create a limited read-only user on our databases
  2. We create a cron job to dump our database in /home/backupuser/backup on our server.
  3. We use rsync to retrieve our backup folder from our Raspberry Pi. cronjob with normal user.

You can follow these tutorials treating about bullet points 1 and 2:

Once you have followed the tutorials you will only have to create a cron job on your Raspberry Pi.

Use crontab -e to edit the cron jobs. And add the following job:

# Backup remote databases everyday at 01:00
0 1 * * * rsync -a myserver.net:/backup /home/backupuser/