Commit Graph
3 Commits
Author SHA1 Message Date
ducoterra 3cdbb405ab add WireGuard wg0 watchdog units for always-on SSH
When SSH to a Pi targets a WireGuard tunnel address, reachability depends on
three things that each fail independently: the unit starting at boot, the NAT
mapping staying open, and the peer hostname still resolving. Rebooting onto a
network with no route to the endpoint previously meant no way back in.

- wg-quick@wg0.service.d/10-boot-resilience.conf: retry a failed boot-time up
  forever (Restart=on-failure, RestartSec=15, StartLimitIntervalSec=0) and cap
  endpoint resolution (WG_ENDPOINT_RESOLUTION_RETRIES=60, TimeoutStartSec=120)
  instead of hanging on the stock infinity.
- wg0-watchdog: POSIX sh healer, oldest symptom first - missing interface or
  peer restarts the unit, a missing PersistentKeepalive is set directly, a
  stale handshake re-points the peer at a freshly resolved endpoint, and a
  very stale one restarts. Restarts are gap-limited so a broken network cannot
  make the unit flap, endpoint resolution falls back to the kernel endpoint
  and then to a last-known-good cache (tunnel DNS may only be reachable
  through the tunnel), and nothing it did not create is ever removed.
  Supports --dry-run.
- wg0-watchdog.service/.timer: oneshot on a 60s timer, first check 30s after
  boot, Persistent=true to catch up after downtime, TimeoutStartSec=120 and
  Nice=10 so a restart cannot wedge the watchdog.
- wg0.conf.template: documents /etc/wireguard/wg0.conf with placeholders only.
  Deliberately omits the DNS key so name resolution does not depend on the
  tunnel (Debian wg-quick has no `DNS = none`), and sets PersistentKeepalive
  25 to hold the NAT mapping for inbound SSH.
- wireguard.md: runbook for the placeholder table, failure modes, deploy, and
  verification.

The real wg0.conf and key material never enter the repo.

LLM-Generated: true
2026-10-16 09:07:56 -04:00
ducoterra bdee581480 add self-healing root SSH key systemd units
Headless Pis lose /root/.ssh/authorized_keys to imaging, first-boot tooling,
or config management, which locks out the only SSH account that can fix it.

- ensure-root-ssh-key: idempotent POSIX sh installer. Appends managed public
  keys from /usr/local/share/root-ssh-keys/authorized_keys, never removes or
  rewrites foreign keys, skips lines whose first field is not a known key
  algorithm, and enforces 0700 / 0600 root:root on the key dir and file.
- ensure-root-ssh-key.service: oneshot, ordered Before=ssh.service so the key
  lands before the daemon starts; ConditionPathExists skips it when the
  managed keys file is absent.
- ensure-root-ssh-key.path: PathChanged= + PathModified= on authorized_keys so
  deletes, renames, and in-place edits are healed at runtime, not just at boot.
  Writes only when a key is missing, so the unit settles after one no-op run.
- authorized_keys.example: template with commented placeholder keys only; the
  real file stays on the host.
- root-ssh-key.md: runbook with deploy, verification, revocation steps, and
  the gotcha that systemd has no PathMissing= key.

LLM-Generated: true
2026-10-16 09:05:54 -04:00
ducoterra be7a4cf0ce Add Raspberry Pi headless configuration notes
Document bootfs headless setup: userconf.txt password hash, and
discovery/login steps via nmap and ssh.

LLM-Generated: true
2026-10-10 19:37:23 -04:00