#!/bin/bash # ============================================================================= # WOOO AIOps - Credential escrow verification marker # 2026-05-06 ogt + Codex: 建立不含 secret 的人工金庫覆核 marker。 # # 這個腳本不讀、不寫、不列印任何 credential。它只在人工確認密碼管理器 # 或離線加密金庫可用後,寫入 timestamp / item / evidence_id。 # ============================================================================= set -euo pipefail # This helper is often used to print copy/paste-safe operator commands. # Keep the shared library startup banner quiet by default; real marker writes # still emit their explicit success line below. export BACKUP_COMMON_QUIET="${BACKUP_COMMON_QUIET:-1}" source "$(dirname "$0")/common.sh" ESCROW_DIR="${BACKUP_BASE}/escrow-evidence" BACKUP_HEALTH_EXPORTER="${BACKUP_HEALTH_EXPORTER:-/home/wooo/scripts/backup-health-textfile-exporter.py}" BACKUP_HEALTH_TEXTFILE_DIR="${BACKUP_HEALTH_TEXTFILE_DIR:-/home/wooo/node_exporter_textfiles}" TEXTFILE_REFRESH_ENABLED="${TEXTFILE_REFRESH_ENABLED:-1}" ITEM="" EVIDENCE_ID="" NOTE="" MODE="write" DRY_RUN=0 ALLOWED_ITEMS=( "restic_repository_password" "offsite_provider_credentials" "break_glass_admin_credentials" "dns_registrar_recovery" "oauth_ai_provider_recovery" ) usage() { cat <<'USAGE' Usage: mark-credential-escrow-verified.sh --item --evidence-id [--note ] mark-credential-escrow-verified.sh --item --evidence-id --dry-run mark-credential-escrow-verified.sh --status mark-credential-escrow-verified.sh --missing-commands Allowed items: restic_repository_password offsite_provider_credentials break_glass_admin_credentials dns_registrar_recovery oauth_ai_provider_recovery Rules: - evidence-id must be a non-secret reference such as a vault item id, ticket id, sealed envelope id, or recovery checklist id. - Do not pass passwords, tokens, recovery codes, or secret URLs. - Placeholder values such as EVIDENCE_ID_FOR_* or VAULT-ITEM-ID are rejected. USAGE } is_allowed_item() { local item="$1" for allowed in "${ALLOWED_ITEMS[@]}"; do [ "${item}" = "${allowed}" ] && return 0 done return 1 } reject_suspicious_value() { local label="$1" local value="$2" if [ "${#value}" -gt 160 ]; then echo "${label} 太長;只允許短 evidence id,不允許 secret material" >&2 return 1 fi if grep -Eq '(BEGIN |PRIVATE KEY|[A-Za-z0-9+/]{40,}={0,2})' <<<"${value}" \ || grep -Eiq '(password|token|secret)[[:space:]]*[:=]' <<<"${value}"; then echo "${label} 看起來可能含 secret;拒絕寫入 marker" >&2 return 1 fi if grep -Eiq '^(EVIDENCE_ID_FOR_|VAULT-ITEM-ID$|TODO$|TBD$|CHANGE_ME$|CHANGEME$|REPLACE_ME$|EXAMPLE)' <<<"${value}"; then echo "${label} 是 placeholder;請換成真實、非 secret 的證據 ID" >&2 return 1 fi if grep -Eiq 'https?://|ssh://|file://' <<<"${value}"; then echo "${label} 看起來像 URL;請改用不含 secret 的短 evidence id" >&2 return 1 fi return 0 } status() { install -d -m 750 "${ESCROW_DIR}" for item in "${ALLOWED_ITEMS[@]}"; do local path="${ESCROW_DIR}/${item}.last_verified" if [ -f "${path}" ]; then printf '%s present ' "${item}" sed -n 's/^timestamp=//p;s/^evidence_id=/evidence_id=/p' "${path}" | tr '\n' ' ' printf '\n' else printf '%s missing\n' "${item}" fi done } print_missing_commands() { install -d -m 750 "${ESCROW_DIR}" local missing=0 for item in "${ALLOWED_ITEMS[@]}"; do local path="${ESCROW_DIR}/${item}.last_verified" [ -f "${path}" ] && continue missing=$((missing + 1)) cat </dev/null 2>&1; then echo "TEXTFILE_REFRESHED ${BACKUP_HEALTH_TEXTFILE_DIR}/backup_health.prom" return 0 fi echo "TEXTFILE_REFRESH_FAILED exporter=${BACKUP_HEALTH_EXPORTER}" >&2 return 0 } while [ "$#" -gt 0 ]; do case "$1" in --item) ITEM="${2:-}" shift 2 ;; --evidence-id) EVIDENCE_ID="${2:-}" shift 2 ;; --note) NOTE="${2:-}" shift 2 ;; --dry-run) DRY_RUN=1 shift ;; --status) status exit 0 ;; --missing-commands) MODE="missing-commands" shift ;; -h|--help) usage exit 0 ;; *) echo "Unknown argument: $1" >&2 usage >&2 exit 2 ;; esac done if [ "${MODE}" = "missing-commands" ]; then print_missing_commands exit 0 fi if [ -z "${ITEM}" ] || [ -z "${EVIDENCE_ID}" ]; then usage >&2 exit 2 fi if ! is_allowed_item "${ITEM}"; then echo "不允許的 escrow item: ${ITEM}" >&2 usage >&2 exit 2 fi reject_suspicious_value "evidence-id" "${EVIDENCE_ID}" [ -n "${NOTE}" ] && reject_suspicious_value "note" "${NOTE}" marker="${ESCROW_DIR}/${ITEM}.last_verified" timestamp="$(date +%s)" if [ "${DRY_RUN}" = "1" ]; then echo "DRY_RUN=1" echo "MARKER_WOULD_WRITE ${marker}" echo "ITEM=${ITEM}" echo "EVIDENCE_ID_ACCEPTED=1" exit 0 fi install -d -m 750 "${ESCROW_DIR}" cat > "${marker}" <