summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xppsnapback411
1 files changed, 411 insertions, 0 deletions
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<ppsnapback> I<module> [I<backup_dir>]
+
+=head1 DESCRIPTION
+
+B<ppsnapback> makes backup snapshots using rsync, possibly pulling or pushing
+data from or to a B<ssh> connection. When B<ppsnapback> is pushing data, it
+can use fakeroot to save owners, groups and permissions.
+
+B<ppsnapback> reads its configuration from a local hierarchy of files based at
+I<backup_dir>. If I<backup_dir> is not specified, it default to
+F<~/backups/>. Each backup module should have its own directory located under
+I<backup_dir>.
+
+A default configuration is read from F<I<backup_dir>/default-config> and the
+module configuration is read from F<I<backup_dir>/I<module>/config>.
+
+Additional exclude lists using the rsync(1) format are read from
+F<I<backup_dir>/default-exclude> and F<I<backup_dir>/I<module>/exclude>.
+
+The configuration files are shell files sourced by B<ppsnapback>. They are
+supposed to set configuration variables but can use any bash(1) shell
+constructions to do so.
+
+B<ppsnapback> 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</SEE
+ALSO>). 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<ppsnapback> 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<I<backup_dir>/local_example/config>:
+
+ path=/home
+
+That's all! Run B<ppsnapback> using:
+
+ B<ppsnapback> local_example I<backup_dir>
+
+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<ppsnapback> do not
+need to be installed on the remote host.
+
+Content of F<I<backup_dir>/pull_example/config>:
+
+ path=myuser@myhost.example.com:/home/myuser/
+ privkey=myhost-myuser-backup
+
+Content of F<myhost.example.com:/home/myuser/.ssh/authorized_keys> (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</SECURING SSH CONNECTION> for more details.
+
+Run B<ppsnapback> using:
+
+ B<ppsnapback> pull_example I<backup_dir>
+
+=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<ppsnapback> do not need to be installed on the remote host.
+
+Content of F<I<backup_dir>/push_nosnap_example/config>:
+
+ path=/home/myuser
+ remote=myuser@backup.example.com:backups/push_nosnap_example
+ privkey=myhost-myuser-backup
+
+Content of F<backup.example.com:/home/myuser/.ssh/authorized_keys> (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</SECURING SSH CONNECTION> for more details.
+
+Run B<ppsnapback> using:
+
+ B<ppsnapback> push_nosnap_example I<backup_dir>
+
+=head2 Push Remote Backup Repository With Snapshot Rotation
+
+This case is similar to the previous case, but requires that B<ppsnapback> is
+installed on the remote host to perform the snapshots rotation.
+
+Content of F<I<backup_dir>/push_example/config>:
+
+ path=/home/myuser
+ remote=myuser@backup.example.com
+ privkey=myhost-myuser-backup
+
+The file F<backup.example.com:I<backup_dir>/push_example/config> can be empty
+but it must exist.
+
+Content of F<backup.example.com:/home/myuser/.ssh/authorized_keys> (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</SECURING SSH CONNECTION> for more details.
+
+Run B<ppsnapback> using:
+
+ B<ppsnapback> push_example I<backup_dir>
+
+=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<backup.example.com:I<backup_dir>/push_example/config>:
+
+ fakeroot=fakeroot.dat
+
+If users and groups do not match at the backup host, add the following option
+to B<BOTH> sides:
+
+ extra=--numeric-ids
+
+=head1 CONFIGURATION VARIABLES
+
+=over
+
+=item I<path>
+
+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<ppsnapback>
+behavior to avoid creating an additional directory level at the destination.
+You can find more about this in the rsync(1) manual.
+
+=item I<extra>
+
+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<privkey>
+
+This is the path to an optional ssh(1) private key, relative to the module
+directory.
+
+=item I<remote>
+
+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</EXAMPLES> 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<backlog>
+
+This variable is the number of older snapshots to keep in the backup
+repository. It defaults to 3.
+
+=item I<fakeroot>
+
+As B<ppsnapback> 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<ppsnapback>, 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<ppsnapback> 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<backup_dir> if not provided on the command line.
+
+=back
+
+=head1 FILES
+
+=over
+
+=item F<$HOME/backups/>
+
+Default directory for I<backup_dir>.
+
+=item F<I<backup_dir>/default-config>
+
+Default configuration, sourced for every module.
+
+=item F<I<backup_dir>/default-exclude>
+
+Default exclude list, used for every module, using the rsync(1) format.
+
+=item F<I<backup_dir>/I<module>/config>
+
+Module configuration.
+
+=item F<I<backup_dir>/I<module>/exclude>
+
+Module exclude, using the rsync(1) format.
+
+=item F<~/.ssh/authorized_keys>
+
+Not a B<ppsnapback> file, but used by ssh(1) to authorize and limit
+connections using private/public keys.
+
+=back
+
+=head1 BUGS
+
+B<ppsnapback> 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<ppsnapback> does not handle spaces in I<module> and I<backup_dir> arguments
+or in the I<privkey> and I<fakeroot> configuration variables. Support for
+spaces could be added easily but will clutter the source code with
+double-quotes. The I<path> or I<remote> 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
+