From b50144addb22fe088033281f49083a29224d7652 Mon Sep 17 00:00:00 2001 From: Nicolas Schodet Date: Mon, 28 Sep 2009 19:22:46 +0200 Subject: initial import. --- ppsnapback | 411 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 411 insertions(+) create mode 100755 ppsnapback diff --git a/ppsnapback b/ppsnapback new file mode 100755 index 0000000..520f32d --- /dev/null +++ b/ppsnapback @@ -0,0 +1,411 @@ +#!/bin/bash +# +# ppsnapback - push or pull snapshot backups using rsync +# +# Copyright (C) 2006-2007 Nicolas Schodet. +# +# See end of file for documentation, copyright and license. +# +# Man page can be generated using: +# +# $ pod2man -c '' -r '' ppsnapback > ppsnapback.1 +# + +set -e + +MODULE=${1:-none} +BASEDIR=${2:-$HOME/backups} +MD=$BASEDIR/$MODULE + +# Read configuration files. +if [[ ! -f $MD/config ]]; then + echo "no module '$MODULE'" >&2 + echo "Usage: $0 MODULE [BACKUP_DIR]" >&2 + exit 1 +fi +[[ -f $BASEDIR/default-config ]] && source $BASEDIR/default-config +source $MD/config +backlog=${backlog:-3} + +if [[ $path ]]; then + # The tabulation is a rsync quirk to disable word splitting. + RSH="ssh${privkey:+ -o IdentitiesOnly yes -i $MD/$privkey}" + [[ -f $BASEDIR/default-exclude ]] && \ + DEFAULT_EXCLUDES="--exclude-from $BASEDIR/default-exclude" + [[ -f $MD/exclude ]] && \ + EXCLUDES="--exclude-from $MD/exclude" + if [[ $remote ]]; then + if [[ $remote == *:* ]]; then + # Push backup to a remote server. + rsync -a --delete-excluded --rsh="$RSH" \ + $DEFAULT_EXCLUDES $EXCLUDES $extra "$path" "$remote" + else + # Push backup to a remote ppsnapback. + rsync -a --delete-excluded --rsh="$RSH" \ + --rsync-path=please-configure-ssh-key \ + $DEFAULT_EXCLUDES $EXCLUDES $extra "$path" "$remote":invalid + fi + else + # Store backup to the local repository. + rm -rf $MD/new + rsync -a --delete-excluded --rsh="$RSH" \ + $DEFAULT_EXCLUDES $EXCLUDES $extra \ + --link-dest=../0 "$path" $MD/new + touch $MD/new + # Rotate. + [[ -d $MD/$backlog ]] && rm -rf $MD/$backlog + for ((i=backlog-1; i >= 0; i--)); do + [[ -d $MD/$i ]] && mv $MD/$i $MD/$((i+1)) + done + mv $MD/new $MD/0 + fi +elif [[ $fakeroot && -w $MD/$fakeroot && $UID -ne 0 ]]; then + # Restart using fakeroot. + fakeroot -i $MD/$fakeroot -s $MD/$fakeroot -- $0 $MODULE $BASEDIR +else + # Receiver mode, called from ssh. + #rm -rf $MD/new + rsync --server -a --delete-excluded \ + $extra --link-dest=../0 . $MD/new + touch $MD/new + # Rotate. + [[ -d $MD/$backlog ]] && rm -rf $MD/$backlog + for ((i=backlog-1; i >= 0; i--)); do + [[ -d $MD/$i ]] && mv $MD/$i $MD/$((i+1)) + done + mv $MD/new $MD/0 +fi + +exit 0 + +__END__ + +=head1 NAME + +ppsnapback - push or pull snapshot backups using rsync + +=head1 SYNOPSIS + +B I [I] + +=head1 DESCRIPTION + +B makes backup snapshots using rsync, possibly pulling or pushing +data from or to a B connection. When B is pushing data, it +can use fakeroot to save owners, groups and permissions. + +B reads its configuration from a local hierarchy of files based at +I. If I is not specified, it default to +F<~/backups/>. Each backup module should have its own directory located under +I. + +A default configuration is read from F/default-config> and the +module configuration is read from F/I/config>. + +Additional exclude lists using the rsync(1) format are read from +F/default-exclude> and F/I/exclude>. + +The configuration files are shell files sourced by B. They are +supposed to set configuration variables but can use any bash(1) shell +constructions to do so. + +B will construct a set of directly accessible snapshots, using +hardlinks to limit hard disk usage. It is based on a Mike Rubel's article +"Easy Automated Snapshot-Style Backups with Linux and Rsync" (see L). The F<0> directory in the module directory will contain the most +recent snapshot. The F<1> directory will contain the previous snapshot and so +on... + +B aims to keep a simple and readable source code in order to allow +its users to hack any new or local features in it. + +=head1 EXAMPLES + +=head2 Local Backups + +This case shows a configuration in which data to backup and backup repository +is on the same host. + +Content of F/local_example/config>: + + path=/home + +That's all! Run B using: + + B local_example I + +Be careful not to backup the backup repository! + +=head2 Pull Remote Data to Backup + +This case shows a configuration in which data to backup is located on a remote +host. Ssh private key is used to secure the connection. B do not +need to be installed on the remote host. + +Content of F/pull_example/config>: + + path=myuser@myhost.example.com:/home/myuser/ + privkey=myhost-myuser-backup + +Content of F (line +breaks introduced by an anti-slash are provided for readability): + + from="backuphost.example.com",\ + command="/usr/bin/rsync --server --sender -logDtpr \ + . /home/myuser/",\ + no-pty,no-port-forwarding,no-x11-forwarding,no-agent-forwarding \ + ssh-rsa AAA...rest of public key... + +The private key has been generated using ssh-keygen(1). See +L for more details. + +Run B using: + + B pull_example I + +=head2 Push Remote Backup Repository With No Snapshot Rotation + +This case shows a configuration in which local data to backup is pushed on a +remote host, but with no snapshot rotation. Ssh private key is used to secure +the connection. B do not need to be installed on the remote host. + +Content of F/push_nosnap_example/config>: + + path=/home/myuser + remote=myuser@backup.example.com:backups/push_nosnap_example + privkey=myhost-myuser-backup + +Content of F (line +breaks introduced by an anti-slash are provided for readability): + + from="myhost.example.com",\ + command="/usr/bin/rsync --server -logDtpr --delete-excluded \ + . backups/push_nosnap_example",\ + no-pty,no-port-forwarding,no-x11-forwarding,no-agent-forwarding \ + ssh-rsa AAA...rest of public key... + +The private key has been generated using ssh-keygen(1). See +L for more details. + +Run B using: + + B push_nosnap_example I + +=head2 Push Remote Backup Repository With Snapshot Rotation + +This case is similar to the previous case, but requires that B is +installed on the remote host to perform the snapshots rotation. + +Content of F/push_example/config>: + + path=/home/myuser + remote=myuser@backup.example.com + privkey=myhost-myuser-backup + +The file F/push_example/config> can be empty +but it must exist. + +Content of F (line +breaks introduced by an anti-slash are provided for readability): + + from="myhost.example.com",\ + command="/path/to/ppsnapback push_example",\ + no-pty,no-port-forwarding,no-x11-forwarding,no-agent-forwarding \ + ssh-rsa AAA...rest of public key... + +The private key has been generated using ssh-keygen(1). See +L for more details. + +Run B using: + + B push_example I + +=head2 Push Remote Backup Repository With Fakeroot + +This case is similar to the previous case, but requires that fakeroot(1) is +installed on the remote host. The advantage of using fakeroot(1) is that file +owners, groups and permissions are also saved. + +The only difference is in the file +F/push_example/config>: + + fakeroot=fakeroot.dat + +If users and groups do not match at the backup host, add the following option +to B sides: + + extra=--numeric-ids + +=head1 CONFIGURATION VARIABLES + +=over + +=item I + +This is the path to data to backup. Path can be a local path or remote path +using the rsync(1) syntax. A trailing slash changes the B +behavior to avoid creating an additional directory level at the destination. +You can find more about this in the rsync(1) manual. + +=item I + +This variable is passed to rsync(1) as additional options. Be careful, most +of these options must also be used at the remote end of the ssh(1) connection. + +=item I + +This is the path to an optional ssh(1) private key, relative to the module +directory. + +=item I + +If this variable is defined, backups are not stored locally but on a remote +host. In this case, this variable is the path to the remote backup +repository. If the remote host does not override the sent command, a simple +rsync(1) will be used with no snapshot rotation. See L for more +informations. If the remote host does override the sent command (this is the +recommended usage) only the host part of this variable does really matter. + +=item I + +This variable is the number of older snapshots to keep in the backup +repository. It defaults to 3. + +=item I + +As B use normal files to store the backups, permissions, owners +and groups can only be saved if it is run as root. Another alternative is to +use fakeroot(1) to simulate a root environment. If this variable is set, it +gives the path relative to the module directory to the file used by +fakeroot(1) to save additional information like owners, groups and +permissions. This file must exist prior to be used by B, you can +create it using touch(1). + +=back + +=head1 SECURING SSH CONNECTION + +Doing backups should be an automated process and therefore, there can not be a +person to type a ssh(1) password when they are done. + +The solution is to use a password less ssh(1) private key. Please see +ssh-keygen(1) for details about how to use it. + +The procedure to install a key is something like: + + ssh-keygen -t rsa -f backup-key -C "backup key" -N "" + cat backup-key.pub | ssh remotehost "cat >> .ssh/authorized_keys" + +One problem with this system is that anyone owning the private key file can +connect using ssh on the remotehost and do anything he want. + +Happily, ssh(1) provide a way to restrict commands executed on the remote +host. In order to do this, edit the .ssh/authorized_keys on the remote host +and add the following before the key you have just copied (line breaks +introduced by an anti-slash are provided for readability): + + from="myhost.example.com",\ + command="/path/to/my/command",\ + no-pty,no-port-forwarding,no-x11-forwarding,no-agent-forwarding \ + ssh-rsa AAA...rest of public key... + +Now, the private key only works from myhost.example.com to run the command +indicated. This method can be used to limit the usage of the private key to +backup. + +=head1 AUTOMATING EXECUTION + +To automatically execute the backup, place B in your crontab(5). +For example: + + 0 * * * * /path/to/ppsnapback my_backup_every_4_hours + 4 42 * * 0 /path/to/ppsnapback my_weekly_backup + +See your crontab(1), cron(8) and crontab(5) local documentation for more +details. + +=head1 ENVIRONMENT VARIABLE + +=over + +=item HOME + +Used to deduce I if not provided on the command line. + +=back + +=head1 FILES + +=over + +=item F<$HOME/backups/> + +Default directory for I. + +=item F/default-config> + +Default configuration, sourced for every module. + +=item F/default-exclude> + +Default exclude list, used for every module, using the rsync(1) format. + +=item F/I/config> + +Module configuration. + +=item F/I/exclude> + +Module exclude, using the rsync(1) format. + +=item F<~/.ssh/authorized_keys> + +Not a B file, but used by ssh(1) to authorize and limit +connections using private/public keys. + +=back + +=head1 BUGS + +B does not read rsync(1) options passed by the ssh connection. +This means that any extra option added on one end should also be added at the +other end. The variables SSH_ORIGINAL_COMMAND and SSH2_ORIGINAL_COMMAND could +be read to fix this issue, taking care to accept only reasonable options. + +=head1 RESTRICTIONS + +B does not handle spaces in I and I arguments +or in the I and I configuration variables. Support for +spaces could be added easily but will clutter the source code with +double-quotes. The I or I variables however accept spaces. + +=head1 SEE ALSO + +rsync(1), ssh(1), ssh-keygen(1), bash(1), fakeroot(1), crontab(5) + +Mike Rubel's article "Easy Automated Snapshot-Style Backups with Linux and +Rsync" http://www.mikerubel.org/computers/rsync_snapshots/ + +=head1 AUTHOR + +Nicolas Schodet, http://ni.fr.eu.org/ppsnapback + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2006 Nicolas Schodet. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 2 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 59 Temple +Place - Suite 330, Boston, MA 02111-1307, USA. + +=cut + -- cgit v1.2.3