diff --git a/MQTT_DEVICE_AUTH.md b/MQTT_DEVICE_AUTH.md index f057b51..aafdebf 100644 --- a/MQTT_DEVICE_AUTH.md +++ b/MQTT_DEVICE_AUTH.md @@ -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 diff --git a/docker-compose.yml b/docker-compose.yml index e3e3848..d535bb2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,6 +14,7 @@ services: - "1883:1883" - "8883:8883" - "8884:8884" + - "127.0.0.1:9001:9001" volumes: - ./mosquitto:/mosquitto diff --git a/mosquitto/config/mosquitto.conf b/mosquitto/config/mosquitto.conf index e201680..8ee63fd 100644 --- a/mosquitto/config/mosquitto.conf +++ b/mosquitto/config/mosquitto.conf @@ -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//# diff --git a/nginx/dev.hub.umagul.net.conf b/nginx/dev.hub.umagul.net.conf index f49a5b8..28773f4 100644 --- a/nginx/dev.hub.umagul.net.conf +++ b/nginx/dev.hub.umagul.net.conf @@ -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 / {