duplicity
duplicity unterstützt gelöschte Dateien, volle Unix-Berechtigungen, Verzeichnisse und symbolische Links, fifos und Gerätedateien, aber keine Hardlinks. Schön ist, dass es eine ganze Reihe an Backends unterstützt (anzugeben im URL-Format), darunter neben lokalem Dateisystem, SSH/scp, WebDAV und rsync auch S3- und Swift-Buckets.
duplicity sichert Dateien und Verzeichnisse, indem es mit Hilfe von gpg
verschlüsselte „Volumes“ im tar-Format erstellt und diese auf einen entfernten oder lokalen Dateiserver hochlädt. Da duplicity librsync
verwendet, sind die inkrementellen Archive sehr klein und enthalten nur die Teile der Dateien, die sich seit der letzten Sicherung geändert haben.
Ohne weitere Angabe von Parametern erstellt duplicity automatisch mit gzip komprimierte .difftar
-Dateien, mit einer maximalen Grösse von 200 MB. Sobald solch eine Datei vorliegt, wird sie mit GnuPG verschlüsselt und ins Backend hochgeladen. Danach wird das Backup fortgesetzt.
Bemerkung
So sieht ein gpg-Aufruf auf, den duplicity unter der Haube nutzt: gpg --passphrase-fd 14 --logger-fd 9 --batch --no-tty --recipient B854C23CFBAB3B9D --no-secmem-warning --ignore-mdc-error --pinentry-mode=loopback --encrypt
- Links
Homepage: https://duplicity.gitlab.io/
Source Code: https://gitlab.com/duplicity
Umgang mit duplicity
duplicity findet sich im EPEL-Repo, und ist dort stets sehr aktuell.
dnf -y install duplicity
Um keine Passwörter zur Verschlüsselung der Backups verwenden zu müssen, GPG-Schlüssel für das Chiffrieren der Backup-Repos erzeugen.
Dry Run:
# add parameter --dry-run
Tipp
duplicity erwartet Parameter-Angaben immer vor allen Pfadangaben.
Backup erstellen
Ohne Angabe von Parametern fragt duplicity ein Passwort ab, um die Repos zu verschlüsseln. Anschliessend erzeugt der erste Lauf ein Full-, und alle weiteren Läufe ein inkrementelles Backup:
duplicity /etc file:///backup/etc
Full-Backup mit bereits vorhandenen GPG-Schlüsseln erzeugen:
# backup /etc to /backup/etc
duplicity full --progress --encrypt-key=$GPG_KEY /etc file:///backup/etc
Mit jedem Full-Backup kommen diese Dateien hinzu:
duplicity-full.20220103T131323Z.manifest.gpg
duplicity-full.20220103T131323Z.vol1.difftar.gpg
duplicity-full-signatures.20220103T131323Z.sigtar.gpg
Inkrementelles Backup erzeugen:
duplicity incremental --encrypt-key=$GPG_KEY /etc file:///backup/etc
Mit jedem inkrementellen Backup kommen diese Dateien hinzu:
duplicity-inc.20220103T131323Z.to.20220103T141327Z.manifest.gpg
duplicity-inc.20220103T131323Z.to.20220103T141327Z.vol1.difftar.gpg
duplicity-new-signatures.20220103T131323Z.to.20220103T141327Z.sigtar.gpg
Volles Backup nach jeder Woche erzwingen:
# enforcing a full backup every month
duplicity incremental --full-if-older-than 1W /etc file:///backup/etc
Tipp
duplicity legt Cache-Dateien unter $USER/.cache/duplicity
ab. Aber Achtung: Man sollte nie die gleiche $BACKUP_DEST
fuer mehrere verschiedene Backups verwenden, da das Cache-Directory basierend auf dem Hash vom $BACKUP_DEST
berechnet wird. Bei gleicher $BACKUP_DEST
wird der gefundene Cache vom anderen Backup überschrieben (z. B. Deleting local /root/.cache/duplicity/5d4d982584...e4a79d/duplicity-new-signatures.20230328T220402Z.to.20230329T220402Z.sigtar.gz (not authoritative at backend).
).
Dateien von der Sicherung ausnehmen:
duplicity --exclude **/*.git* --exclude **/log ...
Backup findet dann statt, wenn kein anderer Prozess nach I/O fragt, und dann mit hohem „Nice“-Wert:
ionice --class 3 nice --adjustment=19 duplicity ...
Vorhandene Backups auflisten:
duplicity collection-status --encrypt-key=$GPG_KEY file:///backup/etc
Im Backup enthaltene Dateien auflisten:
duplicity list-current-files --encrypt-key=$GPG_KEY file:///backup/etc
Alle Dateien im Backup mit dem im Dateisystem vergleichen (und die Namen der geänderten Dateien auflisten):
duplicity verify --encrypt-key=$GPG_KEY --verbosity=notice file:///backup/etc /etc
Restore
Kompletter Restore (Zielverzeichnis darf dabei nicht existieren):
duplicity restore --progress --encrypt-key=$GPG_KEY file:///backup/etc /etc
Restore einer einzelnen Datei /etc/ssh/sshd_config
vor drei Tagen oder für ein konkretes Backup-Datum nach /tmp/test1
wiederherstellen:
duplicity restore --time 2023-04-05 --encrypt-key=$GPG_KEY --file-to-restore ssh/sshd_config file:///backup/etc /tmp/test1
duplicity restore --time 3D --encrypt-key=$GPG_KEY --file-to-restore ssh/sshd_config file:///backup/etc /tmp/test1
Kompletter Restore mit alternativen temporären Verzeichnis (welches vorher angelegt werden muss; Zielverzeichnis darf dabei nicht existieren):
duplicity restore --progress --tempdir /alternativ/folder --encrypt-key=$GPG_KEY file:///backup/etc /etc
Backups > 90 Tage löschen:
duplicity remove-older-than 90D --encrypt-key=$GPG_KEY --force file:///backup/etc
Angaben für --verbosity
:
error
warning
notice
info
debug
Verwendung in Kombination mit LFOps
Bei den folgenden Anweisungen wird angenommen, dass duplicity mit der LFOps Ansible-Rolle der Linuxfabrik installiert und konfiguriert wurde.
Geloggt wird nach /var/log/duplicity/\*.log
.
Wichtig
Solange duba
noch keinen einfachen Restore-Modus bietet, muss bei der nachfolgenden direkten Arbeit mit duplicity
zuerst das Environment mit folgenden Variablen gefüttert werden. Die Angaben finden sich in /etc/duba/duba.json
.
# Backup behaviour
export BACKUP_DEST='swift://server.example.org'
# Swift Storage Backend
CF_BACKEND='swift'
export SWIFT_AUTHURL='https://swiss-backup02.infomaniak.com/identity/v3'
export SWIFT_USERNAME='SBI-AB123456'
export SWIFT_PASSWORD='mypassword'
export SWIFT_AUTHVERSION='3'
export SWIFT_TENANTNAME='sb_project_SBI-AB123456'
# GPG
export GPG_KEY='TheShortLocalGPGKey'
export PASSPHRASE=''
Python venv aktivieren (duplicity wird in der venv /opt/python-venv/duplicity
installiert):
source /opt/python-venv/duplicity/bin/activate
Restore von /etc
nach /tmp/etc
auf der gleichen Maschine.
# show the available versions
duplicity collection-status --encrypt-key="$GPG_KEY" "$BACKUP_DEST/etc"
# show the available files
duplicity list-current-files --encrypt-key="$GPG_KEY" "$BACKUP_DEST/etc"
mkdir /tmp/etc
# do the restore
duplicity restore --progress --encrypt-key=$GPG_KEY "$BACKUP_DEST/etc" /tmp/etc
Restore von /etc/ssh/sshd_config
auf der gleichen Machine:
# show the available versions
duplicity collection-status --encrypt-key="$GPG_KEY" "$BACKUP_DEST/etc"
# show the available files
duplicity list-current-files --encrypt-key="$GPG_KEY" "$BACKUP_DEST/etc"
# do the restore
duplicity restore --progress --encrypt-key=$GPG_KEY --file-to-restore ssh/sshd_config "$BACKUP_DEST/etc" /tmp/sshd_config
# do the restore with alternative temp folder (in case the /tmp Volume is to small)
duplicity restore --progress --encrypt-key=$GPG_KEY --tempdir /alternativ/folder --file-to-restore ssh/sshd_config "$BACKUP_DEST/etc" /tmp/sshd_config
Restore einer Datei (/root/.gnupg
) nach /tmp/gnupg
zu lokaler Admin-Maschine:
# import the master GPG private key (look in Bitwarden)
gpg --import /tmp/private-key.txt
# check if the key is present
gpg --list-secret-keys
# set GPG_KEY to the master key
export GPG_KEY='TheShortMasterGPGKey'
# unset PASSPHRASE so it is asked interactively (or set it to the correct passphrase)
unset PASSPHRASE
# do the restore
duplicity restore --progress --encrypt-key=$GPG_KEY --file-to-restore .gnupg "$BACKUP_DEST/etc" /tmp/gnupg
# cleanup (but make sure that no active backup/restore is running at the moment)
rm -rf ~/.cache/duplicity
Disaster-Recovery (Maschine wurde neu aufgesetzt und fährt noch kein duplicity). Im Beispiel Restore von /etc
nach /tmp/etc
:
# restore /root/.gnupg using the method above (Restore zu lokaler Admin-Maschine)
# check if the private key of the server and is present
gpg --list-secret-keys
# install and configure duplicity using ansible
ansible-playbook --inventory /path/to/inventory linuxfabrik.lfops.duplicity --limit vm
mkdir /tmp/etc
# do the restore
duplicity restore --progress --encrypt-key=$GPG_KEY "$BACKUP_DEST/etc" /tmp/etc
Verschlüssung und Signierung
Duplicity verwendet immmer asymmetrische Verschlüsslung. Das heisst, es werden immer Key-Pairs mit einem Public- und einem Private-Key verwendet.
Bei der Verschlüssung wird das Backup mit dem Public-Key verschlüsselt, und kann danach nur mit dem entsprechenden Private-Key gelesen werden (Integritätssicherung).
Bei der Signierung wird das Backup mit dem Private-Key signiert, und dies kann danach nur mit dem entsprechenden Public-Key gelesen werden (Vertraulichkeit).
Die Verschlüsslung und Signierung kann mit dem gleichen Key-Pair gemacht werden - oder wie im folgendem Beispiel mit getrennten Schlüsselpaaren:
duplicity_enc
: Encryption-Keyduplicity_sign
: Signing-Key
Sind beide Keys ausgesstellt, muss der Encryption-Key mit dem Signing-Key signiert werden:
gpg --local-user duplicity_sign --sign-key duplicity_enc
# unattended
gpg --quick-sign-key $Fingerprint_SignKey $mail-address
duplicity lässt sich dann für alle Operationen wie folgt aufrufen:
duplicity ... --encrypt-key=0415328762E6F9AE --sign-key=CBBE7A2DE48F4F3D ...
Bei einem Backup muss das Passwort für den Signing-Key, bei Zugriff auf das Backup-Repo das Passwort für den Encryption-Key übergeben werden.
Full und inkrementelle Backups brauchen den Encryption-Key nicht, solange der Cache unter $USER/.cache/duplicity
mit den Daten auf dem Backend konsistent ist. Falls dies nicht der Fall ist, wird der Encryption-Key (und somit dessen Passwort) benötigt.
Duplicity kann mehrere GPG-Keys zur Encryption verwenden, mit geringem Overhead (Details siehe Sektion 5.1 von RFC 2440). Die Backups können mit jedem einzelnen der Encryption-Keys entschlüsselt werden. Dies erlaubt es, für mehrere Server einen „Master“-Key zu verwenden. Der Private-Key des Master-Keys wird zur Verschlüssung nicht gebraucht, und kann daher auf der lokalen Admin-Maschine bleiben. Der Server hat weiterhin sein eigenes Key-Pair, zur Verschlüssung und zur einfacheren Wiederherstellung von Files. Und im Disaster-Fall kann man mit dem Master-Key auf jeden Fall die Backups lesen.
S3-Backend
Siehe auch S3.
Anmeldeinformationen für S3 können wie folgt übergeben werden:
per Umgebungsvariablen
AWS_ACCESS_KEY_ID
undAWS_SECRET_ACCESS_KEY
durch die Datei
~/.aws/credentials
durch die Datei
~/.aws/config
durch die Verwendung der Dateien
~/.boto
oder/etc/boto.cfg
im Stil von boto2
Das URL-Schema lautet boto3+s3://
.
Swift-Backend
Siehe auch Swift.
Sicherung, im Beispiel das /etc
-Verzeichnis gegen Infomaniak „Swiss Backup“:
pip3 install python-swiftclient
pip3 install python-keystoneclient
# if device was created up to 2020-10:
#export SWIFT_AUTHURL='https://swiss-backup.infomaniak.com/identity/v3'
# if device was created after 2020-10:
export SWIFT_AUTHURL='https://swiss-backup02.infomaniak.com/identity/v3'
export SWIFT_AUTHVERSION='3'
export SWIFT_PASSWORD='password'
export SWIFT_TENANTNAME='sb_project_SBI-XXXXXXXX'
export SWIFT_USERNAME='SBI-XXXXXXXX'
Backup erstellen:
duplicity full --encrypt-key=$GPG_KEY /etc swift://$(hostname)/etc
Siehe auch https://www.arsouyes.org/en/blog/2020/17_backup_OVH.
duplicity Return Codes
Bedeutung der Warning-Codes (Stand 2022-03-03):
1: generic
2: orphaned_sig
3: unnecessary_sig
4: unmatched_sig
5: incomplete_backup
6: orphaned_backup
7: ftp_ncftp_v320
8: cannot_iterate
9: cannot_stat
10: cannot_read
11: no_sig_for_time
12: cannot_process
13: process_skipped
Bedeutung der Error-Codes (Stand 2022-03-03):
1 generic
2: command_line
3: hostname_mismatch
4: no_manifests
5: mismatched_manifests
6: unreadable_manifests
7: cant_open_filelist
8: bad_url
9: bad_archive_dir
10: bad_sign_key
11: restore_dir_exists
12: verify_dir_doesnt_exist
13: backup_dir_doesnt_exist
14: file_prefix_error
15: globbing_error
16: redundant_inclusion
17: inc_without_sigs
18: no_sigs
19: restore_dir_not_found
20: no_restore_files
21: mismatched_hash
22: unsigned_volume
23: user_error
24: boto_old_style
25: boto_lib_too_old
26: boto_calling_format
27: ftp_ncftp_missing
28: ftp_ncftp_too_old
30: exception
31: gpg_failed
32: s3_bucket_not_style
33: not_implemented
34: get_freespace_failed
35: not_enough_freespace
36: get_ulimit_failed
37: maxopen_too_low
38: connection_failed
39: restart_file_not_found
40: gio_not_available
42 source_dir_mismatch
43: ftps_lftp_missing
44: volume_wrong_size
45: enryption_mismatch
46: pythonoptimize_set
47: dpbx_nologin
48: bad_request
49: s3_kms_no_id
50: backend_error
51: backend_permission_denied
52: backend_not_found
53: backend_no_space
54: backend_command_error
55: backend_code_error
126: error code for pkexec
127: error code for pkexec
255: error code for gksu
duplicity Cheat Sheet
Time Formats: s, m, h, D, W, M, Y
duplicity 0.8.22:
Usage:
duplicity [full|incremental] [options] source_dir target_url
duplicity [restore] [options] source_url target_dir
duplicity verify [options] source_url target_dir
duplicity collection-status [options] target_url
duplicity list-current-files [options] target_url
duplicity cleanup [options] target_url
duplicity remove-older-than time [options] target_url
duplicity remove-all-but-n-full count [options] target_url
duplicity remove-all-inc-of-but-n-full count [options] target_url
duplicity replicate source_url target_url
Backends and their URL formats:
azure://container_name
b2://account_id[:application_key]@bucket_name/[some_dir/]
boto3+s3://bucket_name[/prefix]
cf+http://container_name
dpbx:///some_dir
file:///some_dir
ftp://user[:password]@other.host[:port]/some_dir
ftps://user[:password]@other.host[:port]/some_dir
gdocs://user[:password]@other.host/some_dir
for gdrive:// a <service-account-url> like the following is required
<serviceaccount-name>@<serviceaccount-name>.iam.gserviceaccount.com
gdrive://<service-account-url>/target-folder/?driveID=<SHARED DRIVE ID> (for GOOGLE Shared Drive)
gdrive://<service-account-url>/target-folder/?myDriveFolderID=<google-myDrive-folder-id> (for GOOGLE MyDrive)
hsi://user[:password]@other.host[:port]/some_dir
imap://user[:password]@other.host[:port]/some_dir
mega://user[:password]@other.host/some_dir
megav2://user[:password]@other.host/some_dir
mf://user[:password]@other.host/some_dir
onedrive://some_dir
pca://container_name
pydrive://user@other.host/some_dir
rclone://remote:/some_dir
rsync://user[:password]@other.host[:port]/relative_path
rsync://user[:password]@other.host[:port]//absolute_path
rsync://user[:password]@other.host[:port]::/module/some_dir
s3+http://bucket_name[/prefix]
s3://other.host[:port]/bucket_name[/prefix]
scp://user[:password]@other.host[:port]/some_dir
ssh://user[:password]@other.host[:port]/some_dir
swift://container_name
tahoe://alias/directory
webdav://user[:password]@other.host/some_dir
webdavs://user[:password]@other.host/some_dir
Commands:
cleanup <target_url>
collection-status <target_url>
full <source_dir> <target_url>
incr <source_dir> <target_url>
list-current-files <target_url>
remove-all-but-n-full <count> <target_url>
remove-all-inc-of-but-n-full <count> <target_url>
remove-older-than <time> <target_url>
replicate <source_url> <target_url>
restore <source_url> <target_dir>
verify <target_url> <source_dir>
Options:
-h, --help show this help message and exit
--allow-source-mismatch
--archive-dir=path
--asynchronous-upload
--compare-data
--copy-links
--dry-run
--encrypt-key=gpg-key-id
--encrypt-secret-keyring=path
--encrypt-sign-key=gpg-key-id
--exclude=shell_pattern
--exclude-device-files
--exclude-filelist=filename
--exclude-if-present=filename
--exclude-other-filesystems
--exclude-regexp=regular_expression
--exclude-older-than=time
--file-prefix=FILE_PREFIX
--file-prefix-manifest=FILE_PREFIX_MANIFEST
--file-prefix-archive=FILE_PREFIX_ARCHIVE
--file-prefix-signature=FILE_PREFIX_SIGNATURE
-r path, --file-to-restore=path
--force
--ftp-passive
--ftp-regular
--full-if-older-than=time
--gio
--gpg-binary=path
--gpg-options=options
--hidden-encrypt-key=gpg-key-id
--idr-fakeroot=path
--ignore-errors
--imap-full-address
--imap-mailbox=imap_mailbox
--include=shell_pattern
--include-filelist=filename
--include-regexp=regular_expression
--log-fd=file_descriptor
--log-file=filename
--log-timestamp
--max-blocksize=number
--name=backup name
--no-encryption
--no-compression
--no-print-statistics
--null-separator
--num-retries=number
--numeric-owner
--do-not-restore-ownership
--old-filenames
--metadata-sync-mode=METADATA_SYNC_MODE
--par2-redundancy=number
--par2-options=options
--par2-volumes=number
--progress
--progress-rate=number
--pydevd
--rename=RENAME
-t time, --restore-time=time, --time=time
--rsync-options=options
--s3-european-buckets
--s3-use-rrs
--s3-use-ia
--s3-use-glacier
--s3-use-glacier-ir
--s3-use-deep-archive
--s3-use-onezone-ia
--s3-use-new-style
--s3-unencrypted-connection
--s3-multipart-chunk-size=number
--s3-multipart-max-procs=number
--s3-multipart-max-timeout=number
--s3-use-multiprocessing
--s3-use-server-side-encryption
--s3-use-server-side-kms-encryption
--s3-kms-key-id=S3_KMS_KEY_ID
--s3-kms-grant=S3_KMS_GRANT
--s3-region-name=S3_REGION_NAME
--s3-endpoint-url=S3_ENDPOINT_URL
--swift-storage-policy=policy
--azure-max-single-put-size=number
--azure-max-block-size=number
--azure-max-connections=number
--azure-blob-tier=Hot|Cool|Archive
--scp-command=command
--sftp-command=command
--cf-backend=pyrax|cloudfiles
--b2-hide-files
--short-filenames
--sign-key=gpg-key-id
--ssh-askpass
--ssh-options=options
--ssl-cacert-file=pem formatted bundle of certificate authorities
--ssl-cacert-path=path to a folder with certificate authority files
--ssl-no-check-certificate
--tempdir=path
--timeout=seconds
--time-separator=char
--use-agent
-v [0-9], --verbosity=[0-9]
-V, --version
--mf-purge
--mp-segment-size=number
--volsize=number
--file-changed=path
--no-files-changed
--show-changes-in-set=number
--backend-retry-delay=seconds
Troubleshooting
- Warning, found signatures but no corresponding backup files
Unvollständige Backups entfernen:
duplicity cleanup <target_url>
- Temp space has 443584512 available, backup needs approx 482344960.
Nicht genügend Platz im temporären Verzeichnis vorhanden (meistens
/tmp
). Die Werte werden in Bytes angegeben.- ERROR 30 AssertionError: Cannot set filename of remote manifest to b’duplicity-inc.20240115T221802Z.to.20240116T221802Z.manifest.gpg‘; already set to b’duplicity-inc.20240115T221802Z.to.20240116T221802Z.manifest.part‘.
Meist hilft es, den lokalen Cache zu löschen:
rm -rf $HOME/.cache/duplicity
- ERROR 50 get, Giving up after 5 attempts. OSError: No space left on device
Nicht genügend Platz im temporären Verzeichnis vorhanden (meistens
/tmp
).
Built on 2024-09-30