bytelyst-devops-tools/scripts/hermes-emergency-bundle-create.sh

110 lines
3.2 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<'USAGE'
Usage:
hermes-emergency-bundle-create.sh [output-dir]
Creates a GPG-encrypted emergency bundle containing sensitive recovery files
that are intentionally excluded from the normal GitHub Hermes backups.
Default output-dir:
/root/hermes-emergency-bundles
Passphrase:
Interactive GPG prompt by default.
Or set BUNDLE_PASSPHRASE_FILE=/root/path/to/passphrase-file for unattended use.
Safety:
- Does not print secret values.
- Uses an allow-list of sensitive recovery files.
- Does not include logs, caches, locks, PIDs, or sandboxes.
USAGE
}
if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then
usage
exit 0
fi
OUT_DIR="${1:-/root/hermes-emergency-bundles}"
STAMP="$(date -u +%Y%m%dT%H%M%SZ)"
HOST="$(hostname -s 2>/dev/null || hostname)"
WORK_DIR="$(mktemp -d)"
PAYLOAD_DIR="$WORK_DIR/payload"
ARCHIVE="$WORK_DIR/hermes-emergency-bundle-${HOST}-${STAMP}.tar.zst"
OUT_FILE="$OUT_DIR/hermes-emergency-bundle-${HOST}-${STAMP}.tar.zst.gpg"
cleanup() {
rm -rf "$WORK_DIR"
}
trap cleanup EXIT
install -d -m 700 "$OUT_DIR"
install -d -m 700 "$PAYLOAD_DIR"
copy_if_exists() {
src="$1"
dest="$PAYLOAD_DIR/${src#/}"
if [ -e "$src" ]; then
install -d -m 700 "$(dirname "$dest")"
cp -a "$src" "$dest"
printf '%s\n' "${src#/}" >> "$PAYLOAD_DIR/MANIFEST.paths"
fi
}
# Root Hermes sensitive state.
copy_if_exists /root/.hermes/.env
copy_if_exists /root/.hermes/auth.json
copy_if_exists /root/.hermes/state.db
copy_if_exists /root/.hermes/state.db-shm
copy_if_exists /root/.hermes/state.db-wal
# Uma Hermes sensitive state.
copy_if_exists /home/uma/.hermes/.env
copy_if_exists /home/uma/.hermes/auth.json
copy_if_exists /home/uma/.hermes/state.db
copy_if_exists /home/uma/.hermes/state.db-shm
copy_if_exists /home/uma/.hermes/state.db-wal
# Git and local registry credentials used for recovery operations.
copy_if_exists /root/.git-credentials
copy_if_exists /root/.gitea_admin_password
copy_if_exists /root/.gitea_npm_token
copy_if_exists /root/.gitea_npm_token_home
# Tailscale machine state is sensitive. Restoring it is optional; a fresh
# `tailscale up` login is often cleaner, but this preserves a break-glass copy.
copy_if_exists /var/lib/tailscale/tailscaled.state
if [ ! -s "$PAYLOAD_DIR/MANIFEST.paths" ]; then
echo "No emergency files found to bundle." >&2
exit 1
fi
cat > "$PAYLOAD_DIR/README.txt" <<README
Hermes emergency bundle for ${HOST}
Created UTC: ${STAMP}
This encrypted bundle contains sensitive files excluded from normal GitHub
backups, such as .env files, provider auth state, Git credentials, local Gitea
tokens, optional Tailscale state, and Hermes state.db files.
Decrypt only into a staging directory first. Inspect paths before copying
anything into a live VM.
README
tar -C "$PAYLOAD_DIR" -I zstd -cf "$ARCHIVE" .
gpg_args=(--symmetric --cipher-algo AES256 --output "$OUT_FILE")
if [ -n "${BUNDLE_PASSPHRASE_FILE:-}" ]; then
gpg_args=(--batch --yes --pinentry-mode loopback --passphrase-file "$BUNDLE_PASSPHRASE_FILE" "${gpg_args[@]}")
fi
gpg "${gpg_args[@]}" "$ARCHIVE"
chmod 600 "$OUT_FILE"
echo "Encrypted emergency bundle created: $OUT_FILE"
echo "Included path list is encrypted inside the bundle; no secret values printed."