backup stuff
This commit is contained in:
parent
01b83a80ec
commit
27e036eccd
16 changed files with 513 additions and 0 deletions
55
ansible/roles/backup_store/README.md
Normal file
55
ansible/roles/backup_store/README.md
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# `backup_store`
|
||||
|
||||
Pulls already-encrypted backup artefacts from every source host onto
|
||||
`small-backups-box`, on a timer, and expires them per source.
|
||||
|
||||
Generalises the hand-written `pull-backups.sh` that had one hardcoded source
|
||||
(`arbret`). That job's behaviour is preserved exactly: same source path, same
|
||||
90 days, same destination directory.
|
||||
|
||||
## This host holds no key
|
||||
|
||||
Everything pulled here is ciphertext produced by `backup_source` on the source
|
||||
host. The box cannot read any of it — the age identity lives only on lapy. That
|
||||
is deliberate: the machine holding every backup should not also be able to open
|
||||
them.
|
||||
|
||||
## One failing source must not stop the others
|
||||
|
||||
The script is `set -uo pipefail`, **not** `-e`. Each source runs in its own
|
||||
function, failures are counted, and the script exits non-zero at the end so
|
||||
systemd marks the unit failed. A dead host costs you that one source, not the
|
||||
whole run.
|
||||
|
||||
This is the specific failure the whole plan exists to prevent: the laptop jobs
|
||||
aborted on first error and then silently produced empty directories for nine
|
||||
months.
|
||||
|
||||
## Trust points one way
|
||||
|
||||
The box authenticates with `~/.ssh/id_pull` to an unprivileged, dedicated
|
||||
account on each source (`backup-pull`, or `arbret` on prd-arbret), authorised
|
||||
with `restrict`. That account can read one directory and do nothing else — no
|
||||
sudo, no pty, no forwarding. A compromised backup box cannot reach into
|
||||
production.
|
||||
|
||||
## Addressing: names, never IPs
|
||||
|
||||
Sources are addressed by name. The job this replaced hardcoded spacey's IP; the
|
||||
droplet was later rebuilt, the address was recycled to a stranger, and the
|
||||
backup failed silently from 2025-12-01 while the directory listing still looked
|
||||
healthy.
|
||||
|
||||
Two kinds of name are in play:
|
||||
|
||||
- **Tailnet members** (vipy, memos-box, …) → MagicDNS names. These require a
|
||||
headscale ACL grant from `tag:small-backups-box` to the source's `:22`; without
|
||||
it the box cannot even resolve the peer, let alone reach it.
|
||||
- **spacey** is *not* a tailnet member — it is the headscale control server — so
|
||||
its backup is pulled over the public internet via `headscale.contrapeso.xyz`,
|
||||
which follows the host if the droplet is rebuilt.
|
||||
|
||||
## Retention here is the long tail
|
||||
|
||||
Sources keep a few days locally; this box keeps 90 (or whatever the source entry
|
||||
says). Losing the source's local copy is expected.
|
||||
Loading…
Add table
Add a link
Reference in a new issue