docs: документация переведена на канон av-dev-pm
- беклог и план переехали в docs/tasks (38 задач, 11 целей), слаги переименованы с транслита на английские, 85 ссылок поправлены - conventions.md разобран в docs/conventions/, local-research.md — в docs/research/, review-journal.md — в docs/review.md с разделом настройки конвейера; заведены security.md, adr/ и .pm.json - шаг docs.py check добавлен в task gate; поведение в architecture.md помечено девятью маркерами долга, database.md получил настройки с числовым значением
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
# Модель угроз
|
||||
|
||||
## Периметр
|
||||
|
||||
**Находки строятся против целевого периметра: сервис открыт в публичный
|
||||
интернет.** Целевой контур — VPS **rivendell** (Timeweb) за **Caddy**, который
|
||||
терминирует TLS; сам сервис слушает plain HTTP на localhost контейнера. Наружу
|
||||
открыты два контура на разных поддоменах: **приём** (телефон, токен записи) и
|
||||
**чтение вместе с MCP** (агенты и приложения, токен чтения). Отдельного контура
|
||||
у MCP нет.
|
||||
|
||||
**Сегодняшний контур другой, и это переходное состояние, а не модель.** Сервис
|
||||
живёт на рабочей машине, телефон достаёт до него только по локальной сети,
|
||||
проверка токенов **выключена сознательно**, `config.docker.toml` коммитится без
|
||||
секретов. Сервис предупреждает на старте обоими сообщениями (`write auth
|
||||
disabled`, `read auth disabled`), но стартовать не отказывается.
|
||||
|
||||
Отсюда правило для проходов ревью: **выключенная сегодня проверка токенов — не
|
||||
дефект, а объявленное состояние**; дефектом является путь, который остаётся
|
||||
открытым и после включения токенов. Закрытие сегодняшнего контура — задача
|
||||
«Управление токенами и секретами», решается перед деплоем.
|
||||
|
||||
Цена контуров разная и определяет ранжирование: открытый приём означает мусор
|
||||
во входе, открытое чтение — **выгрузку всей истории здоровья** любому, кто нашёл
|
||||
порт.
|
||||
|
||||
## Недоверенный вход
|
||||
|
||||
Отправитель контролирует целиком:
|
||||
|
||||
- **Тело доставки** — JSON от Health Auto Export: имена метрик, единицы,
|
||||
значения, метки времени, имена источников и устройств, имена секций, `id`
|
||||
тренировок и записей, содержимое маршрута.
|
||||
- **Заголовки доставки** — включая `automation-id`, `automation-aggregation`,
|
||||
`User-Agent`, `Accept-Language`, `Upload-Complete`; они пишутся в `delivery` и
|
||||
участвуют в выводе слоя. Заголовки полуправдивы: `automation-aggregation`
|
||||
реальной гранулярности не описывает (разведка, находка 33).
|
||||
- **Размер тела** — предела на одну сущность нет; наблюдалось 63 МиБ на одной
|
||||
координате и 768 МиБ пика кучи на теле 40 МиБ.
|
||||
|
||||
Позже к этому добавится **содержимое родного экспорта Apple** — zip-архив с
|
||||
`export.xml`, который выбирает человек, но формируется он устройством и по
|
||||
объёму (3,6 млн записей) глазами не проверяется.
|
||||
|
||||
Ответы внешних систем в недоверенный вход не входят: исходящих вызовов у
|
||||
сервиса нет.
|
||||
|
||||
## Из чего строятся пути и ключи
|
||||
|
||||
- **Путь в архиве** — `<storage.archive_dir>/raw/ГГГГ/ММ/ДД/<ulid>.json.gz`.
|
||||
Дата берётся из времени приёма, имя файла — из ULID, сгенерированного нами.
|
||||
**Ни один сегмент пути не берётся из тела или заголовков доставки** — это и
|
||||
есть защита от выхода за пределы каталога, и она держится ровно на этом.
|
||||
- **Координатный ключ точки** — `метрика + слой + начало + конец`. Имя метрики
|
||||
приходит из тела и в путь на диске не попадает, но попадает в ключ, в лог и в
|
||||
ответ каталога. Любое значение из чужого JSON, попадающее в ключ, в лог или в
|
||||
отчёт, имеет названный предел длины.
|
||||
- **Ключ сущности** — `род секции + id` из HealthKit для `record`, `id` для
|
||||
`workout`. `id` приходит из тела.
|
||||
- **Файл базы и каталог архива** — из конфига, не из запроса.
|
||||
|
||||
## Что разграничивает доступ
|
||||
|
||||
Статический токен в заголовке `Authorization: Bearer …`; список допустимых
|
||||
токенов — в конфиге. HAE умеет слать произвольные заголовки, этого достаточно.
|
||||
|
||||
Токены **раздельные**: запись (приём) и чтение. Клиент, читающий данные, писать
|
||||
не может. MCP пользуется токеном чтения. Ролей, пользователей и сессий нет —
|
||||
данные одного человека, разграничение только по контурам.
|
||||
|
||||
Конфиг с токенами лежит отдельным томом под `0600`; реальный `config.toml` не
|
||||
коммитится, секреты рендерит деплой.
|
||||
|
||||
## Что чувствительнее чего
|
||||
|
||||
По убыванию:
|
||||
|
||||
1. **Данные о здоровье** — значения точек, тела доставок, содержимое архива.
|
||||
Утечка необратима и невосполнима: это история конкретного человека за годы.
|
||||
2. **Токен чтения** — открывает всю ту же историю целиком.
|
||||
3. **Токен записи** — открывает загрязнение витрины; лечится пересборкой
|
||||
журнала, то есть обратимо.
|
||||
4. **Метаданные потока** — имена устройств, `automation-id`, объёмы и время
|
||||
доставок. Выдают распорядок дня и модель телефона.
|
||||
|
||||
Отсюда правило логов: тела запросов и значения точек — только на `DEBUG` и с
|
||||
обрезкой; токены — никогда, ни на каком уровне. Ничего из `./data` не попадает
|
||||
ни в git, ни в логи выше `DEBUG`, ни в вывод агента — это проверяет `task gate`.
|
||||
|
||||
## Что вне модели
|
||||
|
||||
Перечислено явно, чтобы враждебный проход не выдумывал угрозу сам.
|
||||
|
||||
- **Компрометация самой машины rivendell и её оператора.** Получивший shell
|
||||
получает и базу, и архив, и конфиг; шифрования на покое нет.
|
||||
- **Компрометация телефона и учётной записи Apple.** Источник данных доверенный
|
||||
по построению.
|
||||
- **TLS, сертификаты и защита от сетевых атак** — целиком на Caddy; сервис
|
||||
слушает plain HTTP и об этом знает.
|
||||
- **DoS и исчерпание ресурсов как злонамеренное действие.** Пределы на размер
|
||||
тела и заголовков нужны против **своего же телефона**, который шлёт 63 МиБ
|
||||
честно; сценарий «злоумышленник выкачивает диск» не рассматривается — контур
|
||||
приёма закрыт токеном, а токен есть только у одного устройства.
|
||||
- **Многопользовательность, ролевая модель, аудит доступа.** Данные одного
|
||||
человека; журнала обращений к чтению нет и не планируется.
|
||||
- **Стойкость статического токена к подбору.** Токен длинный и генерируется
|
||||
вне сервиса; ограничения частоты запросов нет.
|
||||
- **Подмена содержимого доставки в пути.** Закрывается TLS на Caddy; подписи
|
||||
тела нет.
|
||||
Reference in New Issue
Block a user