feat(backup): stage the FV firewall config in ana-docker's nightly restic run
The FV edge firewall was not backed up anywhere. Its config now lands in
/var/lib/restic/stage/fv-gateway-config.xml via ana-docker's pre-backup hook,
so the existing 01:00 restic snapshot captures it. ana-docker is one of the
three egress addresses the firewall's WAN allowlist permits, which is why the
pull lives there rather than with the FV hardware — a site that has lost power
cannot back itself up, and FV lost power two days ago.
Non-fatal by design: an unreachable firewall must not abort the nightly
database dumps. But a bad pull must not be promoted either. The summary loop
only rejects EMPTY staged files, and this endpoint answers an auth failure
with a perfectly non-empty HTML error page — which would have been backed up
as a firewall config that is the right size and restores nothing. The block
checks the body really contains <opnsense> and writes nothing otherwise.
Three tests cover it, including the HTML-error-page case. The first draft of
those tests was worthless: _fv returned a Path out of a TemporaryDirectory
context, so the tree was deleted before the assertions ran and every
exists()-is-False check passed regardless of what the script did. Only the
positive test failed, which is the sole reason the broken negatives were
caught. They now snapshot inside the tempdir's lifetime, and the docstring
says why.
Also records two OPNsense API lessons in docs/pfi/opnsense-api-reference.md:
endpoints are actions and must never be probed for existence by POSTing at
them — that is how /api/core/system/reboot took the FV site dark for 3.5
minutes while looking for an apply call this same file already documented —
and the apply step is service/reconfigure, which auth/user notably lacks, so
an API-only key edit persists in config.xml and does nothing until the OS user
sync runs at boot.
Credentials in /etc/restic/fv-gateway.env (root:600), template committed,
values vaulted as fv-gateway/opnsense-api-{key,secret}. Pre-change config
snapshot vaulted as fv-gateway/config-backup-20260914.
This commit is contained in:
@@ -13,6 +13,14 @@
|
||||
# - gitea (`gitea dump` captures DB + repos + config + LFS)
|
||||
# - openwebui (local SQLite × 2 — main db + ChromaDB vector store)
|
||||
#
|
||||
# Also staged here (not a container):
|
||||
# - fv-gateway (OPNsense config.xml for the Fountain Valley edge
|
||||
# firewall, pulled over its WAN admin API). ana-docker is
|
||||
# one of the three egress addresses that firewall's
|
||||
# allowlist permits, which is why the pull lives on this
|
||||
# host rather than with the FV hardware — a site that has
|
||||
# lost power cannot back itself up.
|
||||
#
|
||||
# External DB credentials live in /etc/restic/dbcreds.env (root:600).
|
||||
# Template: configs/restic/ana-docker/dbcreds.env.example in the repo.
|
||||
#
|
||||
@@ -48,6 +56,13 @@ if [ -r "$CREDS" ]; then
|
||||
set -a; . "$CREDS"; set +a
|
||||
fi
|
||||
|
||||
# FV gateway API creds, same shape and posture as dbcreds.env (root:600).
|
||||
# Template: configs/restic/ana-docker/fv-gateway.env.example in the repo.
|
||||
FVCREDS=${RESTIC_FV_CREDS_FILE:-/etc/restic/fv-gateway.env}
|
||||
if [ -r "$FVCREDS" ]; then
|
||||
set -a; . "$FVCREDS"; set +a
|
||||
fi
|
||||
|
||||
# ---------- synapse (internal Postgres) ---------------------------------------
|
||||
if docker inspect synapse-db >/dev/null 2>&1; then
|
||||
log "dumping synapse postgres"
|
||||
@@ -155,6 +170,33 @@ else
|
||||
log "skip openwebui: container not present"
|
||||
fi
|
||||
|
||||
# ---------- fv-gateway (OPNsense edge firewall config) -------------------------
|
||||
# Non-fatal by design: a firewall we cannot reach must not abort the nightly
|
||||
# database dumps. But a bad pull must not be PROMOTED either — the summary loop
|
||||
# below only rejects EMPTY files, and this endpoint answers an auth failure or a
|
||||
# captive portal with a perfectly non-empty HTML error page. So validate that the
|
||||
# body is really an OPNsense config and write nothing at all otherwise.
|
||||
FV_HOST=${FV_GATEWAY_HOST:-172.83.89.66}
|
||||
if [ -n "${FV_API_KEY:-}" ] && [ -n "${FV_API_SECRET:-}" ]; then
|
||||
log "pulling fv-gateway config from $FV_HOST"
|
||||
fv_tmp="$WORK/.fv-config.raw"
|
||||
if curl -fsS --max-time 60 -u "$FV_API_KEY:$FV_API_SECRET" \
|
||||
-o "$fv_tmp" "http://$FV_HOST/api/core/backup/download/this" 2>/dev/null; then
|
||||
if head -c 200 "$fv_tmp" | grep -q '<opnsense>'; then
|
||||
mv -f -- "$fv_tmp" "$WORK/fv-gateway-config.xml"
|
||||
log "fv-gateway config staged ($(wc -c < "$WORK/fv-gateway-config.xml") bytes)"
|
||||
else
|
||||
rm -f -- "$fv_tmp"
|
||||
warn "fv-gateway: response was not an OPNsense config (auth failure or error page?)"
|
||||
fi
|
||||
else
|
||||
rm -f -- "$fv_tmp"
|
||||
warn "fv-gateway: config pull failed (site unreachable?)"
|
||||
fi
|
||||
else
|
||||
log "skip fv-gateway: no API creds in $FVCREDS"
|
||||
fi
|
||||
|
||||
# ---------- summary -----------------------------------------------------------
|
||||
if [ "$ERRORS" -ne 0 ]; then
|
||||
log "FAILED: $ERRORS required database dump(s) failed; previous stage preserved"
|
||||
|
||||
Reference in New Issue
Block a user