summaryrefslogtreecommitdiff
path: root/f3s/freebsd-hosts
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-07-20 22:22:37 +0300
committerPaul Buetow <paul@buetow.org>2026-07-20 22:22:37 +0300
commitda0270e66e8397fc8acc08ecf3449351b43f9dda (patch)
treeaec6a2aa8f7a8c746608c8f765ed01062eaae566 /f3s/freebsd-hosts
parent1c67c58dbe78d1e9c571a3872f33f9eedc0f0409 (diff)
f3s: add zusb removable backup pool scripts (zusb-load/zusb-unload)
4-disk raidz2 USB-SATA pool used as offline backup storage, loaded ~once per quarter. Migrated from t450 to the f3s USB-key scheme: zusb/data/enc rekeyed from passphrase to a raw key on the F3S_KEYS sticks at /keys/zusb.key. Scripts + key deployed to all f-hosts (f0/f1/f2/f3) so the disk stack can be re-plugged to any host. Not auto-mounted.
Diffstat (limited to 'f3s/freebsd-hosts')
-rw-r--r--f3s/freebsd-hosts/zusb/README.md98
-rw-r--r--f3s/freebsd-hosts/zusb/zusb-load42
-rw-r--r--f3s/freebsd-hosts/zusb/zusb-unload27
3 files changed, 167 insertions, 0 deletions
diff --git a/f3s/freebsd-hosts/zusb/README.md b/f3s/freebsd-hosts/zusb/README.md
new file mode 100644
index 0000000..60bd259
--- /dev/null
+++ b/f3s/freebsd-hosts/zusb/README.md
@@ -0,0 +1,98 @@
+# f3s zusb — Removable USB Backup Pool
+
+`zusb` is a **4-disk raidz2 ZFS pool on 1.8 TB USB-SATA disks** (ASMT ASM235CM
+bridges) used as the **offline backup storage device**. It is plugged in and
+loaded **roughly once per quarter** to back up data, then exported and
+unplugged again. Because it is offline most of the time, it is **not**
+auto-imported or auto-mounted at boot — it is loaded manually with
+`zusb-load`.
+
+The pool is hosted on whichever f-host it is currently plugged into (f1 today).
+The load/unload scripts and the encryption key are deployed to **all f-hosts**
+(f0/f1/f2/f3), so the disk stack can be re-plugged to any host and loaded there
+without any per-host setup.
+
+## Pool & Encryption
+
+- Pool: `zusb` (raidz2 over 4 × 1.8 TB USB disks, ~5.4 TB usable).
+- Encryption root: `zusb/data/enc`, `keyformat=raw`,
+ `keylocation=file:///keys/zusb.key` — the raw 32-byte key lives on the
+ `F3S_KEYS` UFS USB stick at `/keys/zusb.key`, exactly like the other f-host
+ ZFS keys (see [`../keys/README.md`](../keys/README.md)). All child datasets
+ (`zusb/data/enc/{backups,books,documents,games,git,mail,music,pictures,
+ videos,yoga,…}`) inherit from this root.
+- `zusb` is deliberately **not** listed in any host's `zfskeys_datasets`, so
+ boot-time key loading skips it and a missing/offline pool never blocks boot.
+
+`zusb/data/enc` was rekeyed from its original passphrase key (t450's
+`zroot/secret/zroot.enc.key`) to the raw key on the stick when the pool was
+migrated from t450 to the f3s hosts. The old passphrase key on t450 is now
+obsolete for `zusb`.
+
+## Installed Files
+
+- `/usr/local/bin/zusb-load` — mounts the `F3S_KEYS` stick, imports `zusb`,
+ loads the encryption key for `zusb/data/enc` from `/keys/zusb.key`, and mounts
+ all datasets.
+- `/usr/local/bin/zusb-unload` — snapshots `zusb` for safety (via
+ `/opt/snonux/bin/zfs/zfs.snapshot` when `/opt` is mounted, with a timestamped
+ fallback) and exports the pool so the disks can be unplugged.
+
+Both scripts are host-independent (they reference only `zusb`, `zusb/data/enc`,
+and `/keys/zusb.key`) and are byte-identical across all f-hosts.
+
+## Host Configuration
+
+On each f-host install the scripts:
+
+```sh
+doas install -o root -g wheel -m 0755 zusb-load /usr/local/bin/zusb-load
+doas install -o root -g wheel -m 0755 zusb-unload /usr/local/bin/zusb-unload
+```
+
+Put the raw key on every host's `F3S_KEYS` stick (`/keys` is mounted read-only
+at boot, so remount read-write to add it). The same 32-byte key file goes on
+all four sticks:
+
+```sh
+doas mount -u -o rw /keys
+doas install -o root -g wheel -m 0400 zusb.key /keys/zusb.key
+doas mount -u -o ro /keys
+```
+
+The raw key is **not** committed to this repo (it is secret key material). It
+is copied host-to-host from a stick that already has it.
+
+## Usage
+
+```sh
+doas /usr/local/bin/zusb-load # plug the disks in, then load + mount
+doas zfs list -r zusb
+# ... run the quarterly backup ...
+doas /usr/local/bin/zusb-unload # snapshot + export, then unplug the disks
+```
+
+## Verification
+
+```sh
+ls -l /usr/local/bin/zusb-load /usr/local/bin/zusb-unload
+ls -l /keys/zusb.key
+sha256 /usr/local/bin/zusb-load /usr/local/bin/zusb-unload # identical on all hosts
+sha256 /keys/zusb.key # identical on all sticks
+```
+
+After `zusb-load`:
+
+```sh
+zpool status zusb
+zfs list -r zusb -o name,keystatus,mounted,mountpoint
+```
+
+## Origin
+
+Ported from `t450:/root/bin/zusb-load.csh` and `zusb-unload.csh`. The t450
+flow unlocked a passphrase-protected `zroot/secret` keystore and then read
+`/zroot/secret/zroot.enc.key`; on the f-hosts the raw key lives directly on the
+`F3S_KEYS` stick, matching the existing f-host ZFS key scheme. The t450
+unload's `zfs.snapshot zroot` step was dropped (f-host `zroot` is the host boot
+pool, not part of this workflow). \ No newline at end of file
diff --git a/f3s/freebsd-hosts/zusb/zusb-load b/f3s/freebsd-hosts/zusb/zusb-load
new file mode 100644
index 0000000..6ef54f3
--- /dev/null
+++ b/f3s/freebsd-hosts/zusb/zusb-load
@@ -0,0 +1,42 @@
+#!/bin/sh
+# zusb-load — import the zusb raidz2 pool (4 x 1.8T USB-SATA disks) and unlock
+# zusb/data/enc using the raw key on the F3S_KEYS stick, then mount everything.
+#
+# Ported from t450:/root/bin/zusb-load.csh to f1's f3s USB-key scheme. The old
+# t450 flow unlocked a passphrase-protected zroot/secret keystore and then read
+# /zroot/secret/zroot.enc.key; on f1 the raw key lives directly on the F3S_KEYS
+# stick at /keys/zusb.key, exactly like the other f-host secrets
+# (see f3s-storage/references/usb-keys.md). zusb/data/enc was rekeyed from
+# passphrase to a raw key to match that scheme.
+
+set -eu
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
+
+POOL=zusb
+ENCROOT=zusb/data/enc
+KEYFILE=/keys/zusb.key
+
+# 1. Make sure the F3S_KEYS stick is mounted (provides $KEYFILE).
+/usr/local/sbin/f3s-mount-keys --strict
+
+if [ ! -r "$KEYFILE" ]; then
+ echo "zusb-load: key file $KEYFILE missing or unreadable" >&2
+ exit 1
+fi
+
+# 2. Import the pool if it is not already imported.
+if ! zpool list "$POOL" >/dev/null 2>&1; then
+ zpool import "$POOL"
+fi
+
+# 3. Load the encryption key for the encrypted root dataset from the stick.
+if [ "$(zfs get -H -o value keystatus "$ENCROOT")" = "unavailable" ]; then
+ zfs load-key "$ENCROOT"
+fi
+
+# 4. Mount everything.
+zfs mount -a
+
+echo "zusb loaded:"
+zfs list -r "$POOL" -o name,keystatus,mounted,mountpoint \ No newline at end of file
diff --git a/f3s/freebsd-hosts/zusb/zusb-unload b/f3s/freebsd-hosts/zusb/zusb-unload
new file mode 100644
index 0000000..d486d57
--- /dev/null
+++ b/f3s/freebsd-hosts/zusb/zusb-unload
@@ -0,0 +1,27 @@
+#!/bin/sh
+# zusb-unload — snapshot zusb for safety, then export the pool.
+#
+# Ported from t450:/root/bin/zusb-unload.csh. The t450 version also snapshotted
+# zroot (its boot pool); that step is dropped here because f1's zroot is the
+# host boot pool and is not part of this workflow. The zfs.snapshot helper
+# ships under /opt (mounted from zusb/data/opt) and is used when present, with
+# a plain timestamped snapshot as a fallback.
+
+set -eu
+
+PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
+
+POOL=zusb
+SNAPBIN=/opt/snonux/bin/zfs/zfs.snapshot
+
+# 1. Snapshot zusb for safety before export.
+if [ -x "$SNAPBIN" ]; then
+ "$SNAPBIN" "$POOL"
+else
+ zfs snapshot -r "${POOL}@unload-$(date +%Y%m%d-%H%M%S)"
+fi
+
+# 2. Export the pool.
+zpool export "$POOL"
+
+echo "zusb exported." \ No newline at end of file