# Бэкап серверов на rpi (fr1, ru1) Внешняя площадка для VPS — домашняя малина. Схема «малина забирает, сервер шифрует»: ```mermaid 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
tar | zstd | age -r <ОТКРЫТЫЙ ключ> S-->>R: stdout: шифротекст S-->>R: stderr: "SHA256 " шифротекста Note over R: сверяет sha256, кладёт
/mnt/backup/servers//<профиль>/ ``` - **Малина не может расшифровать то, что хранит.** Шифрует сервер открытым ключом 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 command `sudo -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). Архив можно расшифровывать потоком, не храня открытым на диске: ```bash # список файлов 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 исключён — он был бы рваной копией) ``` ⚠️ **Потеря закрытого ключа = все бэкапы бесполезны.** Минимум две копии в разных местах, и пробное восстановление раз в квартал.