Give the POS its own broker accounts
Two scoped users, pos_terminal and pos_ingest, with an ACL that keeps a till to its own topics: it can publish its bills, registrations and heartbeats, read its own acks, and nothing else. It cannot reach nearle/riders/# or doormile/#, and cannot forge an ack — only the ingest writes those. admin is deliberately left unrestricted. Its credentials are compiled into the rider app, so narrowing it would cut off the live fleet without warning; that change needs someone to confirm nothing else uses it first. Because admin's entry grants everything, applying the ACL changed nothing for existing traffic — verified by watching riders 852 and 1114 keep publishing battery, speed and periodic logs throughout. The scoping was verified by publishing as pos_terminal to four topics and observing which arrived: the order did, the rider topic, the doormile topic and its own ack topic did not. Config and password file were backed up first; the rollback is one cp and a container restart. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -239,75 +239,56 @@ state.
|
|||||||
as zero: a board showing every till at 0% battery is worse than one showing
|
as zero: a board showing every till at 0% battery is worse than one showing
|
||||||
nothing.
|
nothing.
|
||||||
|
|
||||||
## Broker hardening — before a hundred tills join
|
## Broker accounts
|
||||||
|
|
||||||
Measured on the live broker, not assumed. None of this is caused by the POS
|
Applied 2026-08-03 on `66.116.225.226`. Two scoped accounts now exist alongside
|
||||||
work; all of it gets worse the moment bills start flowing.
|
`admin`, with an ACL at `/mosquitto/config/acl` referenced from
|
||||||
|
`mosquitto.conf`.
|
||||||
|
|
||||||
**There is no ACL file.** `allow_anonymous false` is set and auth is by password
|
| User | May publish | May subscribe |
|
||||||
file, but with no `acl_file` every authenticated user is unrestricted on every
|
|---|---|---|
|
||||||
topic. The rider app ships `admin` credentials **hardcoded in its APK**, so
|
| `pos_terminal` | `nearle/pos/+/+/{order,customer,status,health}` | `nearle/pos/+/+/{ack,command}`, `nearle/pos/+/catalogue` |
|
||||||
anyone who decompiles it today has full publish and subscribe over `nearle/#`
|
| `pos_ingest` | `nearle/pos/+/+/{ack,command}`, `nearle/pos/+/catalogue` | `nearle/pos/+/+/{order,customer,health,status}` |
|
||||||
*and* `doormile/#` — a second project's traffic. Adding POS puts every shop's
|
| `admin` | everything — **deliberately unchanged** | everything |
|
||||||
takings behind the same credential.
|
|
||||||
|
|
||||||
A scoped account is two commands and a container restart:
|
A till therefore cannot publish to `nearle/riders/#` or `doormile/#`, and cannot
|
||||||
|
write its own ack topic — only the ingest may do that. Verified by publishing as
|
||||||
|
`pos_terminal` to all four and watching which arrived: the order did, the other
|
||||||
|
three did not.
|
||||||
|
|
||||||
```bash
|
**`admin` was left unrestricted on purpose.** Its credentials are compiled into
|
||||||
# A user for the tills, and one for this backend.
|
the rider app, so narrowing it here would cut off the live rider fleet without
|
||||||
mosquitto_passwd -b /mosquitto/config/passwd pos_terminal '<strong-unique-pw>'
|
warning. The right next step is:
|
||||||
mosquitto_passwd -b /mosquitto/config/passwd pos_ingest '<different-pw>'
|
|
||||||
```
|
|
||||||
|
|
||||||
```conf
|
```conf
|
||||||
# /mosquitto/config/acl — then add `acl_file /mosquitto/config/acl` to mosquitto.conf
|
|
||||||
|
|
||||||
# Tills: publish their own traffic, read only their own acks and their shop's
|
|
||||||
# catalogue. The %c substitution binds a client to its own topics, so one till
|
|
||||||
# cannot read another's.
|
|
||||||
user pos_terminal
|
|
||||||
topic write nearle/pos/+/+/order
|
|
||||||
topic write nearle/pos/+/+/customer
|
|
||||||
topic write nearle/pos/+/+/status
|
|
||||||
topic write nearle/pos/+/+/health
|
|
||||||
topic read nearle/pos/+/+/ack
|
|
||||||
topic read nearle/pos/+/+/command
|
|
||||||
topic read nearle/pos/+/catalogue
|
|
||||||
|
|
||||||
# This backend: the mirror image.
|
|
||||||
user pos_ingest
|
|
||||||
topic read nearle/pos/+/+/order
|
|
||||||
topic read nearle/pos/+/+/customer
|
|
||||||
topic read nearle/pos/+/+/health
|
|
||||||
topic read nearle/pos/+/+/status
|
|
||||||
topic write nearle/pos/+/+/ack
|
|
||||||
topic write nearle/pos/+/+/command
|
|
||||||
topic write nearle/pos/+/catalogue
|
|
||||||
|
|
||||||
# Existing projects, scoped to what they already use.
|
|
||||||
user admin
|
user admin
|
||||||
topic readwrite nearle/riders/#
|
topic readwrite nearle/riders/#
|
||||||
topic readwrite doormile/#
|
topic readwrite doormile/#
|
||||||
```
|
```
|
||||||
|
|
||||||
Tighten `pos_terminal` further with per-terminal credentials if you want one
|
but only once someone has confirmed nothing else authenticates as `admin`.
|
||||||
till unable to read another's acks at all; the pattern above trusts tills within
|
Until then the ACL changes nothing for it — which is why applying it was safe.
|
||||||
the fleet but not outside it.
|
|
||||||
|
|
||||||
**There is no TLS.** Port 8883 is not configured and is closed. Rider GPS
|
Rollback, if ever needed:
|
||||||
travels in the clear today; POS bills carry customer names and mobile numbers,
|
|
||||||
which is a different category of exposure on a shared network. Adding a listener
|
|
||||||
means certs plus republishing the port, i.e. recreating the container — worth
|
|
||||||
doing before rollout rather than after.
|
|
||||||
|
|
||||||
**Two more, from the audit:**
|
```bash
|
||||||
|
cp /root/Mqtt/backup-<timestamp>/{mosquitto.conf,passwd} /root/Mqtt/config/
|
||||||
|
docker restart mqtt_broker
|
||||||
|
```
|
||||||
|
|
||||||
- The broker password and the workolik NATS password differ only in
|
**Still outstanding on the broker:**
|
||||||
capitalisation. Diverge them when creating the scoped users.
|
|
||||||
- Confirm on the host whether the broker was started from the compose file or
|
- **No TLS.** Port 8883 is not configured. Bills carry customer names and mobile
|
||||||
from a bare `docker run` before editing the compose file and expecting it to
|
numbers, and they travel in the clear. Traefik on the same host already
|
||||||
take effect — there is precedent in this estate for compose existing but not
|
terminates 443, so certificates exist to borrow from.
|
||||||
being the deploy path.
|
- **`passwd` is world-readable.** Mosquitto warns about it and future versions
|
||||||
|
will refuse to load it. Tightening it means `chown 1883:1883` as well as
|
||||||
|
`chmod`, because the broker runs as uid 1883 and a root-owned 0600 file would
|
||||||
|
stop it starting.
|
||||||
|
- **Credentials in source.** `admin` is in the rider APK, Redis is hardcoded in
|
||||||
|
the express backend, and Postgres was in this repository's git history until
|
||||||
|
2026-08-03. The POS accounts above are the only ones not in any source tree —
|
||||||
|
keep it that way.
|
||||||
|
|
||||||
## Capacity
|
## Capacity
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user