Complete the header template across Partnership, Kernel, Deployment and Plugin

Finishes the pass: every script now documents its safeguards, and the deliberate absences
in the sourced libraries are recorded so they are not "corrected" later.
This commit is contained in:
Gmer4Lfe
2026-08-01 22:44:23 -04:00
parent 5c4f8db497
commit c377ddfcca
24 changed files with 1237 additions and 32 deletions
+124 -27
View File
@@ -2,40 +2,137 @@
# ==============================================================================================
# ============================= SSH Setup ======================================================
# ==============================================================================================
#
# PURPOSE
# ─────────────────────────────────────────────────────────────────────────────
# Generates the SSH keypair for rsync automation and installs it on the remote server.
# Every cross-host operation in the ecosystem — rsync, conf sync, fallback container
# control, the upgrade webhook — authenticates with this key. If it is missing or broken,
# the mesh silently degrades to single-host.
#
# Key named after this server: hostname lowercased, unraid- prefix stripped.
# unRAID-Gmer4Lfe → gmer4lfe_rsync_automation
# unRAID-Gmer4Lfe → gmer4lfe_rsync_automation
# unRAID-Jayred365 → jayred365_rsync_automation
# Idempotent — skips generation if key already exists (use --force to regenerate).
# Updates host*.conf with key path on success.
#
# ── MODES ─────────────────────────────────────────────────────────────────────────────────────
# (default) — generate key if missing, copy to remote, update conf
# --force — regenerate key even if it exists, re-copy to remote
# --validate — test SSH auth to remote, track strikes, notify at limit
# --status — show key state, fingerprint, remote connectivity
# --dry-run — preview without creating, copying, or updating conf
# Idempotent — skips generation if the key already exists (--force to regenerate).
# Updates host*.conf with the key path on success.
#
# ── STRIKE SYSTEM (--validate) ────────────────────────────────────────────────────────────────
# Called during partnership --check cycles to detect broken SSH auth.
# Tracks consecutive SSH auth failures — not network unreachability.
# Remote Tailscale IP unreachable = network issue → not counted as SSH strike.
# Remote reachable but SSH auth fails = key issue → strike incremented.
# Strikes reset automatically after SSH_STRIKE_RESET_HRS of clean connectivity.
# At SSH_MAX_STRIKES: notify + return exit 2 (caller can escalate).
# State: DATA_DIR/ssh_strikes_{REMOTE_SERVER_NAME}.db
# ==============================================================================================
# OPERATIONAL MODEL
# ==============================================================================================
#
# ── CONFIGURATION (master.conf) ───────────────────────────────────────────────────────────────
# SSH_MAX_STRIKES — consecutive failures before notifying (default 5)
# SSH_STRIKE_RESET_HRS — hours since last failure before counter resets (default 24)
# Setup (default):
# 1. Key exists? → skip generation unless --force
# 2. Generate keypair, named from this host
# 3. Copy the public key to the remote's authorized_keys
# 4. Verify authentication actually works before claiming success
# 5. Write the key path into host*.conf
#
# Validate (--validate), called during partnership --check cycles:
# Remote unreachable on Tailscale → network problem, NOT counted as a strike
# Remote reachable but SSH auth fails → key problem, strike incremented
# Clean connectivity for SSH_STRIKE_RESET_HRS → strikes reset automatically
# At SSH_MAX_STRIKES → notify and return exit 2 so the caller can escalate
#
# State: DATA_DIR/ssh_strikes_{REMOTE_SERVER_NAME}.db
#
# ==============================================================================================
# DESIGN PRINCIPLES
# ==============================================================================================
#
# Distinguish Unreachable From Unauthorised
# The strike system counts SSH auth failures only. A partner that is simply offline is a
# network condition, and counting it would fire a key-rotation alarm every time the remote
# reboots. Only "I can reach you but you will not let me in" is a key problem.
#
# Idempotent by Default, Destructive Only on Request
# A bare run never replaces an existing key. Regenerating invalidates every authorized_keys
# entry the old key was in — including on hosts this script is not talking to right now —
# so it requires --force explicitly.
#
# Verify Before Recording
# The key path is written into host*.conf only after authentication has been proven to
# work. Recording a key that does not authenticate would leave every downstream script
# pointed at a credential that silently fails.
#
# Strikes Reset on Recovery
# Counters clear themselves after a period of clean connectivity, so a transient outage
# does not accumulate toward an alarm across unrelated weeks.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
# Root Enforcement
# Reads and writes /root/.ssh and installs keys on the remote as root.
#
# Lock Acquisition
# acquire_lock prevents concurrent runs. Two instances generating or copying keys at once
# could leave authorized_keys holding a key whose private half was already replaced.
#
# Host Detection
# detect_hosts() resolves MY_ID / REMOTE_ID for key naming and remote targeting.
#
# Existing Key Protection
# Generation is skipped when a key is present. Overwriting requires --force.
#
# Auth Verified Before Conf Write
# host*.conf is updated only after a successful authentication test.
#
# Network-vs-Auth Discrimination
# Unreachable remotes never increment the strike counter — see Design Principles.
#
# Strike Ceiling
# SSH_MAX_STRIKES bounds how long a genuinely broken key goes unreported, and exit 2 lets
# the caller decide whether that is escalation-worthy.
#
# Local-Only Escape Hatch
# --local-only generates the key without touching the remote, for onboarding a partner
# that is not reachable yet.
#
# Dry Run Support
# --dry-run previews generation, copy and conf update without performing any.
#
# ==============================================================================================
# CONFIGURATION
# ==============================================================================================
#
# master.conf
#
# SSH_MAX_STRIKES
# Consecutive SSH auth failures before notifying (default: 5)
#
# SSH_STRIKE_RESET_HRS
# Hours of clean connectivity before the strike counter resets (default: 24)
#
# host*.conf
#
# HOST*_SSH_KEY
# Written by this script on success. Read by rsync.sh, conf_sync.sh, fallback.sh and
# upgrade_webhook_handler.sh — every cross-host operation depends on it.
#
# ==============================================================================================
# RUNTIME MODES
# ==============================================================================================
#
# ssh_setup.sh
# Initial setup — generate if missing, copy to remote, update conf. Idempotent.
#
# ssh_setup.sh --force
# Regenerate the key even if one exists, and re-copy to the remote.
#
# ssh_setup.sh --validate
# Health check with strike tracking. Exit 2 at the strike limit.
#
# ssh_setup.sh --status
# Show key state, fingerprint, and remote connectivity. Then exit.
#
# ssh_setup.sh --local-only
# Generate the key locally and skip the remote copy.
#
# ssh_setup.sh --dry-run / --log
# Supported by every mode above.
#
# ── USAGE ─────────────────────────────────────────────────────────────────────────────────────
# Partnership/ssh_setup.sh — initial setup (idempotent)
# Partnership/ssh_setup.sh --force — regenerate + re-copy
# Partnership/ssh_setup.sh --validate — health check + strike tracking
# Partnership/ssh_setup.sh --status — show key and connectivity state
# Partnership/ssh_setup.sh --local-only — generate key locally, skip remote copy
# Any mode supports --dry-run and --log
# ==============================================================================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"