Authenticate the node dashboard, and the broker connection per node
Two unauthenticated surfaces closed on the edge node. Dashboard and API: the local dashboard and every /api/* route were open to anything on the node's LAN. Adds a login page plus session-cookie auth for the browser, and cookie-or-Basic for the API so scripted callers stay possible. Passwords are hashed with stdlib scrypt (no new dependency, this runs on a Pi) and compared in constant time; the salt and session-signing secret persist in credentials.json. Startup warns while the default password is still in place. No non-browser callers of the node API exist today (C2 talks to nodes over MQTT and nodes call C2 outbound), so nothing breaks. Adds python-multipart, which FastAPI's Form() needs for the login POST and which was missing from requirements entirely. MQTT: nodes authenticated with a shared drb-node password, and the broker ACL keyed off %c — the client-supplied client id — so any holder of that one password could claim another node's topic namespace. Nodes now connect as username=<node_id>, password=<their C2-issued api_key>, which mosquitto's dynamic-security plugin checks, with the ACL keyed off the authenticated %u. TLS is gated on MQTT_TLS and uses default CA verification. The old key_request MQTT path stays in place behind TODO(mqtt-cutover) markers as the fallback until the cutover is proven; a node with no api_key on disk logs a clear repeated refusal rather than spinning. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
a61a7b2c31
commit
87633ab50d
+40
-3
@@ -5,11 +5,32 @@ NODE_LAT=0.0
|
||||
NODE_LON=0.0
|
||||
|
||||
# MQTT — point to your C2 server
|
||||
#
|
||||
# Post-cutover (MQTT-PUBLIC-AUTH-PLAN.md dynsec revision): there is no shared
|
||||
# node login any more. The node authenticates as username=NODE_ID,
|
||||
# password=<this node's C2-issued api_key> automatically — nothing to set
|
||||
# here for that; the api_key is provisioned via MQTT after an admin approves
|
||||
# the node (see credentials.json) and does not go in this file.
|
||||
#
|
||||
# Local/dev, pointed at a plaintext broker on :1883: leave MQTT_TLS unset.
|
||||
MQTT_BROKER=localhost
|
||||
MQTT_PORT=1883
|
||||
# Must match MQTT_NODE_USER/MQTT_NODE_PASS in the server's top-level .env
|
||||
MQTT_USER=drb-node
|
||||
MQTT_PASS=change-me-node
|
||||
MQTT_TLS=false
|
||||
|
||||
# Production, pointed at the public broker (real Let's Encrypt cert, default
|
||||
# CA verification — do not disable it):
|
||||
# MQTT_BROKER=mqtt.<domain>
|
||||
# MQTT_PORT=8883
|
||||
# MQTT_TLS=true
|
||||
|
||||
# DEPRECATED / REMOVED post-cutover — the shared "drb-node" login these
|
||||
# backed no longer exists on the server (dynsec creates only per-node
|
||||
# clients, keyed by api_key; see Server/drb-c2-core/app/internal/dynsec.py).
|
||||
# Leave unset for any node pointed at a cut-over broker. Only meaningful as a
|
||||
# legacy fallback if MQTT_BROKER still points at a pre-cutover broker running
|
||||
# mosquitto's old password_file auth.
|
||||
# MQTT_USER=drb-node
|
||||
# MQTT_PASS=change-me-node
|
||||
|
||||
# C2 server for audio upload (leave blank to disable upload)
|
||||
C2_URL=http://localhost:8888
|
||||
@@ -79,6 +100,22 @@ TRIM_SILENCE_GUARD_SECONDS=0.25
|
||||
# OP25 container (usually no need to change)
|
||||
OP25_API_URL=http://localhost:8001
|
||||
OP25_TERMINAL_URL=http://localhost:8081
|
||||
# DEBUGGING AID, NOT A DEPLOYMENT OPTION. Both OP25's control API (:8001) and
|
||||
# its HTTP terminal (:8081) have NO authentication, so they are bound to
|
||||
# 127.0.0.1 by default — reachable only from other containers on this same
|
||||
# host (they share its network namespace), not from the site's LAN. Setting
|
||||
# this to true rebinds both to 0.0.0.0, exposing unauthenticated OP25
|
||||
# start/stop/config-rewrite and the raw terminal to anyone on that LAN. Only
|
||||
# for local development off a real node; leave false everywhere else.
|
||||
OP25_DEBUG_EXPOSE=false
|
||||
|
||||
# --- Local dashboard / API login ---------------------------------------------
|
||||
# Protects the node's local dashboard (port 80) and JSON API. The node is
|
||||
# reachable by anyone on whatever site's LAN it's deployed to, so this MUST be
|
||||
# changed before the node leaves the bench — the default below is flagged at
|
||||
# every startup in the logs until it's changed.
|
||||
DASHBOARD_USERNAME=admin
|
||||
DASHBOARD_PASSWORD=CHANGE-ME-drb-default
|
||||
|
||||
# Container registry — set these to pull pre-built images instead of building locally.
|
||||
# Must match the DOCKER_ORG variable and repo name configured in Gitea.
|
||||
|
||||
Reference in New Issue
Block a user