cmd/commands: add root_key flag to bakemacaroon command

This commit is contained in:
Oliver Gugger
2024-10-09 09:06:23 +02:00
parent 6e932c6bc8
commit 882f25667e

View File

@@ -10,6 +10,7 @@ import (
"strings" "strings"
"unicode" "unicode"
"github.com/lightningnetwork/lnd/fn"
"github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lncfg"
"github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/macaroons" "github.com/lightningnetwork/lnd/macaroons"
@@ -38,6 +39,12 @@ var (
Usage: "the condition of the custom caveat to add, can be " + Usage: "the condition of the custom caveat to add, can be " +
"empty if custom caveat doesn't need a value", "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{ var bakeMacaroonCommand = cli.Command{
@@ -48,7 +55,7 @@ var bakeMacaroonCommand = cli.Command{
ArgsUsage: "[--save_to=] [--timeout=] [--ip_address=] " + ArgsUsage: "[--save_to=] [--timeout=] [--ip_address=] " +
"[--custom_caveat_name= [--custom_caveat_condition=]] " + "[--custom_caveat_name= [--custom_caveat_condition=]] " +
"[--root_key_id=] [--allow_external_permissions] " + "[--root_key_id=] [--allow_external_permissions] " +
"permissions...", "[--root_key=] permissions...",
Description: ` Description: `
Bake a new macaroon that grants the provided permissions and Bake a new macaroon that grants the provided permissions and
optionally adds restrictions (timeout, IP address) to it. 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 To get a list of all available URIs and permissions, use the
"lncli listpermissions" command. "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{ Flags: []cli.Flag{
cli.StringFlag{ cli.StringFlag{
@@ -95,14 +108,13 @@ var bakeMacaroonCommand = cli.Command{
Usage: "whether permissions lnd is not familiar with " + Usage: "whether permissions lnd is not familiar with " +
"are allowed", "are allowed",
}, },
bakeFromRootKeyFlag,
}, },
Action: actionDecorator(bakeMacaroon), Action: actionDecorator(bakeMacaroon),
} }
func bakeMacaroon(ctx *cli.Context) error { func bakeMacaroon(ctx *cli.Context) error {
ctxc := getContext() ctxc := getContext()
client, cleanUp := getClient(ctx)
defer cleanUp()
// Show command help if no arguments. // Show command help if no arguments.
if ctx.NArg() == 0 { 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 var rawMacaroon *macaroon.Macaroon
// RPC call. 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)
}
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{ req := &lnrpc.BakeMacaroonRequest{
Permissions: parsedPermissions, Permissions: parsedPermissions,
RootKeyId: rootKeyID, RootKeyId: rootKeyID,
AllowExternalPermissions: ctx.Bool("allow_external_permissions"), AllowExternalPermissions: ctx.Bool(
"allow_external_permissions",
),
} }
resp, err := client.BakeMacaroon(ctxc, req) resp, err := client.BakeMacaroon(ctxc, req)
if err != nil { if err != nil {
return err return err
} }
// Now we should have gotten a valid macaroon. Unmarshal it so we can // Now we should have gotten a valid macaroon. Unmarshal it so
// add first-party caveats (if necessary) to it. // we can add first-party caveats (if necessary) to it.
macBytes, err := hex.DecodeString(resp.Macaroon) macBytes, err := hex.DecodeString(resp.Macaroon)
if err != nil { if err != nil {
return err return err
} }
unmarshalMac := &macaroon.Macaroon{} rawMacaroon = &macaroon.Macaroon{}
if err = unmarshalMac.UnmarshalBinary(macBytes); err != nil { if err = rawMacaroon.UnmarshalBinary(macBytes); err != nil {
return err return err
} }
}
// Now apply the desired constraints to the macaroon. This will always // Now apply the desired constraints to the macaroon. This will always
// create a new macaroon object, even if no constraints are added. // create a new macaroon object, even if no constraints are added.
constrainedMac, err := applyMacaroonConstraints(ctx, unmarshalMac) constrainedMac, err := applyMacaroonConstraints(ctx, rawMacaroon)
if err != nil { if err != nil {
return err return err
} }
macBytes, err = constrainedMac.MarshalBinary() macBytes, err := constrainedMac.MarshalBinary()
if err != nil { if err != nil {
return err return err
} }