Duplicity: Difference between revisions

From miki
Jump to navigation Jump to search
Line 78: Line 78:
Install in crontab (<code>crontab -e</code>) by adding line:
Install in crontab (<code>crontab -e</code>) by adding line:
0 0 * * * /root/scripts/etc/backup.sh >>/var/log/duplicity/etc.log
0 0 * * * /root/scripts/etc/backup.sh >>/var/log/duplicity/etc.log

== Complete system backup script ==
We give a set of configuration and scripts to perform the backup of a complete system, typically an internet server. This must be installed as user '''root'''.

;Generate GPG key
<source lang="bash">
# gpg --gen-key
gpg --full-generate-key # Better, avoid system defaults
# Choose: RSA 3072, never expire
gpg -a -o root@server.org.asc --export-secret-keys root@server.org
</source>
:Key generation may fail on remote system due to lack of HW RNG. In that case the key must be generated on another system and imported, and then '''trusted'''
<source lang="bash">
gpg --import root@server.org.asc
gpg --edit-key root@server.org # Use 'trust' to set trust to max.
</source>
:Get the key identifiers. These are the strings <code>SSSSSSSS</code> and <code>EEEEEEEE</code> for the signing and encryption key to use.
<source lang="bash">
gpg --list-key root@server.org
# pub 3072R/SSSSSSSS 2019-04-18
# uid root server.org <root@server.org>
# sub 3072R/EEEEEEEE 2019-04-18
</source>

;Install
<source lang="bash">
apt install duplicity keychain
</source>

;Enable keychain
* keychain must be loaded when logging in as root. For security, an existing keychain is always deleted on logging. To enable the backup, we must log at least once as boot after system restart so that SSH credentials are available to duplicity.
: Add to {{file|~/.bashrc}}:
<source lang="bash">
# KEYCHAIN
# keychain will keep SSH passphrase after logout for cron script, but
# ... will delete it and ask again anytime we log back in (for intruders).
# ... except if within a tmux session started by root
if ! [ -n "$TMUX" -a -O "${TMUX/,*/}" ]; then
keychain --nogui --clear id_rsa
fi
. ~/.keychain/$HOSTNAME-sh
</source>

;Duplicity configuration
* Create file {{file|~/.duplicity.conf}}, editing the fields as necessary
<source lang="bash">
# Backup - duplicity config
#
# Source: https://wiki.debian.org/Duplicity
#
# Set a few variable to ease duplicity usage:
#
# source .duplicity.conf
# duplicity $OPTS collection-status $RPATH/etc # Get status for backup 'etc'
# duplicity $OPTS list-current-files $RPATH/etc # Get list of files
# duplicity $OPTS --time 3D list-current-files $RPATH/etc # Get list of files
# duplicity $OPTS restore $RPATH/etc etc # Restore to folder 'etc' locally
# duplicity $OPTS $RPATH/etc etc # Idem


## Remote settings
# host
HOST=backupserver.org
# user
NAME=backupuser
# path
LPATH=/absolute/path/to/backup
# URL (notice double slash in path as required for absolute paths)
RPATH=scp://$NAME@$HOST/$LPATH
# Mkdir command
MKDIR="ssh $NAME@$HOST mkdir -p"
# SSH credentials
[[ -f /root/.keychain/$HOSTNAME-sh ]] && source /root/.keychain/$HOSTNAME-sh


## GPG keys and passphrase (will be cleared on backup)
SIGNKEY=SSSSSSSS
ENCRKEY=EEEEEEEE
export PASSPHRASE='XXXXXXXXXXXXX'
export SIGN_PASSPHRASE=$PASSPHRASE
export GNUPGHOME=/root/.gnupg


