# План доработок IoT — до production-ready (2026-10-05) > Основа: два независимых анализа + ревью Опуса (построчные ссылки) + моя > перепроверка по коду. Сгруппировано в 4 трека, внутри — по приоритету. > Приоритеты: P0 — потеря/компрометация данных или чужие данные; P1 — важные > риски эксплуатации; P2 — обвязка/гигиена; ПЛАТФОРМА — вне нашего кода. --- ## Трек 1. Безопасность / tenant-изоляция ### P0-1. Связать JWT `sub` с `{namespace}` - Сейчас: `validateJWT` проверяет только наличие `sub`/`exp`/подписи, `sub` никак не сверяется с `{namespace}` из URL (`middleware/auth.go:62`); обработчики берут ns из пути (`handler/devices.go:91`, `handler/telemetry.go:19`). - Что: извлекать `sub` из claims, передавать в контекст запроса, в middleware (`/v1`) сверять с `{namespace}` → 403 при несовпадении. - Файлы: `middleware/auth.go`, `router.go`, `handler/*.go`. ### P0-2. Сделать проверку подписи обязательной - Сейчас: при пустом `JWT_HMAC_SECRET` подпись не проверяется — токен подделывается (`auth.go:70`); дефолт секрета пустой, не в required (`config.go:118`). - Что: при `AUTH_TEST_MODE=false` требовать `JWT_HMAC_SECRET` (иначе отказ на старте); `exp` сделать обязательным. - Файлы: `config.go`, `auth.go`. ### P0-3. Не отдавать пароль устройства повторно + хешировать - Сейчас: пароль в БД открытым текстом (`store/open.go:21` mqtt_password TEXT), отдаётся через `GET /devices/{name}` (`handler/devices.go:189`); EMQX `password_hash_algorithm name=plain` (`emqx.conf:46`). - Что: пароль отдавать единожды при создании; в БД хранить hash; EMQX перевести на алгоритм хэша. Миграция существующих строк. - Файлы: `store/open.go`, `handler/devices.go`, `emqx/emqx.conf`. ### P1-4. Удалить HTTP-auth/ACL `/internal/mqtt/auth|acl` - Сейчас: EMQX ходит по PG-auth напрямую (`emqx.conf`, `backend=postgresql`). HTTP-эндпоинты — неиспользуемый fallback, висят на публичном порту 9090 без аутентификации (`router.go:50-51`) → перебор паролей устройств. - Что: удалить роуты и обработчики `MQTTAuth`/`MQTTAcl` (и зависимый код). - Файлы: `router.go`, `handler/mqtt_auth.go`. ### P1-5. Включить `AUTH_TEST_MODE=false` на проде - Сейчас: включён для диагностики DevOps (HISTORY §38); любой Bearer проходит. - Что: выставить false + валидный `JWT_HMAC_SECRET`, убедиться в деплое. ### P2-6. TLS верификация PG и EMQX - Сейчас: `sslmode=require` (без verify-full) и EMQX `verify=verify_none` (`emqx.conf:40`) — шифрование без проверки сертификата. - Что: `sslmode=verify-full` при наличии CA; убрать `verify_none` где возможно. --- ## Трек 2. Надёжность доставки телеметрии ### P0-7. Устранить окно потери сообщения (SQS-удаление до записи в PG) - Сейчас: `consumer.go:78` удаляет из SQS после `processTelemetry`==nil, но `InsertTelemetry` при неполном батче лишь кладёт в память и возвращает nil (`iot_telemetry_store.go:356-376`); flush-ошибка неполного батча только логируется (`iot_telemetry_store.go:133`). Краш/сбой flush = безвозвратная потеря. - Что: либо синхронная запись (убрать «возврат nil до записи»), либо явный acknowledge только после подтверждения flush + при ошибке возвращать сообщение в очередь (видимость + не удалять). - Файлы: `consumer/processor.go`, `iotpg/iot_telemetry_store.go`. ### P0-8. Идемпотентность входа (dedup) - Сейчас: `iot_telemetry` id BIGSERIAL без ключа дедупликации; envelope без message-id (`bridge/handler.go:19`). At-least-once SQS → молчаливые дубли. - Что: добавить message-id в envelope, UNIQUE-индекс/ON CONFLICT DO NOTHING по (namespace, message_id) в таблице телеметрии. - Файлы: `bridge/handler.go`, `iotpg/iot_telemetry_store.go`, схема БД. ### P1-9. `/health` отражает реальное состояние - Сейчас: `healthHandler` всегда `{"status":"ok"}` (`router.go:72`), не отражает bridge/consumer/PG/SQS; liveness зелёный при упавшем consumer (`main.go:106`). - Что: ready-проверки подсистем; `main().Run` bridge/consumer — не глотать ошибку, а помечать неготовность; `/healthz` (liveness) отделить от `/ready` (readiness). - Файлы: `router.go`, `main.go`. ### P1-10. Bridge: поведение при переполнении и ретраях — осознанно - Сейчас: дроп при переполнении RAM-очереди (`sender.go:73`) и после 3 ретраев (`sender.go:112`) — потери без метрик. - Что: метрики дропов; при исчерпании ретраев — не молчаливый дроп, а лог+счётчик (или при желании DLQ в shared-sqs). - Файлы: `bridge/sender.go`. --- ## Трек 3. Тесты, CI, метрики ### P1-11. Go-тесты - Сейчас: `*_test.go` нет ни одного. - Что: unit-тесты критичных мест — `validateJWT` (tenant-привязка), `InsertTelemetry`/`flushAll` (потеря), `processor` (дубли), `devices` (CRUD + пароль), `mqtt_auth` (constant-time). Приоритет — на путях из P0-1..P0-8. - Файлы: `*_test.go` рядом с пакетами. ### P1-12. CI - Сейчас: `.github/workflows` нет; в `.github/` только правила. - Что: workflow: `go vet` + `go test ./...` + `gofmt` check; сборка образа. - Файлы: `.github/workflows/ci.yml`. ### P2-13. `/metrics` + rate-limiting - Сейчас: `/metrics` заявлен в комментарии (`config/defaults.go:6`), но роут не зарегистрирован в `router.go`; prometheus — только indirect dep. Rate-limit нет. - Что: зарегистрировать `/metrics` (если решено), добавить rate-limiting на `/v1`. - Файлы: `router.go`, `middleware/`. --- ## Трек 4. Гигиена репозитория и документов ### P2-14. Вычистить легаси - Сейчас: `cmd/iot-operator`, `controllers/`, `internal/api/`, `cmd/mqtt-bridge`, `cmd/sqs-consumer`; k8s/controller-runtime в `go.mod` (12 строк); бинарники в `bin/`, `ws-probe/ws-probe`. `LEGACY.md` обещает убрать после переписывания хендлеров — оно сделано. - Что: удалить легаси-код (после P1-4), `go mod tidy`, убрать бинарники из дерева. - Файлы: `go.mod`, дерево. ### P2-15. Согласовать доки с кодом (дрейф) - Сейчас: `doc/deployment-nubes-production.md` — образы `v0.1.2`; Makefile — `v0.1.13`; `loadtests/README.md` — visibility 30с (в коде 120с) и старая блокировка MQTT-обработчика (уже исправлена). - Что: привести версии и параметры в доках к фактическим. - Файлы: `doc/deployment-nubes-production.md`, `loadtests/README.md`. ### P2-16. Emqx cookie из env - Сейчас: `cookie = "iot-emqx-cookie-mvp"` захардкожен (`emqx.conf`). - Что: задавать cookie через env/деплой. - Файлы: `emqx/emqx.conf`. --- ## ПЛАТФОРМА (вне нашего кода, но блокирует прод) 1. **wss обрывы ~150с** на внешнем шлюзе (детерминированно, доказано с 3 точек). Тикет Nubes; до фикса — устройства должны авто-переподключаться (MQTT keepalive ≤60с). 2. **MSS/MTU-баг** — обрыв ответов >~15–64 КБ. Обход уже есть (offset-пагинация, внутренний SQS endpoint); нужен фикс на стороне Nubes. --- ## Рекомендуемый порядок выполнения 1. P0-1, P0-2 (tenant-граница + подпись) — закрывают горизонтальный доступ. 2. P0-7, P0-8 (доставка + dedup) — закрывают потерю/дубли. 3. P0-5 (выключить AUTH_TEST_MODE) — быстро, на проде. 4. P0-3, P1-4, P1-5 (пароли + лишние эндпоинты). 5. P1-9, P1-10 (health + метрики дропов). 6. P1-11, P1-12 (тесты + CI). 7. P2-13..P2-16 (гигиена).