bridge: MessageID=хэш(дедуп), классификация permanent/transient SQS-ошибок, бесконечный ретрай подписки, валидация ns/deviceID
This commit is contained in:
@@ -5,13 +5,15 @@
|
||||
package bridge
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"log/slog"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
mqtt "github.com/eclipse/paho.mqtt.golang"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// TelemetryEnvelope — сообщение, перекладываемое в SQS (разбирается consumer).
|
||||
@@ -42,6 +44,13 @@ func newMessageHandler(dispatcher *sqsDispatcher, log *slog.Logger) mqtt.Message
|
||||
ns := parts[0]
|
||||
deviceID := parts[2]
|
||||
|
||||
// Defense-in-depth: EMQX ACL — основной барьер, но при его ошибке/отключении
|
||||
// бридж принял бы произвольный namespace. Дополнительно проверяем формат.
|
||||
if !identRe.MatchString(ns) || !identRe.MatchString(deviceID) {
|
||||
log.Warn("bridge: invalid namespace/device in topic, skipping", "topic", topic)
|
||||
return
|
||||
}
|
||||
|
||||
// Не-JSON payload оборачиваем в строку.
|
||||
rawPayload := json.RawMessage(payload)
|
||||
if !json.Valid(payload) {
|
||||
@@ -55,7 +64,20 @@ func newMessageHandler(dispatcher *sqsDispatcher, log *slog.Logger) mqtt.Message
|
||||
Topic: topic,
|
||||
Payload: rawPayload,
|
||||
ReceivedAt: time.Now().UTC().Format(time.RFC3339Nano),
|
||||
MessageID: uuid.NewString(),
|
||||
MessageID: messageIDFor(ns, deviceID, topic, payload),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// identRe — безопасный идентификатор (namespace/device_id): буквы/цифры/дефис,
|
||||
// не с дефиса, ≤64 символов. Defense-in-depth поверх EMQX ACL.
|
||||
var identRe = regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9-]{0,63}$`)
|
||||
|
||||
// messageIDFor — детерминированный id сообщения для дедупа.
|
||||
// Повторная QoS1-доставка того же (device, topic, payload) даёт тот же id,
|
||||
// поэтому ON CONFLICT (device_id, message_id) в БД дедуплицирует дубликаты
|
||||
// (раньше uuid на каждый колбэк — дедуп на повторе не срабатывал).
|
||||
func messageIDFor(ns, deviceID, topic string, payload []byte) string {
|
||||
h := sha256.Sum256([]byte(ns + "|" + deviceID + "|" + topic + "|" + string(payload)))
|
||||
return hex.EncodeToString(h[:])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user