From ef647d33953faf7088c40b1743bfb7f933ad1f78 Mon Sep 17 00:00:00 2001 From: Suriya Date: Mon, 3 Aug 2026 18:01:05 +0530 Subject: [PATCH] Give the POS its own broker accounts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- POS_TERMINAL_INGEST.md | 91 +++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 55 deletions(-) diff --git a/POS_TERMINAL_INGEST.md b/POS_TERMINAL_INGEST.md index 0d8dfe4..80a90f1 100644 --- a/POS_TERMINAL_INGEST.md +++ b/POS_TERMINAL_INGEST.md @@ -239,75 +239,56 @@ state. as zero: a board showing every till at 0% battery is worse than one showing 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 -work; all of it gets worse the moment bills start flowing. +Applied 2026-08-03 on `66.116.225.226`. Two scoped accounts now exist alongside +`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 -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 -anyone who decompiles it today has full publish and subscribe over `nearle/#` -*and* `doormile/#` — a second project's traffic. Adding POS puts every shop's -takings behind the same credential. +| User | May publish | May subscribe | +|---|---|---| +| `pos_terminal` | `nearle/pos/+/+/{order,customer,status,health}` | `nearle/pos/+/+/{ack,command}`, `nearle/pos/+/catalogue` | +| `pos_ingest` | `nearle/pos/+/+/{ack,command}`, `nearle/pos/+/catalogue` | `nearle/pos/+/+/{order,customer,health,status}` | +| `admin` | everything — **deliberately unchanged** | everything | -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 -# A user for the tills, and one for this backend. -mosquitto_passwd -b /mosquitto/config/passwd pos_terminal '' -mosquitto_passwd -b /mosquitto/config/passwd pos_ingest '' -``` +**`admin` was left unrestricted on purpose.** Its credentials are compiled into +the rider app, so narrowing it here would cut off the live rider fleet without +warning. The right next step is: ```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 topic readwrite nearle/riders/# topic readwrite doormile/# ``` -Tighten `pos_terminal` further with per-terminal credentials if you want one -till unable to read another's acks at all; the pattern above trusts tills within -the fleet but not outside it. +but only once someone has confirmed nothing else authenticates as `admin`. +Until then the ACL changes nothing for it — which is why applying it was safe. -**There is no TLS.** Port 8883 is not configured and is closed. Rider GPS -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. +Rollback, if ever needed: -**Two more, from the audit:** +```bash +cp /root/Mqtt/backup-/{mosquitto.conf,passwd} /root/Mqtt/config/ +docker restart mqtt_broker +``` -- The broker password and the workolik NATS password differ only in - capitalisation. Diverge them when creating the scoped users. -- Confirm on the host whether the broker was started from the compose file or - from a bare `docker run` before editing the compose file and expecting it to - take effect — there is precedent in this estate for compose existing but not - being the deploy path. +**Still outstanding on the broker:** + +- **No TLS.** Port 8883 is not configured. Bills carry customer names and mobile + numbers, and they travel in the clear. Traefik on the same host already + terminates 443, so certificates exist to borrow from. +- **`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