Each IoT device can only pub/sub to its own topics: {namespace}/{deviceId}/#
Any attempt to access foreign topics → EMQX denies and disconnects.
Changes:
- internal/api/handler: add MQTTAcl handler (POST /internal/mqtt/acl)
- internal/api/router: register /internal/mqtt/acl route
- deployments/k8s/emqx.yaml: add HTTP authorization backend, no_match=deny
- Operator v0.1.52 deployed
Tested: own topic ALLOWED, foreign topic → authorization_permission_denied + disconnect
5.1 KiB
Лог мышления — 2026-04-04-02
Агент: GitHub Copilot (Claude Sonnet 4.6)
Продолжение сессии от 2026-04-04 (файл 2026-04-04.md достиг 681 строки).
Задача: MQTT WebSocket через nginx-ingress — отладка 404 → 400
Контекст
Порт 1883 заблокирован NSX-T Edge firewall на уровне облака.
Временный workaround: EMQX WebSocket listener (8083) → nginx-ingress → iot.kube5s.ru/mqtt.
Что нашёл
Проблема 1 — 404: Ingress не получил ADDRESS (IP не присвоен).
- Причина: annotation
nginx.ingress.kubernetes.io/configuration-snippetзаблокирован в nginx-controller v1.12.6. По умолчаниюallow-snippet-annotations=false. - Решение: убрал
configuration-snippetиз Ingress. - Результат: Ingress получил sync (Scheduled for sync), но ADDRESS всё ещё пустой — это нормально для данного контроллера.
Проблема 2 — 400: После удаления snippet стало 400 вместо 404.
- nginx конфиг для
iot.kube5s.ruправильный:proxy_set_header Upgrade $http_upgrade— ЕСТЬ (nginx-ingress добавляет автоматически)proxy_http_version 1.1— ЕСТЬproxy_send_timeout/read_timeout 3600s— ЕСТЬproxy_pass → sless-emqx-ws-8083— ЕСТЬ
- Значит 400 приходит от EMQX, а не от nginx.
Гипотезы почему EMQX возвращает 400
EMQX WebSocket listener конфиг:
listeners.ws.default {
bind = "0.0.0.0:8083"
max_connections = 512
}
Нет явного websocket.path. По умолчанию EMQX ожидает путь /mqtt.
Но nginx из-за pathType: Prefix на /mqtt может передавать путь без trailing slash.
Вариант A: nginx передаёт запрос на /mqtt (без /) — EMQX ожидает /mqtt → должно работать.
Вариант B: EMQX в 5.5.1 ожидает ws://host/mqtt без всякого proxy path rewrite и есть баг с X-Forwarded-Proto — nginx шлёт http, а EMQX может редиректить.
Вариант C: ssl_redirect: true в nginx конфиге означает что HTTP запрос перенаправляется на HTTPS, и там 400.
Следующий шаг
Проверить $ssl_redirect = true в nginx server block для нашего хоста:
- Если да — nginx делает redirect HTTP→HTTPS
- Добавить аннотацию
nginx.ingress.kubernetes.io/ssl-redirect: "false"или тестировать через HTTPS
Правило (новое)
Если файл doc/thinking/ГГГГ-ММ-ДД.md стал большим — создавать ГГГГ-ММ-ДД-02.md, ГГГГ-ММ-ДД-03.md и т.д.
Задача: Анализ безопасности MQTT multitenancy
Что исследовал
Пользователь спросил об угрозах межтенантного проникновения.
Изучил:
emqx.confв поде —authorization { no_match = allow }— после CONNECT всё разрешеноinternal/api/handler/iot_device_handler.go—MQTTAuthвозвращает только{"result":"allow"}без ACL rules
Вывод
Auth (CONNECT) защищён:
- HTTP auth endpoint проверяет namespace+deviceId+password (constant-time compare)
- enabled=true проверяется
- Secret изолирован по namespace
ACL на pub/sub НЕТ:
no_match = allow— аутентифицированный клиент может SUBSCRIBE на любой топик- EMQX HTTP auth plugin поддерживает возврат ACL rules в ответе на auth
- Формат ответа:
{"result":"allow","acl":[{"permission":"allow","action":"all","topic":"sless/ns/+"}]} - Текущий
mqttAuthResponseсодержит толькоResult string— ACL поле отсутствует
Риски по приоритету
- Критично: User A может SUBSCRIBE
sless/#и читать все IoT данные всех пользователей - Средне: Нет rate limit на MQTT — один клиент может flood брокер
- Низко: Нет TLS на 8083 (WebSocket без шифрования) — данные видны в сети
План фикса
Добавить в mqttAuthResponse поле ACL []aclRule и возвращать из MQTTAuth:
{
"result": "allow",
"acl": [
{"permission": "allow", "action": "publish", "topic": "sless/{ns}/{deviceId}"},
{"permission": "allow", "action": "subscribe", "topic": "sless/{ns}/{deviceId}"},
{"permission": "deny", "action": "all", "topic": "#"}
]
}
Ждём подтверждения от пользователя перед реализацией.