reject_node leaves the node's dynsec login and node_keys in place — a rejected node can still use MQTT and /upload #195

Closed
opened 2026-10-04 01:11:47 -04:00 by logan · 1 comment
Owner

Found while fixing node-26#14 (approve_node no longer rotates the key, server-26#194).

routers/nodes.py reject_node() only sets approval_status: rejected. It does not call dynsec.delete_node_client() and does not delete node_keys/{id}. So a node that was approved and later rejected keeps:

  • a working MQTT login
  • a valid /upload key, because upload.py checks node_keys, not approval_status
  • /systems read access via require_node_service_or_firebase_token

On top of that, GET /nodes/{id}/credentials returns the key whenever the status is approved, so re-approving a rejected node now (after #194) hands back the same old key. That is intended, but it means "reject" has never been a revoke.

Fix direction: either reject also revokes (dynsec delete_node_client + delete node_keys), or the UI labels Reject honestly and a separate Revoke exists. Also have upload.py check approval_status.

Found while fixing node-26#14 (`approve_node` no longer rotates the key, server-26#194). `routers/nodes.py` `reject_node()` only sets `approval_status: rejected`. It does not call `dynsec.delete_node_client()` and does not delete `node_keys/{id}`. So a node that was approved and later rejected keeps: - a working MQTT login - a valid `/upload` key, because `upload.py` checks `node_keys`, not `approval_status` - `/systems` read access via `require_node_service_or_firebase_token` On top of that, `GET /nodes/{id}/credentials` returns the key whenever the status is `approved`, so re-approving a rejected node now (after #194) hands back the same old key. That is intended, but it means "reject" has never been a revoke. Fix direction: either reject also revokes (dynsec `delete_node_client` + delete `node_keys`), or the UI labels Reject honestly and a separate Revoke exists. Also have `upload.py` check `approval_status`.
Author
Owner

PR #206 (#206): reject now deletes the dynsec login and node_keys (status flips to rejected first, fail-closed), /upload requires approval_status=approved, and reconcile_all() revokes rejected nodes on startup so already-rejected nodes get cleaned up on deploy. Re-approve after reject mints a fresh key.

PR #206 (https://git.vpn.cusano.net/logan/server-26/pulls/206): reject now deletes the dynsec login and node_keys (status flips to rejected first, fail-closed), /upload requires approval_status=approved, and reconcile_all() revokes rejected nodes on startup so already-rejected nodes get cleaned up on deploy. Re-approve after reject mints a fresh key.
logan closed this issue 2026-10-04 18:28:10 -04:00
logan added the area/autharea/mqtt
type
security
labels 2026-10-05 02:04:59 -04:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: logan/server-26#195