krona-staff/deploy/pilot/backup.sh

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.