doc(sqs): мой критический анализ отчёта Opus (перепроверка по коду)
This commit is contained in:
@@ -0,0 +1,91 @@
|
|||||||
|
# Критический анализ отчёта Opus (мой разбор, 2026-10-05)
|
||||||
|
|
||||||
|
> Кем: я (DeepSeek). Что: критический разбор отчёта Opus
|
||||||
|
> (`opus-report-2026-10-05.md`) по архитектуре shared-sqs.
|
||||||
|
> Метод: ключевые утверждения Опуса перепроверялись по коду напрямую, а не
|
||||||
|
> принимались на веру. Источники — «файл:строка».
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что я перепроверил по коду (факты)
|
||||||
|
|
||||||
|
| Утверждение Опуса | Вердикт | Источник в коде |
|
||||||
|
|---|---|---|
|
||||||
|
| P0.1: SecretKey детерминирован из email + соль в исходнике | ✅ подтверждено | `tenant_store.go:289-292` `secretKeyFromEmail` = `SHA256(lower(email)+":shared-sqs:secret-key:v1")`; соль — константа `secretKeySalt` (`tenant_store.go:286`); `accessKeyFromEmail` = `SHA256(email)[:12]` |
|
||||||
|
| P0.2: JWT без проверки подписи, доверие на PingNubesAPI | ✅ подтверждено | `jwt.go` — парсинг структуры + `exp`; `PingNubesAPI` принимает, если хоть один стенд не 401/403 |
|
||||||
|
| Кэш: отозванный токен живёт до ~7 мин | ✅ подтверждено | `cache.go`: `cacheTTL=5m`, `cacheGraceTTL=2m` |
|
||||||
|
| Глобальный мьютекс на весь сервис | ✅ подтверждено | `globals.go:20-23` — один `SyncQueues.RWMutex` + `Queues map[string]*Queue` |
|
||||||
|
| Сиротство очередей при пересчёте legacy-тенанта | ✅ подтверждено | `tenant_store.go:202-205` — комментарий в коде сам это описывает |
|
||||||
|
| MessageRetentionPeriod не реализован | ✅ подтверждено | `models.go` — `// TODO - not used` |
|
||||||
|
|
||||||
|
Ложных утверждений не найдено.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Сильные стороны отчёта
|
||||||
|
|
||||||
|
1. **P0.1 — самая ценная находка.** Детерминированный вывод SecretKey из
|
||||||
|
публичного email — реальный, доказуемый по коду дефект. Указан не только
|
||||||
|
механизм, но и точные функции. Именно это реально блокирует «готовность SQS».
|
||||||
|
2. **Верно разделены SigV4 и JWT.** SigV4 проверяется полноценно
|
||||||
|
(`auth_middleware.go`: canonical request, `subtle.ConstantTimeCompare`,
|
||||||
|
clock skew, presigned); JWT — нет. Большинство поверхностных ревью их путают.
|
||||||
|
3. **Классификация P0/P1/P2 адекватна** — безопасность+durability отделены от
|
||||||
|
масштабируемости и гигиены.
|
||||||
|
4. **Пункты 3–7** (глобальный лок, SPOF, async-Redis, нет RetentionPeriod,
|
||||||
|
плоский слайс) — архитектурно точны.
|
||||||
|
5. **Раздел «противоречия док↔код» конкретен** (особенно №1: док про
|
||||||
|
`sub`/`bySub`/`sless-`, код про `email`/`byEmail`/`t-`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Слабые места и оговорки
|
||||||
|
|
||||||
|
1. **P0.2 переоценён как самостоятельный «критичный».** Вектор «подмена email
|
||||||
|
→ доступ» опасен **только в связке с P0.1**: неверный email даёт чужие креды,
|
||||||
|
потому что креды детерминированы из email. Исправь P0.1 (случайные креды в
|
||||||
|
Redis) — и неверный email из JWT уже не даст чужих ключей, только
|
||||||
|
провиженинг под чужим именем (менее критично). Опус не связал P0.1 и P0.2
|
||||||
|
явно, хотя атака работает попарно. Стоит оформить как один инцидент.
|
||||||
|
|
||||||
|
2. **П.4 «SPOF/нет горизонтального масштабирования»** — справедливо
|
||||||
|
архитектурно, но сервис и проектировался single-instance managed
|
||||||
|
HTTP-контейнером (README). Это осознанное ограничение, а не баг. В разряд
|
||||||
|
«высокого риска сейчас» ставить преждевременно — это долгосрочная
|
||||||
|
архитектурная развилка, а не текущий блокер.
|
||||||
|
|
||||||
|
3. **П.5 (async-Redis = потеря подтверждённых сообщений) — полуверно.**
|
||||||
|
Сообщение в памяти уже есть (клиент получил 200 = сообщение в памяти,
|
||||||
|
источнике правды). Теряется только при краше пода **до** async-`HSet`, и
|
||||||
|
только это узкое окно. Окно реально, но «нарушение durability» подано
|
||||||
|
сильнее, чем есть: теряются не все сообщения, а только при краше в окне.
|
||||||
|
|
||||||
|
4. **П.10 (timing side-channel admin-токена)** — технически верно, но admin-токен
|
||||||
|
приходит как высокоэнтропийный bearer; практическая эксплуатируемость timing-атаки
|
||||||
|
через сеть у одного короткого сравнения ничтожна. Формально в P2 и есть, но
|
||||||
|
вес в подаче чуть завышен.
|
||||||
|
|
||||||
|
5. **П.14 (`/metrics` публичен)** — стоит оговорка: лейбл `tenant` = tenantID
|
||||||
|
(`t-...`), а НЕ SecretKey. Утечка реальна (число очередей/сообщений по
|
||||||
|
тенанту), но это не утечка кредов. Формулировка корректна, но без оговорки
|
||||||
|
читается страшнее.
|
||||||
|
|
||||||
|
6. **Пробел контекста (не ошибка Опуса, а следствие моего allowlist-запрета):**
|
||||||
|
отчёт не отделяет «код-блокировку» от «платформенной блокировки» — периодические
|
||||||
|
таймауты ~31–33с и MSS-баг для тел >~1400 байт (разобрано в
|
||||||
|
`doc/thinking/nubes-ticket.md`). Даже идеальный код их не уберёт. Итоговый
|
||||||
|
вывод о «готовности SQS» надо читать с этой поправкой.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Итоговая оценка
|
||||||
|
|
||||||
|
Отчёт — **добротный, по фактам точный**. Из 16 пунктов рисков ложных нет;
|
||||||
|
спорные — только **вес/приоритет**:
|
||||||
|
- P0.2 связать с P0.1 (один инцидент);
|
||||||
|
- п.4 (SPOF) и п.10 (timing) слегка переоценены для текущей single-instance роли.
|
||||||
|
|
||||||
|
Главная ценность: **P0.1 (детерминированный SecretKey)** — подтверждённый по
|
||||||
|
коду блокер готовности, который до этого так жёстко не формулировался.
|
||||||
|
Единственный реальный пробел — платформенный контекст (таймауты/MSS), но он
|
||||||
|
возник из моего же allowlist и устраняется дополнением, не исправлением.
|
||||||
Reference in New Issue
Block a user