From af252c4498e9fed480139767ed1bbc62ee0f883a Mon Sep 17 00:00:00 2001 From: tdv Date: Tue, 30 Sep 2025 12:27:42 +0300 Subject: [PATCH] added EMQX for MQTT protocol support --- docker-compose.yaml | 25 +++++++++++++++ nginx/dev.conf | 11 +++++++ readme.md | 77 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 110 insertions(+), 3 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index a42a9a2..693d99c 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -134,6 +134,31 @@ services: - snoopBack - proxy + # NEW: EMQX MQTT broker + emqx: + image: emqx/emqx:latest # EMQX 5.x + restart: unless-stopped + environment: + # set a fixed node name (nice when you later add clustering) + EMQX_NODE__NAME: emqx@node1 + # set dashboard admin user/pass (change these in prod!) + EMQX_DASHBOARD__DEFAULT_USERNAME: admin + EMQX_DASHBOARD__DEFAULT_PASSWORD: changeme123 + # optional: enable WebSocket listener on 8083 (on by default in 5.x) + # EMQX_LISTENERS__WS__DEFAULT__ENABLE: "true" + volumes: + - emqx-data:/opt/emqx/data + - emqx-log:/opt/emqx/log + ports: + - "1883:1883" # MQTT (TCP) + - "8083:8083" # MQTT over WebSocket (WS) + # - "8883:8883" # MQTT over TLS (uncomment when you add certs) + # - "8084:8084" # WSS (uncomment with TLS) + - "18083:18083" # Dashboard + networks: + - snoopBack + - proxy # so Nginx can reverse-proxy WS at /mqtt/ws + volumes: pgdata: miniodata: diff --git a/nginx/dev.conf b/nginx/dev.conf index 572d4b1..1a144c3 100644 --- a/nginx/dev.conf +++ b/nginx/dev.conf @@ -53,4 +53,15 @@ server { proxy_pass http://mediamtx:8889/; proxy_http_version 1.1; } + + # --- MQTT over WebSocket (EMQX) --- + # Clients connect to ws:///mqtt/ws + location /mqtt/ws { + proxy_pass http://emqx:8083/mqtt; # EMQX WS path is /mqtt + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + } } \ No newline at end of file diff --git a/readme.md b/readme.md index 5094417..f123bf7 100644 --- a/readme.md +++ b/readme.md @@ -20,9 +20,10 @@ vault kv put kv/snoop \ minio_presign_ttl_seconds="900" ``` -Unseal Key: gQrvvJBaGR4CpmkoVq93tWqk5dTpioMAHtNHKMWNlH0= +Unseal Key 1: XdERN+/hxR9RjLC/S8c+y0omToYvB7Qs1jaUenZQvphD +Unseal Key 2: VBhPBtYcq1GAk7ByPfAsamxV4tJOZ49chAYxxOvc49Oj -Root Token: root +Initial Root Token: hvs.tZ4eh9P18sCZ5c1PZIz59EmH { @@ -35,4 +36,74 @@ Root Token: root "minio_records_bucket": "records", "minio_livestream_bucket": "livestream", "minio_presign_ttl_seconds": "900" -} \ No newline at end of file +} + +### Stand up internal CA (root + intermediate) +```bash +# Enable PKI backends (root + intermediate) +vault secrets enable -path=pki_root pki +vault secrets tune -max-lease-ttl=87600h pki_root # 10y + +vault write pki_root/root/generate/internal \ + common_name="Snoop Root CA" key_type=ec key_bits=256 ttl=87600h + +vault secrets enable -path=pki_iot pki +vault secrets tune -max-lease-ttl=17520h pki_iot # 2y + +# Create an intermediate CSR +vault write -field=csr pki_iot/intermediate/generate/internal \ + common_name="Snoop IoT Intermediate" key_type=ec key_bits=256 ttl=17520h > iot_int.csr + +# Sign intermediate with root +vault write -field=certificate pki_root/root/sign-intermediate csr=@iot_int.csr \ + format=pem_bundle ttl=17520h > iot_int_signed.pem + +vault write -field=issuing_ca pki_root/root/sign-intermediate \ + csr=@iot_int.csr format=pem_bundle ttl=17520h > root_ca.pem + +# Set the signed intermediate in Vault +vault write pki_iot/intermediate/set-signed certificate=@iot_int_signed.pem +``` + +### Configure URLs + a role for devices +```bash +# Publish issuing + CRL URLs (Nginx will fetch CRL periodically) +vault write pki_iot/config/urls \ + issuing_certificates="https://vault.example.local/v1/pki_iot/ca" \ + crl_distribution_points="https://vault.example.local/v1/pki_iot/crl" + +# Role that limits cert subjects to your device GUIDs +vault write pki_iot/roles/device \ + allow_any_name=false \ + allowed_uri_sans="urn:device:*" \ + allowed_other_sans="" \ + allow_ip_sans=false \ + allowed_domains="" \ + enforce_hostnames=false \ + key_type=ec key_bits=256 \ + max_ttl=720h ttl=720h # 30 days +``` + +### Enrollment flow (device CSR → signed cert) +- Device side + - Generate keypair on the device (never export private key). + - Create CSR with CN= and URI SAN urn:device:. + +- Server side + - Backend verifies a one-time enrollment token (or pre-shared bootstrap secret), calls: +```bash +vault write pki_iot/sign/device \ + csr=@device.csr \ + uri_sans="urn:device:" \ + ttl=720h > device_cert.pem +``` + +### Revocation +- Immediate kill: your backend stores bad serials; deny in app logic. + - CRL: `vault write pki_iot/revoke serial_number=""`. + - Fetch CRL periodically to a local file for Nginx: +```bash +curl -fsSL https://vault.example.local/v1/pki_iot/crl -o /etc/nginx/iot.crl +nginx -s reload +``` + - Automate via systemd timer/cron (e.g., every 10–15 min).