Files
voltsrage 2a3ef62a7d
CI / frontend (push) Failing after 57s
CI / backend (push) Failing after 6m27s
Add deployment
2026-08-05 00:26:20 +08:00

52 lines
1.9 KiB
Bash

#!/usr/bin/env bash
# Backs up the ASP.NET Data Protection keyring. Without this keyring, PHI
# encrypted by Phase 32 cannot be decrypted — a database backup alone is not
# sufficient to restore the system. See docs/ops/phi-encryption-runbook.md.
#
# Volume name matches docker-compose.prod.yml `name: vigilcare` → vigilcare_dp_keys.
# Do not derive the name from the host directory; /opt/vigilcare would otherwise
# produce a different volume than a checkout named VigilCareClinical.
#
# Usage (on the production host):
# ./scripts/backup-dp-keys.sh
# BACKUP_DIR=/mnt/offsite/vigilcare/dp-keys ./scripts/backup-dp-keys.sh
#
# Cron example (daily 02:15 UTC):
# 15 2 * * * /opt/vigilcare/scripts/backup-dp-keys.sh >> /var/log/vigilcare-dp-backup.log 2>&1
set -euo pipefail
VOLUME_NAME="${VOLUME_NAME:-vigilcare_dp_keys}"
BACKUP_DIR="${BACKUP_DIR:-/var/backups/vigilcare/dp-keys}"
STAMP="$(date -u +%Y%m%dT%H%M%SZ)"
ARCHIVE="${BACKUP_DIR}/dp-keys-${STAMP}.tar.gz"
if ! docker volume inspect "${VOLUME_NAME}" >/dev/null 2>&1; then
echo "ERROR: Docker volume '${VOLUME_NAME}' not found." >&2
echo "Confirm docker-compose.prod.yml uses 'name: vigilcare' and the API has started once." >&2
exit 1
fi
mkdir -p "${BACKUP_DIR}"
chmod 700 "${BACKUP_DIR}"
echo "Backing up volume ${VOLUME_NAME}${ARCHIVE}"
docker run --rm \
-v "${VOLUME_NAME}:/keys:ro" \
-v "${BACKUP_DIR}:/backup" \
alpine tar czf "/backup/dp-keys-${STAMP}.tar.gz" -C /keys .
chmod 600 "${ARCHIVE}"
# Refuse empty archives (volume had no key XML yet).
if ! tar tzf "${ARCHIVE}" | grep -q '\.xml$'; then
echo "ERROR: archive contains no *.xml key files — refusing to keep empty backup." >&2
rm -f "${ARCHIVE}"
exit 1
fi
echo "Keyring backed up to ${ARCHIVE}"
# Retain 30 days.
find "${BACKUP_DIR}" -name 'dp-keys-*.tar.gz' -mtime +30 -delete
echo "Retention: removed archives older than 30 days under ${BACKUP_DIR}"