# Directories to backup, and duplicity options if any
# (from http://unix.stackexchange.com/questions/1067/what-directories-do-i-need-to-back-up)
BACKDIRS=()
BACKDIRS+=('/etc')
BACKDIRS+=('/root --exclude /root/.cache/duplicity')
BACKDIRS+=('/home')
BACKDIRS+=('/var --exclude /var/cache --exclude /var/lock --exclude /var/run --exclude /var/tmp')
BACKDIRS+=('/var/cache') # Keep /var/cache to have all .deb files for reinstall
# Below we backup everything else, to ease reinstall
BACKDIRS+=('/ --exclude /etc --exclude /home --exclude /var --exclude /root --exclude /tmp')
# ... or we exclude binaries. Reinstall will take packages from /var/cache/apt
# BACKDIRS+=('/ --exclude /etc --exclude /home --exclude /var --exclude /root --exclude /tmp --exclude /lib --include /usr/local --exclude /usr')


## OPTIONS - Standard
OPTS=""
OPTS="$OPTS --encrypt-key $ENCRKEY --sign-key $SIGNKEY" # GPG options
OPTS="$OPTS --exclude-other-filesystems" # Don't cross filesystem boundaries
OPTS="$OPTS --full-if-older-than 1M" # Full if older than 1 month, incr otherwise
#OPTS="$OPTS --exclude **/pictures/XXX" # More exclude patterns


## OPTIONS - Cleanup
# Will require --force to effectively remove the files
OPTS_CLEANUP="remove-older-than 2M"
</source>

;Backup script
* Create file {{file|~/bin/backup.sh}}
<source lang="bash">
#! /bin/bash
# Backup - duplicity script
#
# Source: https://wiki.debian.org/Duplicity

# uncomment for debug
#set -x

CONF=duplicity.conf
source /root/.$CONF

# Build some files first
rm -f /tmp/$CONF /tmp/README.txt
sed -r "s/PASSPHRASE='.*'/PASSPHRASE='XXXXXXXXXXXXX'/" /root/.$CONF > /tmp/$CONF
echo -e "Backup files for host ober.noekeon.org\nGenerated by duplicity with script backup.sh.\nSee $CONF for examples of use (including restore).\n\nBackup signed and encrypted with gpg key:\n\n" > /tmp/README.txt
gpg --list-key $ENCRKEY >> /tmp/README.txt
echo >> /tmp/README.txt
gpg -a --export-secret-keys $ENCRKEY >> /tmp/README.txt

