|
#!/bin/bash |
|
# [email protected] (2025-06-08) |
|
set -euf -o pipefail |
|
|
|
######################################################################## |
|
####################### START OF CONFIGURATION ######################### |
|
######################################################################## |
|
|
|
# Custom arguments for various commands. |
|
TAR_ARGS=() |
|
PBC_ARGS=() |
|
|
|
# dpkg --get-selections: Set to an empty string to disable this feature. |
|
DPKG_FILE="/tmp/dpkg.selections" |
|
|
|
# pvereport: Set to an empty string to disable this feature. |
|
PVE_FILE="/tmp/pvereport.txt" |
|
|
|
# List of common Debian directories to be included in TAR files and PBS snapshots. |
|
# It's recommended to use absolute paths and drop the leading slash from each element. |
|
# NOTE: DON'T LIST INDIVIDUAL FILES HERE! THIS IS NOT SUPPORTED BY PROXMOX-BACKUP-CLIENT. |
|
BACKUP_DIRS=(etc root usr/local var/cache/debconf var/spool/cron var/lib/vz/snippets) |
|
|
|
# TAR filename and temporary location. Supports compressed file extensions like gz/bz2/xz/... |
|
TAR_FILE="/tmp/backup_$(hostname -s).$(date --utc +'%Y%m%d-%H%M%S').tar.xz" |
|
|
|
# Upload URL and arguments for cURL. Could be FTP or any other protocol supported by cURL. (SFTP, HTTP, ...) |
|
# If you're only using --local or --pbs, you could set this to an empty string to disable this feature. |
|
# It's recommended to store credentials in the ~/.netrc file! But this requires --netrc in CURL_ARGS. |
|
CURL_URL="ftp://backup-destination.example.net/pve-backup/$(date --utc +'%Y')/" |
|
CURL_ARGS=(--netrc --ssl-reqd --ftp-create-dirs) |
|
|
|
# Custom command and arguments to wake-up device before uploading files. |
|
# Multiple commands are possible with something like this: bash -c '...' |
|
CURL_WOL=() |
|
PBC_WOL=() |
|
|
|
### |
|
# [OPTIONAL] Load PBS credentials from external file, for simpler reuse. |
|
# Only required if PBS_REPOSITORY and PBS_PASSWORD are not exported yet! |
|
# Alternatively you could set these variables in this script... |
|
# |
|
# > export PBS_REPOSITORY="user!token@server" |
|
# > export PBS_PASSWORD="secretpassword" |
|
# |
|
# DON'T FORGET TO PROTECT THE FILE! (chmod 0600 ...) |
|
# |
|
# There are many environment variables for proxmox-backup-client, see full list: |
|
# |
|
# https://pbs.proxmox.com/docs/backup-client.html#environment-variables |
|
### |
|
PBC_CREDENTIALS=~/.config/proxmox-backup/default-credentials.sh |
|
|
|
### |
|
# [OPTIONAL] Path to encryption key for PBS, if not already set by PBC_CREDENTIALS file. |
|
# |
|
# - Option 1) Create a new key in the default location: proxmox-backup-client key create --kdf none |
|
# - Option 2) Use existing key from PVE storage configuration: ln -s /etc/pve/priv/storage/example.enc ~/.config/proxmox-backup/encryption-key.json |
|
# |
|
# proxmox-backup-client will search the default location without further configuration! |
|
# Only change this variable, if your key is located elsewhere or you're super paranoid. |
|
### |
|
PBC_ENCRYPTION=~/.config/proxmox-backup/encryption-key.json |
|
|
|
#### |
|
## [OPTIONAL] Custom PBS namespace, if not already set by PBC_CREDENTIALS file. |
|
## Don't forget the --ns option when calling proxmox-backup-client directly! |
|
#### |
|
#PBC_NAMESPACE= |
|
|
|
######################################################################## |
|
######################## END OF CONFIGURATION ########################## |
|
######################################################################## |
|
|
|
function stderr |
|
{ |
|
printf '%s\n' "$*" >&2 |
|
} |
|
|
|
function errexit |
|
{ |
|
stderr "$(basename "$0"):" "$@" |
|
exit 1 |
|
} |
|
|
|
function usage |
|
{ |
|
cat <<- EOF >&2 |
|
Usage: $0 [OPTION] |
|
|
|
-l, --local Do not upload TAR file, just output the filename and exit. |
|
-p, --pbs Do not create TAR file, use proxmox-backup-client instead. |
|
EOF |
|
exit 1 |
|
} |
|
|
|
#shellcheck disable=SC2317 |
|
function cleanup |
|
{ |
|
for file in "${cleanupFiles[@]}" |
|
do |
|
if [[ -n "$file" && -f "$file" ]] |
|
then |
|
rm -f "$file" |
|
fi |
|
done |
|
} |
|
|
|
function prepareDPKGFile |
|
{ |
|
if [[ -n "$DPKG_FILE" ]] |
|
then |
|
cleanupFiles+=("$DPKG_FILE") |
|
dpkg --get-selections > "$DPKG_FILE" |
|
fi |
|
} |
|
|
|
function preparePVEFile |
|
{ |
|
if [[ -n "$PVE_FILE" ]] |
|
then |
|
cleanupFiles+=("$PVE_FILE") |
|
touch "$PVE_FILE" |
|
chmod 0600 "$PVE_FILE" |
|
pvereport 2>/dev/null 1> "$PVE_FILE" ||: |
|
fi |
|
} |
|
|
|
function taskTAR |
|
{ |
|
[[ -z "$CURL_URL" && "$local" -ne 1 ]] && errexit "cURL upload disabled! Check your configuration..." |
|
[[ -z "$TAR_FILE" ]] && errexit "TAR_FILE is empty! Check your configuration..." |
|
|
|
cleanupFiles+=("$TAR_FILE") |
|
touch "$TAR_FILE" |
|
chmod 0600 "$TAR_FILE" |
|
tar --create --auto-compress --file="$TAR_FILE" --directory="/" "${TAR_ARGS[@]}" "${TAR_SRCFILES[@]}" |
|
|
|
if [[ "$local" -eq 1 ]] |
|
then |
|
cleanupFiles=("${cleanupFiles[@]/$TAR_FILE}") |
|
printf '%s\n' "$TAR_FILE" |
|
exit 0 |
|
fi |
|
|
|
checksumTAR="$TAR_FILE.sha512" |
|
cleanupFiles+=("$checksumTAR") |
|
(cd "$(dirname "$TAR_FILE")" && sha512sum -- "$(basename "$TAR_FILE")" > "$checksumTAR") ||: |
|
[[ -s "$checksumTAR" ]] || errexit "Could not calculate TAR checksum!" |
|
|
|
[[ -n "${CURL_WOL[*]}" ]] && "${CURL_WOL[@]}" >/dev/null |
|
curl --silent --show-error --upload-file "{$TAR_FILE,$checksumTAR}" "${CURL_ARGS[@]}" "$CURL_URL" |
|
} |
|
|
|
function taskPBS |
|
{ |
|
if [[ -n "${PBC_CREDENTIALS:-}" && -f "$PBC_CREDENTIALS" ]] |
|
then |
|
source "$PBC_CREDENTIALS" |
|
fi |
|
|
|
local src |
|
local name |
|
local -a pbcargs |
|
local -a backupspec |
|
for src in "${PBC_SRCDIRS[@]}" |
|
do |
|
src=$(sed -r 's#^#/#;s#^[/]{2,}#/#g' <<< "$src") |
|
name=$(sed -r 's/[^A-Z0-9_]/-/ig;s/[-]{2,}/-/g;s/^[-]+//' <<< "$src") |
|
|
|
if [[ ! -d "$src" ]] |
|
then |
|
stderr "[WARNING] $src is not a directory!" |
|
continue |
|
fi |
|
|
|
backupspec+=("${name}.pxar:${src}") |
|
done |
|
|
|
[[ "${#backupspec[@]}" -eq 0 ]] && errexit "Directory list for PBS is empty!" |
|
[[ -n "$DPKG_FILE" ]] && backupspec+=("dpkg-selections.conf:$DPKG_FILE") |
|
[[ -n "$PVE_FILE" ]] && backupspec+=("pvereport.log:$PVE_FILE") |
|
[[ -n "${PBC_NAMESPACE:-}" ]] && pbcargs+=(--ns "$PBC_NAMESPACE") |
|
[[ -n "${PBC_ENCRYPTION:-}" ]] && pbcargs+=(--keyfile "$PBC_ENCRYPTION") |
|
pbcargs+=(--backup-type "host" --all-file-systems 1) |
|
|
|
# Disable "info" output. |
|
PBS_LOG=${PBS_LOG:-error} |
|
export PBS_LOG |
|
|
|
[[ -n "${PBC_WOL[*]}" ]] && "${PBC_WOL[@]}" >/dev/null |
|
proxmox-backup-client backup "${backupspec[@]}" "${PBC_ARGS[@]}" "${pbcargs[@]}" |
|
} |
|
|
|
PBC_SRCDIRS=("${BACKUP_DIRS[@]}") |
|
TAR_SRCFILES=("${BACKUP_DIRS[@]}") |
|
[[ -n "$DPKG_FILE" ]] && TAR_SRCFILES+=("${DPKG_FILE:1}") |
|
[[ -n "$PVE_FILE" ]] && TAR_SRCFILES+=("${PVE_FILE:1}") |
|
|
|
pbs=0 |
|
local=0 |
|
while [[ $# -gt 0 ]] |
|
do |
|
case "$1" in |
|
-l|--local) |
|
local=1 |
|
;; |
|
|
|
-p|--pbs) |
|
pbs=1 |
|
;; |
|
|
|
*) |
|
usage |
|
;; |
|
esac |
|
shift |
|
done |
|
|
|
if [[ "$local" -eq 1 && "$pbs" -eq 1 ]] |
|
then |
|
errexit '-l (--local) cannot be used with -p (--pbs)!' |
|
fi |
|
|
|
cleanupFiles=() |
|
trap cleanup EXIT |
|
prepareDPKGFile |
|
preparePVEFile |
|
|
|
if [[ "$pbs" -eq 1 ]] |
|
then |
|
taskPBS |
|
else |
|
taskTAR |
|
fi |