An abandoned wait loop locked the only Grafana account out for a full day, by re-arming its own lockout
- date
- 20260915
- what happened
- On 20260915 the dashboard tool's single administrative account was locked out continuously, with no working login for anyone. A leftover readiness-check loop, started two days earlier by an agent session that had since ended, was still polling a dashboard API about twelve times a minute with the correct credentials. A second abandoned loop was hammering the inventory-discovery service every two seconds.
- what it cost
- 24 hours with the dashboard tool's only account locked out, plus the loop's command line, which held the administrative secret as a plain argument, exposing it in the process list for two days.
- what changed
- Any wait loop launched over a remote session must be explicitly bounded with a timeout, or run from a location where it dies with the session that started it. Secrets are never passed as literal command-line arguments.
- the check now
- Before finishing work on any host, scan the running processes for lingering wait-loop patterns left behind by the session.
The mechanism here is counterintuitive enough to be worth sitting with. Something tripped the dashboard account’s failed-login lockout once. After that, every request from the abandoned readiness loop was rejected specifically because of the lockout. Each rejection counted as another failed attempt, which re-armed the same lockout. Twelve times a minute, for a day. The loop’s credentials were entirely correct, proven the moment the lockout finally cleared. Resetting the password would have changed nothing at all, because the lockout was a property of the account rather than of the secret being presented to it.
A background process that keeps retrying against a target which is actively rejecting it for exceeding a retry limit is not harmless just because it is authorised. It becomes the mechanism that keeps the door locked. That generalises to any automated retry against a rate-limited or lockout-protected target. The retry must eventually stop, or it converts a transient block into a self-sustaining one.
The loops themselves came from a pattern that is easy to reach for and easy to forget: an agent session waits for a service to come up, backgrounded on a remote host. Then the session ends and nothing ever kills the loop. Two of them were running, days apart, from work that had long since finished. The rule now is that such a wait is bounded by an explicit timeout, or it runs somewhere it will die with its session. A scan for lingering loop processes is part of finishing work on a host.
The second finding is the plainer one. The loop held its secret as a literal argument, so it sat in the process list, readable, for as long as the loop ran.
Source: node0 lessons v0.1, lesson 2.15. Sanitized: checklist v0.1, 20260921; names pass only; voice pass 20260921. Part of oznog.com/node0.
