9.7 KiB
9.7 KiB
План доработок 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:21mqtt_password TEXT), отдаётся черезGET /devices/{name}(handler/devices.go:189); EMQXpassword_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) и EMQXverify=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_telemetryid 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().Runbridge/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 ./...+gofmtcheck; сборка образа. - Файлы:
.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.
ПЛАТФОРМА (вне нашего кода, но блокирует прод)
- wss обрывы ~150с на внешнем шлюзе (детерминированно, доказано с 3 точек). Тикет Nubes; до фикса — устройства должны авто-переподключаться (MQTT keepalive ≤60с).
- MSS/MTU-баг — обрыв ответов >~15–64 КБ. Обход уже есть (offset-пагинация, внутренний SQS endpoint); нужен фикс на стороне Nubes.
Рекомендуемый порядок выполнения
- P0-1, P0-2 (tenant-граница + подпись) — закрывают горизонтальный доступ.
- P0-7, P0-8 (доставка + dedup) — закрывают потерю/дубли.
- P0-5 (выключить AUTH_TEST_MODE) — быстро, на проде.
- P0-3, P1-4, P1-5 (пароли + лишние эндпоинты).
- P1-9, P1-10 (health + метрики дропов).
- P1-11, P1-12 (тесты + CI).
- P2-13..P2-16 (гигиена).