main
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
# ADR-0003: Изоляция брейнов через отдельные PostgreSQL БД
|
||||
|
||||
**Дата:** 2026-05-20
|
||||
**Статус:** Принято
|
||||
|
||||
## Контекст
|
||||
|
||||
ZBrain хостит несколько брейнов с разным уровнем чувствительности данных:
|
||||
|
||||
- **zetit** — внутренние процессы ZETIT, не публичные
|
||||
- **telerapharma** — корпоративная инфраструктура с NDA
|
||||
- **personal** — личные данные владельца
|
||||
- **community** — данные о Смоленской 10, дом-чат, ЖКХ
|
||||
|
||||
Утечка MCP токена с правами на один брейн **не должна** давать доступ к другим брейнам.
|
||||
|
||||
Варианты изоляции в Postgres (от слабой к сильной):
|
||||
|
||||
1. **Одна БД, одна схема, отдельные таблицы с префиксами** — слабая изоляция, любой SQL injection даёт доступ ко всему
|
||||
2. **Одна БД, отдельные schema'ы (zetit.pages, telerapharma.pages)** — изоляция через `search_path`, но один Postgres user видит всё
|
||||
3. **Одна БД, отдельные пользователи с row-level security** — сложно, легко допустить ошибку в RLS политиках
|
||||
4. **Отдельные БД с отдельными пользователями** — изоляция на уровне СУБД
|
||||
|
||||
## Решение
|
||||
|
||||
Вариант 4: **отдельная БД + отдельный Postgres-пользователь на каждый брейн**.
|
||||
|
||||
```
|
||||
PostgreSQL cluster
|
||||
├── brainhub (БД)
|
||||
│ └── владелец: brainhub
|
||||
│ └── таблицы: users, sessions, tokens, brains, sources, projects, audit_log
|
||||
│
|
||||
├── gbrain_zetit (БД)
|
||||
│ └── владелец: gbrain_zetit
|
||||
│ └── полная схема gbrain (pages, chunks, embeddings, links, ...)
|
||||
│
|
||||
├── gbrain_telerapharma (БД)
|
||||
│ └── владелец: gbrain_telerapharma
|
||||
│ └── ...
|
||||
│
|
||||
├── gbrain_personal (БД)
|
||||
│ └── владелец: gbrain_personal
|
||||
│ └── ...
|
||||
│
|
||||
└── gbrain_community (БД)
|
||||
└── владелец: gbrain_community
|
||||
└── ...
|
||||
```
|
||||
|
||||
Каждый gbrain-instance подключается к Postgres под своим пользователем и **физически не видит** другие БД:
|
||||
|
||||
```sql
|
||||
-- Под пользователем gbrain_zetit
|
||||
\l
|
||||
-- Покажет только gbrain_zetit (и template/postgres)
|
||||
|
||||
\c gbrain_telerapharma
|
||||
-- ERROR: permission denied for database gbrain_telerapharma
|
||||
```
|
||||
|
||||
Brainhub использует свою БД `brainhub` под пользователем `brainhub` (тоже не видит остальные).
|
||||
|
||||
## Реализация
|
||||
|
||||
В `scripts/create-brain.sh`:
|
||||
|
||||
```sql
|
||||
CREATE USER gbrain_${BRAIN_NAME} WITH PASSWORD '${random_password}';
|
||||
CREATE DATABASE gbrain_${BRAIN_NAME} OWNER gbrain_${BRAIN_NAME};
|
||||
GRANT ALL PRIVILEGES ON DATABASE gbrain_${BRAIN_NAME} TO gbrain_${BRAIN_NAME};
|
||||
-- НЕ выдаём этому юзеру ничего на other databases
|
||||
```
|
||||
|
||||
Пароль для каждого gbrain-юзера генерируется случайно и хранится:
|
||||
1. В `/var/lib/zbrain/brains/<name>/config.json` (chmod 600, owner zbrain)
|
||||
2. В DATABASE_URL внутри systemd unit (Environment="DATABASE_URL=...")
|
||||
|
||||
`brainhub` НЕ хранит эти пароли в своей БД (они не нужны для admin-операций) — только знает на каком порту слушает gbrain instance.
|
||||
|
||||
## Последствия
|
||||
|
||||
### Плюсы
|
||||
- Физическая изоляция через ACL Postgres
|
||||
- SQL injection в одном брейне не даёт доступ к другим
|
||||
- pg_dump каждого брейна тривиально по отдельности
|
||||
- Удаление брейна = одна команда `DROP DATABASE gbrain_X`
|
||||
- Разные параметры тюнинга на разные брейны (если потребуется)
|
||||
|
||||
### Минусы
|
||||
- Нельзя сделать JOIN между брейнами в SQL (но это и не нужно — поиск через brainhub)
|
||||
- Каждая БД имеет свои индексы, общая нагрузка на shared_buffers Postgres делится
|
||||
- Backup делает дамп каждой БД отдельно (несложно)
|
||||
|
||||
### Митигация
|
||||
- shared_buffers задан с запасом (2GB на 8GB RAM)
|
||||
- HNSW индексы строятся в `maintenance_work_mem=512MB`, одного брейна за раз достаточно
|
||||
|
||||
## Альтернативы рассмотренные
|
||||
|
||||
1. **Row-level security в одной БД** — сложно, требует консистентного применения политик на каждой таблице gbrain (а у gbrain их десятки). Один пропущенный CREATE POLICY = утечка
|
||||
2. **Отдельные Postgres-кластеры на каждый брейн** — overkill, 4× процессов, 4× shared_buffers
|
||||
3. **Отдельные VM на каждый брейн** — экстремальная изоляция, не нужна на нашем масштабе
|
||||
Reference in New Issue
Block a user