From 882f25667ea7f9133f81a0c6ecec7da79a30c5fc Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Wed, 9 Oct 2024 09:06:23 +0200 Subject: [PATCH] cmd/commands: add root_key flag to bakemacaroon command --- cmd/commands/cmd_macaroon.go | 92 ++++++++++++++++++++++++++---------- 1 file changed, 67 insertions(+), 25 deletions(-) diff --git a/cmd/commands/cmd_macaroon.go b/cmd/commands/cmd_macaroon.go index 149c7db45..15c29380a 100644 --- a/cmd/commands/cmd_macaroon.go +++ b/cmd/commands/cmd_macaroon.go @@ -10,6 +10,7 @@ import ( "strings" "unicode" + "github.com/lightningnetwork/lnd/fn" "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/macaroons" @@ -38,6 +39,12 @@ var ( Usage: "the condition of the custom caveat to add, can be " + "empty if custom caveat doesn't need a value", } + bakeFromRootKeyFlag = cli.StringFlag{ + Name: "root_key", + Usage: "if the root key is known, it can be passed directly " + + "as a hex encoded string, turning the command into " + + "an offline operation", + } ) var bakeMacaroonCommand = cli.Command{ @@ -48,7 +55,7 @@ var bakeMacaroonCommand = cli.Command{ ArgsUsage: "[--save_to=] [--timeout=] [--ip_address=] " + "[--custom_caveat_name= [--custom_caveat_condition=]] " + "[--root_key_id=] [--allow_external_permissions] " + - "permissions...", + "[--root_key=] permissions...", Description: ` Bake a new macaroon that grants the provided permissions and optionally adds restrictions (timeout, IP address) to it. @@ -74,6 +81,12 @@ var bakeMacaroonCommand = cli.Command{ To get a list of all available URIs and permissions, use the "lncli listpermissions" command. + + If the root key is known (for example because "lncli create" was used + with a custom --mac_root_key value), it can be passed directly as a + hex encoded string using the --root_key flag. This turns the command + into an offline operation and the macaroon will be created without + calling into the server's RPC endpoint. `, Flags: []cli.Flag{ cli.StringFlag{ @@ -95,14 +108,13 @@ var bakeMacaroonCommand = cli.Command{ Usage: "whether permissions lnd is not familiar with " + "are allowed", }, + bakeFromRootKeyFlag, }, Action: actionDecorator(bakeMacaroon), } func bakeMacaroon(ctx *cli.Context) error { ctxc := getContext() - client, cleanUp := getClient(ctx) - defer cleanUp() // Show command help if no arguments. if ctx.NArg() == 0 { @@ -154,36 +166,66 @@ func bakeMacaroon(ctx *cli.Context) error { ) } - // Now we have gathered all the input we need and can do the actual - // RPC call. - req := &lnrpc.BakeMacaroonRequest{ - Permissions: parsedPermissions, - RootKeyId: rootKeyID, - AllowExternalPermissions: ctx.Bool("allow_external_permissions"), - } - resp, err := client.BakeMacaroon(ctxc, req) - if err != nil { - return err - } + var rawMacaroon *macaroon.Macaroon + switch { + case ctx.IsSet(bakeFromRootKeyFlag.Name): + macRootKey, err := hex.DecodeString( + ctx.String(bakeFromRootKeyFlag.Name), + ) + if err != nil { + return fmt.Errorf("unable to parse macaroon root key: "+ + "%w", err) + } - // Now we should have gotten a valid macaroon. Unmarshal it so we can - // add first-party caveats (if necessary) to it. - macBytes, err := hex.DecodeString(resp.Macaroon) - if err != nil { - return err - } - unmarshalMac := &macaroon.Macaroon{} - if err = unmarshalMac.UnmarshalBinary(macBytes); err != nil { - return err + ops := fn.Map(func(p *lnrpc.MacaroonPermission) bakery.Op { + return bakery.Op{ + Entity: p.Entity, + Action: p.Action, + } + }, parsedPermissions) + + rawMacaroon, err = macaroons.BakeFromRootKey(macRootKey, ops) + if err != nil { + return fmt.Errorf("unable to bake macaroon: %w", err) + } + + default: + client, cleanUp := getClient(ctx) + defer cleanUp() + + // Now we have gathered all the input we need and can do the + // actual RPC call. + req := &lnrpc.BakeMacaroonRequest{ + Permissions: parsedPermissions, + RootKeyId: rootKeyID, + AllowExternalPermissions: ctx.Bool( + "allow_external_permissions", + ), + } + resp, err := client.BakeMacaroon(ctxc, req) + if err != nil { + return err + } + + // Now we should have gotten a valid macaroon. Unmarshal it so + // we can add first-party caveats (if necessary) to it. + macBytes, err := hex.DecodeString(resp.Macaroon) + if err != nil { + return err + } + rawMacaroon = &macaroon.Macaroon{} + if err = rawMacaroon.UnmarshalBinary(macBytes); err != nil { + return err + } } // Now apply the desired constraints to the macaroon. This will always // create a new macaroon object, even if no constraints are added. - constrainedMac, err := applyMacaroonConstraints(ctx, unmarshalMac) + constrainedMac, err := applyMacaroonConstraints(ctx, rawMacaroon) if err != nil { return err } - macBytes, err = constrainedMac.MarshalBinary() + macBytes, err := constrainedMac.MarshalBinary() if err != nil { return err }