Documentation
¶
Overview ¶
Package comqttauth manages comqtt broker authentication and authorization state from the dashboard. Each Backend implementation reads and writes the same on-disk/on-wire shape comqtt's corresponding plugin/auth/* runtime hooks already consume, so a change made through the dashboard is visible to the running broker on its next lookup without any synchronization layer or process restart.
Backends supported in v0.3.0:
- file: auth.Ledger YAML file (built-in comqtt hook)
- redis: plugin/auth/redis HSET/HGETALL key shape
- mysql: plugin/auth/mysql configurable auth/acl tables
- postgres: plugin/auth/postgresql configurable auth/acl tables
The dashboard's Auth and ACL pages consume Backend through a single interface. The active backend is selected by the cmd-binary based on cfg.Auth.Datasource and constructed via factory.New().
This package does not manage the dashboard's own operator credentials (admin/viewer roles) - those live in dashboard/auth and are unrelated to MQTT-broker auth.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrConflict = errors.New("comqttauth: conflict")
ErrConflict is returned when a write would create a duplicate of a unique record (e.g. a second user with the same username).
var ErrInvalidRule = errors.New("comqttauth: invalid regex rule")
ErrInvalidRule is returned when a regex rule fails validation (invalid regex, invalid CIDR, empty topic patterns, etc).
var ErrNotFound = errors.New("comqttauth: not found")
ErrNotFound is returned when a requested record does not exist.
var ErrUnsupported = errors.New("comqttauth: operation not supported by this backend")
ErrUnsupported is returned by Backend methods when the operation is not supported by the active backend (e.g. user CRUD against an http-delegated auth backend, or wildcard-subject ACL queries against a key-value store that only indexes by exact subject).
Functions ¶
This section is empty.
Types ¶
type ACLRule ¶
type ACLRule struct {
ID string `json:"id"`
Subject string `json:"subject"`
Topic string `json:"topic"`
Access Access `json:"access"`
}
ACLRule is the wire shape for one row in the ACL table. ID is backend- assigned and opaque to callers (a row id for SQL backends, the topic filter for redis, or the slice index for the file backend).
type Access ¶
type Access uint8
Access matches comqtt's auth.Access constants. The dashboard exposes the four values directly in ACL editing forms; backends store them as the same byte values.
type AuthMode ¶
type AuthMode uint8
AuthMode selects what the lookup key is for auth and ACL records. It mirrors comqtt's auth.Access constants where it is overloaded as a mode indicator (0=anon, 1=username, 2=clientid).
type AuthTable ¶
AuthTable mirrors plugin/auth/{mysql,postgresql}.AuthTable so the dashboard reads and writes the same physical table the broker reads.
type Backend ¶
type Backend interface {
// Kind returns a short stable name for the backend ("file", "redis",
// "mysql", "postgres"). Used in admin UI badges and structured logging.
Kind() string
// Mode returns how user records are keyed (username or clientid). The UI
// labels Subject columns accordingly.
Mode() AuthMode
// HashType returns the password hash algorithm this backend writes. The UI
// labels password fields with this so operators know what they are
// configuring.
HashType() HashType
// Users returns all user records. Returns an empty slice (not nil) when
// no users exist.
Users(ctx context.Context) ([]User, error)
// GetUser returns the user with the given subject, or ErrNotFound.
GetUser(ctx context.Context, subject string) (*User, error)
// PutUser upserts a user record. plaintextPassword is hashed per
// HashType() before write. Pass empty plaintextPassword to leave the
// stored password unchanged on update; ErrNotFound on update of a missing
// subject; the implementation distinguishes create vs update by existence
// of the record.
PutUser(ctx context.Context, u User, plaintextPassword string) error
// DeleteUser removes the user with the given subject. Returns ErrNotFound
// when no record matched.
DeleteUser(ctx context.Context, subject string) error
// Rules returns ACL rules for the given subject, or all rules when
// subject is empty. Returns an empty slice when no rules match.
Rules(ctx context.Context, subject string) ([]ACLRule, error)
// PutRule inserts or updates an ACL rule. If r.ID is empty, the
// implementation creates a new record and returns its assigned id.
// Otherwise the existing record with that id is replaced; ErrNotFound if
// the id does not exist.
PutRule(ctx context.Context, r ACLRule) (string, error)
// DeleteRule removes the ACL rule with the given id. ErrNotFound if no
// record matched.
DeleteRule(ctx context.Context, id string) error
// RegexRules returns all stored regex rules ordered by Order ascending.
// Returns an empty slice (not nil) when no rules are stored.
RegexRules(ctx context.Context) ([]RegexRule, error)
// PutRegexRule stores a rule. If r.ID is empty, the backend assigns
// one and returns it. If r.ID is set, the backend upserts that id.
PutRegexRule(ctx context.Context, r RegexRule) (id string, err error)
// DeleteRegexRule removes the rule by id. Returns ErrNotFound when no
// rule matches.
DeleteRegexRule(ctx context.Context, id string) error
// GetRegexSeeded reports whether the first-time auto-seed has happened
// against this backend. Used by the dashboard to decide whether to seed
// a default rule on startup.
GetRegexSeeded(ctx context.Context) (bool, error)
// SetRegexSeeded marks the backend as seeded. Idempotent.
SetRegexSeeded(ctx context.Context) error
// Close releases any underlying connections. Idempotent.
Close() error
}
Backend is the unified interface the dashboard's Auth and ACL pages call. Each plugin/auth/* in comqtt has a corresponding implementation under this package's sub-trees.
All Context-taking methods are expected to honor cancellation and obey any deadline set by callers (typically a 3s default applied by the handler layer to keep dashboard responsiveness predictable).
func New ¶
New constructs the Backend for cfg.Kind. Returns an error if the per-Kind sub-config is missing or if Kind is unknown.
Chunks 2-5 of v0.3.0 fill in the per-backend constructors. Until then, each constructor returns a stub that responds to interface calls with ErrUnsupported so the surrounding dashboard handler scaffold can be wired independently.
type Config ¶
type Config struct {
Kind string
// Mode is the lookup key used by user records.
// ACLMode is the lookup key used by ACL records. Comqtt allows these to
// differ (e.g. auth-by-username, ACL-by-clientid) so we keep them
// independent.
Mode AuthMode
ACLMode AuthMode
// HashType is the algorithm used for password storage. HashKey is the
// shared secret consumed by HMAC-* variants and ignored otherwise.
HashType HashType
HashKey string
// Per-backend specifics. The factory consults the field matching Kind
// and returns an error if it is nil.
File *FileConfig
Redis *RedisConfig
SQL *SQLConfig
}
Config is the disjoint-union of per-backend configurations. Exactly one of File/Redis/SQL is consulted, picked by Kind. Mode, ACLMode, HashType, and HashKey apply across all backends.
Kind is the canonical lowercase backend name: "file" | "redis" | "mysql" | "postgres". Pass-through from the broker's --auth-ds flag.
type FileConfig ¶
type FileConfig struct {
// Path is the YAML file. Must be writable by the dashboard process; the
// file backend writes via atomic rename so a partial flush never makes
// the broker see half a config.
Path string
}
FileConfig points at a YAML ledger file matching the shape consumed by comqtt's built-in mqtt/hooks/auth.Hook running in LedgerMode.
type HashType ¶
type HashType uint8
HashType identifies which hashing algorithm a backend uses for stored passwords. Values match comqtt's plugin/auth.HashType so a dashboard configured with the same hash as the broker writes hashes the broker can verify.
type Hook ¶ added in v0.2.0
Hook implements mqtt.Hook. It provides OnACLCheck only; OnConnectAuthenticate is left to comqtt's upstream plugin/auth/* hook in coexist mode.
On no-rule-match, OnACLCheck returns true so the AND-chain of hooks doesn't false-deny legitimate traffic that upstream has already allowed.
func (*Hook) ID ¶ added in v0.2.0
ID returns the hook's identifier (used by mqtt.Server for logging).
func (*Hook) Init ¶ added in v0.2.0
Init configures the hook with the runtime Backend. Called by mqtt.Server at AddHook time. The passed config must be a *HookOptions.
func (*Hook) OnACLCheck ¶ added in v0.2.0
OnACLCheck evaluates regex rules and returns the verdict. Returns true (allow) when no rule matches, so the AND-combine with upstream's auth hook doesn't false-deny legitimate traffic.
type HookOptions ¶ added in v0.2.0
type HookOptions struct {
// Backend supplies the rules at runtime. Required.
Backend Backend
// Logger receives debug / info events. Optional; defaults to slog.Default().
Logger *slog.Logger
}
HookOptions configures the runtime Hook. Pass via Server.AddHook.
type Permission ¶ added in v0.2.0
type Permission int8
Permission decides whether a rule's match results in allow or deny. Integer values are stable across versions; do not renumber.
const ( PermissionAllow Permission = 1 PermissionDeny Permission = 0 )
func ParsePermission ¶ added in v0.2.0
func ParsePermission(s string) (Permission, bool)
ParsePermission parses the string form. Returns the zero value (deny) and false when the input is not recognised.
func (Permission) String ¶ added in v0.2.0
func (p Permission) String() string
String returns the YAML / JSON / human-readable form.
type RedisConfig ¶
type RedisConfig struct {
Addr string
Username string
Password string
DB int
// AuthKeyPrefix defaults to "comqtt:auth" when empty (matching
// plugin/auth/redis.defaultAuthkeyPrefix).
AuthKeyPrefix string
// ACLKeyPrefix defaults to "comqtt:acl" when empty.
ACLKeyPrefix string
}
RedisConfig matches the lookup shape of comqtt's plugin/auth/redis: a HASH at AuthKeyPrefix (default "comqtt:auth") and per-subject HASH at ACLKeyPrefix:<subject>.
type RegexRule ¶ added in v0.2.0
type RegexRule struct {
ID string // backend-assigned on Put
Order int // lower runs first
Permission Permission
SubjectKind SubjectKind
SubjectPattern string // empty (any) | "re:<regex>" | "cidr:<cidr>" | literal
Action RuleAction
TopicPatterns []string // length >= 1; each is an MQTT topic filter
}
RegexRule is one regex authorization rule. Rules are evaluated in ascending Order; first match wins (allow or deny). When no rule matches, the Hook default-allows so the AND-chain with comqtt's upstream auth plugin doesn't false-deny legitimate traffic.
QoS and Retain fields from the v0.4.0 design spec are deferred to v0.4.1 because comqtt's OnACLCheck signature does not carry packet QoS or retain flag.
type RuleAction ¶ added in v0.2.0
type RuleAction int8
RuleAction matches against the operation the client is attempting. Integer values are stable across versions.
const ( ActionPub RuleAction = 0 ActionSub RuleAction = 1 ActionAll RuleAction = 2 )
func ParseRuleAction ¶ added in v0.2.0
func ParseRuleAction(s string) (RuleAction, bool)
ParseRuleAction parses the string form.
func (RuleAction) String ¶ added in v0.2.0
func (a RuleAction) String() string
String returns the YAML / JSON / human-readable form.
type SQLConfig ¶
SQLConfig is shared between MySQL and Postgres backends; Driver selects. Driver is one of "mysql" | "postgres". DSN is the database/sql connection string the chosen driver expects.
type SubjectKind ¶ added in v0.2.0
type SubjectKind int8
SubjectKind selects which client attribute the rule's subject pattern is matched against. Integer values are stable across versions.
const ( SubjectUsername SubjectKind = 0 SubjectClientID SubjectKind = 1 SubjectIPAddr SubjectKind = 2 SubjectCertCN SubjectKind = 3 SubjectCertSubject SubjectKind = 4 )
func ParseSubjectKind ¶ added in v0.2.0
func ParseSubjectKind(s string) (SubjectKind, bool)
ParseSubjectKind parses the string form.
func (SubjectKind) String ¶ added in v0.2.0
func (s SubjectKind) String() string
String returns the YAML / JSON / human-readable form.
type User ¶
type User struct {
// Subject is the lookup key for this record: a username when the backend
// is configured AuthMode=Username, or a clientID when AuthMode=ClientID.
Subject string `json:"subject"`
// Allow is whether the user may connect. False denies authentication
// without removing the record (useful for temporary lockouts).
Allow bool `json:"allow"`
}
User is the wire shape returned by Backend.Users / Backend.GetUser. Passwords are never read back through this struct; PutUser takes the plaintext separately.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
comqttauth-broker
command
Single-mode comqtt broker with comqttauth.Hook installed alongside the upstream auth hook.
|
Single-mode comqtt broker with comqttauth.Hook installed alongside the upstream auth hook. |
|
comqttauth-broker-cluster
command
Cluster-mode comqtt broker with comqttauth.Hook installed alongside the upstream auth hook.
|
Cluster-mode comqtt broker with comqttauth.Hook installed alongside the upstream auth hook. |
|
examples
|
|
|
file
command
File backend example: comqtt broker + comqttauth, both reading the same on-disk YAML ledger.
|
File backend example: comqtt broker + comqttauth, both reading the same on-disk YAML ledger. |
|
mysql
command
MySQL backend example: comqtt broker + comqttauth, both reading the same auth/acl tables.
|
MySQL backend example: comqtt broker + comqttauth, both reading the same auth/acl tables. |
|
postgres
command
Postgres backend example: comqtt broker + comqttauth, both reading the same auth/acl tables.
|
Postgres backend example: comqtt broker + comqttauth, both reading the same auth/acl tables. |
|
redis
command
Redis backend example: comqtt broker + comqttauth, both reading the same comqtt-shaped Redis keys (HASH comqtt:auth, HASH comqtt:acl:<subject>).
|
Redis backend example: comqtt broker + comqttauth, both reading the same comqtt-shaped Redis keys (HASH comqtt:auth, HASH comqtt:acl:<subject>). |
|
internal
|
|
|
brokerauth
Package brokerauth glues comqttauth.Hook onto a comqtt server using the same parsed auth Options that the upstream auth hook consumes.
|
Package brokerauth glues comqttauth.Hook onto a comqtt server using the same parsed auth Options that the upstream auth hook consumes. |