Skip to content

Commit c2eb98d

Browse files
committed
multi: add PermissionsManager
In this commit, a new PermissionsManager is added. It handles all the active permissions that Lit has access to. This moves us away from using global variables for permission lists. This change might seem overkill on its own but hugely simplifies the permission management once we add lnd subserver permissions.
1 parent 824e94a commit c2eb98d

File tree

5 files changed

+145
-91
lines changed

5 files changed

+145
-91
lines changed

itest/litd_mode_integrated_test.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -834,7 +834,12 @@ func bakeSuperMacaroon(cfg *LitNodeConfig, readOnly bool) (string, error) {
834834
lndAdminCtx := macaroonContext(ctxt, lndAdminMacBytes)
835835
lndConn := lnrpc.NewLightningClient(rawConn)
836836

837-
superMacPermissions := terminal.GetAllPermissions(readOnly)
837+
permsMgr, err := terminal.NewPermissionsManager()
838+
if err != nil {
839+
return "", err
840+
}
841+
842+
superMacPermissions := permsMgr.ActivePermissions(readOnly)
838843
nullID := [4]byte{}
839844
superMacHex, err := terminal.BakeSuperMacaroon(
840845
lndAdminCtx, lndConn, session.NewSuperMacaroonRootKeyID(nullID),

rpc_proxy.go

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import (
2424
"google.golang.org/grpc/metadata"
2525
"google.golang.org/grpc/status"
2626
"google.golang.org/grpc/test/bufconn"
27-
"gopkg.in/macaroon-bakery.v2/bakery"
2827
"gopkg.in/macaroon.v2"
2928
)
3029

@@ -59,8 +58,7 @@ func (e *proxyErr) Unwrap() error {
5958
// component.
6059
func newRpcProxy(cfg *Config, validator macaroons.MacaroonValidator,
6160
superMacValidator session.SuperMacaroonValidator,
62-
permissionMap map[string][]bakery.Op,
63-
bufListener *bufconn.Listener) *rpcProxy {
61+
permsMgr *PermissionsManager, bufListener *bufconn.Listener) *rpcProxy {
6462

6563
// The gRPC web calls are protected by HTTP basic auth which is defined
6664
// by base64(username:password). Because we only have a password, we
@@ -77,7 +75,7 @@ func newRpcProxy(cfg *Config, validator macaroons.MacaroonValidator,
7775
p := &rpcProxy{
7876
cfg: cfg,
7977
basicAuth: basicAuth,
80-
permissionMap: permissionMap,
78+
permsMgr: permsMgr,
8179
macValidator: validator,
8280
superMacValidator: superMacValidator,
8381
bufListener: bufListener,
@@ -146,9 +144,9 @@ func newRpcProxy(cfg *Config, validator macaroons.MacaroonValidator,
146144
// +---------------------+
147145
//
148146
type rpcProxy struct {
149-
cfg *Config
150-
basicAuth string
151-
permissionMap map[string][]bakery.Op
147+
cfg *Config
148+
basicAuth string
149+
permsMgr *PermissionsManager
152150

153151
macValidator macaroons.MacaroonValidator
154152
superMacValidator session.SuperMacaroonValidator
@@ -345,17 +343,17 @@ func (p *rpcProxy) makeDirector(allowLitRPC bool) func(ctx context.Context,
345343
// handled by the integrated daemons that are hooking into lnd's
346344
// gRPC server.
347345
switch {
348-
case isFaradayURI(requestURI) && p.cfg.faradayRemote:
346+
case p.permsMgr.IsFaradayURI(requestURI) && p.cfg.faradayRemote:
349347
return outCtx, p.faradayConn, nil
350348

351-
case isLoopURI(requestURI) && p.cfg.loopRemote:
349+
case p.permsMgr.IsLoopURI(requestURI) && p.cfg.loopRemote:
352350
return outCtx, p.loopConn, nil
353351

354-
case isPoolURI(requestURI) && p.cfg.poolRemote:
352+
case p.permsMgr.IsPoolURI(requestURI) && p.cfg.poolRemote:
355353
return outCtx, p.poolConn, nil
356354

357355
// Calls to LiT session RPC aren't allowed in some cases.
358-
case isLitURI(requestURI) && !allowLitRPC:
356+
case p.permsMgr.IsLitURI(requestURI) && !allowLitRPC:
359357
return outCtx, nil, status.Errorf(
360358
codes.Unimplemented, "unknown service %s",
361359
requestURI,
@@ -373,7 +371,7 @@ func (p *rpcProxy) UnaryServerInterceptor(ctx context.Context, req interface{},
373371
info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{},
374372
error) {
375373

376-
uriPermissions, ok := p.permissionMap[info.FullMethod]
374+
uriPermissions, ok := p.permsMgr.URIPermissions(info.FullMethod)
377375
if !ok {
378376
return nil, fmt.Errorf("%s: unknown permissions "+
379377
"required for method", info.FullMethod)
@@ -414,7 +412,7 @@ func (p *rpcProxy) StreamServerInterceptor(srv interface{},
414412
ss grpc.ServerStream, info *grpc.StreamServerInfo,
415413
handler grpc.StreamHandler) error {
416414

417-
uriPermissions, ok := p.permissionMap[info.FullMethod]
415+
uriPermissions, ok := p.permsMgr.URIPermissions(info.FullMethod)
418416
if !ok {
419417
return fmt.Errorf("%s: unknown permissions required "+
420418
"for method", info.FullMethod)
@@ -503,31 +501,31 @@ func (p *rpcProxy) basicAuthToMacaroon(basicAuth, requestURI string,
503501
macData []byte
504502
)
505503
switch {
506-
case isLndURI(requestURI):
504+
case p.permsMgr.IsLndURI(requestURI):
507505
_, _, _, macPath, macData = p.cfg.lndConnectParams()
508506

509-
case isFaradayURI(requestURI):
507+
case p.permsMgr.IsFaradayURI(requestURI):
510508
if p.cfg.faradayRemote {
511509
macPath = p.cfg.Remote.Faraday.MacaroonPath
512510
} else {
513511
macPath = p.cfg.Faraday.MacaroonPath
514512
}
515513

516-
case isLoopURI(requestURI):
514+
case p.permsMgr.IsLoopURI(requestURI):
517515
if p.cfg.loopRemote {
518516
macPath = p.cfg.Remote.Loop.MacaroonPath
519517
} else {
520518
macPath = p.cfg.Loop.MacaroonPath
521519
}
522520

523-
case isPoolURI(requestURI):
521+
case p.permsMgr.IsPoolURI(requestURI):
524522
if p.cfg.poolRemote {
525523
macPath = p.cfg.Remote.Pool.MacaroonPath
526524
} else {
527525
macPath = p.cfg.Pool.MacaroonPath
528526
}
529527

530-
case isLitURI(requestURI):
528+
case p.permsMgr.IsLitURI(requestURI):
531529
macPath = p.cfg.MacaroonPath
532530

533531
default:
@@ -580,7 +578,7 @@ func (p *rpcProxy) basicAuthToMacaroon(basicAuth, requestURI string,
580578
func (p *rpcProxy) convertSuperMacaroon(ctx context.Context, macHex string,
581579
fullMethod string) ([]byte, error) {
582580

583-
requiredPermissions, ok := p.permissionMap[fullMethod]
581+
requiredPermissions, ok := p.permsMgr.URIPermissions(fullMethod)
584582
if !ok {
585583
return nil, fmt.Errorf("%s: unknown permissions required for "+
586584
"method", fullMethod)
@@ -605,17 +603,17 @@ func (p *rpcProxy) convertSuperMacaroon(ctx context.Context, macHex string,
605603
// Is this actually a request that goes to a daemon that is running
606604
// remotely?
607605
switch {
608-
case isFaradayURI(fullMethod) && p.cfg.faradayRemote:
606+
case p.permsMgr.IsFaradayURI(fullMethod) && p.cfg.faradayRemote:
609607
return readMacaroon(lncfg.CleanAndExpandPath(
610608
p.cfg.Remote.Faraday.MacaroonPath,
611609
))
612610

613-
case isLoopURI(fullMethod) && p.cfg.loopRemote:
611+
case p.permsMgr.IsLoopURI(fullMethod) && p.cfg.loopRemote:
614612
return readMacaroon(lncfg.CleanAndExpandPath(
615613
p.cfg.Remote.Loop.MacaroonPath,
616614
))
617615

618-
case isPoolURI(fullMethod) && p.cfg.poolRemote:
616+
case p.permsMgr.IsPoolURI(fullMethod) && p.cfg.poolRemote:
619617
return readMacaroon(lncfg.CleanAndExpandPath(
620618
p.cfg.Remote.Pool.MacaroonPath,
621619
))

session_rpcserver.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ type sessionRpcServerConfig struct {
3939
superMacBaker func(ctx context.Context, rootKeyID uint64,
4040
recipe *session.MacaroonRecipe) (string, error)
4141
firstConnectionDeadline time.Duration
42+
permMgr *PermissionsManager
4243
}
4344

4445
// newSessionRPCServer creates a new sessionRpcServer using the passed config.
@@ -205,7 +206,7 @@ func (s *sessionRpcServer) resumeSession(sess *session.Session) error {
205206
mac, err := s.cfg.superMacBaker(
206207
context.Background(), sess.MacaroonRootKey,
207208
&session.MacaroonRecipe{
208-
Permissions: GetAllPermissions(readOnly),
209+
Permissions: s.cfg.permMgr.ActivePermissions(readOnly),
209210
Caveats: caveats,
210211
},
211212
)

subserver_permissions.go

Lines changed: 101 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ var (
2626
}},
2727
}
2828

29-
// whiteListedMethods is a map of all lnd RPC methods that don't require
30-
// any macaroon authentication.
31-
whiteListedMethods = map[string][]bakery.Op{
29+
// whiteListedLNDMethods is a map of all lnd RPC methods that don't
30+
// require any macaroon authentication.
31+
whiteListedLNDMethods = map[string][]bakery.Op{
3232
"/lnrpc.WalletUnlocker/GenSeed": {},
3333
"/lnrpc.WalletUnlocker/InitWallet": {},
3434
"/lnrpc.WalletUnlocker/UnlockWallet": {},
@@ -41,53 +41,71 @@ var (
4141
}
4242
)
4343

44-
// getSubserverPermissions returns a merged map of all subserver macaroon
45-
// permissions.
46-
func getSubserverPermissions() map[string][]bakery.Op {
47-
mapSize := len(faraday.RequiredPermissions) +
48-
len(loop.RequiredPermissions) + len(pool.RequiredPermissions)
49-
result := make(map[string][]bakery.Op, mapSize)
50-
for key, value := range faraday.RequiredPermissions {
51-
result[key] = value
52-
}
53-
for key, value := range loop.RequiredPermissions {
54-
result[key] = value
55-
}
56-
for key, value := range pool.RequiredPermissions {
57-
result[key] = value
58-
}
59-
for key, value := range litPermissions {
60-
result[key] = value
61-
}
62-
return result
44+
// subServerName is a name used to identify a particular Lit sub-server.
45+
type subServerName string
46+
47+
const (
48+
poolPerms subServerName = "pool"
49+
loopPerms subServerName = "loop"
50+
faradayPerms subServerName = "faraday"
51+
litPerms subServerName = "lit"
52+
lndPerms subServerName = "lnd"
53+
)
54+
55+
// PermissionsManager manages the permission lists that Lit requires.
56+
type PermissionsManager struct {
57+
// fixedPerms is constructed once on creation of the PermissionsManager.
58+
// It contains all the permissions that will not change throughout the
59+
// lifetime of the manager. It maps sub-server name to uri to permission
60+
// operations.
61+
fixedPerms map[subServerName]map[string][]bakery.Op
62+
63+
// perms is a map containing all permissions that the manager knows
64+
// are available for use.
65+
perms map[string][]bakery.Op
6366
}
6467

65-
// getAllMethodPermissions returns a merged map of lnd's and all subservers'
66-
// method macaroon permissions.
67-
func getAllMethodPermissions() map[string][]bakery.Op {
68-
subserverPermissions := getSubserverPermissions()
69-
lndPermissions := lnd.MainRPCServerPermissions()
70-
mapSize := len(subserverPermissions) + len(lndPermissions) +
71-
len(whiteListedMethods)
72-
result := make(map[string][]bakery.Op, mapSize)
73-
for key, value := range lndPermissions {
74-
result[key] = value
68+
// NewPermissionsManager constructs a new PermissionsManager instance and
69+
// collects any of the fixed permissions.
70+
func NewPermissionsManager() (*PermissionsManager, error) {
71+
permissions := make(map[subServerName]map[string][]bakery.Op)
72+
permissions[faradayPerms] = faraday.RequiredPermissions
73+
permissions[loopPerms] = loop.RequiredPermissions
74+
permissions[poolPerms] = pool.RequiredPermissions
75+
permissions[litPerms] = litPermissions
76+
permissions[lndPerms] = lnd.MainRPCServerPermissions()
77+
for k, v := range whiteListedLNDMethods {
78+
permissions[lndPerms][k] = v
7579
}
76-
for key, value := range subserverPermissions {
77-
result[key] = value
78-
}
79-
for key, value := range whiteListedMethods {
80-
result[key] = value
80+
81+
allPerms := make(map[string][]bakery.Op)
82+
for _, perms := range permissions {
83+
for k, v := range perms {
84+
allPerms[k] = v
85+
}
8186
}
82-
return result
87+
88+
return &PermissionsManager{
89+
fixedPerms: permissions,
90+
perms: allPerms,
91+
}, nil
8392
}
8493

85-
// GetAllPermissions retrieves all the permissions needed to bake a super
86-
// macaroon.
87-
func GetAllPermissions(readOnly bool) []bakery.Op {
88-
dedupMap := make(map[string]map[string]bool)
94+
// URIPermissions returns a list of permission operations for the given URI if
95+
// the uri is known to the manager. The second return parameter will be false
96+
// if the URI is unknown to the manager.
97+
func (pm *PermissionsManager) URIPermissions(uri string) ([]bakery.Op, bool) {
98+
ops, ok := pm.perms[uri]
99+
return ops, ok
100+
}
89101

90-
for _, methodPerms := range getAllMethodPermissions() {
102+
// ActivePermissions returns all the available active permissions that the
103+
// manager is aware of. Optionally, readOnly can be set to true if only the
104+
// read-only permissions should be returned.
105+
func (pm *PermissionsManager) ActivePermissions(readOnly bool) []bakery.Op {
106+
// De-dup the permissions and optionally apply the read-only filter.
107+
dedupMap := make(map[string]map[string]bool)
108+
for _, methodPerms := range pm.perms {
91109
for _, methodPerm := range methodPerms {
92110
if methodPerm.Action == "" || methodPerm.Entity == "" {
93111
continue
@@ -119,32 +137,56 @@ func GetAllPermissions(readOnly bool) []bakery.Op {
119137
return result
120138
}
121139

122-
// isLndURI returns true if the given URI belongs to an RPC of lnd.
123-
func isLndURI(uri string) bool {
124-
_, ok := lnd.MainRPCServerPermissions()[uri]
125-
return ok
140+
// GetLitPerms returns a map of all permissions that the manager is aware of
141+
// _except_ for any LND permissions. In other words, this returns permissions
142+
// for which the external validator of Lit is responsible.
143+
func (pm *PermissionsManager) GetLitPerms() map[string][]bakery.Op {
144+
mapSize := len(pm.fixedPerms[litPerms]) +
145+
len(pm.fixedPerms[faradayPerms]) +
146+
len(pm.fixedPerms[loopPerms]) + len(pm.fixedPerms[poolPerms])
147+
148+
result := make(map[string][]bakery.Op, mapSize)
149+
for key, value := range pm.fixedPerms[faradayPerms] {
150+
result[key] = value
151+
}
152+
for key, value := range pm.fixedPerms[loopPerms] {
153+
result[key] = value
154+
}
155+
for key, value := range pm.fixedPerms[poolPerms] {
156+
result[key] = value
157+
}
158+
for key, value := range pm.fixedPerms[litPerms] {
159+
result[key] = value
160+
}
161+
return result
162+
}
163+
164+
// IsLndURI returns true if the given URI belongs to an RPC of lnd.
165+
func (pm *PermissionsManager) IsLndURI(uri string) bool {
166+
_, lndCall := pm.fixedPerms[lndPerms][uri]
167+
return lndCall
126168
}
127169

128-
// isLoopURI returns true if the given URI belongs to an RPC of loopd.
129-
func isLoopURI(uri string) bool {
130-
_, ok := loop.RequiredPermissions[uri]
170+
// IsLoopURI returns true if the given URI belongs to an RPC of loopd.
171+
func (pm *PermissionsManager) IsLoopURI(uri string) bool {
172+
_, ok := pm.fixedPerms[loopPerms][uri]
131173
return ok
132174
}
133175

134-
// isFaradayURI returns true if the given URI belongs to an RPC of faraday.
135-
func isFaradayURI(uri string) bool {
136-
_, ok := faraday.RequiredPermissions[uri]
176+
// IsFaradayURI returns true if the given URI belongs to an RPC of faraday.
177+
func (pm *PermissionsManager) IsFaradayURI(uri string) bool {
178+
_, ok := pm.fixedPerms[faradayPerms][uri]
137179
return ok
138180
}
139181

140-
// isPoolURI returns true if the given URI belongs to an RPC of poold.
141-
func isPoolURI(uri string) bool {
142-
_, ok := pool.RequiredPermissions[uri]
182+
// IsPoolURI returns true if the given URI belongs to an RPC of poold.
183+
func (pm *PermissionsManager) IsPoolURI(uri string) bool {
184+
_, ok := pm.fixedPerms[poolPerms][uri]
143185
return ok
144186
}
145187

146-
// isLitURI returns true if the given URI belongs to an RPC of LiT.
147-
func isLitURI(uri string) bool {
148-
_, ok := litPermissions[uri]
188+
// IsLitURI returns true if the given URI belongs to an RPC of LiT.
189+
func (pm *PermissionsManager) IsLitURI(uri string) bool {
190+
_, ok := pm.fixedPerms[litPerms][uri]
149191
return ok
150192
}

0 commit comments

Comments
 (0)