Files
sless/deployments/k8s/emqx.yaml
T
Naeel b23ae40975 security(iot): MQTT ACL isolation via EMQX HTTP authorization
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
2026-04-04 17:51:51 +03:00

198 lines
5.7 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Создано: 2026-04-04
# EMQX MQTT-брокер для IoT-сервиса (namespace: sless).
#
# Архитектура:
# IoT Device → MQTT CONNECT → EMQX (HTTP auth → sless-operator:9090/internal/mqtt/auth)
# EMQX → MQTT PUBLISH → sless-iot-bridge (paho subscriber) → RabbitMQ queue iot.{ns}.telemetry
# RabbitMQ → event-dispatcher → serverless function
#
# EMQX 5.x конфиг через emqx.conf (HOCON формат), монтируется как ConfigMap volume.
# НЕ используем env vars для конфигурации EMQX 5.x — они не поддерживаются аналогично 4.x.
#
# Порты:
# 1883 — MQTT (plaintext)
# 8883 — MQTTS (TLS, для prod надо настроить certSecret)
# 8083 — MQTT over WebSocket
# 18083 — EMQX Dashboard (admin/public по умолчанию — менять в prod!)
#
# Применение: kubectl apply -f deployments/k8s/emqx.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
name: emqx-config
namespace: sless
data:
# emqx.conf — HOCON конфиг для EMQX 5.5.x
# Раздел authentication: HTTP Backend для проверки MQTT credentials IoT-устройств.
# Наш сервис (sless-operator) ищет Secret iot-{deviceId} и сравнивает пароль.
emqx.conf: |
## EMQX 5.x configuration (HOCON format)
## Изменено: 2026-04-04
## Обязательные поля node — без них EMQX 5.x падает при старте
## node.cookie — секрет кластерного Erlang-соединения, для single-node любая строка
## node.data_dir — директория данных (mnesia, конфиги), должна существовать в контейнере
node {
name = "emqx@127.0.0.1"
cookie = "sless-emqx-cookie-mvp"
data_dir = "/opt/emqx/data"
}
## HTTP Auth Backend для IoT-устройств
## EMQX посылает POST с {username, password, clientid} → наш сервис отвечает {"result":"allow"|"deny"}
authentication = [
{
mechanism = password_based
backend = http
enable = true
method = post
url = "http://sless-operator.sless.svc:9090/internal/mqtt/auth"
body {
username = "${username}"
password = "${password}"
clientid = "${clientid}"
}
headers {
"content-type" = "application/json"
}
connect_timeout = 5s
request_timeout = 5s
## allow_timeout_error = false — если наш сервис не отвечает, deny (безопаснее)
pool_size = 8
}
]
## Authorization (ACL) — HTTP backend для изоляции топиков по устройству.
## no_match = deny: если HTTP backend недоступен или не ответил — запрещаем.
## Endpoint /internal/mqtt/acl возвращает allow только для топиков {ns}/{deviceId}/#
authorization {
no_match = deny
deny_action = disconnect
cache {
enable = true
max_size = 32
ttl = 1m
}
sources = [
{
type = http
enable = true
method = post
url = "http://sless-operator.sless.svc:9090/internal/mqtt/acl"
body {
username = "${username}"
clientid = "${clientid}"
action = "${action}"
topic = "${topic}"
}
headers {
"content-type" = "application/json"
}
connect_timeout = 5s
request_timeout = 5s
pool_size = 8
}
]
}
## MQTT настройки
mqtt {
max_packet_size = 1MB
max_topic_levels = 10
retain_available = false
}
## Listeners — только plaintext MQTT для MVP
## TLS (8883) отключён — настроить при необходимости
listeners.tcp.default {
bind = "0.0.0.0:1883"
max_connections = 1024
}
listeners.ws.default {
bind = "0.0.0.0:8083"
max_connections = 512
}
## Dashboard
dashboard {
listeners.http {
bind = 18083
}
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: emqx
namespace: sless
labels:
app: emqx
spec:
replicas: 1
selector:
matchLabels:
app: emqx
template:
metadata:
labels:
app: emqx
spec:
containers:
- name: emqx
image: emqx/emqx:5.5.1
ports:
- name: mqtt
containerPort: 1883
- name: ws
containerPort: 8083
- name: dashboard
containerPort: 18083
volumeMounts:
- name: emqx-conf
mountPath: /opt/emqx/etc/emqx.conf
subPath: emqx.conf
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
readinessProbe:
tcpSocket:
port: 1883
initialDelaySeconds: 20
periodSeconds: 10
timeoutSeconds: 5
livenessProbe:
tcpSocket:
port: 1883
initialDelaySeconds: 40
periodSeconds: 20
volumes:
- name: emqx-conf
configMap:
name: emqx-config
---
apiVersion: v1
kind: Service
metadata:
name: emqx
namespace: sless
spec:
selector:
app: emqx
ports:
- name: mqtt
port: 1883
targetPort: 1883
- name: ws
port: 8083
targetPort: 8083
- name: dashboard
port: 18083
targetPort: 18083