232 lines
12 KiB
Markdown
232 lines
12 KiB
Markdown
# Synapse Backupper
|
||
|
||
Инструмент для резервного копирования PostgreSQL [Synapse](https://github.com/element-hq/synapse) с композитным шифрованием на основе двух механизмов обмена ключами (dual-KEM).
|
||
|
||
## Обзор
|
||
|
||
`synapse-backupper` создает зашифрованные дампы PostgreSQL с использованием **композитной** схемы шифрования, которая объединяет постквантовый KEM (ML-KEM-768) и классический KEM (X25519). Для расшифровки резервной копии требуются оба ключа (модель AND). Инструмент поддерживает два режима работы: разовое резервное копирование (`backup`) и планировщик (`run`), который выполняет копирование по расписанию cron.
|
||
|
||
Копии хранятся локально на диске; устаревшие копии автоматически удаляются по возрасту.
|
||
|
||
## Установка
|
||
|
||
Сборка Docker-образа:
|
||
|
||
```bash
|
||
make docker-build
|
||
```
|
||
|
||
В результате собирается образ `synapse-backupper:latest`. Внутри образа находится скомпилированный бинарник, клиентские утилиты PostgreSQL, а сам контейнер запускается от непривилегированного пользователя.
|
||
|
||
## Быстрый старт
|
||
|
||
### 1. Генерация ключей шифрования
|
||
|
||
Сгенерируйте обе пары ключей (постквантовую и классическую) с помощью подкоманды `keygen`:
|
||
|
||
```bash
|
||
mkdir -p ./keys
|
||
docker run --rm -u root -v "$(pwd)/keys:/keys" synapse-backupper:latest \
|
||
keygen --type both --out-prefix /keys/synapse
|
||
chown -R "$(id -u):$(id -g)" ./keys
|
||
```
|
||
|
||
> **Примечание:** Образ контейнера по умолчанию запускается от непривилегированного пользователя `app`. Подкоманда `keygen` должна писать в смонтированный с хоста каталог, поэтому здесь она запускается от `root`, а затем владение файлами возвращается текущему пользователю. Если вы не работаете от `root`, скорректируйте команду `chown` (например, через `sudo`).
|
||
|
||
В каталоге `./keys/` появятся четыре файла:
|
||
|
||
| Файл | Назначение |
|
||
|------|------------|
|
||
| `synapse.pq.pub.pem` | Постквантовый публичный ключ (для резервного копирования) |
|
||
| `synapse.pq.priv.pem` | Постквантовый приватный ключ (для восстановления, хранить офлайн) |
|
||
| `synapse.classical.pub.pem` | Классический публичный ключ (для резервного копирования) |
|
||
| `synapse.classical.priv.pem` | Классический приватный ключ (для восстановления, хранить офлайн) |
|
||
|
||
**Важно:** приватные ключи никогда не следует монтировать в контейнер, который делает резервные копии. Храните их офлайн или на отдельном хосте, предназначенном только для восстановления.
|
||
|
||
### 2. Режимы работы
|
||
|
||
#### Режим планировщика (`run`)
|
||
|
||
Команда по умолчанию запускает планировщик по расписанию cron и HTTP-эндпоинт для проверки здоровья:
|
||
|
||
```bash
|
||
docker run -d -u root \
|
||
--name synapse-backupper \
|
||
-v "$(pwd)/keys:/keys:ro" \
|
||
-v "$(pwd)/backups:/backups" \
|
||
-e APP_PG_HOST=db \
|
||
-e APP_PG_DATABASE=postgres \
|
||
-e APP_PG_USER=synapse \
|
||
-e APP_PG_PASSWORD=changeme \
|
||
-e APP_PQ_PUBLIC_KEY_PATH=/keys/synapse.pq.pub.pem \
|
||
-e APP_CLASSICAL_PUBLIC_KEY_PATH=/keys/synapse.classical.pub.pem \
|
||
-e APP_BACKUP_DIR=/backups \
|
||
synapse-backupper:latest
|
||
```
|
||
|
||
По умолчанию резервное копирование выполняется ежедневно в `03:00`. Расписание задается параметром `backup.cron` в конфигурационном файле или переменной окружения `APP_BACKUP_CRON`.
|
||
|
||
#### Разовое резервное копирование (`backup`)
|
||
|
||
Выполнить одну резервную копию немедленно:
|
||
|
||
```bash
|
||
docker run --rm -u root \
|
||
-v "$(pwd)/keys:/keys:ro" \
|
||
-v "$(pwd)/backups:/backups" \
|
||
-e APP_PG_HOST=5432 \
|
||
-e APP_PG_DATABASE=postgres \
|
||
-e APP_PG_USER=synapse \
|
||
-e APP_PG_PASSWORD=changeme \
|
||
-e APP_PQ_PUBLIC_KEY_PATH=/keys/synapse.pq.pub.pem \
|
||
-e APP_CLASSICAL_PUBLIC_KEY_PATH=/keys/synapse.classical.pub.pem \
|
||
-e APP_BACKUP_DIR=/backups \
|
||
synapse-backupper:latest backup
|
||
```
|
||
|
||
Результирующий файл получает имя `synapse-<timestamp>.dump.pqenc` и сохраняется в каталог резервных копий. Дамп создается в формате PostgreSQL custom (`--format=custom`), который ожидает `pg_restore`.
|
||
|
||
### 3. Восстановление на отдельном хосте
|
||
|
||
Процедура восстановления специально выполняется на хосте, который **не** имеет доступа к базе данных. Достаточно только приватных ключей.
|
||
|
||
```bash
|
||
docker run --rm \
|
||
-v "$(pwd)/keys:/keys:ro" \
|
||
-v "$(pwd)/backups:/backups:ro" \
|
||
-v "$(pwd)/restored:/out" \
|
||
synapse-backupper:latest restore \
|
||
--in /backups/synapse-20260102-150405.dump.pqenc \
|
||
--privkey-pq /keys/synapse.pq.priv.pem \
|
||
--privkey-classical /keys/synapse.classical.priv.pem \
|
||
--out /out/restored.dump
|
||
```
|
||
|
||
После этого восстановите расшифрованный дамп стандартными средствами PostgreSQL:
|
||
|
||
```bash
|
||
pg_restore --clean --if-exists --dbname=synapse restored.dump
|
||
```
|
||
|
||
## Конфигурация
|
||
|
||
Приоритет источников настроек: флаги командной строки → переменные окружения `APP_*` → файл конфигурации → значения по умолчанию.
|
||
|
||
Примеры в разделе «Быстрый старт» используют переменные окружения. Префикс — `APP_`, вложенные ключи разделяются подчеркиванием, например:
|
||
|
||
- `APP_PG_HOST`
|
||
- `APP_PG_DATABASE`
|
||
- `APP_BACKUP_DIR`
|
||
- `APP_PQ_PUBLIC_KEY_PATH`
|
||
- `APP_CLASSICAL_PUBLIC_KEY_PATH`
|
||
|
||
Чтобы использовать файл конфигурации, сгенерируйте пример и смонтируйте его в контейнер:
|
||
|
||
```bash
|
||
docker run --rm synapse-backupper:latest generate-config --lang ru > config.yaml
|
||
```
|
||
|
||
Отредактируйте `config.yaml`, затем смонтируйте его в `/config.yaml` при запуске контейнера:
|
||
|
||
```bash
|
||
docker run -d \
|
||
--name synapse-backupper \
|
||
-v "$(pwd)/config.yaml:/config.yaml" \
|
||
-v "$(pwd)/keys:/keys:ro" \
|
||
-v "$(pwd)/backups:/backups" \
|
||
synapse-backupper:latest
|
||
```
|
||
|
||
Переменные окружения переопределяют значения из файла конфигурации.
|
||
|
||
## Docker Compose
|
||
|
||
Планировщик можно запустить через Docker Compose. Пример `docker-compose.yml`:
|
||
|
||
```yaml
|
||
version: '3.8'
|
||
services:
|
||
synapse-backupper:
|
||
image: synapse-backupper:latest
|
||
volumes:
|
||
- ./backups:/backups
|
||
- ./keys:/keys:ro
|
||
environment:
|
||
- APP_PG_HOST=synapse-pg
|
||
- APP_PG_DATABASE=synapse
|
||
- APP_PG_USER=synapse
|
||
- APP_PG_PASSWORD=secret
|
||
- APP_PQ_PUBLIC_KEY_PATH=/keys/synapse.pq.pub.pem
|
||
- APP_CLASSICAL_PUBLIC_KEY_PATH=/keys/synapse.classical.pub.pem
|
||
- APP_BACKUP_DIR=/backups
|
||
- APP_BACKUP_CRON=0 0 3 * * *
|
||
healthcheck:
|
||
test: ["CMD", "synapse-backupper", "healthcheck"]
|
||
interval: 30s
|
||
timeout: 3s
|
||
start_period: 10s
|
||
restart: unless-stopped
|
||
```
|
||
|
||
> **Важно:** В каталоге `./keys` должны находиться только публичные ключи (`*.pub.pem`). Перед запуском контейнера переместите приватные ключи (`*.priv.pem`) на офлайн-хост или хост, предназначенный только для восстановления.
|
||
>
|
||
> Перед промышленным развёртыванием замените `APP_PG_PASSWORD=secret` на надёжный пароль; указанное значение только пример.
|
||
|
||
Запуск планировщика:
|
||
|
||
```bash
|
||
docker compose up -d
|
||
```
|
||
|
||
## Модель безопасности
|
||
|
||
### Многоуровневая защита
|
||
|
||
Резервные копии шифруются по **композитной** схеме: общий секрет вычисляется независимо от постквантового и классического KEM, а полезная нагрузка шифруется симметричным ключом, полученным из обоих секретов. Злоумышленник должен взломать **оба** механизма, чтобы получить данные.
|
||
|
||
### Модель AND
|
||
|
||
Для расшифровки требуются **оба** приватных ключа. Команда `restore` завершится с ошибкой, если хотя бы один ключ отсутствует или неверен. Это исключает компрометацию копий при компрометации одного ключа.
|
||
|
||
### Plug-and-play KEM
|
||
|
||
Реализации KEM регистрируются в runtime-реестре по идентификатору схемы. Новые постквантовые или классические алгоритмы можно добавлять без изменения логики резервного копирования и восстановления.
|
||
|
||
### Изоляция приватных ключей
|
||
|
||
Приватные ключи **не нужны** при создании резервной копии. Контейнер, выполняющий бэкап, монтирует только публичные ключи (`:ro`). Приватные ключи должны находиться на отдельном хосте восстановления или в офлайн-хранилище, что сужает зону поражения при компрометации сервера резервного копирования.
|
||
|
||
## Ограничения
|
||
|
||
Это MVP-релиз. Следующие возможности **не реализованы**:
|
||
|
||
- **S3 / объектное хранилище** — копии пишутся только в локальный каталог.
|
||
- **Развертывание в Kubernetes** — Helm-чарт и оператор не предоставляются.
|
||
- **Потоковое / инкрементальное резервное копирование** — каждый запуск создает полный `pg_dump`.
|
||
- **Поддержка нескольких баз данных** — предполагается одна база данных на инстанс.
|
||
|
||
## Разработка
|
||
|
||
Запуск unit-тестов:
|
||
|
||
```bash
|
||
make test
|
||
```
|
||
|
||
Запуск сквозного интеграционного теста (требуется Docker):
|
||
|
||
```bash
|
||
make integration-test
|
||
```
|
||
|
||
Кросс-компиляция:
|
||
|
||
```bash
|
||
make cross-build
|
||
```
|
||
|
||
## Лицензия
|
||
|
||
MIT
|