lncli: support list/delete on marcaroon IDs
This commit is contained in:
parent
c0e2513350
commit
c8a2916d91
@ -1,11 +1,13 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
@ -18,7 +20,7 @@ var bakeMacaroonCommand = cli.Command{
|
|||||||
Name: "bakemacaroon",
|
Name: "bakemacaroon",
|
||||||
Category: "Macaroons",
|
Category: "Macaroons",
|
||||||
Usage: "Bakes a new macaroon with the provided list of permissions " +
|
Usage: "Bakes a new macaroon with the provided list of permissions " +
|
||||||
"and restrictions",
|
"and restrictions.",
|
||||||
ArgsUsage: "[--save_to=] [--timeout=] [--ip_address=] permissions...",
|
ArgsUsage: "[--save_to=] [--timeout=] [--ip_address=] permissions...",
|
||||||
Description: `
|
Description: `
|
||||||
Bake a new macaroon that grants the provided permissions and
|
Bake a new macaroon that grants the provided permissions and
|
||||||
@ -48,6 +50,10 @@ var bakeMacaroonCommand = cli.Command{
|
|||||||
Name: "ip_address",
|
Name: "ip_address",
|
||||||
Usage: "the IP address the macaroon will be bound to",
|
Usage: "the IP address the macaroon will be bound to",
|
||||||
},
|
},
|
||||||
|
cli.Uint64Flag{
|
||||||
|
Name: "root_key_id",
|
||||||
|
Usage: "the numerical root key ID used to create the macaroon",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Action: actionDecorator(bakeMacaroon),
|
Action: actionDecorator(bakeMacaroon),
|
||||||
}
|
}
|
||||||
@ -66,6 +72,7 @@ func bakeMacaroon(ctx *cli.Context) error {
|
|||||||
savePath string
|
savePath string
|
||||||
timeout int64
|
timeout int64
|
||||||
ipAddress net.IP
|
ipAddress net.IP
|
||||||
|
rootKeyID uint64
|
||||||
parsedPermissions []*lnrpc.MacaroonPermission
|
parsedPermissions []*lnrpc.MacaroonPermission
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
@ -89,6 +96,10 @@ func bakeMacaroon(ctx *cli.Context) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctx.IsSet("root_key_id") {
|
||||||
|
rootKeyID = ctx.Uint64("root_key_id")
|
||||||
|
}
|
||||||
|
|
||||||
// A command line argument can't be an empty string. So we'll check each
|
// A command line argument can't be an empty string. So we'll check each
|
||||||
// entry if it's a valid entity:action tuple. The content itself is
|
// entry if it's a valid entity:action tuple. The content itself is
|
||||||
// validated server side. We just make sure we can parse it correctly.
|
// validated server side. We just make sure we can parse it correctly.
|
||||||
@ -122,6 +133,7 @@ func bakeMacaroon(ctx *cli.Context) error {
|
|||||||
// RPC call.
|
// RPC call.
|
||||||
req := &lnrpc.BakeMacaroonRequest{
|
req := &lnrpc.BakeMacaroonRequest{
|
||||||
Permissions: parsedPermissions,
|
Permissions: parsedPermissions,
|
||||||
|
RootKeyId: rootKeyID,
|
||||||
}
|
}
|
||||||
resp, err := client.BakeMacaroon(context.Background(), req)
|
resp, err := client.BakeMacaroon(context.Background(), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -180,3 +192,80 @@ func bakeMacaroon(ctx *cli.Context) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var listMacaroonIDsCommand = cli.Command{
|
||||||
|
Name: "listmacaroonids",
|
||||||
|
Category: "Macaroons",
|
||||||
|
Usage: "List all macaroons root key IDs in use.",
|
||||||
|
Action: actionDecorator(listMacaroonIDs),
|
||||||
|
}
|
||||||
|
|
||||||
|
func listMacaroonIDs(ctx *cli.Context) error {
|
||||||
|
client, cleanUp := getClient(ctx)
|
||||||
|
defer cleanUp()
|
||||||
|
|
||||||
|
req := &lnrpc.ListMacaroonIDsRequest{}
|
||||||
|
resp, err := client.ListMacaroonIDs(context.Background(), req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
printRespJSON(resp)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var deleteMacaroonIDCommand = cli.Command{
|
||||||
|
Name: "deletemacaroonid",
|
||||||
|
Category: "Macaroons",
|
||||||
|
Usage: "Delete a specific macaroon ID.",
|
||||||
|
ArgsUsage: "root_key_id",
|
||||||
|
Description: `
|
||||||
|
Remove a macaroon ID using the specified root key ID. For example:
|
||||||
|
|
||||||
|
lncli deletemacaroonid 1
|
||||||
|
|
||||||
|
WARNING
|
||||||
|
When the ID is deleted, all macaroons created from that root key will
|
||||||
|
be invalidated.
|
||||||
|
|
||||||
|
Note that the default root key ID 0 cannot be deleted.
|
||||||
|
`,
|
||||||
|
Action: actionDecorator(deleteMacaroonID),
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteMacaroonID(ctx *cli.Context) error {
|
||||||
|
client, cleanUp := getClient(ctx)
|
||||||
|
defer cleanUp()
|
||||||
|
|
||||||
|
// Validate args length. Only one argument is allowed.
|
||||||
|
if ctx.NArg() != 1 {
|
||||||
|
return cli.ShowCommandHelp(ctx, "deletemacaroonid")
|
||||||
|
}
|
||||||
|
|
||||||
|
rootKeyIDString := ctx.Args().First()
|
||||||
|
|
||||||
|
// Convert string into uint64.
|
||||||
|
rootKeyID, err := strconv.ParseUint(rootKeyIDString, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("root key ID must be a positive integer")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the value is not equal to DefaultRootKeyID. Note that the
|
||||||
|
// server also validates the root key ID when removing it. However, we check
|
||||||
|
// it here too so that we can give users a nice warning.
|
||||||
|
if bytes.Equal([]byte(rootKeyIDString), macaroons.DefaultRootKeyID) {
|
||||||
|
return fmt.Errorf("deleting the default root key ID 0 is not allowed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the actual RPC call.
|
||||||
|
req := &lnrpc.DeleteMacaroonIDRequest{
|
||||||
|
RootKeyId: rootKeyID,
|
||||||
|
}
|
||||||
|
resp, err := client.DeleteMacaroonID(context.Background(), req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
printRespJSON(resp)
|
||||||
|
return nil
|
||||||
|
}
|
@ -301,6 +301,8 @@ func main() {
|
|||||||
verifyChanBackupCommand,
|
verifyChanBackupCommand,
|
||||||
restoreChanBackupCommand,
|
restoreChanBackupCommand,
|
||||||
bakeMacaroonCommand,
|
bakeMacaroonCommand,
|
||||||
|
listMacaroonIDsCommand,
|
||||||
|
deleteMacaroonIDCommand,
|
||||||
trackPaymentCommand,
|
trackPaymentCommand,
|
||||||
versionCommand,
|
versionCommand,
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,8 @@ func New(cfg *Config) (*Server, lnrpc.MacaroonPerms, error) {
|
|||||||
// yet, exist, so we need to create it with the help of the
|
// yet, exist, so we need to create it with the help of the
|
||||||
// main macaroon service.
|
// main macaroon service.
|
||||||
invoicesMac, err := cfg.MacService.NewMacaroon(
|
invoicesMac, err := cfg.MacService.NewMacaroon(
|
||||||
context.Background(), macaroons.DefaultRootKeyID, macaroonOps...,
|
context.Background(), macaroons.DefaultRootKeyID,
|
||||||
|
macaroonOps...,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -166,7 +166,8 @@ func New(cfg *Config) (*Server, lnrpc.MacaroonPerms, error) {
|
|||||||
// exist, so we need to create it with the help of the main
|
// exist, so we need to create it with the help of the main
|
||||||
// macaroon service.
|
// macaroon service.
|
||||||
routerMac, err := cfg.MacService.NewMacaroon(
|
routerMac, err := cfg.MacService.NewMacaroon(
|
||||||
context.Background(), macaroons.DefaultRootKeyID, macaroonOps...,
|
context.Background(), macaroons.DefaultRootKeyID,
|
||||||
|
macaroonOps...,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -113,7 +113,8 @@ func New(cfg *Config) (*Server, lnrpc.MacaroonPerms, error) {
|
|||||||
// exist, so we need to create it with the help of the main
|
// exist, so we need to create it with the help of the main
|
||||||
// macaroon service.
|
// macaroon service.
|
||||||
signerMac, err := cfg.MacService.NewMacaroon(
|
signerMac, err := cfg.MacService.NewMacaroon(
|
||||||
context.Background(), macaroons.DefaultRootKeyID, macaroonOps...,
|
context.Background(), macaroons.DefaultRootKeyID,
|
||||||
|
macaroonOps...,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
44
rpcserver.go
44
rpcserver.go
@ -194,6 +194,12 @@ var (
|
|||||||
"onchain", "offchain", "address", "message",
|
"onchain", "offchain", "address", "message",
|
||||||
"peers", "info", "invoices", "signer", "macaroon",
|
"peers", "info", "invoices", "signer", "macaroon",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the --no-macaroons flag is used to start lnd, the macaroon service
|
||||||
|
// is not initialized. errMacaroonDisabled is then returned when
|
||||||
|
// macaroon related services are used.
|
||||||
|
errMacaroonDisabled = fmt.Errorf("macaroon authentication disabled, " +
|
||||||
|
"remove --no-macaroons flag to enable")
|
||||||
)
|
)
|
||||||
|
|
||||||
// stringInSlice returns true if a string is contained in the given slice.
|
// stringInSlice returns true if a string is contained in the given slice.
|
||||||
@ -6383,8 +6389,7 @@ func (r *rpcServer) BakeMacaroon(ctx context.Context,
|
|||||||
// If the --no-macaroons flag is used to start lnd, the macaroon service
|
// If the --no-macaroons flag is used to start lnd, the macaroon service
|
||||||
// is not initialized. Therefore we can't bake new macaroons.
|
// is not initialized. Therefore we can't bake new macaroons.
|
||||||
if r.macService == nil {
|
if r.macService == nil {
|
||||||
return nil, fmt.Errorf("macaroon authentication disabled, " +
|
return nil, errMacaroonDisabled
|
||||||
"remove --no-macaroons flag to enable")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
helpMsg := fmt.Sprintf("supported actions are %v, supported entities "+
|
helpMsg := fmt.Sprintf("supported actions are %v, supported entities "+
|
||||||
@ -6416,10 +6421,11 @@ func (r *rpcServer) BakeMacaroon(ctx context.Context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert root key id from uint64 to bytes. Because the DefaultRootKeyID is
|
// Convert root key id from uint64 to bytes. Because the
|
||||||
// a digit 0 expressed in a byte slice of a string "0", we will keep the IDs
|
// DefaultRootKeyID is a digit 0 expressed in a byte slice of a string
|
||||||
// in the same format - all must be numeric, and must be a byte slice of
|
// "0", we will keep the IDs in the same format - all must be numeric,
|
||||||
// string value of the digit, e.g., uint64(123) to string(123).
|
// and must be a byte slice of string value of the digit, e.g.,
|
||||||
|
// uint64(123) to string(123).
|
||||||
rootKeyID := []byte(strconv.FormatUint(req.RootKeyId, 10))
|
rootKeyID := []byte(strconv.FormatUint(req.RootKeyId, 10))
|
||||||
|
|
||||||
// Bake new macaroon with the given permissions and send it binary
|
// Bake new macaroon with the given permissions and send it binary
|
||||||
@ -6442,15 +6448,15 @@ func (r *rpcServer) BakeMacaroon(ctx context.Context,
|
|||||||
|
|
||||||
// ListMacaroonIDs returns a list of macaroon root key IDs in use.
|
// ListMacaroonIDs returns a list of macaroon root key IDs in use.
|
||||||
func (r *rpcServer) ListMacaroonIDs(ctx context.Context,
|
func (r *rpcServer) ListMacaroonIDs(ctx context.Context,
|
||||||
req *lnrpc.ListMacaroonIDsRequest) (*lnrpc.ListMacaroonIDsResponse, error) {
|
req *lnrpc.ListMacaroonIDsRequest) (
|
||||||
|
*lnrpc.ListMacaroonIDsResponse, error) {
|
||||||
|
|
||||||
rpcsLog.Debugf("[listmacaroonids]")
|
rpcsLog.Debugf("[listmacaroonids]")
|
||||||
|
|
||||||
// If the --no-macaroons flag is used to start lnd, the macaroon service
|
// If the --no-macaroons flag is used to start lnd, the macaroon service
|
||||||
// is not initialized. Therefore we can't show any IDs.
|
// is not initialized. Therefore we can't show any IDs.
|
||||||
if r.macService == nil {
|
if r.macService == nil {
|
||||||
return nil, fmt.Errorf("macaroon authentication disabled, " +
|
return nil, errMacaroonDisabled
|
||||||
"remove --no-macaroons flag to enable")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rootKeyIDByteSlice, err := r.macService.ListMacaroonIDs(ctx)
|
rootKeyIDByteSlice, err := r.macService.ListMacaroonIDs(ctx)
|
||||||
@ -6474,21 +6480,21 @@ func (r *rpcServer) ListMacaroonIDs(ctx context.Context,
|
|||||||
|
|
||||||
// DeleteMacaroonID removes a specific macaroon ID.
|
// DeleteMacaroonID removes a specific macaroon ID.
|
||||||
func (r *rpcServer) DeleteMacaroonID(ctx context.Context,
|
func (r *rpcServer) DeleteMacaroonID(ctx context.Context,
|
||||||
req *lnrpc.DeleteMacaroonIDRequest) (*lnrpc.DeleteMacaroonIDResponse, error) {
|
req *lnrpc.DeleteMacaroonIDRequest) (
|
||||||
|
*lnrpc.DeleteMacaroonIDResponse, error) {
|
||||||
|
|
||||||
rpcsLog.Debugf("[deletemacaroonid]")
|
rpcsLog.Debugf("[deletemacaroonid]")
|
||||||
|
|
||||||
// If the --no-macaroons flag is used to start lnd, the macaroon service
|
// If the --no-macaroons flag is used to start lnd, the macaroon service
|
||||||
// is not initialized. Therefore we can't show any IDs.
|
// is not initialized. Therefore we can't delete any IDs.
|
||||||
if r.macService == nil {
|
if r.macService == nil {
|
||||||
return nil, fmt.Errorf("macaroon authentication disabled, " +
|
return nil, errMacaroonDisabled
|
||||||
"remove --no-macaroons flag to enable")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert root key id from uint64 to bytes. Because the DefaultRootKeyID is
|
// Convert root key id from uint64 to bytes. Because the
|
||||||
// a digit 0 expressed in a byte slice of a string "0", we will keep the IDs
|
// DefaultRootKeyID is a digit 0 expressed in a byte slice of a string
|
||||||
// in the same format - all must be digit, and must be a byte slice of
|
// "0", we will keep the IDs in the same format - all must be digit, and
|
||||||
// string value of the digit.
|
// must be a byte slice of string value of the digit.
|
||||||
rootKeyID := []byte(strconv.FormatUint(req.RootKeyId, 10))
|
rootKeyID := []byte(strconv.FormatUint(req.RootKeyId, 10))
|
||||||
deletedIDBytes, err := r.macService.DeleteMacaroonID(ctx, rootKeyID)
|
deletedIDBytes, err := r.macService.DeleteMacaroonID(ctx, rootKeyID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -6496,8 +6502,8 @@ func (r *rpcServer) DeleteMacaroonID(ctx context.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &lnrpc.DeleteMacaroonIDResponse{
|
return &lnrpc.DeleteMacaroonIDResponse{
|
||||||
// If the root key ID doesn't exist, it won't be deleted. We will return
|
// If the root key ID doesn't exist, it won't be deleted. We
|
||||||
// a response with deleted = false, otherwise true.
|
// will return a response with deleted = false, otherwise true.
|
||||||
Deleted: deletedIDBytes != nil,
|
Deleted: deletedIDBytes != nil,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user