Skip to content

Update broker auth proto - #61

Open
dborovcanin wants to merge 4 commits into
absmach:mainfrom
dborovcanin:broker-auth-proto
Open

Update broker auth proto#61
dborovcanin wants to merge 4 commits into
absmach:mainfrom
dborovcanin:broker-auth-proto

Conversation

@dborovcanin

Copy link
Copy Markdown
Contributor

What type of PR is this?

What does this do?

Which issue(s) does this PR fix/relate to?

Have you included tests for your changes?

Did you document any new/modified features?

Notes

Atom implements FluxMQ's fluxmq.auth.v1.AuthService, so a broker can
authenticate clients and authorize topics against Atom with no adapter
service in between.

The proto is vendored verbatim from FluxMQ. Its package line is part of
the wire contract — the path a broker dials is derived from it — so it
must not be renamed. How a topic names an object is configuration
(ATOM_BROKER_TOPIC_TEMPLATE), which keeps any particular deployment's
topic vocabulary out of Atom; an adapter service is still the right
answer where the mapping needs more than a grammar.

Two invariants the implementation rests on:

- Denials are answers, not errors. Every rejection returns a successful
  RPC carrying a false verdict; only infrastructure failure returns a
  gRPC error. A broker wraps this callout in a circuit breaker, and a
  tripped breaker rejects every client connection, so one device
  retrying a stale password must not be able to take the broker's whole
  auth path down. Rate limiting is on that list because it is the
  failure a bad client can trigger at will.

- Tenant comes from the subject, not from config or the topic.
  Authenticate resolves the identifier across tenants and the entity's
  own tenant comes back with it, so the zero-configuration case needs
  no tenant in the topic and no username grammar. A {tenant} template
  segment only scopes alias resolution; it is deliberately not checked
  against the subject's tenant, because cross-tenant grants are
  legitimate and that call belongs to the PDP.

Off by default. It is the only gRPC service here with no bearer token to
check, so it authenticates its caller at the transport via the
listener's mTLS client CA; mounted on a plaintext listener, anything
that can reach the port could authenticate and authorize as any
principal. A startup warning fires if it is enabled without a client CA.
A broker carries topics that address no object — a health probe such as
hc/<tenant> names nothing Atom can resolve — so no policy could describe
them and every request for one was denied. ATOM_BROKER_TOPIC_ALLOW lists
topics authorized without consulting the PDP.

This is the only authorization bypass in the callout, so it defaults to
empty and its patterns are ordinary MQTT filters: an operator writes the
narrowest shape that covers the operational topic rather than reaching
for a prefix.

A broker '#' covers its position and everything below it, so only a
pattern that is itself '#' there is broad enough to admit it. Letting
'+' match it would silently widen the bypass past what the operator
wrote — hc/+ would admit a subscription to the whole hc subtree.
The wire path a broker dials is derived from the proto package, so serving
`fluxmq.auth.v1` put one implementation's name in the public surface of
every peer that speaks the contract — including Atom's. Nothing in the
messages is FluxMQ-specific; the same shape covers MQTT, AMQP, CoAP and
HTTP, and other brokers and providers implement it.

Renaming costs nothing structurally. The Go import path upstream is
derived from the file's location rather than the package, so consumers
keep the same imports and symbols, and only the dialled path changes.

It is still a breaking wire change: a broker dialling the new path
against a service serving the old one gets UNIMPLEMENTED. Atom, the
brokers, and any adapter service have to move together.
Atom implements a proto it does not own, and nothing rebuilds the copy.
An upstream change was therefore discovered at runtime — as an
UNIMPLEMENTED from a renamed service, or worse, as a field that still
decodes but no longer means what Atom thinks it means.

scripts/check-vendored-proto.sh diffs the vendored file against the ref
pinned in proto/broker/v1/REF, and CI runs it. For that diff to stay
trustworthy the copy has to be byte-identical, so Atom's own notes moved
out of the proto and into VENDOR.md beside it: a check that has to
forgive expected differences stops catching the one that matters.

Vendoring a second proto into the buf module had also broken two things
that nothing here runs, so neither had surfaced:

- buf lint failed on twenty-odd violations in the vendored file. Its
  style is upstream's, and Atom cannot fix it without breaking the
  byte-for-byte match, so it is excluded from lint and breaking.
- buf generate silently replaced apidocs/grpc-reference.md with the
  broker contract, dropping Atom's own gRPC surface from the docs.
  protoc-gen-doc writes one file per invocation, so a second package
  does not extend that file. Generation is now scoped to Atom's protos.

make proto regenerates both outputs. The asymmetry is worth knowing: the
Rust bindings are not checked in — build.rs runs tonic-build into OUT_DIR
on every compile — but grpc-reference.md is, and goes stale silently.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant