lnrpc+rpcserver: support list/delete on marcaroon IDs

This commit is contained in:
yyforyongyu 2020-07-24 00:36:42 +08:00
parent f362f7670b
commit c0e2513350
No known key found for this signature in database
GPG Key ID: 9BCD95C4FF296868
13 changed files with 1383 additions and 756 deletions

12
lnd.go

@ -862,8 +862,8 @@ func genMacaroons(ctx context.Context, svc *macaroons.Service,
// access invoice related calls. This is useful for merchants and other
// services to allow an isolated instance that can only query and
// modify invoices.
invoiceMac, err := svc.Oven.NewMacaroon(
ctx, bakery.LatestVersion, nil, invoicePermissions...,
invoiceMac, err := svc.NewMacaroon(
ctx, macaroons.DefaultRootKeyID, invoicePermissions...,
)
if err != nil {
return err
@ -879,8 +879,8 @@ func genMacaroons(ctx context.Context, svc *macaroons.Service,
}
// Generate the read-only macaroon and write it to a file.
roMacaroon, err := svc.Oven.NewMacaroon(
ctx, bakery.LatestVersion, nil, readPermissions...,
roMacaroon, err := svc.NewMacaroon(
ctx, macaroons.DefaultRootKeyID, readPermissions...,
)
if err != nil {
return err
@ -896,8 +896,8 @@ func genMacaroons(ctx context.Context, svc *macaroons.Service,
// Generate the admin macaroon and write it to a file.
adminPermissions := append(readPermissions, writePermissions...)
admMacaroon, err := svc.Oven.NewMacaroon(
ctx, bakery.LatestVersion, nil, adminPermissions...,
admMacaroon, err := svc.NewMacaroon(
ctx, macaroons.DefaultRootKeyID, adminPermissions...,
)
if err != nil {
return err

@ -129,6 +129,11 @@ description):
* BakeMacaroon
* Bakes a new macaroon with the provided list of permissions and
restrictions
* ListMacaroonIDs
* List all the macaroon root key IDs that are in use.
* DeleteMacaroonID
* Remove a specific macaroon root key ID from the database and invalidates
all macaroons derived from the key with that ID.
## Service: WalletUnlocker

@ -16,6 +16,7 @@ import (
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/macaroons"
"google.golang.org/grpc"
"gopkg.in/macaroon-bakery.v2/bakery"
)
@ -119,8 +120,9 @@ func New(cfg *Config) (*Server, lnrpc.MacaroonPerms, error) {
// At this point, we know that the chain notifier macaroon
// doesn't yet, exist, so we need to create it with the help of
// the main macaroon service.
chainNotifierMac, err := cfg.MacService.Oven.NewMacaroon(
context.Background(), bakery.LatestVersion, nil,
chainNotifierMac, err := cfg.MacService.NewMacaroon(
context.Background(),
macaroons.DefaultRootKeyID,
macaroonOps...,
)
if err != nil {

@ -15,6 +15,7 @@ import (
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/macaroons"
)
const (
@ -99,9 +100,8 @@ func New(cfg *Config) (*Server, lnrpc.MacaroonPerms, error) {
// At this point, we know that the invoices macaroon doesn't
// yet, exist, so we need to create it with the help of the
// main macaroon service.
invoicesMac, err := cfg.MacService.Oven.NewMacaroon(
context.Background(), bakery.LatestVersion, nil,
macaroonOps...,
invoicesMac, err := cfg.MacService.NewMacaroon(
context.Background(), macaroons.DefaultRootKeyID, macaroonOps...,
)
if err != nil {
return nil, nil, err

@ -133,6 +133,10 @@ http:
- selector: lnrpc.Lightning.BakeMacaroon
post: "/v1/macaroon"
body: "*"
- selector: lnrpc.Lightning.ListMacaroonIDs
get: "/v1/macaroon/ids"
- selector: lnrpc.Lightning.DeleteMacaroonID
delete: "/v1/macaroon/{root_key_id}"
# walletunlocker.proto
- selector: lnrpc.WalletUnlocker.GenSeed

@ -15,6 +15,7 @@ import (
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/macaroons"
"github.com/lightningnetwork/lnd/routing"
"github.com/lightningnetwork/lnd/routing/route"
@ -164,9 +165,8 @@ func New(cfg *Config) (*Server, lnrpc.MacaroonPerms, error) {
// At this point, we know that the router macaroon doesn't yet,
// exist, so we need to create it with the help of the main
// macaroon service.
routerMac, err := cfg.MacService.Oven.NewMacaroon(
context.Background(), bakery.LatestVersion, nil,
macaroonOps...,
routerMac, err := cfg.MacService.NewMacaroon(
context.Background(), macaroons.DefaultRootKeyID, macaroonOps...,
)
if err != nil {
return nil, nil, err

File diff suppressed because it is too large Load Diff

@ -1913,6 +1913,78 @@ func local_request_Lightning_BakeMacaroon_0(ctx context.Context, marshaler runti
}
func request_Lightning_ListMacaroonIDs_0(ctx context.Context, marshaler runtime.Marshaler, client LightningClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ListMacaroonIDsRequest
var metadata runtime.ServerMetadata
msg, err := client.ListMacaroonIDs(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Lightning_ListMacaroonIDs_0(ctx context.Context, marshaler runtime.Marshaler, server LightningServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ListMacaroonIDsRequest
var metadata runtime.ServerMetadata
msg, err := server.ListMacaroonIDs(ctx, &protoReq)
return msg, metadata, err
}
func request_Lightning_DeleteMacaroonID_0(ctx context.Context, marshaler runtime.Marshaler, client LightningClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq DeleteMacaroonIDRequest
var metadata runtime.ServerMetadata
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["root_key_id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "root_key_id")
}
protoReq.RootKeyId, err = runtime.Uint64(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "root_key_id", err)
}
msg, err := client.DeleteMacaroonID(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Lightning_DeleteMacaroonID_0(ctx context.Context, marshaler runtime.Marshaler, server LightningServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq DeleteMacaroonIDRequest
var metadata runtime.ServerMetadata
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["root_key_id"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "root_key_id")
}
protoReq.RootKeyId, err = runtime.Uint64(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "root_key_id", err)
}
msg, err := server.DeleteMacaroonID(ctx, &protoReq)
return msg, metadata, err
}
// RegisterLightningHandlerServer registers the http handlers for service Lightning to "mux".
// UnaryRPC :call LightningServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
@ -2874,6 +2946,46 @@ func RegisterLightningHandlerServer(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_Lightning_ListMacaroonIDs_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Lightning_ListMacaroonIDs_0(rctx, inboundMarshaler, server, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Lightning_ListMacaroonIDs_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("DELETE", pattern_Lightning_DeleteMacaroonID_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Lightning_DeleteMacaroonID_0(rctx, inboundMarshaler, server, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Lightning_DeleteMacaroonID_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
@ -3975,6 +4087,46 @@ func RegisterLightningHandlerClient(ctx context.Context, mux *runtime.ServeMux,
})
mux.Handle("GET", pattern_Lightning_ListMacaroonIDs_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Lightning_ListMacaroonIDs_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Lightning_ListMacaroonIDs_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("DELETE", pattern_Lightning_DeleteMacaroonID_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Lightning_DeleteMacaroonID_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Lightning_DeleteMacaroonID_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
@ -4084,6 +4236,10 @@ var (
pattern_Lightning_SubscribeChannelBackups_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v1", "channels", "backup", "subscribe"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_Lightning_BakeMacaroon_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "macaroon"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_Lightning_ListMacaroonIDs_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "macaroon", "ids"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_Lightning_DeleteMacaroonID_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"v1", "macaroon", "root_key_id"}, "", runtime.AssumeColonVerbOpt(true)))
)
var (
@ -4192,4 +4348,8 @@ var (
forward_Lightning_SubscribeChannelBackups_0 = runtime.ForwardResponseStream
forward_Lightning_BakeMacaroon_0 = runtime.ForwardResponseMessage
forward_Lightning_ListMacaroonIDs_0 = runtime.ForwardResponseMessage
forward_Lightning_DeleteMacaroonID_0 = runtime.ForwardResponseMessage
)

@ -491,6 +491,19 @@ service Lightning {
offline.
*/
rpc BakeMacaroon (BakeMacaroonRequest) returns (BakeMacaroonResponse);
/* lncli: `listmacaroonids`
ListMacaroonIDs returns all root key IDs that are in use.
*/
rpc ListMacaroonIDs (ListMacaroonIDsRequest)
returns (ListMacaroonIDsResponse);
/* lncli: `deletemacaroonid`
DeleteMacaroonID deletes the specified macaroon ID and invalidates all
macaroons derived from that ID.
*/
rpc DeleteMacaroonID (DeleteMacaroonIDRequest)
returns (DeleteMacaroonIDResponse);
}
message Utxo {
@ -3327,12 +3340,31 @@ message MacaroonPermission {
message BakeMacaroonRequest {
// The list of permissions the new macaroon should grant.
repeated MacaroonPermission permissions = 1;
// The root key ID used to create the macaroon, must be a positive integer.
uint64 root_key_id = 2;
}
message BakeMacaroonResponse {
// The hex encoded macaroon, serialized in binary format.
string macaroon = 1;
}
message ListMacaroonIDsRequest {
}
message ListMacaroonIDsResponse {
// The list of root key IDs that are in use.
repeated uint64 root_key_ids = 1;
}
message DeleteMacaroonIDRequest {
// The root key ID to be removed.
uint64 root_key_id = 1;
}
message DeleteMacaroonIDResponse {
// A boolean indicates that the deletion is successful.
bool deleted = 1;
}
message Failure {
enum FailureCode {
/*

@ -1426,6 +1426,62 @@
]
}
},
"/v1/macaroon/ids": {
"get": {
"summary": "lncli: `listmacaroonids`\nListMacaroonIDs returns all root key IDs that are in use.",
"operationId": "ListMacaroonIDs",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/lnrpcListMacaroonIDsResponse"
}
},
"default": {
"description": "An unexpected error response",
"schema": {
"$ref": "#/definitions/runtimeError"
}
}
},
"tags": [
"Lightning"
]
}
},
"/v1/macaroon/{root_key_id}": {
"delete": {
"summary": "lncli: `deletemacaroonid`\nDeleteMacaroonID deletes the specified macaroon ID and invalidates all\nmacaroons derived from that ID.",
"operationId": "DeleteMacaroonID",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/lnrpcDeleteMacaroonIDResponse"
}
},
"default": {
"description": "An unexpected error response",
"schema": {
"$ref": "#/definitions/runtimeError"
}
}
},
"parameters": [
{
"name": "root_key_id",
"description": "The root key ID to be removed.",
"in": "path",
"required": true,
"type": "string",
"format": "uint64"
}
],
"tags": [
"Lightning"
]
}
},
"/v1/newaddress": {
"get": {
"summary": "lncli: `newaddress`\nNewAddress creates a new address under control of the local wallet.",
@ -2399,6 +2455,11 @@
"$ref": "#/definitions/lnrpcMacaroonPermission"
},
"description": "The list of permissions the new macaroon should grant."
},
"root_key_id": {
"type": "string",
"format": "uint64",
"description": "The root key ID used to create the macaroon, must be a positive integer."
}
}
},
@ -3161,6 +3222,16 @@
"lnrpcDeleteAllPaymentsResponse": {
"type": "object"
},
"lnrpcDeleteMacaroonIDResponse": {
"type": "object",
"properties": {
"deleted": {
"type": "boolean",
"format": "boolean",
"description": "A boolean indicates that the deletion is successful."
}
}
},
"lnrpcDisconnectPeerResponse": {
"type": "object"
},
@ -4095,6 +4166,19 @@
}
}
},
"lnrpcListMacaroonIDsResponse": {
"type": "object",
"properties": {
"root_key_ids": {
"type": "array",
"items": {
"type": "string",
"format": "uint64"
},
"description": "The list of root key IDs that are in use."
}
}
},
"lnrpcListPaymentsResponse": {
"type": "object",
"properties": {

@ -19,6 +19,7 @@ import (
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/macaroons"
"google.golang.org/grpc"
"gopkg.in/macaroon-bakery.v2/bakery"
)
@ -111,9 +112,8 @@ func New(cfg *Config) (*Server, lnrpc.MacaroonPerms, error) {
// At this point, we know that the signer macaroon doesn't yet,
// exist, so we need to create it with the help of the main
// macaroon service.
signerMac, err := cfg.MacService.Oven.NewMacaroon(
context.Background(), bakery.LatestVersion, nil,
macaroonOps...,
signerMac, err := cfg.MacService.NewMacaroon(
context.Background(), macaroons.DefaultRootKeyID, macaroonOps...,
)
if err != nil {
return nil, nil, err

@ -25,6 +25,7 @@ import (
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/btcwallet"
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
"github.com/lightningnetwork/lnd/macaroons"
"github.com/lightningnetwork/lnd/sweep"
"google.golang.org/grpc"
"gopkg.in/macaroon-bakery.v2/bakery"
@ -156,8 +157,9 @@ func New(cfg *Config) (*WalletKit, lnrpc.MacaroonPerms, error) {
// At this point, we know that the wallet kit macaroon doesn't
// yet, exist, so we need to create it with the help of the
// main macaroon service.
walletKitMac, err := cfg.MacService.Oven.NewMacaroon(
context.Background(), bakery.LatestVersion, nil,
walletKitMac, err := cfg.MacService.NewMacaroon(
context.Background(),
macaroons.DefaultRootKeyID,
macaroonOps...,
)
if err != nil {

@ -12,6 +12,7 @@ import (
"net/http"
"runtime"
"sort"
"strconv"
"strings"
"sync"
"sync/atomic"
@ -107,6 +108,10 @@ var (
Entity: "signer",
Action: "read",
},
{
Entity: "macaroon",
Action: "read",
},
}
// writePermissions is a slice of all entities that allow write
@ -148,6 +153,10 @@ var (
Entity: "macaroon",
Action: "generate",
},
{
Entity: "macaroon",
Action: "write",
},
}
// invoicePermissions is a slice of all the entities that allows a user
@ -429,6 +438,14 @@ func mainRPCServerPermissions() map[string][]bakery.Op {
Entity: "macaroon",
Action: "generate",
}},
"/lnrpc.Lightning/ListMacaroonIDs": {{
Entity: "macaroon",
Action: "read",
}},
"/lnrpc.Lightning/DeleteMacaroonID": {{
Entity: "macaroon",
Action: "write",
}},
"/lnrpc.Lightning/SubscribePeerEvents": {{
Entity: "peers",
Action: "read",
@ -6399,10 +6416,16 @@ func (r *rpcServer) BakeMacaroon(ctx context.Context,
}
}
// Convert root key id from uint64 to bytes. Because the DefaultRootKeyID is
// a digit 0 expressed in a byte slice of a string "0", we will keep the IDs
// in the same format - all must be numeric, 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))
// Bake new macaroon with the given permissions and send it binary
// serialized and hex encoded to the client.
newMac, err := r.macService.Oven.NewMacaroon(
ctx, bakery.LatestVersion, nil, requestedPermissions...,
newMac, err := r.macService.NewMacaroon(
ctx, rootKeyID, requestedPermissions...,
)
if err != nil {
return nil, err
@ -6417,6 +6440,68 @@ func (r *rpcServer) BakeMacaroon(ctx context.Context,
return resp, nil
}
// ListMacaroonIDs returns a list of macaroon root key IDs in use.
func (r *rpcServer) ListMacaroonIDs(ctx context.Context,
req *lnrpc.ListMacaroonIDsRequest) (*lnrpc.ListMacaroonIDsResponse, error) {
rpcsLog.Debugf("[listmacaroonids]")
// If the --no-macaroons flag is used to start lnd, the macaroon service
// is not initialized. Therefore we can't show any IDs.
if r.macService == nil {
return nil, fmt.Errorf("macaroon authentication disabled, " +
"remove --no-macaroons flag to enable")
}
rootKeyIDByteSlice, err := r.macService.ListMacaroonIDs(ctx)
if err != nil {
return nil, err
}
var rootKeyIDs []uint64
for _, value := range rootKeyIDByteSlice {
// Convert bytes into uint64.
id, err := strconv.ParseUint(string(value), 10, 64)
if err != nil {
return nil, err
}
rootKeyIDs = append(rootKeyIDs, id)
}
return &lnrpc.ListMacaroonIDsResponse{RootKeyIds: rootKeyIDs}, nil
}
// DeleteMacaroonID removes a specific macaroon ID.
func (r *rpcServer) DeleteMacaroonID(ctx context.Context,
req *lnrpc.DeleteMacaroonIDRequest) (*lnrpc.DeleteMacaroonIDResponse, error) {
rpcsLog.Debugf("[deletemacaroonid]")
// If the --no-macaroons flag is used to start lnd, the macaroon service
// is not initialized. Therefore we can't show any IDs.
if r.macService == nil {
return nil, fmt.Errorf("macaroon authentication disabled, " +
"remove --no-macaroons flag to enable")
}
// Convert root key id from uint64 to bytes. Because the DefaultRootKeyID is
// a digit 0 expressed in a byte slice of a string "0", we will keep the IDs
// in the same format - all must be digit, and must be a byte slice of
// string value of the digit.
rootKeyID := []byte(strconv.FormatUint(req.RootKeyId, 10))
deletedIDBytes, err := r.macService.DeleteMacaroonID(ctx, rootKeyID)
if err != nil {
return nil, err
}
return &lnrpc.DeleteMacaroonIDResponse{
// If the root key ID doesn't exist, it won't be deleted. We will return
// a response with deleted = false, otherwise true.
Deleted: deletedIDBytes != nil,
}, nil
}
// FundingStateStep is an advanced funding related call that allows the caller
// to either execute some preparatory steps for a funding workflow, or manually
// progress a funding workflow. The primary way a funding flow is identified is