openwisp / openwisp/openwisp-controller
[bug] Changing org shared_secret prevents devices from re-registering after losing stored credentials
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 773
- Forks
- 315
- Avg merge
- 2d 16h
- Merged PRs (30d)
- 14
Description
Describe the bug
Changing an organization's shared_secret does not affect already-registered devices (they have UUID+KEY stored locally). However, if a device subsequently loses its stored UUID+KEY (factory reset, reflash, manual config clear), it becomes permanently unable to re-register.
The agent computes key = md5(mac_address + "+" + shared_secret) using the new secret; the server looks up by key and doesn't find it (the stored key used the old secret); it falls back to creating a new device, which fails on unique_together constraints for (mac_address, organization).
There is no fallback matching by (mac_address, organization) when key lookup fails, and no mechanism to update existing device keys when shared_secret changes.
Root cause
-
Deterministic key —
generate_key():md5("{}+{}".format(mac_address, shared_secret)). Changingshared_secretproduces a different key for the same mac address. -
Key set once at creation —
save(): only generates key ifself.keyis empty; never updates it later. -
Lookup by key only —
DeviceRegisterView.post():Device.objects.get(key=key)on line 402. If key doesn't match, falls toinit_object()(line 421) which creates a new device — which then fails onunique_together. -
Unique constraints —
unique_together=(mac_address, organization),(hardware_id, organization), plus_validate_unique_name()for(name, organization). -
Agent erases shared_secret —
openwisp.agent: computes key ~line 195, then post-registration (~lines 294-298) stores UUID+KEY and clearsshared_secret. On restarts, if UUID+KEY already exist, registration is skipped (~line 308). -
Default settings:
CONSISTENT_REGISTRATION=True,REGISTRATION_SELF_CREATION=True,DEVICE_NAME_UNIQUE=True,HARDWARE_ID_ENABLED=False.
Steps To Reproduce
- Register a device normally (e.g.
mac_address=AA:BB:CC:DD:EE:FF, orgshared_secret="old_secret"). Server storeskey = md5("AA:BB:CC:DD:EE:FF+old_secret"). - Change the org's
shared_secretto"new_secret"in the admin interface (Organization → Config Settings). - On the device, clear the stored UUID and KEY and restore shared_secret:
This simulates a device that lost its credentials (e.g. after reflash) while using the current org shared_secret.uci set openwisp.http.uuid="" uci set openwisp.http.key="" uci set openwisp.http.shared_secret="new_secret" uci commit openwisp /etc/init.d/openwisp restart - The agent boots and attempts registration:
- Computes
key = md5("AA:BB:CC:DD:EE:FF+new_secret")— different from the stored key. - Sends POST to
/controller/register/with the new key. - Server does
Device.objects.get(key=...)→ DoesNotExist. - Server tries to create a new device with same
mac_address,name, andorganization. - Fails with
Device with this Name and Organization already exists.orconfig device with this Mac address and Organization already exists.
- Computes
Expected behavior
One of:
-
When key lookup fails during re-registration, the server should fall back to matching by
(mac_address, organization). If a device with matching mac and org is found, the server should update the existing device's key to the newly computed one and proceed with registration — rather than attempting to create a duplicate and failing. -
The server should update all existing device keys in the organization when
shared_secretis changed. -
At minimum, prevent changing
shared_secretin the admin UI when the organization already has registered devices, or show a clear warning about the consequences.
System Information
Applies to base openwisp-controller (no settings overridden: CONSISTENT_REGISTRATION, DEVICE_NAME_UNIQUE, REGISTRATION_SELF_CREATION all at defaults).
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with DeviceRegisterView.post() in openwisp_controller/config/controller/views.py, then read generate_key() and save() in openwisp_controller/config/base/device.py and the referenced unique constraints. Reproduce registration after changing shared_secret and clearing credentials; done means an existing device identified by MAC and organization can re-register without a duplicate-device error, with the intended key behavior covered by tests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- django, python
- Domain
- backend, networking
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100