Repository navigation
One-time macaroon rotation for leaked macaroons - #11
Merged
Merged
Conversation
Kukks
force-pushed
the
macaroon-root-key-rotation
branch
from
August 7, 2026 18:59
56d9e7b to
c10d0ad
Compare
Incident response for macaroons that leaked off a node. Deleting the macaroon files is not enough on its own. genDefaultMacaroons only recreates files that are missing, and re-bakes them from the same bakery root key in macaroons.db, so a leaked macaroon keeps validating and the new file is just an equivalent token next to it. macaroons.db has to go as well; lnd then creates a new root key and regenerates its own macaroons on the next unlock. Every macaroon on the volume dies with that root key, not just the three lnd manages, so all of them are cleared instead of being left behind as tokens that no longer work: hand baked ones from lncli bakemacaroon --save_to, scoped macaroons handed to other services, stale copies in backup folders. Clearing the network dir copy of admin.macaroon matters in particular, because lnd skips regenerating a file that still exists, so a leftover there would never self-heal. find is used rather than a fixed list so nothing on the volume is missed, and -exec rm rather than -delete because busybox find on the alpine image may be built without it. This runs in the entrypoint before lnd starts, so nothing is holding the files open. Set LND_MACAROON_ROTATION_ID to arm it; a marker on the data volume keeps it to one rotation per id, and bumping the id rotates again. With the variable unset the entrypoint behaves exactly as before. Every client of an affected node has to be re-paired afterwards.
Kukks
force-pushed
the
macaroon-root-key-rotation
branch
from
August 7, 2026 19:12
c10d0ad to
489b439
Compare
rockstardev
self-requested a review
August 7, 2026 19:21
rockstardev
approved these changes
Aug 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Incident response for macaroons that leaked off a node.
Why the macaroon files alone are not enough
The obvious fix — delete
admin.macaroonand friends and let lnd recreate them — does not revoke anything.genDefaultMacaroons(lnd.go) only recreates files that are missing, and re-bakes them from the same bakery root key inmacaroons.db. The leaked macaroon keeps validating; the new file is just an equivalent token sitting next to it.macaroons.dbhas to go as well. lnd then creates a new root key and regenerates its own macaroons on the next unlock, and every macaroon issued from the old key is dead.Clearing all of them, not just lnd's three
Every macaroon on the volume dies with that root key, not only the three lnd manages. Leaving the rest on disk just leaves tokens lying around that no longer work — hand baked ones from
lncli bakemacaroon --save_to, scoped macaroons handed to other services, stale copies in backup folders.Clearing the network dir copy of
admin.macaroonmatters in particular: once the root key is gone it is a dead token, andgenDefaultMacaroonsskips regenerating it precisely because the file still exists, so it would never self-heal.findis used rather than a fixed list so nothing on the volume is missed, and-exec rmrather than-deletebecause busyboxfindon the alpine amd64 image may be built without-delete(the arm images are debian, where it exists). Each removed path is printed, so the container log is an audit trail of what was revoked.Running in the entrypoint before lnd starts means nothing is holding the files open, and no restart dance is needed.
How it is triggered
Set
LND_MACAROON_ROTATION_ID. A marker on the data volume ($LND_DATA/.macaroon-rotated-<id>) keeps it to one rotation per id; bump the id to rotate again after a future incident. With the variable unset the entrypoint behaves exactly as before.Every client of an affected node has to be re-paired afterwards. Anything that reads the macaroon from the shared volume path recovers on restart; anything holding a copy (a macaroon pasted as hex into a config, RTL/ThunderHub/Zeus/Alby, plugins) needs re-pairing. Channels and funds are untouched — macaroons are auth only.
Test plan
The rotation block was extracted from the committed file and exercised directly:
macaroons.dbremoved, marker writtenLND_CHAIN/LND_ENVIRONMENTunset, soNETWORK/ENVemptywallet.db,channel.db,walletunlock.jsonandtls.certsurvive every case.bash -npasses.Not verified: this has not been run in a real container, so lnd-side regeneration after the rotation is untested. One regtest cycle would close that.
Worth raising separately
walletunlock.jsonstores the wallet password and the cipher seed mnemonic in cleartext on the data volume. If the exploit read files off that volume, the seed is compromised and rotating macaroons does not help — the funds need to move to a new wallet.