fix(S2-a): expose scoped app MQTT through WSS

This commit is contained in:
Brent Perteet
2026-08-20 15:43:42 -05:00
parent b66ae2cc47
commit 9ffe021354
4 changed files with 24 additions and 5 deletions

View File

@@ -14,10 +14,12 @@ exposes three listeners, each with a different trust model:
|------|----------|------|---------------|
| `1883` | MQTT (plaintext) | username/password | internal services (e.g. the Laravel subscriber, Python publisher) |
| `8883` | MQTT over TLS | **client certificate** | field devices |
| `8884` | MQTT over TLS | username/password (server cert only) | scoped app clients and administrators |
| `443` (`/mqtt` → loopback `9001`) | MQTT over WSS/TLS | username/password | scoped app clients |
| `8884` | MQTT over TLS | username/password (server cert only) | scoped app clients and administrators on networks that expose the raw port |
The former anonymous WebSocket listener on `9001` is disabled. The current web portal receives
live updates from the backend rather than connecting directly to Mosquitto.
The former anonymous WebSocket listener on `9001` is now authenticated, uses the same ACL as the
TLS listeners, and is bound to host loopback only. Nginx exposes it as WSS at `/mqtt` on port 443;
the web portal itself continues to receive live updates from the backend.
The TLS listeners use `mosquitto/certs/public-fullchain.pem` and
`mosquitto/certs/public-privkey.pem`, copied from the host's Let's Encrypt certificate during

View File

@@ -14,6 +14,7 @@ services:
- "1883:1883"
- "8883:8883"
- "8884:8884"
- "127.0.0.1:9001:9001"
volumes:
- ./mosquitto:/mosquitto

View File

@@ -6,6 +6,14 @@ password_file /mosquitto/config/passwd
acl_file /mosquitto/config/devices.acl
allow_anonymous false
# Authenticated MQTT over WebSocket for app clients. Docker binds this listener only to
# host loopback; nginx supplies the public WSS/TLS endpoint at /mqtt on port 443.
listener 9001 0.0.0.0
protocol websockets
password_file /mosquitto/config/passwd
acl_file /mosquitto/config/devices.acl
allow_anonymous false
# TLS MQTT — devices authenticate with client certificates (port 8883)
# require_certificate true forces client cert; cert CN becomes the MQTT username.
# ACL restricts each device to devices/<serial_number>/#

View File

@@ -21,8 +21,16 @@ server {
client_max_body_size 20m;
location /mqtt {
# Anonymous MQTT-over-WebSocket was removed for Sprint 2 security criterion B1.
return 410;
# Authenticated, ACL-confined MQTT-over-WebSocket. Mosquitto is bound to loopback;
# nginx terminates publicly trusted TLS so phones can use standard port 443.
proxy_pass http://127.0.0.1:9001/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 300s;
}
location / {