Local snapshots lived only on the Pi running OpenBAO, so a dead SD card took
both the service and its backups. This was the top remaining hardening item.
Scope note: fids2 is 192.168.0.234, same LAN as the Pi. This is genuinely
OFF-HOST but not off-site -- it covers SD-card death, hardware failure and bad
upgrades, not fire, theft, or LAN-wide ransomware.
Encrypted with GPG to HOME_SECURE (07E23DC55C0FCF76) before anything touches
the share. Two reasons:
- The CIFS mount is uid=1000,file_mode=0664, so the root-only 0600 on
/var/backups/openbao is LOST on arrival. Ciphertext makes the share's
permissions irrelevant, which is what makes it safe to include the unseal
material and ship a genuinely restorable DR set.
- NOT the transit engine, deliberately: you would need a working OpenBAO to
decrypt the backup you are restoring because OpenBAO is broken. Only the
public key is on the Pi (committed here; verified no private-key blocks).
The script never talks to OpenBAO, so it still runs while OpenBAO is down.
Bundle = both raft snapshots + init-output.json + unsealer-init.json + a
generated RESTORE.md carrying the restore ORDER (unsealer first, then main)
and the no-downgrade warning, so the recovery instructions travel inside the
backup rather than living only in a repo the Pi might take with it.
Verified by full round-trip from the NAS copy: decrypt, extract, gzip -t and
sha256sum -c both snapshots, and confirmed the recovered unseal keys are
byte-identical to the live ones. Plaintext was shredded afterwards.
Two guards, both tested to actually fire:
- Refuses to ship if the newest local snapshot is older than 48h, rather
than quietly uploading a stale DR copy. This is the exact failure that
went unnoticed for 24 days (tested: 120h-old snapshot -> exit 1).
- Verifies the destination is really on a cifs/smb filesystem. Found during
testing: as root a bare `mkdir -p` SUCCEEDS when the automount is down,
creating a local directory under the mount point, so every "offsite"
backup would silently land on the same Pi. Checking that some path is a
mountpoint was not enough -- it now stats the filesystem type of the
destination's deepest existing ancestor (tested: ext2/ext3 -> exit 1).
OnFailure mails an alert like the other units. Retains 30 bundles (~143KB
each). Daily at 03:40, a clear gap after the 02:30 local snapshot.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NFtVLA7VVqXL5G2S18c4Jk
12 lines
432 B
Desktop File
12 lines
432 B
Desktop File
[Unit]
|
|
Description=Ship an encrypted OpenBAO DR bundle to the NAS (fids2)
|
|
After=network-online.target home-lutz-nfs_projects.automount
|
|
Wants=network-online.target
|
|
OnFailure=openbao-alert@%n.service
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
# Runs as root: reads /var/backups/openbao (0700 root) and the 0600 unseal
|
|
# material. Encrypts to HOME_SECURE before anything touches the share.
|
|
ExecStart=/home/lutz/Projects/OpenBAO/scripts/offsite-backup.sh
|