Бэкап серверов на rpi (fr1, ru1)
Внешняя площадка для VPS — домашняя малина. Схема «малина забирает, сервер шифрует»:
sequenceDiagram
participant R as rpi (таймер 02:15)
participant S as сервер (fr1 / ru1)
R->>S: ssh -i /etc/server-backup/id_ed25519 <профиль>
Note over S: forced command backup-export<br/>tar | zstd | age -r <ОТКРЫТЫЙ ключ>
S-->>R: stdout: шифротекст
S-->>R: stderr: "SHA256 <hex>" шифротекста
Note over R: сверяет sha256, кладёт<br/>/mnt/backup/servers/<host>/<профиль>/
- Малина не может расшифровать то, что хранит. Шифрует сервер открытым ключом age,
закрытый ключ только у владельца: Mac
~/.config/age/servers-backup.key(+ резервная копия вне малины). Взлом малины или промпт-инъекция агента Astra = утечка только шифротекста. - Сервер не получает доступа к малине (в отличие от схемы «сервер пушит через обратный
туннель»): ключ малины на сервере умеет только запустить выгрузку (
restrict,command=…). - Целостность: sha256 шифротекста считает сервер, малина сверяет при приёме и раз в неделю
(
servers-backup verify). Подмену/порчу внутри поймает и age при расшифровке (AEAD).
Что входит
| Хост/профиль | Когда | Содержимое | Размер |
|---|---|---|---|
| ru1 daily | ежедневно | /etc целиком (OpenVPN, AWG, mtproxy, tproxy, iptables, юниты), /opt (MTProxy, xray), /srv, /usr/local, ~mik (easy-rsa — CA OpenVPN, клиентские .ovpn, wireguard, gost+LE, smart-routing), /root, crontab |
~17 МБ |
| fr1 daily | ежедневно | /etc целиком (AWG-ключи, nginx, letsencrypt, tg-notify…), /srv/gitea + согласованный снимок gitea.db (snap/gitea.db, через sqlite .backup), /usr/local, ~rus (monitoring с секретами, bin, MyVault, .ssh), /root, crontab |
~2.1 ГБ |
| fr1 weekly | вс | docker-тома claude-channels_claude-home (⚠️ ключи Astra к хостам), grafana, alertmanager, pushgateway; ~tgclaude (imgbot-data, imgbot, боты) |
~10 ГБ |
Не входит сознательно: том Prometheus (метрики, 7.9 ГБ, потеря не критична), ~rus/backups
и ~tgclaude/backups (разовые архивы — скопировать один раз отдельно), спящий openclaw,
кэши .claude/.cache, сборки (badvpn, telemt-build).
Хранение: daily — 14, weekly — 8, monthly — 12 (хардлинк первого daily месяца).
Файлы
fr1-backup-export→ fr1~rus/bin/backup-export(читает root-файлы черезdocker run -v /:/host:ro: sudo на fr1 по паролю, а rus в группе docker).age— статический бинарник~rus/bin/age.ru1-backup-export→ ru1/usr/local/sbin/backup-export(root, 750), forced commandsudo -n …уmik(у него sudo NOPASSWD).age—/usr/local/bin/age.rpi/servers-backup,rpi/servers-backup.{service,timer}→ rpi/usr/local/bin,/etc/systemd/system. Ключ/etc/server-backup/id_ed25519(root 600), host keys серверов закреплены в/etc/server-backup/known_hosts. Итог прогона → метрикиbackup_*{job="backup_servers"}, алертBackupStale(>26 ч) иBackupFailed.
Восстановление
Нужен закрытый ключ (на Mac). Архив можно расшифровывать потоком, не храня открытым на диске:
# список файлов
ssh rpi 'sudo sh -c "cat /mnt/backup/servers/ru1/daily/<файл>.age"' \
| age -d -i ~/.config/age/servers-backup.key | zstd -dq | tar -tvf -
# достать один файл/каталог (например CA OpenVPN)
ssh rpi 'sudo sh -c "cat /mnt/backup/servers/ru1/daily/<файл>.age"' \
| age -d -i ~/.config/age/servers-backup.key | zstd -dq | tar -xvf - home/mik/easy-rsa
# fr1: пути в архиве с префиксом host/ (tar шёл из контейнера с / на /host):
# репозитории — host/srv/gitea/data/git/repositories/…, БД gitea — snap/gitea.db
# (живой host/srv/gitea/data/gitea.db исключён — он был бы рваной копией)
⚠️ Потеря закрытого ключа = все бэкапы бесполезны. Минимум две копии в разных местах, и пробное восстановление раз в квартал.