53 lines
2.4 KiB
Bash
53 lines
2.4 KiB
Bash
#!/usr/bin/env sh
|
|
# Encrypt before writing to disk; keep the private recipient key off this host.
|
|
set -eu
|
|
umask 077
|
|
|
|
: "${BACKUP_DIRECTORY:?Set a dedicated absolute backup directory}"
|
|
: "${BACKUP_RECIPIENT_CERT:?Set the public recipient certificate path}"
|
|
BACKUP_TRANSFER_MODE="${BACKUP_TRANSFER_MODE:-push}"
|
|
case "$BACKUP_DIRECTORY" in /*) ;; *) echo 'Backup directory must be absolute' >&2; exit 1 ;; esac
|
|
BACKUP_DIRECTORY=$(realpath -m -- "$BACKUP_DIRECTORY")
|
|
case "$BACKUP_DIRECTORY" in /|/home|/tmp|/var|/usr|/etc|/opt|/srv|/root|/mnt|/media) echo 'Use a dedicated backup directory' >&2; exit 1 ;; esac
|
|
case "$BACKUP_TRANSFER_MODE" in
|
|
push)
|
|
: "${BACKUP_REMOTE:?Set an off-host scp destination, for example backup@host:/backups/staff/}"
|
|
case "$BACKUP_REMOTE" in -*|*\ *|*\;*|*\`*|*\$*) echo 'Invalid backup destination' >&2; exit 1 ;; esac
|
|
case "$BACKUP_REMOTE" in *:/*/) ;; *) echo 'Off-host destination must be host:/absolute/directory/' >&2; exit 1 ;; esac
|
|
;;
|
|
external-pull) ;;
|
|
*) echo 'BACKUP_TRANSFER_MODE must be push or external-pull' >&2; exit 1 ;;
|
|
esac
|
|
test -r "$BACKUP_RECIPIENT_CERT"
|
|
openssl x509 -in "$BACKUP_RECIPIENT_CERT" -noout -checkend 0 >/dev/null
|
|
cd "$(dirname "$0")"
|
|
mkdir -p "$BACKUP_DIRECTORY"
|
|
stamp=$(date -u '+%Y%m%dT%H%M%SZ')
|
|
name="krona-staff-$stamp-$$.dump.cms"
|
|
output="$BACKUP_DIRECTORY/$name"
|
|
scratch=$(mktemp -d "$BACKUP_DIRECTORY/.backup-XXXXXX")
|
|
producer=''
|
|
cleanup() {
|
|
if [ -n "$producer" ]; then kill "$producer" 2>/dev/null || true; fi
|
|
rm -f -- "$scratch/dump.pipe" "$scratch/encrypted.partial"
|
|
rmdir -- "$scratch"
|
|
}
|
|
trap cleanup EXIT HUP INT TERM
|
|
mkfifo "$scratch/dump.pipe"
|
|
docker compose exec -T postgres sh -eu -c 'exec pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Fc' > "$scratch/dump.pipe" &
|
|
producer=$!
|
|
openssl cms -encrypt -binary -aes-256-cbc -outform DER -stream \
|
|
-in "$scratch/dump.pipe" -out "$scratch/encrypted.partial" "$BACKUP_RECIPIENT_CERT"
|
|
wait "$producer"
|
|
producer=''
|
|
test -s "$scratch/encrypted.partial"
|
|
mv -- "$scratch/encrypted.partial" "$output"
|
|
(cd "$BACKUP_DIRECTORY" && sha256sum "$name" > "$name.sha256")
|
|
if [ "$BACKUP_TRANSFER_MODE" = push ]; then
|
|
scp -B "$output" "$output.sha256" "$BACKUP_REMOTE"
|
|
copy_status=completed
|
|
else
|
|
copy_status=pending_pull
|
|
fi
|
|
printf 'backup=%s\nchecksum=%s\noff_host_copy=%s\n' "$output" "$output.sha256" "$copy_status"
|
|
# This is an encrypted backup, not evidence of a successful restore drill.
|