Duplicity: Difference between revisions
Jump to navigation
Jump to search
(2 intermediate revisions by the same user not shown) | |||
Line 6: | Line 6: | ||
== Basic usage == |
== Basic usage == |
||
;Relative vs absolute paths |
|||
* In all examples below, the paths on the remote server are '''relative''' (<code>ftp://FtpUserID@ftp.domain.com/etcunset</code>, <code>scp://scp_user@scp.domain.com/var/</code>). |
|||
* To use an '''absolute''' path, one must add an '''extra''' slash, like in <code>scp://scp_user@scp.domain.com//absolute/path/to/var/</code>. |
|||
;Basic backup: |
;Basic backup: |
||
<source lang=bash> |
<source lang=bash> |
||
Line 27: | Line 31: | ||
;Collection status |
;Collection status |
||
<source lang="bash"> |
<source lang="bash"> |
||
duplicity collection-status scp://scp_user@scp.domain.com/var/ var |
|||
jjjj |
|||
</source> |
</source> |
||
Line 33: | Line 37: | ||
;Basic file listing: |
;Basic file listing: |
||
<source lang=bash> |
<source lang=bash> |
||
# Note that remote paths are RELATIVE to user HOME directory. |
|||
# Absolute paths would require an extra /: |
|||
# |
|||
# duplicity list-current-files scp://scp_user@scp.domain.com//absolute/path/to/var/ var |
|||
# Using ftp |
# Using ftp |
||
duplicity list-current-files ftp://FtpUserID@ftp.domain.com/etcunset PASSPHRASE |
duplicity list-current-files ftp://FtpUserID@ftp.domain.com/etcunset PASSPHRASE |
||
Line 47: | Line 46: | ||
;Basic restore |
;Basic restore |
||
<source lang=bash> |
<source lang=bash> |
||
# Restore latest backup from /etc here |
|||
# Note that remote paths are RELATIVE to user HOME directory. |
|||
# Absolute paths would require an extra /: |
|||
# |
|||
# duplicity restore scp://scp_user@scp.domain.com//absolute/path/to/var/ . |
|||
duplicity ftp://FtpUserID@ftp.domain.com/etc . |
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 |
duplicity --file-to-restore apt/sources.list ftp://FtpUserID@ftp.domain.com/etc /home/user/sources.list |
||
</source> |
</source> |
||
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 |
||
=== Compare backups === |
|||
From [https://superuser.com/questions/916137/how-to-show-differences-between-two-duplicity-backups SuperUser]: |
|||
<source lang="bash"> |
|||
duplicity list-current-files --time now "${DEST}" > /tmp/today |
|||
duplicity list-current-files --time 1D "${DEST}" > /tmp/yesterday |
|||
grep -Fxvf /tmp/{today,yesterday} |
|||
</source> |
|||
== 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...). |
Latest revision as of 09:01, 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
Compare backups
From SuperUser:
duplicity list-current-files --time now "${DEST}" > /tmp/today
duplicity list-current-files --time 1D "${DEST}" > /tmp/yesterday
grep -Fxvf /tmp/{today,yesterday}
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
andEEEEEEEE
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...).