Files
IoT/doc/thinking/remediation-plan-2026-10-05.md
T

9.7 KiB
Raw Blame History

План доработок 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.
  • Сейчас: 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 (гигиена).