Backup system
Imported both the server- and client-side backup scripts.
This commit is contained in:
commit
d9f75447a6
24 changed files with 1043 additions and 0 deletions
5
README
Normal file
5
README
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
This repo contains various scripts which I figure could be useful to someone.
|
||||||
|
|
||||||
|
backup/ A set of backup scripts
|
||||||
|
|
||||||
|
License: WTFPL (http://sam.zoy.org/wtfpl/)
|
8
backup/README
Normal file
8
backup/README
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
This set of scripts can be used to implement a rather primitive backup system.
|
||||||
|
|
||||||
|
server/
|
||||||
|
The scripts and example configuration files for the backup
|
||||||
|
server.
|
||||||
|
|
||||||
|
ssh-client/
|
||||||
|
Scripts that can be used on remote systems.
|
107
backup/server/README
Normal file
107
backup/server/README
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
Backup server scripts
|
||||||
|
======================
|
||||||
|
|
||||||
|
The scripts in this directory implement a backup "server". While quite
|
||||||
|
primitive, it supports a rather flexible configuration and can be customised in
|
||||||
|
various ways (e.g. support for new types of data fetching).
|
||||||
|
|
||||||
|
|
||||||
|
Installation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
1/ Copy the backup script to /usr/local/sbin
|
||||||
|
2/ Copy the share/ directory to /usr/local/share/backup (omit the
|
||||||
|
"postprocess" script if you don't need it - see below for more info)
|
||||||
|
3/ Copy backup.conf and backup.conf.d/ to /etc
|
||||||
|
4/ Configure the server
|
||||||
|
5/ Add cron jobs to execute backups (see crontab.example)
|
||||||
|
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
--------------
|
||||||
|
|
||||||
|
The main configuration file, backup.conf, defines a few variables. You need to
|
||||||
|
modify some of these values for the system to function properly (e.g. title
|
||||||
|
for backup reports, main archive storage location, and possibly the log
|
||||||
|
directory). It also allows you to modify the location of the data fetching and
|
||||||
|
postprocessing scripts, as well as the location of the rest of the
|
||||||
|
configuration, should you need / want to do that.
|
||||||
|
|
||||||
|
The main configuration files (backup.conf.d/*.conf) include some documentation.
|
||||||
|
They are mostly self-explanatory, with the exception of the "fetch modes" thing.
|
||||||
|
|
||||||
|
|
||||||
|
Fetch modes
|
||||||
|
------------
|
||||||
|
|
||||||
|
A "fetch mode" associates a data acquisition script (share/fetch-*) and a
|
||||||
|
specific configuration for this script. Fetch modes must be listed in the
|
||||||
|
backup.conf.d/fetch-modes.conf file. Hosts are then associated to a fetch mode.
|
||||||
|
|
||||||
|
When the backup archives from a host need to be generated, the mode's
|
||||||
|
configuration will be read from backup.conf.d/fetch/<name of the mode>.conf if
|
||||||
|
it exists (otherwise defaults will be assumed). After that, the backup script
|
||||||
|
also attempts to load backup.conf.d/<name of the mode>/<name of the host>.conf
|
||||||
|
if it exists.
|
||||||
|
|
||||||
|
Two fetch scripts are provided:
|
||||||
|
|
||||||
|
fetch-local Fetch data from a mounted filesystem
|
||||||
|
BASE Base directory for all "hosts"
|
||||||
|
Default: /
|
||||||
|
ROOT Root directory of a host relative to the base
|
||||||
|
Default: /
|
||||||
|
|
||||||
|
fetch-ssh Fetch data through SSH (see ../ssh-client/)
|
||||||
|
SSH_HOST Host to connect to
|
||||||
|
SSH_KEY Private key to use
|
||||||
|
SSH_PORT Port to connect to
|
||||||
|
Default: 22
|
||||||
|
SSH_USER User to log in as
|
||||||
|
Default: user running the server script
|
||||||
|
|
||||||
|
|
||||||
|
Default postprocessing script
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
The default postprocessing script will encrypt all archives using a fixed key,
|
||||||
|
and send them to some remote server using FTP.
|
||||||
|
|
||||||
|
To enable the script, simply make sure the "share/postprocess" script is
|
||||||
|
present and executable.
|
||||||
|
|
||||||
|
The script's configuration includes the server's name or address, the
|
||||||
|
credentials needed to log in, and the amount of remote rotations; in addition,
|
||||||
|
a second file contains the encryption key.
|
||||||
|
|
||||||
|
|
||||||
|
Customisation - Fetch scripts
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
Fetch scripts must be written in Bash and must define a function named "FETCH".
|
||||||
|
The function will output the archive's data on its standard output; any error
|
||||||
|
should be written to the standard error stream.
|
||||||
|
|
||||||
|
When the function is called, all variables loaded from the fetch mode or host
|
||||||
|
configuration files will be present, as well as the following variables:
|
||||||
|
|
||||||
|
backup_directory The directory to backup, from types.conf
|
||||||
|
backup_exclude An array of directories to exclude from the
|
||||||
|
resulting archive, as defined in
|
||||||
|
exclude.conf
|
||||||
|
|
||||||
|
|
||||||
|
Customisation - Postprocessing
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
The postprocessing script is launched by the main script when it starts. It
|
||||||
|
will be passed the name of a temporary directory in which its data resides.
|
||||||
|
|
||||||
|
The first thing a postprocessing script ought to do is create a file named "pid"
|
||||||
|
in that directory, writing its ... PID ... into it (thank you, Captain Obvious!)
|
||||||
|
|
||||||
|
The main script will then write a host name and backup type identifier whenever
|
||||||
|
it finishes fetching an archive.
|
||||||
|
|
||||||
|
Anything written to the standard output or standard error stream will end up in
|
||||||
|
the main report.
|
441
backup/server/backup
Executable file
441
backup/server/backup
Executable file
|
@ -0,0 +1,441 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
|
||||||
|
MODE="$1"
|
||||||
|
[ "x$MODE" = "x" ] && exit 1
|
||||||
|
|
||||||
|
source /etc/backup.conf
|
||||||
|
if ! [ -d "$BACKUP_TARGET" ]; then
|
||||||
|
echo "missing target directory $BACKUP_TARGET"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
function checkMode
|
||||||
|
{
|
||||||
|
local tocheck="$1"
|
||||||
|
local mode=
|
||||||
|
for mode in `grep -v '^#' "${BACKUP_CONFS}/modes.conf" | awk '{ print $1 }'`; do
|
||||||
|
[ "x$mode" = "x$tocheck" ] && return
|
||||||
|
done
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function checkType
|
||||||
|
{
|
||||||
|
local tocheck="$1"
|
||||||
|
local btype=
|
||||||
|
for btype in `grep -v '^#' "${BACKUP_CONFS}/types.conf" | awk '{ print $1 }'`; do
|
||||||
|
[ "x$btype" = "x$tocheck" ] && return 0
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getHosts
|
||||||
|
{
|
||||||
|
grep -v '^#' "${BACKUP_CONFS}/hosts.conf" | awk '{ print $1 }'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function printHostHeader
|
||||||
|
{
|
||||||
|
local host="$1"
|
||||||
|
local id=
|
||||||
|
local name=
|
||||||
|
|
||||||
|
grep '^'"$host" "${BACKUP_CONFS}/hosts.conf" | while read id name; do
|
||||||
|
if [ "x$id" = "x$host" ]; then
|
||||||
|
echo "$name (identifier: $id)";
|
||||||
|
break;
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getBackupTypes
|
||||||
|
{
|
||||||
|
local mode="$1"
|
||||||
|
local host="$2"
|
||||||
|
local cmode=
|
||||||
|
local chost=
|
||||||
|
local ctypes=
|
||||||
|
local ctype=
|
||||||
|
grep -v '^#' "${BACKUP_CONFS}/modes.conf" | while read cmode chost ctypes; do
|
||||||
|
if [ "x$cmode" != "x$mode" ] && [ "x$cmode" != "x*" ]; then
|
||||||
|
continue;
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "x$chost" != "x$host" ] && [ "x$chost" != "x*" ]; then
|
||||||
|
continue;
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$ctypes" | sed -e 's/,/ /g'
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getTypeName
|
||||||
|
{
|
||||||
|
local btype="$1"
|
||||||
|
local ctype=
|
||||||
|
local crot=
|
||||||
|
local cdir=
|
||||||
|
local cdesc=
|
||||||
|
|
||||||
|
grep '^'"$btype" "${BACKUP_CONFS}/types.conf" | while read ctype crot cdir cdesc; do
|
||||||
|
if [ "x$ctype" != "x$btype" ]; then
|
||||||
|
continue;
|
||||||
|
fi
|
||||||
|
echo "$cdesc"
|
||||||
|
break
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getTypeRotation
|
||||||
|
{
|
||||||
|
local btype="$1"
|
||||||
|
local ctype=
|
||||||
|
local crot=
|
||||||
|
local cdir=
|
||||||
|
local cdesc=
|
||||||
|
|
||||||
|
grep '^'"$btype" "${BACKUP_CONFS}/types.conf" | while read ctype crot cdir cdesc; do
|
||||||
|
if [ "x$ctype" != "x$btype" ]; then
|
||||||
|
continue;
|
||||||
|
fi
|
||||||
|
echo "$crot"
|
||||||
|
break
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getTypeDirectory
|
||||||
|
{
|
||||||
|
local btype="$1"
|
||||||
|
local ctype=
|
||||||
|
local cdir=
|
||||||
|
local crot=
|
||||||
|
local cdesc=
|
||||||
|
|
||||||
|
grep '^'"$btype" "${BACKUP_CONFS}/types.conf" | while read ctype crot cdir cdesc; do
|
||||||
|
if [ "x$ctype" != "x$btype" ]; then
|
||||||
|
continue;
|
||||||
|
fi
|
||||||
|
echo "$cdir"
|
||||||
|
break
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getTypeExcludes
|
||||||
|
{
|
||||||
|
local btype="$1"
|
||||||
|
local host="$2"
|
||||||
|
local etype=
|
||||||
|
local ehost=
|
||||||
|
local edir=
|
||||||
|
|
||||||
|
grep -v '^#' "${BACKUP_CONFS}/exclude.conf" | while read etype ehost edir; do
|
||||||
|
if [ "x$etype" != "x$btype" ]; then
|
||||||
|
continue;
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "x$ehost" != "x$host" ] && [ "x$ehost" != "x*" ]; then
|
||||||
|
continue;
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$edir"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getFetchMode
|
||||||
|
{
|
||||||
|
local host="$1"
|
||||||
|
local fhost=
|
||||||
|
local fmode=
|
||||||
|
local fparams=
|
||||||
|
|
||||||
|
grep '^'"$host" "${BACKUP_CONFS}/fetch-hosts.conf" | while read fhost fmode fparams; do
|
||||||
|
if [ "x$fhost" = "x$host" ]; then
|
||||||
|
echo $fmode;
|
||||||
|
break;
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getFetchScript
|
||||||
|
{
|
||||||
|
local mode="$1"
|
||||||
|
local fmode=
|
||||||
|
local fscript=
|
||||||
|
local fparams=
|
||||||
|
|
||||||
|
grep '^'"$mode" "${BACKUP_CONFS}/fetch-modes.conf" | while read fmode fscript fparams; do
|
||||||
|
if [ "x$mode" = "x$fmode" ]; then
|
||||||
|
echo "$fscript";
|
||||||
|
break;
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function fetchData
|
||||||
|
{
|
||||||
|
local fetchmode="$1"
|
||||||
|
local fetchscript="$2"
|
||||||
|
local host="$3"
|
||||||
|
local btype="$4"
|
||||||
|
local fetchconf="$5"
|
||||||
|
|
||||||
|
if ! [ -d "$BACKUP_TARGET/$host" ]; then
|
||||||
|
mkdir "$BACKUP_TARGET/$host"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local logfile="`mktemp`"
|
||||||
|
chmod 600 "$logfile"
|
||||||
|
|
||||||
|
local tempfile="`mktemp`"
|
||||||
|
chmod 600 "$tempfile"
|
||||||
|
|
||||||
|
(
|
||||||
|
if [ -f "${BACKUP_CONFS}/fetch/$fetchmode.conf" ]; then
|
||||||
|
source "${BACKUP_CONFS}/fetch/$fetchmode.conf"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "${BACKUP_CONFS}/fetch/$fetchmode/$host.conf" ]; then
|
||||||
|
source "${BACKUP_CONFS}/fetch/$fetchmode/$host.conf"
|
||||||
|
fi
|
||||||
|
|
||||||
|
source "$fetchconf"
|
||||||
|
source "${BACKUP_SCRIPTS}/fetch-$fetchscript"
|
||||||
|
FETCH || exit 1
|
||||||
|
) 2>$logfile | gzip -5 > "$tempfile"
|
||||||
|
|
||||||
|
cat "$logfile"
|
||||||
|
if grep -q 'ERROR' $logfile; then
|
||||||
|
echo -e "\t\t\tBackup files will not be rotated"
|
||||||
|
echo "$host" >>"$ERROR_FILE"
|
||||||
|
rm -f "$tempfile"
|
||||||
|
else
|
||||||
|
if [ $btrot -gt 1 ]; then
|
||||||
|
echo -e "\t\t\tRotating files ..."
|
||||||
|
for index in $( seq $btrot -1 2 ); do
|
||||||
|
local previous=$(( $index - 1 ))
|
||||||
|
if ! [ -f "$BACKUP_TARGET/$host/$btype-$previous.tar.gz" ]; then
|
||||||
|
continue;
|
||||||
|
fi
|
||||||
|
/bin/mv -f "$BACKUP_TARGET/$host/$btype-$previous.tar.gz" \
|
||||||
|
"$BACKUP_TARGET/$host/$btype-$index.tar.gz"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
/bin/mv -f "$tempfile" "$BACKUP_TARGET/$host/$btype-1.tar.gz"
|
||||||
|
echo -e "\t\t\tBackup completed"
|
||||||
|
echo "$host $btype" >&3
|
||||||
|
fi
|
||||||
|
rm -f "$logfile"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function executeBackupType
|
||||||
|
{
|
||||||
|
local btype="$1"
|
||||||
|
local host="$2"
|
||||||
|
|
||||||
|
if ! checkType "$btype"; then
|
||||||
|
echo -e "\tCONFIGURATION ERROR: unknown type '$btype'";
|
||||||
|
echo "$host" >>"$ERROR_FILE"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local btname="`getTypeName "$btype"`"
|
||||||
|
local btrot="`getTypeRotation "$btype"`"
|
||||||
|
echo -e "\t$btname ($btype)"
|
||||||
|
|
||||||
|
local btdir="`getTypeDirectory "$btype"`"
|
||||||
|
local btexclude=( `getTypeExcludes "$btype" "$host"` )
|
||||||
|
local fetchmode="`getFetchMode "$host"`"
|
||||||
|
local fetchscript="`getFetchScript "$fetchmode"`"
|
||||||
|
local index=
|
||||||
|
|
||||||
|
echo -e "\t\tDirectory:\t\t$btdir"
|
||||||
|
for index in $( seq 0 $(( ${#btexclude[@]} - 1 )) ); do
|
||||||
|
echo -e "\t\tExcluded directory:\t${btexclude[$index]}"
|
||||||
|
done
|
||||||
|
echo -e "\t\tFetching:\t\tmode $fetchmode, script $fetchscript"
|
||||||
|
|
||||||
|
if [ -f "${BACKUP_SCRIPTS}/fetch-$fetchscript" ]; then
|
||||||
|
echo -e "\t\tStarting backup..."
|
||||||
|
local fetchconf="`mktemp`"
|
||||||
|
chmod 600 "$fetchconf"
|
||||||
|
{
|
||||||
|
echo "backup_directory=\"$btdir\""
|
||||||
|
echo 'backup_exclude=()'
|
||||||
|
for index in $( seq 0 $(( ${#btexclude[@]} - 1 )) ); do
|
||||||
|
echo 'backup_exclude['$index']="'"${btexclude[$index]}"'"'
|
||||||
|
done
|
||||||
|
} > "$fetchconf"
|
||||||
|
fetchData "$fetchmode" "$fetchscript" "$host" "$btype" "$fetchconf"
|
||||||
|
rm -f "$fetchconf"
|
||||||
|
else
|
||||||
|
echo -e "\t\tCONFIGURATION ERROR: unknown fetch script '$fetchscript'"
|
||||||
|
echo
|
||||||
|
echo "$host" >>"$ERROR_FILE"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function backupHost
|
||||||
|
{
|
||||||
|
local mode="$1"
|
||||||
|
local host="$2"
|
||||||
|
|
||||||
|
local types=( `getBackupTypes "$mode" "$host"` )
|
||||||
|
if [ ${#types[*]} -eq 0 ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "======================================================"
|
||||||
|
echo
|
||||||
|
printHostHeader "$host"
|
||||||
|
echo
|
||||||
|
|
||||||
|
local index=
|
||||||
|
for index in $( seq 0 $(( ${#types[@]} - 1 )) ); do
|
||||||
|
local btype="${types[$index]}"
|
||||||
|
executeBackupType "$btype" "$host"
|
||||||
|
done
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function backupHosts
|
||||||
|
{
|
||||||
|
local mode="$1"
|
||||||
|
local hosts=( `getHosts` )
|
||||||
|
local index=
|
||||||
|
|
||||||
|
for index in $( seq 0 $((${#hosts[@]} - 1)));
|
||||||
|
do
|
||||||
|
local host="${hosts[$index]}"
|
||||||
|
backupHost "$mode" "$host"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function computeTime
|
||||||
|
{
|
||||||
|
local total=$(( $2 - $1 ))
|
||||||
|
date -u -d "@$total" +'%d %H %M %S' | sed -e 's/ 0/ /g' | (
|
||||||
|
read day hour minutes seconds;
|
||||||
|
echo $(( $day - 1 ))' day(s), '$hour' hour(s), '$minutes' minute(s) and '$seconds' second(s)'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getNextLogFile
|
||||||
|
{
|
||||||
|
local base="$BACKUP_LOG/`date +'%Y-%m-%d'`"
|
||||||
|
local counter=1
|
||||||
|
while [ -f "$base-$counter.log" ]; do
|
||||||
|
counter=$(( $counter + 1 ))
|
||||||
|
done
|
||||||
|
echo "$base-$counter.log"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
checkMode "$MODE"
|
||||||
|
|
||||||
|
if [ -f "${BACKUP_SCRIPTS}/postprocess" ]; then
|
||||||
|
PP_DIR=`mktemp -d`
|
||||||
|
chmod 700 $PP_DIR
|
||||||
|
mkfifo -m 600 "$PP_DIR/pp_fifo"
|
||||||
|
bash "${BACKUP_SCRIPTS}/postprocess" "$PP_DIR" <"$PP_DIR/pp_fifo" >"$PP_DIR/log" 2>&1 &
|
||||||
|
exec 3>"$PP_DIR/pp_fifo"
|
||||||
|
else
|
||||||
|
exec 3>/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
ERROR_FILE="`mktemp`"
|
||||||
|
chmod 600 "$ERROR_FILE"
|
||||||
|
LOG_FILE="`mktemp`"
|
||||||
|
chmod 600 "$LOG_FILE"
|
||||||
|
START_FULL="`date +'%Y-%m-%d %H:%M:%S'`"
|
||||||
|
START_TS="`date +'%s'`"
|
||||||
|
|
||||||
|
backupHosts "$MODE" >$LOG_FILE 2>&1
|
||||||
|
END_FULL="`date +'%Y-%m-%d %H:%M:%S'`"
|
||||||
|
END_TS="`date +'%s'`"
|
||||||
|
|
||||||
|
if [ -f "${BACKUP_SCRIPTS}/postprocess" ] && [ -f "$PP_DIR/pid" ]; then
|
||||||
|
PP_PID=`cat $PP_DIR/pid`
|
||||||
|
exec 3>&-
|
||||||
|
while [ -e "/proc/$PP_PID" ]; do
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
PP_FULL="`date +'%Y-%m-%d %H:%M:%S'`"
|
||||||
|
PP_TS="`date +'%s'`"
|
||||||
|
fi
|
||||||
|
|
||||||
|
FINAL_LOG=`getNextLogFile`
|
||||||
|
exec 7>&1 >$FINAL_LOG
|
||||||
|
if [ -z "`cat $ERROR_FILE`" ]; then
|
||||||
|
echo "Successful backup."
|
||||||
|
TAG="BACKUP"
|
||||||
|
else
|
||||||
|
echo "BACKUP FAILURE!"
|
||||||
|
echo
|
||||||
|
echo "The following hosts encountered errors:"
|
||||||
|
for host in `cat "$ERROR_FILE" | sort | uniq`; do
|
||||||
|
echo -e "\t* $host"
|
||||||
|
done
|
||||||
|
TAG="BACKUP-FAILURE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "${BACKUP_SCRIPTS}/postprocess" ] && [ -f "$PP_DIR/log" ] && grep -q ERROR "$PP_DIR/log"; then
|
||||||
|
echo "There were errors during post-processing."
|
||||||
|
if [ "$TAG" = "BACKUP" ]; then
|
||||||
|
TAG="BACKUP-WARNING"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
spent="`computeTime "$START_TS" "$END_TS"`"
|
||||||
|
totsize="`du -sh ${BACKUP_TARGET} | awk '{ print $1 }'`"
|
||||||
|
latestsize="`du -sch ${BACKUP_TARGET}/*/*-1.tar.gz | tail -n 1 | awk '{ print $1 }'`"
|
||||||
|
freesize="`df -Ph ${BACKUP_TARGET} | tail -n 1 | awk '{ print $4 }'`"
|
||||||
|
echo "Full log below."
|
||||||
|
echo
|
||||||
|
echo "======================================================"
|
||||||
|
echo "STATISTICS"
|
||||||
|
echo -e "\tStarted:\t\t$START_FULL"
|
||||||
|
echo -e "\tEnded:\t\t\t$END_FULL"
|
||||||
|
if [ -f "${BACKUP_SCRIPTS}/postprocess" ] && [ -f "$PP_DIR/log" ]; then
|
||||||
|
echo -e "\tBackup time:\t\t$spent"
|
||||||
|
echo -e "\tPost-processing ended:\t$PP_FULL"
|
||||||
|
spent="`computeTime "$START_TS" "$PP_TS"`"
|
||||||
|
fi
|
||||||
|
echo -e "\tTotal time:\t\t$spent"
|
||||||
|
echo -e "\tTotal size:\t\t$totsize"
|
||||||
|
echo -e "\tLatest:\t\t\t$latestsize"
|
||||||
|
echo -e "\tFree space:\t\t$freesize"
|
||||||
|
echo
|
||||||
|
cat "$LOG_FILE"
|
||||||
|
if [ -f "${BACKUP_SCRIPTS}/postprocess" ] && [ -f "$PP_DIR/log" ]; then
|
||||||
|
echo
|
||||||
|
cat "$PP_DIR/log"
|
||||||
|
rm -rf "$PP_DIR"
|
||||||
|
fi
|
||||||
|
exec 1>&7 7>&-
|
||||||
|
|
||||||
|
cat "$FINAL_LOG" | mail -s "[$TAG] - $BACKUP_NAME - `date +'%Y-%m-%d'` - $MODE mode" $BACKUP_EMAIL
|
||||||
|
|
||||||
|
rm -f "$ERROR_FILE" "$LOG_FILE"
|
19
backup/server/backup.conf
Normal file
19
backup/server/backup.conf
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# Main backup server configuration
|
||||||
|
|
||||||
|
# "Title" for the backup, used in e-mail reports
|
||||||
|
BACKUP_NAME="Change me"
|
||||||
|
|
||||||
|
# Directory that contains the various configuration files
|
||||||
|
BACKUP_CONFS=/etc/backup.conf.d
|
||||||
|
|
||||||
|
# Directory that contains the various scripts (fetching, postprocessing)
|
||||||
|
BACKUP_SCRIPTS=/usr/local/share/backup
|
||||||
|
|
||||||
|
# Target directory for the archives
|
||||||
|
BACKUP_TARGET=/change/this
|
||||||
|
|
||||||
|
# Logs directory
|
||||||
|
BACKUP_LOG=/var/log/backup
|
||||||
|
|
||||||
|
# E-mail address to send reports to
|
||||||
|
BACKUP_MAIL=root
|
7
backup/server/backup.conf.d/exclude.conf
Normal file
7
backup/server/backup.conf.d/exclude.conf
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# Locations to exclude from backups, based on backup types and optionally host
|
||||||
|
# names. If a location is to be excluded for all hosts, "*" may be used.
|
||||||
|
#
|
||||||
|
# Backup type Host name Directory to exclude
|
||||||
|
full * /var/tmp
|
||||||
|
full * /var/cache
|
||||||
|
full * /tmp
|
7
backup/server/backup.conf.d/fetch-hosts.conf
Normal file
7
backup/server/backup.conf.d/fetch-hosts.conf
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# For each host to backup, define the fetch mode to use. Fetch modes are
|
||||||
|
# defined in fetch-modes.conf
|
||||||
|
#
|
||||||
|
# Host Mode
|
||||||
|
backup-server self
|
||||||
|
ve-host ve
|
||||||
|
remote-system remote
|
7
backup/server/backup.conf.d/fetch-modes.conf
Normal file
7
backup/server/backup.conf.d/fetch-modes.conf
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# List each "fetch mode", associating an (arbitrary) name with the name of the
|
||||||
|
# script to use.
|
||||||
|
#
|
||||||
|
# Mode Fetch script
|
||||||
|
self local
|
||||||
|
ve local
|
||||||
|
remote ssh
|
12
backup/server/backup.conf.d/fetch/remote.conf
Normal file
12
backup/server/backup.conf.d/fetch/remote.conf
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# Configuration for remote fetches
|
||||||
|
|
||||||
|
# SSH port to connect to
|
||||||
|
SSH_PORT="22"
|
||||||
|
# Backup user on the remote system
|
||||||
|
SSH_USER="rbackup"
|
||||||
|
|
||||||
|
# SSH private key
|
||||||
|
SSH_KEY="/path/to/private.key"
|
||||||
|
|
||||||
|
# Host to connect to. Do not change this unless you know what you're doing.
|
||||||
|
SSH_HOST="$host"
|
7
backup/server/backup.conf.d/fetch/ve.conf
Normal file
7
backup/server/backup.conf.d/fetch/ve.conf
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# This is an example for "local" fetching on a set of OpenVZ VE's whose roots
|
||||||
|
# are mounted under /mnt/virtual-envs
|
||||||
|
|
||||||
|
BASE=/mnt/virtual-envs
|
||||||
|
|
||||||
|
# Then, for each host, there's a file in the ve/ subdirectory which defines the
|
||||||
|
# root.
|
2
backup/server/backup.conf.d/fetch/ve/ve-host.conf
Normal file
2
backup/server/backup.conf.d/fetch/ve/ve-host.conf
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Define the root for a VE host
|
||||||
|
ROOT=1000
|
6
backup/server/backup.conf.d/hosts.conf
Normal file
6
backup/server/backup.conf.d/hosts.conf
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# List all hosts and the title they will be given in the report.
|
||||||
|
#
|
||||||
|
# Host Title
|
||||||
|
backup-server Backup server
|
||||||
|
ve-host Some OpenVZ virtual environment
|
||||||
|
remote-system A remote system
|
11
backup/server/backup.conf.d/modes.conf
Normal file
11
backup/server/backup.conf.d/modes.conf
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# Define backup modes. A backup mode corresponds to a set of locations to
|
||||||
|
# backup; these locations (defined in types.conf and exclude.conf) can be
|
||||||
|
# listed for all hosts (using "*" as the host name) or for some specific hosts.
|
||||||
|
#
|
||||||
|
# When the main backup script is called, its first (and only) parameter should
|
||||||
|
# be the name of the mode.
|
||||||
|
#
|
||||||
|
# Mode Host name Backup types
|
||||||
|
daily * config,logs,varlib
|
||||||
|
daily remote-system home
|
||||||
|
weekly * full
|
1
backup/server/backup.conf.d/post/crypto.key
Normal file
1
backup/server/backup.conf.d/post/crypto.key
Normal file
|
@ -0,0 +1 @@
|
||||||
|
key to use when encrypting files during post-processing goes here
|
13
backup/server/backup.conf.d/post/ftp-access.conf
Normal file
13
backup/server/backup.conf.d/post/ftp-access.conf
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Configuration file for the example post-processing script
|
||||||
|
|
||||||
|
# The FTP server
|
||||||
|
ftp_host="remote-ftp.example.org"
|
||||||
|
|
||||||
|
# User name on the FTP server
|
||||||
|
ftp_user="backup"
|
||||||
|
|
||||||
|
# Password on the FTP server
|
||||||
|
ftp_pass="password"
|
||||||
|
|
||||||
|
# Amount of rotations on the server
|
||||||
|
ftp_rotate=4
|
12
backup/server/backup.conf.d/types.conf
Normal file
12
backup/server/backup.conf.d/types.conf
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# Backup "types" correspond to locations to backup, along with a description
|
||||||
|
# used in the report, and the amount of copies to keep.
|
||||||
|
#
|
||||||
|
# The exclude.conf file is used in conjunction with this file to determine
|
||||||
|
# locations to exclude from each specific backup type.
|
||||||
|
#
|
||||||
|
# Backup type Rotation Directory Description
|
||||||
|
config 7 /etc System configuration
|
||||||
|
logs 7 /var/log Logs
|
||||||
|
varlib 7 /var/lib Various runtime information
|
||||||
|
home 7 /home Home directory
|
||||||
|
full 2 / Full backup
|
6
backup/server/crontab.example
Normal file
6
backup/server/crontab.example
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
SHELL=/bin/sh
|
||||||
|
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
|
||||||
|
|
||||||
|
# m h dom mon dow user command
|
||||||
|
12 1 * * 0 root /usr/local/sbin/backup weekly
|
||||||
|
12 1 * * 1-6 root /usr/local/sbin/backup daily
|
44
backup/server/share/fetch-local
Normal file
44
backup/server/share/fetch-local
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
function FETCH
|
||||||
|
{
|
||||||
|
local fetchroot="/"
|
||||||
|
|
||||||
|
if [ "x$BASE" != "x" ]; then
|
||||||
|
fetchroot="$fetchroot/$BASE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "x$ROOT" != "x" ]; then
|
||||||
|
fetchroot="$fetchroot/$ROOT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
fetchroot="`echo "$fetchroot" | sed -e 's/\/\+/\//g'`"
|
||||||
|
if ! [ -d "$fetchroot" ]; then
|
||||||
|
echo -e "\t\t\tCONFIGURATION ERROR: missing root directory '$fetchroot'" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo -e "\t\t\tRoot directory:\t$fetchroot" >&2
|
||||||
|
|
||||||
|
local tarerrors="`mktemp`"
|
||||||
|
chmod 600 "$tarerrors"
|
||||||
|
|
||||||
|
local command='tar --numeric-owner --one-file-system --ignore-failed-read --warning=none -c'
|
||||||
|
local index=
|
||||||
|
for index in $( seq 0 $(( ${#backup_exclude[@]} - 1 )) ); do
|
||||||
|
command="$command"' "--exclude='"`echo "./${backup_exclude[$index]}" | sed -e 's/\/\+/\//g' -e 's/\/$//'`"'"'
|
||||||
|
done
|
||||||
|
command="$command"' ".'"$backup_directory"'"'
|
||||||
|
|
||||||
|
cd "$fetchroot"
|
||||||
|
eval $command 2>"$tarerrors"
|
||||||
|
if ! [ -z "`cat $tarerrors`" ]; then
|
||||||
|
echo -e "\t\t\tFETCH ERROR: something went wrong while creating the archive:" >&2
|
||||||
|
echo -e "-----------------------------------------------------" >&2
|
||||||
|
cat "$tarerrors" >&2
|
||||||
|
echo -e "-----------------------------------------------------" >&2
|
||||||
|
rm -f "$tarerrors"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
rm -f "$tarerrors"
|
||||||
|
return 0
|
||||||
|
}
|
55
backup/server/share/fetch-ssh
Normal file
55
backup/server/share/fetch-ssh
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
function FETCH
|
||||||
|
{
|
||||||
|
if [ -z "$SSH_KEY" ]; then
|
||||||
|
echo -e "\t\t\tCONFIGURATION ERROR: no SSH key" >&2
|
||||||
|
exit 1
|
||||||
|
elif [ -z "$SSH_HOST" ]; then
|
||||||
|
echo -e "\t\t\tCONFIGURATION ERROR: no destination SSH host" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local command="ssh -T"
|
||||||
|
|
||||||
|
if [ "x$SSH_USER" != "x" ]; then
|
||||||
|
command="$command -l $SSH_USER"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "x$SSH_PORT" != "x" ]; then
|
||||||
|
command="$command -p $SSH_PORT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
command="$command -i $SSH_KEY $SSH_HOST echo"
|
||||||
|
|
||||||
|
local errorfile="`mktemp`"
|
||||||
|
chmod 600 "$errorfile"
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "$backup_directory"
|
||||||
|
local index=
|
||||||
|
for index in $( seq 0 $(( ${#backup_exclude[@]} - 1 )) ); do
|
||||||
|
echo "${backup_exclude[$index]}"
|
||||||
|
done
|
||||||
|
} | eval $command 2>"$errorfile"
|
||||||
|
|
||||||
|
local nerrfile=`mktemp`
|
||||||
|
echo 0 > $nerrfile
|
||||||
|
cat $errorfile | while read line; do
|
||||||
|
if [[ "$line" =~ ^\.\.\.SRC\.\.\..*$ ]]; then
|
||||||
|
local text="`echo "$line" | sed -e 's/^.........//'`"
|
||||||
|
if [[ "$text" =~ ERROR ]]; then
|
||||||
|
echo 1 > $nerrfile;
|
||||||
|
fi
|
||||||
|
printf "\t\t\t%s\n" "$text" >&2
|
||||||
|
else
|
||||||
|
echo -e "\t\t\tCONNECTION ERROR: SSH or the remote script caused errors:" >&2
|
||||||
|
printf "\t\t\t\t%s\n" "$line" >&2
|
||||||
|
echo 1 > $nerrfile
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
local rv="`cat $nerrfile`"
|
||||||
|
rm -f "$nerrfile" "$errorfile"
|
||||||
|
return $rv
|
||||||
|
}
|
||||||
|
|
179
backup/server/share/postprocess
Executable file
179
backup/server/share/postprocess
Executable file
|
@ -0,0 +1,179 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#
|
||||||
|
# An example post-processing script
|
||||||
|
#
|
||||||
|
# This script runs in parallel to the main backup script (once the actual
|
||||||
|
# data fetching is completed, the main script will wait for the post-processing
|
||||||
|
# script to complete).
|
||||||
|
#
|
||||||
|
# It uses openssl to encrypt backup archives, then sends them to a remote FTP
|
||||||
|
# server using kermit; backups of type "full" (i.e. root filesystems) will not
|
||||||
|
# be processed.
|
||||||
|
#
|
||||||
|
|
||||||
|
[ -z "$1" ] && exit 1
|
||||||
|
echo $$ >"$1/pid"
|
||||||
|
|
||||||
|
source /etc/backup.conf
|
||||||
|
source "${BACKUP_CONFS}/post/ftp-access.conf"
|
||||||
|
|
||||||
|
if ! [ -f "${BACKUP_CONFS}/post/crypto.key" ]; then
|
||||||
|
echo "ERROR: no cryptographic key"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
function crypt
|
||||||
|
{
|
||||||
|
openssl enc -kfile "${BACKUP_CONFS}/post/crypto.key" -aes-256-cbc -e
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function makeFTPScript
|
||||||
|
{
|
||||||
|
local script=`mktemp`
|
||||||
|
chmod 600 $script
|
||||||
|
{
|
||||||
|
echo "set ftp passive-mode off"
|
||||||
|
echo "ftp open $ftp_host /user:$ftp_user /password:$ftp_pass"
|
||||||
|
echo "if fail exit 1 Connection failed"
|
||||||
|
echo 'if not \v(ftp_loggedin) exit 1 Login failed'
|
||||||
|
for cmd in "$@"; do
|
||||||
|
if [[ "$cmd" =~ ^lcd\ ]]; then
|
||||||
|
echo "$cmd"
|
||||||
|
else
|
||||||
|
echo "ftp $cmd"
|
||||||
|
echo "if fail exit 1 ftp $cmd: \\v(ftp_message)"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "ftp bye"
|
||||||
|
echo "exit 0"
|
||||||
|
} > $script
|
||||||
|
echo $script
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function executeKermitScript
|
||||||
|
{
|
||||||
|
local script="$1"
|
||||||
|
local dest="$2"
|
||||||
|
|
||||||
|
wermit + < $script > $dest 2>/dev/null
|
||||||
|
local result=$?
|
||||||
|
rm -f "$script"
|
||||||
|
return $result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function fileExists
|
||||||
|
{
|
||||||
|
local file="$1"
|
||||||
|
local script=`makeFTPScript "check $file"`
|
||||||
|
local output=`mktemp`
|
||||||
|
if ! executeKermitScript "$script" $output; then
|
||||||
|
if grep -q '^ftp check ' $output; then
|
||||||
|
echo "no"
|
||||||
|
else
|
||||||
|
echo "error"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "yes"
|
||||||
|
fi
|
||||||
|
rm -f "$output"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function rotateRemoteFilesFor
|
||||||
|
{
|
||||||
|
local host="$1"
|
||||||
|
local btype="$2"
|
||||||
|
|
||||||
|
local commands=()
|
||||||
|
local fnum=
|
||||||
|
for fnum in $( seq $ftp_rotate -1 1 ); do
|
||||||
|
local fname="/encrypted-${host}-${btype}-$fnum.tar.gz"
|
||||||
|
local fe=`fileExists "$fname"`
|
||||||
|
if [ "x$fe" = "xerror" ]; then
|
||||||
|
echo "FTP check error for $fname"
|
||||||
|
exit 1;
|
||||||
|
elif [ "x$fe" = "xyes" ]; then
|
||||||
|
local ncommand=
|
||||||
|
if [ $fnum -eq $ftp_rotate ]; then
|
||||||
|
ncommand="delete $fname"
|
||||||
|
else
|
||||||
|
ncommand="rename $fname /encrypted-${host}-${btype}-$(( $fnum + 1 )).tar.gz"
|
||||||
|
fi
|
||||||
|
commands=( "${commands[@]}" "$ncommand" )
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
[ ${#commands[@]} -eq 0 ] && return 0
|
||||||
|
|
||||||
|
local temp=`mktemp`
|
||||||
|
executeKermitScript `makeFTPScript "${commands[@]}"` "$temp"
|
||||||
|
local rv=$?
|
||||||
|
rm -f "$temp"
|
||||||
|
return $rv
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function putRemoteFileFor
|
||||||
|
{
|
||||||
|
local host="$1"
|
||||||
|
local btype="$2"
|
||||||
|
local tempdir="$3"
|
||||||
|
|
||||||
|
if ! rotateRemoteFilesFor "$host" "$btype"; then
|
||||||
|
return 1;
|
||||||
|
fi
|
||||||
|
|
||||||
|
local temp=`mktemp`
|
||||||
|
executeKermitScript `makeFTPScript "lcd $tempdir" "put encrypted-${host}-${btype}-1.tar.gz"` $temp
|
||||||
|
local rv=$?
|
||||||
|
rm -f "$temp"
|
||||||
|
|
||||||
|
return $rv
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function handleFile
|
||||||
|
{
|
||||||
|
local host="$1"
|
||||||
|
local btype="$2"
|
||||||
|
|
||||||
|
if [ "x$btype" = "xfull" ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "\tCopying data for $host / $btype to FTP server" >&2
|
||||||
|
|
||||||
|
local tempdir="`mktemp -d`"
|
||||||
|
local src="${BACKUP_TARGET}/${host}/${btype}-1.tar.gz"
|
||||||
|
local dest="$tempdir/encrypted-${host}-${btype}-1.tar.gz"
|
||||||
|
cat "$src" | crypt > $dest
|
||||||
|
|
||||||
|
putRemoteFileFor $host $btype $tempdir
|
||||||
|
|
||||||
|
rm -rf "$tempdir"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function initPost
|
||||||
|
{
|
||||||
|
echo "======================================================"
|
||||||
|
echo "POST-PROCESSING BACKUPS"
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishPost
|
||||||
|
{
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
initPost
|
||||||
|
while read host btype; do
|
||||||
|
handleFile $host $btype 2>&1
|
||||||
|
done
|
||||||
|
finishPost
|
31
backup/ssh-client/README
Normal file
31
backup/ssh-client/README
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
Client-side scripts for SSH backup
|
||||||
|
===================================
|
||||||
|
|
||||||
|
The scripts in this directory are meant to be used with the backup server's SSH
|
||||||
|
fetch script.
|
||||||
|
|
||||||
|
|
||||||
|
Installation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
1/ Create an user that uses the backup-user-shell as its shell and
|
||||||
|
/var/lib/rbackup as its home directory.
|
||||||
|
2/ Authorize the server's SSH key (limiting the key to the backup server's
|
||||||
|
address is a good idea) to log in as that specific user
|
||||||
|
3/ Authorize the backup user to run the main script as root (see sudo.example)
|
||||||
|
|
||||||
|
If you want the archive sent to the backup server to be encrypted locally,
|
||||||
|
write the encryption key in the /etc/rbackup-encryption-key file (mode 0600 for
|
||||||
|
root). Otherwise, make sure the file does not exist.
|
||||||
|
|
||||||
|
|
||||||
|
Notes
|
||||||
|
------
|
||||||
|
|
||||||
|
1/ If the backup server is compromised, then so is the system being backed up.
|
||||||
|
|
||||||
|
2/ If you use local encryption (which would mitigate the problem described
|
||||||
|
above), make sure you have a copy of the key somewhere.
|
||||||
|
|
||||||
|
3/ If you want to use something other than /var/lib/rbackup as the user's home
|
||||||
|
directory, you'll have to change the backup-user-shell script.
|
56
backup/ssh-client/backup-client
Executable file
56
backup/ssh-client/backup-client
Executable file
|
@ -0,0 +1,56 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
function printToServer
|
||||||
|
{
|
||||||
|
echo "...SRC...$*" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
function catToServer
|
||||||
|
{
|
||||||
|
sed -e 's/^/...SRC.../' < "$1" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
read backup_directory
|
||||||
|
|
||||||
|
if [ -z "$backup_directory" ]; then
|
||||||
|
printToServer "ERROR: no directory to backup"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
backup_directory="`echo "/$backup_directory" | sed -e 's/\/\+/\//g' -e 's/\/$//'`"
|
||||||
|
if ! [ -d "$backup_directory" ]; then
|
||||||
|
printToServer "ERROR: missing directory $backup_directory"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
backup_exclude=( )
|
||||||
|
while read backup_edir; do
|
||||||
|
backup_exclude=( ${backup_exclude[@]} $backup_edir )
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
command='ionice -c2 -n7 tar --numeric-owner --one-file-system --ignore-failed-read --warning=none -c'
|
||||||
|
index=
|
||||||
|
for index in $( seq 0 $(( ${#backup_exclude[@]} - 1 )) ); do
|
||||||
|
command="$command"' "--exclude='"`echo "./${backup_exclude[$index]}" | sed -e 's/\/\+/\//g' -e 's/\/$//'`"'"'
|
||||||
|
done
|
||||||
|
command="$command"' ".'"$backup_directory"'"'
|
||||||
|
if [ -f "/etc/rbackup-encryption-key" ]; then
|
||||||
|
command="$command | nice -n20 openssl enc -kfile /etc/rbackup-encryption-key -aes-256-cbc -e"
|
||||||
|
fi
|
||||||
|
printToServer "Remote host ready"
|
||||||
|
|
||||||
|
tarerrors="`mktemp`"
|
||||||
|
chmod 600 "$tarerrors"
|
||||||
|
cd /
|
||||||
|
|
||||||
|
eval $command 2>"$tarerrors"
|
||||||
|
if ! [ -z "`cat $tarerrors`" ]; then
|
||||||
|
printToServer "FETCH ERROR: something went wrong while creating the archive:" >&2
|
||||||
|
printToServer "-----------------------------------------------------" >&2
|
||||||
|
catToServer "$tarerrors"
|
||||||
|
printToServer "-----------------------------------------------------" >&2
|
||||||
|
rm -f "$tarerrors"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
rm -f "$tarerrors"
|
3
backup/ssh-client/backup-user-shell
Executable file
3
backup/ssh-client/backup-user-shell
Executable file
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
exec sudo /bin/bash /var/lib/rbackup/backup-client
|
4
backup/ssh-client/sudo.example
Normal file
4
backup/ssh-client/sudo.example
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# Remote backup system, assuming the remote backup user is "rbackup" and
|
||||||
|
# the script has been installed in /var/lib/rbackup
|
||||||
|
#
|
||||||
|
rbackup ALL= (root) NOPASSWD: /bin/bash /var/lib/rbackup/backup-client
|
Loading…
Reference in a new issue