#!/bin/bash # # This is a simple backup script using duplicity. It's supposed to serve as a # starting point and to be adjusted to your system. # # Important steps: # - define a host "backup" in root's .ssh/config # - read the script and adjust to your needs set -e main() { if [[ $UID != 0 ]]; then exec sudo "$0" "$@" fi TMPDIR="$(mktemp -d "/tmp/${0##*/}.XXXXXX")" trap "rm -rf '${TMPDIR}'" EXIT TERM # ensure duplicity keeps its cache at a central location export HOME=/root # if you want to encrypt the backups remove --no-encryption in the duplicity call # and uncomment the lines that contain PASSPHRASE #PASSPHRASE="randomstringhere" # these mountpoints will be excluded excludeMountpoints=( /tmp/ /sys/ /dev/ /proc/ /run/ /mnt/levant/nfs/ /media/ ) # first line that matches wins IFS='' read -r -d '' excludeList < "$TMPDIR/exclude-list" # save some data that's useful for restores local backupDataDir=/root/backup-data/ mkdir -p "$backupDataDir" fdisk -l > "$backupDataDir/fdisk" vgdisplay > "$backupDataDir/vgdisplay" pvdisplay > "$backupDataDir/pvdisplay" lvdisplay > "$backupDataDir/lvdisplay" lvdisplay > "$backupDataDir/lvdisplay" df -a > "$backupDataDir/df" findmnt -l > "$backupDataDir/findmnt" # this does not ignore /proc and network mounts so it's not that useful :( #find / | gzip > /root/full-file-list.txt.gz backup / sftp://backup/$HOSTNAME-backup/full-backup/ --exclude-filelist "$TMPDIR/exclude-list" } backup() { local src=$1 local dest=$2 shift 2 local -a options=() if [[ $(date +%u ) == '1' ]]; then # try to only run full backups on monday (1) options+=(--full-if-older-than 2D) else # force a full backup once in a while options+=(--full-if-older-than 20D) fi #export PASSPHRASE duplicity \ --log-file /var/log/backup.log \ -v5 \ --numeric-owner \ --volsize 250 \ --allow-source-mismatch \ --asynchronous-upload \ --no-encryption \ "${options[@]}" "$@" "$src" "$dest" duplicity --log-file /var/log/backup.log --force remove-older-than 120D "$dest" #export PASSPHRASE="" } ### support functions below ### ## # usage : in_array( $needle, $haystack ) # return : 0 - found # 1 - not found ## in_array() { local needle=$1; shift local item for item in "$@"; do [[ $item = "$needle" ]] && return 0 # Found done return 1 # Not Found } # same as in_array except 0 is returned if any item in haystack starts with needle in_array_startswith() { local needle=$1; shift local item for item in "$@"; do [[ "$needle" == "$item"* ]] && return 0 # Found done return 1 # Not Found } exclude_mountpoints() { local error=0 for fs in "${excludeMountpoints[@]}"; do if [[ $fs != */ ]]; then error=1 echo "Error: excludeMountpoints entry doesn't end with /: $fs" >&2 fi excludeList+="- $fs*"$'\n' done while read line; do local mountpoint=$(echo "$line" | cut -d\ -f2 | sed 's#\040# #g;') local type=$(echo "$line" | cut -d\ -f3) if ! in_array $type "${fsWhitelist[@]}"; then if ! in_array_startswith "$mountpoint/" "${excludeMountpoints[@]}"; then error=1 echo "Warning: mountpoint not excluded: $mountpoint" >&2 fi fi done