#!/usr/bin/env bash # Restores the ASP.NET Data Protection keyring into the production Docker volume. # REQUIRED after volume loss or host migration — without the matching keyring, # every encrypted patient PHI column is permanently unreadable. # # See docs/ops/phi-encryption-runbook.md (Keyring restore). # # Usage: # ./scripts/restore-dp-keys.sh /var/backups/vigilcare/dp-keys/dp-keys-20260804T021500Z.tar.gz # # Stops the API container, replaces volume contents, then leaves the operator # to bring the stack back up (so schema/image tags stay under compose control). set -euo pipefail ARCHIVE="${1:-}" VOLUME_NAME="${VOLUME_NAME:-vigilcare_dp_keys}" COMPOSE_FILE="${COMPOSE_FILE:-/opt/vigilcare/docker-compose.prod.yml}" COMPOSE_DIR="$(dirname "${COMPOSE_FILE}")" API_SERVICE="${API_SERVICE:-api}" if [[ -z "${ARCHIVE}" || ! -f "${ARCHIVE}" ]]; then echo "Usage: $0 " >&2 exit 1 fi if ! tar tzf "${ARCHIVE}" | grep -q '\.xml$'; then echo "ERROR: ${ARCHIVE} does not contain Data Protection *.xml key files." >&2 exit 1 fi echo "Stopping API service (${API_SERVICE}) so the volume is unused..." if [[ -f "${COMPOSE_FILE}" ]]; then docker compose -f "${COMPOSE_FILE}" --env-file "${COMPOSE_DIR}/.env" stop "${API_SERVICE}" || true else echo "WARN: ${COMPOSE_FILE} not found — ensure no container has ${VOLUME_NAME} mounted." >&2 fi if ! docker volume inspect "${VOLUME_NAME}" >/dev/null 2>&1; then echo "Creating volume ${VOLUME_NAME}..." docker volume create "${VOLUME_NAME}" >/dev/null fi echo "Restoring ${ARCHIVE} → volume ${VOLUME_NAME}" # Clear existing keys then extract. Use a throwaway alpine container. docker run --rm \ -v "${VOLUME_NAME}:/keys" \ -v "$(cd "$(dirname "${ARCHIVE}")" && pwd):/backup:ro" \ alpine sh -c "rm -rf /keys/* /keys/.[!.]* 2>/dev/null; tar xzf /backup/$(basename "${ARCHIVE}") -C /keys" echo "Restored key files:" docker run --rm -v "${VOLUME_NAME}:/keys:ro" alpine ls -la /keys echo echo "Next steps:" echo " 1. docker compose -f ${COMPOSE_FILE} --env-file ${COMPOSE_DIR}/.env up -d ${API_SERVICE}" echo " 2. curl -fsS http://localhost:5270/health/ready" echo " 3. Fetch a known patient and confirm firstName/lastName decrypt" echo " 4. Record this restore in the ops log (date, archive name, operator)"