terminal: auto-bake super macaroon on startup

Automatically bake a super macaroon on startup if it doesn't already
exist on disk and the `bake-super-macaroon` option is configured.

On startup, the node verifies if the super macaroon file exists. If
it does, it parses the macaroon, extracts and verifies the version and
root key ID, and asserts that the macaroon permissions exactly match
the expected active permissions. If there is a mismatch, the macaroon
is regenerated and overwritten on disk. If the file does not exist, a
new super macaroon is baked and written directly to disk.

Also validate that the `bake-super-macaroon` option is not enabled when
LND is running in stateless initialization mode, failing startup early
if they are used together.
This commit is contained in:
cyberguru1 2026-06-13 18:24:36 -05:00
parent d3e51fac17
commit c5fbbd4d77
No known key found for this signature in database
GPG key ID: F0FB5ECF1A8786E6

View file

@ -822,6 +822,13 @@ func (g *LightningTerminal) start(ctx context.Context) error {
return fmt.Errorf("could not start litd sub-servers: %v", err)
}
// Bake the super macaroon on startup if configured, now that all local
// and remote sub-servers have been started and active permissions are
// fully known.
if err := g.setupSuperMacaroon(ctx); err != nil {
return fmt.Errorf("could not setup super macaroon: %w", err)
}
// We can now set the status of LiT as running.
g.statusMgr.SetRunning(subservers.LIT)
@ -2192,3 +2199,64 @@ func randId(n int) string {
return string(b)
}
// setupSuperMacaroon bakes a super macaroon and writes it to disk if needed.
func (g *LightningTerminal) setupSuperMacaroon(ctx context.Context) error {
// If the bake-super-macaroon option is set to none, we don't bake a
// macaroon.
if g.cfg.BakeSuperMacaroon == noneChoice {
return nil
}
// If the super macaroon baking option is enabled, we cannot run in
// stateless initialization mode as it won't write any macaroons to the
// filesystem.
if g.cfg.statelessInitMode {
return fmt.Errorf("cannot use bake-super-macaroon " +
"with stateless-init mode")
}
path := g.cfg.SuperMacaroonPath
readOnly := g.cfg.BakeSuperMacaroon == readOnlyChoice
activePerms := g.permsMgr.ActivePermissions(readOnly)
if litmac.SuperMacaroonExists(path) {
matches, err := litmac.MacaroonMatchesPermissions(
path, activePerms,
)
if err == nil && matches {
log.Debugf("Super macaroon already exists at "+
"%v and matches configuration, "+
"skipping bake", path)
return nil
}
if err != nil {
return fmt.Errorf(
"unable to verify super macaroon "+
"permissions at %v, please delete "+
"it if the issue persists: %w",
path, err,
)
}
log.Infof("Super macaroon permissions " +
"differ from configuration, " +
"regenerating...")
}
log.Infof("Baking super macaroon on startup...")
// Bake the super macaroon and write it to disk.
if err := litmac.BakeAndWriteSuperMacaroon(
ctx, g.basicClient, path, activePerms,
); err != nil {
return err
}
log.Infof("Successfully baked and wrote super macaroon to %v", path)
return nil
}