# Backup script + conf first, and fail/exit immediately on error (to avoid getting banned due to too many ssh failure in for loop below)
RPATHSCP=${RPATH/scp:\/\//}
RPATHSCP=${RPATHSCP/\/\//:/}
scp $0 ${RPATHSCP}/backup.sh && scp /tmp/$CONF ${RPATHSCP}/ && scp /tmp/README.txt ${RPATHSCP}/
if [ $? != 0 ]; then
logger "$0 - backup failed for script + conf. See log file (/root/.cache/duplicity/lastbackup.log, or check crontab -l) for details. Aborting!"
rm -f /tmp/$CONF /tmp/README.txt
exit 255
fi
rm -f /tmp/$CONF /tmp/README.txt

# Add options from command line (eg. '--force')
OPTS="$OPTS $*"

# Loop on directories
echo -n "---- Incremental backup of $HOSTNAME ---- "; date
for i in ${!BACKDIRS[*]}
do
# Extract directory name as first keyword in the list - TODO: manage space
set ${BACKDIRS[$i]} # e.g. '/var --exclude /var/cache'
DIR=$1 # e.g '/var'
shift # e.g. '--exclude /var/cache'

echo Starting backup of directory $DIR
# create directory, then backup, then erase old backups
$MKDIR $LPATH$DIR && duplicity $OPTS "$@" $DIR $RPATH$DIR && duplicity "$@" $OPTS $OPTS_CLEANUP $RPATH$DIR || logger "$0 - backup failed for /$DIR. See log file for details."
done

# Loop on directories again to verify integrity
for i in ${!BACKDIRS[*]}
do
# Extract directory name as first keyword in the list - TODO: manage space
set ${BACKDIRS[$i]}
DIR=$1
shift

# verify backup integrity
# echo Verify backup integrity of directory $DIR
# duplicity $OPTS verify $RPATH$DIR $DIR || logger "$0 - backup integrity test failed for /$DIR. See log file for details."
done
echo -n "---- Finished backup on $HOSTNAME ---- "; date

# Exit
exit 0
</source>

* Create crontab entries:
<source lang="text">
35 4 * * 1-6 /root/bin/backup.sh > /root/.cache/duplicity/lastbackup.log 2>&1
35 4 * * 7 /root/bin/backup.sh --force > /root/.cache/duplicity/lastbackup-force.log 2>&1
</source>

* It is best to run the backup once manually to make sure everything is ready (SSH known host, destination directory created...).

Revision as of 05:53, 19 April 2019

Installation

See DuplicityBackupHowto.

sudo apt-get install duplicity
sudo apt-get install ncftp            # to use duplicity ftp backup

Basic usage

Relative vs absolute paths
  • In all examples below, the paths on the remote server are relative (ftp://FtpUserID@ftp.domain.com/etcunset, scp://scp_user@scp.domain.com/var/).
  • To use an absolute path, one must add an extra slash, like in scp://scp_user@scp.domain.com//absolute/path/to/var/.
Basic backup
export PASSPHRASE=SomeLongGeneratedHardToCrackKey
export FTP_PASSWORD=WhateverPasswordYouSetUp
duplicity /etc ftp://FtpUserID@ftp.domain.com/etc
unset PASSPHRASE
unset FTP_PASSWORD

(or change ftp URL: to file:///my/filesystem/path).

Basic verify (compare)
export PASSPHRASE=SomeLongGeneratedHardToCrackKey
export FTP_PASSWORD=WhateverPasswordYouSetUp
duplicity verify ftp://FtpUserID@ftp.domain.com/etc /etc
unset PASSPHRASE
unset FTP_PASSWORD
Collection status
duplicity collection-status scp://scp_user@scp.domain.com/var/ var


Basic file listing
# Using ftp
duplicity list-current-files ftp://FtpUserID@ftp.domain.com/etcunset PASSPHRASE

# Using scp, and GPG
duplicity --encrypt-key $ENCRKEY --sign-key $SIGNKEY list-current-files scp://scp_user@scp.domain.com/var/ var
Basic restore
# Restore latest backup from /etc here
duplicity ftp://FtpUserID@ftp.domain.com/etc .

# Restore /etc as it was 3 days ago
duplicity --time 3D ftp://FtpUserID@ftp.domain.com/etc .

# Restore only one file
duplicity --file-to-restore apt/sources.list ftp://FtpUserID@ftp.domain.com/etc /home/user/sources.list

Cron script

export PASSPHRASE=(insert your value here)
export FTP_PASSWORD=(insert your value here)

# doing a monthly full backup (1M)
duplicity --full-if-older-than 1M /etc ftp://(insert your FTP server here)/etc
# exclude /var/tmp from the backup
duplicity --full-if-older-than 1M --exclude /var/tmp /var ftp://(insert your FTP server here)/var
duplicity --full-if-older-than 1M /root ftp://(insert your FTP server here)/root

# cleaning the remote backup space (deleting backups older than 6 months (6M, alternatives would 1Y fo 1 year etc.)
duplicity remove-older-than 6M --force ftp://(insert your FTP server here)/etc
duplicity remove-older-than 6M --force ftp://(insert your FTP server here)/var
duplicity remove-older-than 6M --force ftp://(insert your FTP server here)/root

unset PASSPHRASE
unset FTP_PASSWORD

Install in crontab (crontab -e) by adding line:

0 0 * * * /root/scripts/etc/backup.sh >>/var/log/duplicity/etc.log

Complete system backup script

We give a set of configuration and scripts to perform the backup of a complete system, typically an internet server. This must be installed as user root.

Generate GPG key
# gpg --gen-key
gpg --full-generate-key                                         # Better, avoid system defaults
                                                                # Choose: RSA 3072, never expire
gpg -a -o root@server.org.asc --export-secret-keys root@server.org
Key generation may fail on remote system due to lack of HW RNG. In that case the key must be generated on another system and imported, and then trusted
gpg --import root@server.org.asc
gpg --edit-key root@server.org                 # Use 'trust' to set trust to max.
Get the key identifiers. These are the strings SSSSSSSS and EEEEEEEE for the signing and encryption key to use.
gpg --list-key root@server.org
# pub   3072R/SSSSSSSS 2019-04-18
# uid                  root server.org <root@server.org>
# sub   3072R/EEEEEEEE 2019-04-18
Install
apt install duplicity keychain
Enable keychain
  • keychain must be loaded when logging in as root. For security, an existing keychain is always deleted on logging. To enable the backup, we must log at least once as boot after system restart so that SSH credentials are available to duplicity.
Add to ~/.bashrc:
# KEYCHAIN
# keychain will keep SSH passphrase after logout for cron script, but
# ... will delete it and ask again anytime we log back in (for intruders).
# ... except if within a tmux session started by root
if ! [ -n "$TMUX" -a -O "${TMUX/,*/}" ]; then
    keychain --nogui --clear id_rsa
fi
. ~/.keychain/$HOSTNAME-sh
Duplicity configuration
  • Create file ~/.duplicity.conf, editing the fields as necessary
# Backup - duplicity config
#
# Source: https://wiki.debian.org/Duplicity
#
# Set a few variable to ease duplicity usage:
#
#     source .duplicity.conf
#     duplicity $OPTS collection-status $RPATH/etc                # Get status for backup 'etc'
#     duplicity $OPTS list-current-files $RPATH/etc               # Get list of files
#     duplicity $OPTS --time 3D list-current-files $RPATH/etc     # Get list of files
#     duplicity $OPTS restore $RPATH/etc etc                      # Restore to folder 'etc' locally
#     duplicity $OPTS $RPATH/etc etc                              # Idem


## Remote settings
# host
HOST=backupserver.org
# user
NAME=backupuser
# path
LPATH=/absolute/path/to/backup
# URL (notice double slash in path as required for absolute paths)
RPATH=scp://$NAME@$HOST/$LPATH
# Mkdir command
MKDIR="ssh $NAME@$HOST mkdir -p"
# SSH credentials
[[ -f /root/.keychain/$HOSTNAME-sh ]] && source /root/.keychain/$HOSTNAME-sh


## GPG keys and passphrase (will be cleared on backup)
SIGNKEY=SSSSSSSS
ENCRKEY=EEEEEEEE
export PASSPHRASE='XXXXXXXXXXXXX'
export SIGN_PASSPHRASE=$PASSPHRASE
export GNUPGHOME=/root/.gnupg


# Directories to backup, and duplicity options if any
# (from http://unix.stackexchange.com/questions/1067/what-directories-do-i-need-to-back-up)
BACKDIRS=()
BACKDIRS+=('/etc')
BACKDIRS+=('/root --exclude /root/.cache/duplicity')
BACKDIRS+=('/home')
BACKDIRS+=('/var --exclude /var/cache --exclude /var/lock --exclude /var/run --exclude /var/tmp')
BACKDIRS+=('/var/cache') # Keep /var/cache to have all .deb files for reinstall
# Below we backup everything else, to ease reinstall
BACKDIRS+=('/ --exclude /etc --exclude /home --exclude /var --exclude /root --exclude /tmp')
# ... or we exclude binaries. Reinstall will take packages from /var/cache/apt
# BACKDIRS+=('/ --exclude /etc --exclude /home --exclude /var --exclude /root --exclude /tmp --exclude /lib --include /usr/local --exclude /usr')


## OPTIONS - Standard
OPTS=""
OPTS="$OPTS --encrypt-key $ENCRKEY --sign-key $SIGNKEY"    # GPG options
OPTS="$OPTS --exclude-other-filesystems"                   # Don't cross filesystem boundaries
OPTS="$OPTS --full-if-older-than 1M"                       # Full if older than 1 month, incr otherwise
#OPTS="$OPTS --exclude **/pictures/XXX"                    # More exclude patterns


## OPTIONS - Cleanup 
# Will require --force to effectively remove the files
OPTS_CLEANUP="remove-older-than 2M"
Backup script
  • Create file ~/bin/backup.sh
#! /bin/bash
# Backup - duplicity script
#
# Source: https://wiki.debian.org/Duplicity

# uncomment for debug
#set -x

CONF=duplicity.conf
source /root/.$CONF

# Build some files first
rm -f /tmp/$CONF /tmp/README.txt
sed -r "s/PASSPHRASE='.*'/PASSPHRASE='XXXXXXXXXXXXX'/" /root/.$CONF > /tmp/$CONF
echo -e "Backup files for host ober.noekeon.org\nGenerated by duplicity with script backup.sh.\nSee $CONF  for examples of use (including restore).\n\nBackup signed and encrypted with gpg key:\n\n" > /tmp/README.txt
gpg --list-key $ENCRKEY >> /tmp/README.txt
echo >> /tmp/README.txt
gpg -a --export-secret-keys $ENCRKEY >> /tmp/README.txt

# Backup script + conf first, and fail/exit immediately on error (to avoid getting banned due to too many ssh failure in for loop below)
RPATHSCP=${RPATH/scp:\/\//}
RPATHSCP=${RPATHSCP/\/\//:/}
scp $0 ${RPATHSCP}/backup.sh && scp /tmp/$CONF ${RPATHSCP}/ && scp /tmp/README.txt ${RPATHSCP}/
if [ $? != 0 ]; then
    logger "$0 - backup failed for script + conf. See log file (/root/.cache/duplicity/lastbackup.log, or check crontab -l) for details. Aborting!"
    rm -f /tmp/$CONF /tmp/README.txt
    exit 255
fi
rm -f /tmp/$CONF /tmp/README.txt

# Add options from command line (eg. '--force')
OPTS="$OPTS $*"

# Loop on directories
echo -n "---- Incremental backup of $HOSTNAME ---- "; date
for i in ${!BACKDIRS[*]}
do
  # Extract directory name as first keyword in the list - TODO: manage space
  set ${BACKDIRS[$i]}              # e.g. '/var --exclude /var/cache'
  DIR=$1                           # e.g  '/var'
  shift                            # e.g. '--exclude /var/cache'

  echo Starting backup of directory $DIR
  # create directory, then backup, then erase old backups
  $MKDIR $LPATH$DIR && duplicity $OPTS "$@" $DIR $RPATH$DIR && duplicity "$@" $OPTS $OPTS_CLEANUP $RPATH$DIR || logger "$0 - backup failed for /$DIR. See log file for details."
done

# Loop on directories again to verify integrity
for i in ${!BACKDIRS[*]}
do
  # Extract directory name as first keyword in the list - TODO: manage space
  set ${BACKDIRS[$i]}
  DIR=$1
  shift

  # verify backup integrity
  # echo Verify backup integrity of directory $DIR
  # duplicity $OPTS verify $RPATH$DIR $DIR || logger "$0 - backup integrity test failed for /$DIR. See log file for details."
done
echo -n "---- Finished backup on $HOSTNAME ---- "; date

# Exit
exit 0
  • Create crontab entries:
35 4 * * 1-6    /root/bin/backup.sh > /root/.cache/duplicity/lastbackup.log 2>&1
35 4 * * 7      /root/bin/backup.sh --force > /root/.cache/duplicity/lastbackup-force.log 2>&1
  • It is best to run the backup once manually to make sure everything is ready (SSH known host, destination directory created...).