github.com/decred/dcrlnd@v0.7.6/rpcperms/interceptor.go (about) 1 package rpcperms 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "sync" 8 "sync/atomic" 9 10 "github.com/decred/dcrlnd/lnrpc" 11 "github.com/decred/dcrlnd/lnrpc/initchainsyncrpc" 12 "github.com/decred/dcrlnd/macaroons" 13 "github.com/decred/dcrlnd/monitoring" 14 "github.com/decred/dcrlnd/subscribe" 15 "github.com/decred/slog" 16 grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" 17 "google.golang.org/grpc" 18 "gopkg.in/macaroon-bakery.v2/bakery" 19 ) 20 21 // rpcState is an enum that we use to keep track of the current RPC service 22 // state. This will transition as we go from startup to unlocking the wallet, 23 // and finally fully active. 24 type rpcState uint8 25 26 const ( 27 // waitingToStart indicates that we're at the beginning of the startup 28 // process. In a cluster evironment this may mean that we're waiting to 29 // become the leader in which case RPC calls will be disabled until 30 // this instance has been elected as leader. 31 waitingToStart rpcState = iota 32 33 // walletNotCreated is the starting state if the RPC server is active, 34 // but the wallet is not yet created. In this state we'll only allow 35 // calls to the WalletUnlockerService. 36 walletNotCreated 37 38 // walletLocked indicates the RPC server is active, but the wallet is 39 // locked. In this state we'll only allow calls to the 40 // WalletUnlockerService. 41 walletLocked 42 43 // walletUnlocked means that the wallet has been unlocked, but the full 44 // RPC server is not yeat ready. 45 walletUnlocked 46 47 // rpcActive means that the RPC server is ready to accept calls. 48 rpcActive 49 50 // serverActive means that the lnd server is ready to accept calls. 51 serverActive 52 ) 53 54 var ( 55 // ErrWaitingToStart is returned if LND is still wating to start, 56 // possibly blocked until elected as the leader. 57 ErrWaitingToStart = fmt.Errorf("waiting to start, RPC services not " + 58 "available") 59 60 // ErrNoWallet is returned if the wallet does not exist. 61 ErrNoWallet = fmt.Errorf("wallet not created, create one to enable " + 62 "full RPC access") 63 64 // ErrWalletLocked is returned if the wallet is locked and any service 65 // other than the WalletUnlocker is called. 66 ErrWalletLocked = fmt.Errorf("wallet locked, unlock it to enable " + 67 "full RPC access") 68 69 // ErrWalletUnlocked is returned if the WalletUnlocker service is 70 // called when the wallet already has been unlocked. 71 ErrWalletUnlocked = fmt.Errorf("wallet already unlocked, " + 72 "WalletUnlocker service is no longer available") 73 74 // ErrRPCStarting is returned if the wallet has been unlocked but the 75 // RPC server is not yet ready to accept calls. 76 ErrRPCStarting = fmt.Errorf("the RPC server is in the process of " + 77 "starting up, but not yet ready to accept calls") 78 79 // macaroonWhitelist defines methods that we don't require macaroons to 80 // access. We also allow these methods to be called even if not all 81 // mandatory middlewares are registered yet. If the wallet is locked 82 // then a middleware cannot register itself, creating an impossible 83 // situation. Also, a middleware might want to check the state of lnd 84 // by calling the State service before it registers itself. So we also 85 // need to exclude those calls from the mandatory middleware check. 86 macaroonWhitelist = map[string]struct{}{ 87 // We allow all calls to the WalletUnlocker without macaroons. 88 "/lnrpc.WalletUnlocker/GenSeed": {}, 89 "/lnrpc.WalletUnlocker/InitWallet": {}, 90 "/lnrpc.WalletUnlocker/UnlockWallet": {}, 91 "/lnrpc.WalletUnlocker/ChangePassword": {}, 92 93 // The State service must be available at all times, even 94 // before we can check macaroons, so we whitelist it. 95 "/lnrpc.State/SubscribeState": {}, 96 "/lnrpc.State/GetState": {}, 97 } 98 ) 99 100 // InterceptorChain is a struct that can be added to the running GRPC server, 101 // intercepting API calls. This is useful for logging, enforcing permissions, 102 // supporting middleware etc. The following diagram shows the order of each 103 // interceptor in the chain and when exactly requests/responses are intercepted 104 // and forwarded to external middleware for approval/modification. Middleware in 105 // general can only intercept gRPC requests/responses that are sent by the 106 // client with a macaroon that contains a custom caveat that is supported by one 107 // of the registered middlewares. 108 // 109 // | 110 // | gRPC request from client 111 // | 112 // +---v--------------------------------+ 113 // | InterceptorChain | 114 // +-+----------------------------------+ 115 // | Log Interceptor | 116 // +----------------------------------+ 117 // | RPC State Interceptor | 118 // +----------------------------------+ 119 // | Macaroon Interceptor | 120 // +----------------------------------+--------> +---------------------+ 121 // | RPC Macaroon Middleware Handler |<-------- | External Middleware | 122 // +----------------------------------+ | - approve request | 123 // | Prometheus Interceptor | +---------------------+ 124 // +-+--------------------------------+ 125 // | validated gRPC request from client 126 // +---v--------------------------------+ 127 // | main gRPC server | 128 // +---+--------------------------------+ 129 // | 130 // | original gRPC request to client 131 // | 132 // +---v--------------------------------+--------> +---------------------+ 133 // | RPC Macaroon Middleware Handler |<-------- | External Middleware | 134 // +---+--------------------------------+ | - modify response | 135 // | +---------------------+ 136 // | edited gRPC request to client 137 // v 138 type InterceptorChain struct { 139 // lastRequestID is the ID of the last gRPC request or stream that was 140 // intercepted by the middleware interceptor. 141 // 142 // NOTE: Must be used atomically! 143 lastRequestID uint64 144 145 // Required by the grpc-gateway/v2 library for forward compatibility. 146 lnrpc.UnimplementedStateServer 147 148 started sync.Once 149 stopped sync.Once 150 151 // state is the current RPC state of our RPC server. 152 state rpcState 153 154 // ntfnServer is a subscription server we use to notify clients of the 155 // State service when the state changes. 156 ntfnServer *subscribe.Server 157 158 // noMacaroons should be set true if we don't want to check macaroons. 159 noMacaroons bool 160 161 // svc is the macaroon service used to enforce permissions in case 162 // macaroons are used. 163 svc *macaroons.Service 164 165 // permissionMap is the permissions to enforce if macaroons are used. 166 permissionMap map[string][]bakery.Op 167 168 // rpcsLog is the logger used to log calles to the RPCs intercepted. 169 rpcsLog slog.Logger 170 171 // registeredMiddleware is a map of all macaroon permission based RPC 172 // middleware clients that are currently registered. The map is keyed 173 // by the middleware's name. 174 registeredMiddleware map[string]*MiddlewareHandler 175 176 // mandatoryMiddleware is a list of all middleware that is considered to 177 // be mandatory. If any of them is not registered then all RPC requests 178 // (except for the macaroon white listed methods and the middleware 179 // registration itself) are blocked. This is a security feature to make 180 // sure that requests can't just go through unobserved/unaudited if a 181 // middleware crashes. 182 mandatoryMiddleware []string 183 184 quit chan struct{} 185 sync.RWMutex 186 } 187 188 // A compile time check to ensure that InterceptorChain fully implements the 189 // StateServer gRPC service. 190 var _ lnrpc.StateServer = (*InterceptorChain)(nil) 191 192 // NewInterceptorChain creates a new InterceptorChain. 193 func NewInterceptorChain(log slog.Logger, noMacaroons bool, 194 mandatoryMiddleware []string) *InterceptorChain { 195 196 return &InterceptorChain{ 197 state: waitingToStart, 198 ntfnServer: subscribe.NewServer(), 199 noMacaroons: noMacaroons, 200 permissionMap: make(map[string][]bakery.Op), 201 rpcsLog: log, 202 registeredMiddleware: make(map[string]*MiddlewareHandler), 203 mandatoryMiddleware: mandatoryMiddleware, 204 quit: make(chan struct{}), 205 } 206 } 207 208 // Start starts the InterceptorChain, which is needed to start the state 209 // subscription server it powers. 210 func (r *InterceptorChain) Start() error { 211 var err error 212 r.started.Do(func() { 213 err = r.ntfnServer.Start() 214 }) 215 216 return err 217 } 218 219 // Stop stops the InterceptorChain and its internal state subscription server. 220 func (r *InterceptorChain) Stop() error { 221 var err error 222 r.stopped.Do(func() { 223 close(r.quit) 224 err = r.ntfnServer.Stop() 225 }) 226 227 return err 228 } 229 230 // SetWalletNotCreated moves the RPC state from either waitingToStart to 231 // walletNotCreated. 232 func (r *InterceptorChain) SetWalletNotCreated() { 233 r.Lock() 234 defer r.Unlock() 235 236 r.state = walletNotCreated 237 _ = r.ntfnServer.SendUpdate(r.state) 238 } 239 240 // SetWalletLocked moves the RPC state from either walletNotCreated to 241 // walletLocked. 242 func (r *InterceptorChain) SetWalletLocked() { 243 r.Lock() 244 defer r.Unlock() 245 246 r.state = walletLocked 247 _ = r.ntfnServer.SendUpdate(r.state) 248 } 249 250 // SetWalletUnlocked moves the RPC state from either walletNotCreated or 251 // walletLocked to walletUnlocked. 252 func (r *InterceptorChain) SetWalletUnlocked() { 253 r.Lock() 254 defer r.Unlock() 255 256 r.state = walletUnlocked 257 _ = r.ntfnServer.SendUpdate(r.state) 258 } 259 260 // SetRPCActive moves the RPC state from walletUnlocked to rpcActive. 261 func (r *InterceptorChain) SetRPCActive() { 262 r.Lock() 263 defer r.Unlock() 264 265 r.state = rpcActive 266 _ = r.ntfnServer.SendUpdate(r.state) 267 } 268 269 // SetServerActive moves the RPC state from walletUnlocked to rpcActive. 270 func (r *InterceptorChain) SetServerActive() { 271 r.Lock() 272 defer r.Unlock() 273 274 r.state = serverActive 275 _ = r.ntfnServer.SendUpdate(r.state) 276 } 277 278 // rpcStateToWalletState converts rpcState to lnrpc.WalletState. Returns 279 // WAITING_TO_START and an error on conversion error. 280 func rpcStateToWalletState(state rpcState) (lnrpc.WalletState, error) { 281 const defaultState = lnrpc.WalletState_WAITING_TO_START 282 var walletState lnrpc.WalletState 283 284 switch state { 285 case waitingToStart: 286 walletState = lnrpc.WalletState_WAITING_TO_START 287 case walletNotCreated: 288 walletState = lnrpc.WalletState_NON_EXISTING 289 case walletLocked: 290 walletState = lnrpc.WalletState_LOCKED 291 case walletUnlocked: 292 walletState = lnrpc.WalletState_UNLOCKED 293 case rpcActive: 294 walletState = lnrpc.WalletState_RPC_ACTIVE 295 case serverActive: 296 walletState = lnrpc.WalletState_SERVER_ACTIVE 297 298 default: 299 return defaultState, fmt.Errorf("unknown wallet state %v", state) 300 } 301 302 return walletState, nil 303 } 304 305 // SubscribeState subscribes to the state of the wallet. The current wallet 306 // state will always be delivered immediately. 307 // 308 // NOTE: Part of the StateService interface. 309 func (r *InterceptorChain) SubscribeState(_ *lnrpc.SubscribeStateRequest, 310 stream lnrpc.State_SubscribeStateServer) error { 311 312 sendStateUpdate := func(state rpcState) error { 313 walletState, err := rpcStateToWalletState(state) 314 if err != nil { 315 return err 316 } 317 318 return stream.Send(&lnrpc.SubscribeStateResponse{ 319 State: walletState, 320 }) 321 } 322 323 // Subscribe to state updates. 324 client, err := r.ntfnServer.Subscribe() 325 if err != nil { 326 return err 327 } 328 defer client.Cancel() 329 330 // Always start by sending the current state. 331 r.RLock() 332 state := r.state 333 r.RUnlock() 334 335 if err := sendStateUpdate(state); err != nil { 336 return err 337 } 338 339 for { 340 select { 341 case e := <-client.Updates(): 342 newState := e.(rpcState) 343 344 // Ignore already sent state. 345 if newState == state { 346 continue 347 } 348 349 state = newState 350 err := sendStateUpdate(state) 351 if err != nil { 352 return err 353 } 354 355 // The response stream's context for whatever reason has been 356 // closed. If context is closed by an exceeded deadline we will 357 // return an error. 358 case <-stream.Context().Done(): 359 if errors.Is(stream.Context().Err(), context.Canceled) { 360 return nil 361 } 362 return stream.Context().Err() 363 364 case <-r.quit: 365 return fmt.Errorf("server exiting") 366 } 367 } 368 } 369 370 // GetState returns the current wallet state. 371 func (r *InterceptorChain) GetState(_ context.Context, 372 _ *lnrpc.GetStateRequest) (*lnrpc.GetStateResponse, error) { 373 374 r.RLock() 375 state := r.state 376 r.RUnlock() 377 378 walletState, err := rpcStateToWalletState(state) 379 if err != nil { 380 return nil, err 381 } 382 383 return &lnrpc.GetStateResponse{ 384 State: walletState, 385 }, nil 386 } 387 388 // AddMacaroonService adds a macaroon service to the interceptor. After this is 389 // done every RPC call made will have to pass a valid macaroon to be accepted. 390 func (r *InterceptorChain) AddMacaroonService(svc *macaroons.Service) { 391 r.Lock() 392 defer r.Unlock() 393 394 r.svc = svc 395 } 396 397 // MacaroonService returns the currently registered macaroon service. This might 398 // be nil if none was registered (yet). 399 func (r *InterceptorChain) MacaroonService() *macaroons.Service { 400 r.RLock() 401 defer r.RUnlock() 402 403 return r.svc 404 } 405 406 // AddPermission adds a new macaroon rule for the given method. 407 func (r *InterceptorChain) AddPermission(method string, ops []bakery.Op) error { 408 r.Lock() 409 defer r.Unlock() 410 411 if _, ok := r.permissionMap[method]; ok { 412 return fmt.Errorf("detected duplicate macaroon constraints "+ 413 "for path: %v", method) 414 } 415 416 r.permissionMap[method] = ops 417 return nil 418 } 419 420 // Permissions returns the current set of macaroon permissions. 421 func (r *InterceptorChain) Permissions() map[string][]bakery.Op { 422 r.RLock() 423 defer r.RUnlock() 424 425 // Make a copy under the read lock to avoid races. 426 c := make(map[string][]bakery.Op) 427 for k, v := range r.permissionMap { 428 s := make([]bakery.Op, len(v)) 429 copy(s, v) 430 c[k] = s 431 } 432 433 return c 434 } 435 436 // RegisterMiddleware registers a new middleware that will handle request/ 437 // response interception for all RPC messages that are initiated with a custom 438 // macaroon caveat. The name of the custom caveat a middleware is handling is 439 // also its unique identifier. Only one middleware can be registered for each 440 // custom caveat. 441 func (r *InterceptorChain) RegisterMiddleware(mw *MiddlewareHandler) error { 442 r.Lock() 443 defer r.Unlock() 444 445 // The name of the middleware is the unique identifier. 446 registered, ok := r.registeredMiddleware[mw.middlewareName] 447 if ok { 448 return fmt.Errorf("a middleware with the name '%s' is already "+ 449 "registered", registered.middlewareName) 450 } 451 452 // For now, we only want one middleware per custom caveat name. If we 453 // allowed multiple middlewares handling the same caveat there would be 454 // a need for extra call chaining logic, and they could overwrite each 455 // other's responses. 456 for name, middleware := range r.registeredMiddleware { 457 if middleware.customCaveatName == mw.customCaveatName { 458 return fmt.Errorf("a middleware is already registered "+ 459 "for the custom caveat name '%s': %v", 460 mw.customCaveatName, name) 461 } 462 } 463 464 r.registeredMiddleware[mw.middlewareName] = mw 465 466 return nil 467 } 468 469 // RemoveMiddleware removes the middleware that handles the given custom caveat 470 // name. 471 func (r *InterceptorChain) RemoveMiddleware(middlewareName string) { 472 r.Lock() 473 defer r.Unlock() 474 475 log.Debugf("Removing middleware %s", middlewareName) 476 477 delete(r.registeredMiddleware, middlewareName) 478 } 479 480 // CustomCaveatSupported makes sure a middleware that handles the given custom 481 // caveat name is registered. If none is, an error is returned, signalling to 482 // the macaroon bakery and its validator to reject macaroons that have a custom 483 // caveat with that name. 484 // 485 // NOTE: This method is part of the macaroons.CustomCaveatAcceptor interface. 486 func (r *InterceptorChain) CustomCaveatSupported(customCaveatName string) error { 487 r.RLock() 488 defer r.RUnlock() 489 490 // We only accept requests with a custom caveat if we also have a 491 // middleware registered that handles that custom caveat. That is 492 // crucial for security! Otherwise a request with an encumbered (=has 493 // restricted permissions based upon the custom caveat condition) 494 // macaroon would not be validated against the limitations that the 495 // custom caveat implicate. Since the map is keyed by the _name_ of the 496 // middleware, we need to loop through all of them to see if one has 497 // the given custom macaroon caveat name. 498 for _, middleware := range r.registeredMiddleware { 499 if middleware.customCaveatName == customCaveatName { 500 return nil 501 } 502 } 503 504 return fmt.Errorf("cannot accept macaroon with custom caveat '%s', "+ 505 "no middleware registered to handle it", customCaveatName) 506 } 507 508 // CreateServerOpts creates the GRPC server options that can be added to a GRPC 509 // server in order to add this InterceptorChain. 510 func (r *InterceptorChain) CreateServerOpts() []grpc.ServerOption { 511 var unaryInterceptors []grpc.UnaryServerInterceptor 512 var strmInterceptors []grpc.StreamServerInterceptor 513 514 // The first interceptors we'll add to the chain is our logging 515 // interceptors, so we can automatically log all errors that happen 516 // during RPC calls. 517 unaryInterceptors = append( 518 unaryInterceptors, errorLogUnaryServerInterceptor(r.rpcsLog), 519 ) 520 strmInterceptors = append( 521 strmInterceptors, errorLogStreamServerInterceptor(r.rpcsLog), 522 ) 523 524 // Next we'll add our RPC state check interceptors, that will check 525 // whether the attempted call is allowed in the current state. 526 unaryInterceptors = append( 527 unaryInterceptors, r.rpcStateUnaryServerInterceptor(), 528 ) 529 strmInterceptors = append( 530 strmInterceptors, r.rpcStateStreamServerInterceptor(), 531 ) 532 533 // We'll add the macaroon interceptors. If macaroons aren't disabled, 534 // then these interceptors will enforce macaroon authentication. 535 unaryInterceptors = append( 536 unaryInterceptors, r.MacaroonUnaryServerInterceptor(), 537 ) 538 strmInterceptors = append( 539 strmInterceptors, r.MacaroonStreamServerInterceptor(), 540 ) 541 542 // Next, we'll add the interceptors for our custom macaroon caveat based 543 // middleware. 544 unaryInterceptors = append( 545 unaryInterceptors, r.middlewareUnaryServerInterceptor(), 546 ) 547 strmInterceptors = append( 548 strmInterceptors, r.middlewareStreamServerInterceptor(), 549 ) 550 551 // Get interceptors for Prometheus to gather gRPC performance metrics. 552 // If monitoring is disabled, GetPromInterceptors() will return empty 553 // slices. 554 promUnaryInterceptors, promStrmInterceptors := 555 monitoring.GetPromInterceptors() 556 557 // Concatenate the slices of unary and stream interceptors respectively. 558 unaryInterceptors = append(unaryInterceptors, promUnaryInterceptors...) 559 strmInterceptors = append(strmInterceptors, promStrmInterceptors...) 560 561 // Create server options from the interceptors we just set up. 562 chainedUnary := grpc_middleware.WithUnaryServerChain( 563 unaryInterceptors..., 564 ) 565 chainedStream := grpc_middleware.WithStreamServerChain( 566 strmInterceptors..., 567 ) 568 serverOpts := []grpc.ServerOption{chainedUnary, chainedStream} 569 570 return serverOpts 571 } 572 573 // errorLogUnaryServerInterceptor is a simple UnaryServerInterceptor that will 574 // automatically log any errors that occur when serving a client's unary 575 // request. 576 func errorLogUnaryServerInterceptor(logger slog.Logger) grpc.UnaryServerInterceptor { 577 return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, 578 handler grpc.UnaryHandler) (interface{}, error) { 579 580 resp, err := handler(ctx, req) 581 if err != nil { 582 // TODO(roasbeef): also log request details? 583 logger.Errorf("[%v]: %v", info.FullMethod, err) 584 } 585 586 return resp, err 587 } 588 } 589 590 // errorLogStreamServerInterceptor is a simple StreamServerInterceptor that 591 // will log any errors that occur while processing a client or server streaming 592 // RPC. 593 func errorLogStreamServerInterceptor(logger slog.Logger) grpc.StreamServerInterceptor { 594 return func(srv interface{}, ss grpc.ServerStream, 595 info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { 596 597 err := handler(srv, ss) 598 if err != nil { 599 logger.Errorf("[%v]: %v", info.FullMethod, err) 600 } 601 602 return err 603 } 604 } 605 606 // checkMacaroon validates that the context contains the macaroon needed to 607 // invoke the given RPC method. 608 func (r *InterceptorChain) checkMacaroon(ctx context.Context, 609 fullMethod string) error { 610 611 // If noMacaroons is set, we'll always allow the call. 612 if r.noMacaroons { 613 return nil 614 } 615 616 // Check whether the method is whitelisted, if so we'll allow it 617 // regardless of macaroons. 618 _, ok := macaroonWhitelist[fullMethod] 619 if ok { 620 return nil 621 } 622 623 r.RLock() 624 svc := r.svc 625 r.RUnlock() 626 627 // If the macaroon service is not yet active, we cannot allow 628 // the call. 629 if svc == nil { 630 return fmt.Errorf("unable to determine macaroon permissions") 631 } 632 633 r.RLock() 634 uriPermissions, ok := r.permissionMap[fullMethod] 635 r.RUnlock() 636 if !ok { 637 return fmt.Errorf("%s: unknown permissions required for method", 638 fullMethod) 639 } 640 641 // Find out if there is an external validator registered for 642 // this method. Fall back to the internal one if there isn't. 643 validator, ok := svc.ExternalValidators[fullMethod] 644 if !ok { 645 validator = svc 646 } 647 648 // Now that we know what validator to use, let it do its work. 649 return validator.ValidateMacaroon(ctx, uriPermissions, fullMethod) 650 } 651 652 // MacaroonUnaryServerInterceptor is a GRPC interceptor that checks whether the 653 // request is authorized by the included macaroons. 654 func (r *InterceptorChain) MacaroonUnaryServerInterceptor() grpc.UnaryServerInterceptor { 655 return func(ctx context.Context, req interface{}, 656 info *grpc.UnaryServerInfo, 657 handler grpc.UnaryHandler) (interface{}, error) { 658 659 // Check macaroons. 660 if err := r.checkMacaroon(ctx, info.FullMethod); err != nil { 661 return nil, err 662 } 663 664 return handler(ctx, req) 665 } 666 } 667 668 // MacaroonStreamServerInterceptor is a GRPC interceptor that checks whether 669 // the request is authorized by the included macaroons. 670 func (r *InterceptorChain) MacaroonStreamServerInterceptor() grpc.StreamServerInterceptor { 671 return func(srv interface{}, ss grpc.ServerStream, 672 info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { 673 674 // Check macaroons. 675 err := r.checkMacaroon(ss.Context(), info.FullMethod) 676 if err != nil { 677 return err 678 } 679 680 return handler(srv, ss) 681 } 682 } 683 684 // checkRPCState checks whether a call to the given server is allowed in the 685 // current RPC state. 686 func (r *InterceptorChain) checkRPCState(srv interface{}) error { 687 // The StateService is being accessed, we allow the call regardless of 688 // the current state. 689 _, ok := srv.(lnrpc.StateServer) 690 if ok { 691 return nil 692 } 693 694 r.RLock() 695 state := r.state 696 r.RUnlock() 697 698 switch state { 699 700 // Do not accept any RPC calls (unless to the state service) until LND 701 // has not started. 702 case waitingToStart: 703 return ErrWaitingToStart 704 705 // If the wallet does not exists, only calls to the WalletUnlocker are 706 // accepted. 707 case walletNotCreated: 708 _, ok := srv.(lnrpc.WalletUnlockerServer) 709 if !ok { 710 return ErrNoWallet 711 } 712 713 // If the wallet is locked, only calls to the WalletUnlocker are 714 // accepted. 715 case walletLocked: 716 _, ok := srv.(lnrpc.WalletUnlockerServer) 717 if !ok { 718 return ErrWalletLocked 719 } 720 721 // If the wallet is unlocked, but the RPC not yet active, we reject. 722 case walletUnlocked: 723 _, ok := srv.(lnrpc.WalletUnlockerServer) 724 if ok { 725 return ErrWalletUnlocked 726 } 727 728 _, isSyncerSvr := srv.(initchainsyncrpc.InitialChainSyncServer) 729 if isSyncerSvr { 730 return nil 731 } 732 733 return ErrRPCStarting 734 735 // If the RPC server or lnd server is active, we allow calls to any 736 // service except the WalletUnlocker. 737 case rpcActive, serverActive: 738 _, ok := srv.(lnrpc.WalletUnlockerServer) 739 if ok { 740 return ErrWalletUnlocked 741 } 742 743 default: 744 return fmt.Errorf("unknown RPC state: %v", state) 745 } 746 747 return nil 748 } 749 750 // rpcStateUnaryServerInterceptor is a GRPC interceptor that checks whether 751 // calls to the given gGRPC server is allowed in the current rpc state. 752 func (r *InterceptorChain) rpcStateUnaryServerInterceptor() grpc.UnaryServerInterceptor { 753 return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, 754 handler grpc.UnaryHandler) (interface{}, error) { 755 756 if err := r.checkRPCState(info.Server); err != nil { 757 return nil, err 758 } 759 760 return handler(ctx, req) 761 } 762 } 763 764 // rpcStateStreamServerInterceptor is a GRPC interceptor that checks whether 765 // calls to the given gGRPC server is allowed in the current rpc state. 766 func (r *InterceptorChain) rpcStateStreamServerInterceptor() grpc.StreamServerInterceptor { 767 return func(srv interface{}, ss grpc.ServerStream, 768 info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { 769 770 if err := r.checkRPCState(srv); err != nil { 771 return err 772 } 773 774 return handler(srv, ss) 775 } 776 } 777 778 // middlewareUnaryServerInterceptor is a unary gRPC interceptor that intercepts 779 // all requests and responses that are sent with a macaroon containing a custom 780 // caveat condition that is handled by registered middleware. 781 func (r *InterceptorChain) middlewareUnaryServerInterceptor() grpc.UnaryServerInterceptor { 782 return func(ctx context.Context, 783 req interface{}, info *grpc.UnaryServerInfo, 784 handler grpc.UnaryHandler) (interface{}, error) { 785 786 // Make sure we don't allow any requests through if one of the 787 // mandatory middlewares is missing. 788 fullMethod := info.FullMethod 789 if err := r.checkMandatoryMiddleware(fullMethod); err != nil { 790 return nil, err 791 } 792 793 // If there is no middleware registered, we don't need to 794 // intercept anything. 795 if !r.middlewareRegistered() { 796 return handler(ctx, req) 797 } 798 799 msg, err := NewMessageInterceptionRequest( 800 ctx, TypeRequest, false, info.FullMethod, req, 801 ) 802 if err != nil { 803 return nil, err 804 } 805 806 requestID := atomic.AddUint64(&r.lastRequestID, 1) 807 err = r.acceptRequest(requestID, msg) 808 if err != nil { 809 return nil, err 810 } 811 812 resp, respErr := handler(ctx, req) 813 if respErr != nil { 814 return resp, respErr 815 } 816 817 return r.interceptResponse( 818 ctx, requestID, false, info.FullMethod, resp, 819 ) 820 } 821 } 822 823 // middlewareStreamServerInterceptor is a streaming gRPC interceptor that 824 // intercepts all requests and responses that are sent with a macaroon 825 // containing a custom caveat condition that is handled by registered 826 // middleware. 827 func (r *InterceptorChain) middlewareStreamServerInterceptor() grpc.StreamServerInterceptor { 828 return func(srv interface{}, 829 ss grpc.ServerStream, info *grpc.StreamServerInfo, 830 handler grpc.StreamHandler) error { 831 832 // Don't intercept the interceptor itself which is a streaming 833 // RPC too! 834 fullMethod := info.FullMethod 835 if fullMethod == lnrpc.RegisterRPCMiddlewareURI { 836 return handler(srv, ss) 837 } 838 839 // Make sure we don't allow any requests through if one of the 840 // mandatory middlewares is missing. We add this check here to 841 // make sure the middleware registration itself can still be 842 // called. 843 if err := r.checkMandatoryMiddleware(fullMethod); err != nil { 844 return err 845 } 846 847 // If there is no middleware registered, we don't need to 848 // intercept anything. 849 if !r.middlewareRegistered() { 850 return handler(srv, ss) 851 } 852 853 // To give the middleware a chance to accept or reject the 854 // establishment of the stream itself (and not only when the 855 // first message is sent on the stream), we send an intercept 856 // request for the stream auth now: 857 msg, err := NewStreamAuthInterceptionRequest( 858 ss.Context(), info.FullMethod, 859 ) 860 if err != nil { 861 return err 862 } 863 864 requestID := atomic.AddUint64(&r.lastRequestID, 1) 865 err = r.acceptRequest(requestID, msg) 866 if err != nil { 867 return err 868 } 869 870 wrappedSS := &serverStreamWrapper{ 871 ServerStream: ss, 872 requestID: requestID, 873 fullMethod: info.FullMethod, 874 interceptor: r, 875 } 876 877 return handler(srv, wrappedSS) 878 } 879 } 880 881 // checkMandatoryMiddleware makes sure that each of the middlewares declared as 882 // mandatory is currently registered. 883 func (r *InterceptorChain) checkMandatoryMiddleware(fullMethod string) error { 884 r.RLock() 885 defer r.RUnlock() 886 887 // Allow calls that are whitelisted for macaroons as well, otherwise we 888 // get into impossible situations where the wallet is locked but the 889 // unlock call is denied because the middleware isn't registered. But 890 // the middleware cannot register itself because the wallet is locked. 891 if _, ok := macaroonWhitelist[fullMethod]; ok { 892 return nil 893 } 894 895 // Not a white listed call so make sure every mandatory middleware is 896 // currently connected to lnd. 897 for _, name := range r.mandatoryMiddleware { 898 if _, ok := r.registeredMiddleware[name]; !ok { 899 return fmt.Errorf("mandatory middleware '%s' is "+ 900 "currently not registered, not allowing any "+ 901 "RPC calls", name) 902 } 903 } 904 905 return nil 906 } 907 908 // middlewareRegistered returns true if there is at least one middleware 909 // currently registered. 910 func (r *InterceptorChain) middlewareRegistered() bool { 911 r.RLock() 912 defer r.RUnlock() 913 914 return len(r.registeredMiddleware) > 0 915 } 916 917 // acceptRequest sends an intercept request to all middlewares that have 918 // registered for it. This means either a middleware has requested read-only 919 // access or the request actually has a macaroon with a caveat the middleware 920 // registered for. 921 func (r *InterceptorChain) acceptRequest(requestID uint64, 922 msg *InterceptionRequest) error { 923 924 r.RLock() 925 defer r.RUnlock() 926 927 for _, middleware := range r.registeredMiddleware { 928 // If there is a custom caveat in the macaroon, make sure the 929 // middleware registered for it. Or if a middleware registered 930 // for read-only mode, it also gets the request. 931 hasCustomCaveat := macaroons.HasCustomCaveat( 932 msg.Macaroon, middleware.customCaveatName, 933 ) 934 if !hasCustomCaveat && !middleware.readOnly { 935 continue 936 } 937 938 msg.CustomCaveatCondition = macaroons.GetCustomCaveatCondition( 939 msg.Macaroon, middleware.customCaveatName, 940 ) 941 942 resp, err := middleware.intercept(requestID, msg) 943 944 // Error during interception itself. 945 if err != nil { 946 return err 947 } 948 949 // Error returned from middleware client. 950 if resp.err != nil { 951 return resp.err 952 } 953 } 954 955 return nil 956 } 957 958 // interceptResponse sends out an intercept request for an RPC response. Since 959 // middleware that hasn't registered for the read-only mode has the option to 960 // overwrite/replace the response, this needs to be handled differently than the 961 // request/auth path above. 962 func (r *InterceptorChain) interceptResponse(ctx context.Context, 963 requestID uint64, isStream bool, fullMethod string, 964 m interface{}) (interface{}, error) { 965 966 r.RLock() 967 defer r.RUnlock() 968 969 currentMessage := m 970 for _, middleware := range r.registeredMiddleware { 971 msg, err := NewMessageInterceptionRequest( 972 ctx, TypeResponse, isStream, fullMethod, currentMessage, 973 ) 974 if err != nil { 975 return nil, err 976 } 977 978 // If there is a custom caveat in the macaroon, make sure the 979 // middleware registered for it. Or if a middleware registered 980 // for read-only mode, it also gets the request. 981 hasCustomCaveat := macaroons.HasCustomCaveat( 982 msg.Macaroon, middleware.customCaveatName, 983 ) 984 if !hasCustomCaveat && !middleware.readOnly { 985 continue 986 } 987 988 msg.CustomCaveatCondition = macaroons.GetCustomCaveatCondition( 989 msg.Macaroon, middleware.customCaveatName, 990 ) 991 992 resp, err := middleware.intercept(requestID, msg) 993 994 // Error during interception itself. 995 if err != nil { 996 return nil, err 997 } 998 999 // Error returned from middleware client. 1000 if resp.err != nil { 1001 return nil, resp.err 1002 } 1003 1004 // The message was replaced, make sure the next middleware in 1005 // line receives the updated message. 1006 if !middleware.readOnly && resp.replace { 1007 currentMessage = resp.replacement 1008 } 1009 } 1010 1011 return currentMessage, nil 1012 } 1013 1014 // serverStreamWrapper is a struct that wraps a server stream in a way that all 1015 // requests and responses can be intercepted individually. 1016 type serverStreamWrapper struct { 1017 // ServerStream is the stream that's being wrapped. 1018 grpc.ServerStream 1019 1020 requestID uint64 1021 1022 fullMethod string 1023 1024 interceptor *InterceptorChain 1025 } 1026 1027 // SendMsg is called when lnd sends a message to the client. This is wrapped to 1028 // intercept streaming RPC responses. 1029 func (w *serverStreamWrapper) SendMsg(m interface{}) error { 1030 newMsg, err := w.interceptor.interceptResponse( 1031 w.ServerStream.Context(), w.requestID, true, w.fullMethod, m, 1032 ) 1033 if err != nil { 1034 return err 1035 } 1036 1037 return w.ServerStream.SendMsg(newMsg) 1038 } 1039 1040 // RecvMsg is called when lnd wants to receive a message from the client. This 1041 // is wrapped to intercept streaming RPC requests. 1042 func (w *serverStreamWrapper) RecvMsg(m interface{}) error { 1043 err := w.ServerStream.RecvMsg(m) 1044 if err != nil { 1045 return err 1046 } 1047 1048 msg, err := NewMessageInterceptionRequest( 1049 w.ServerStream.Context(), TypeRequest, true, w.fullMethod, 1050 m, 1051 ) 1052 if err != nil { 1053 return err 1054 } 1055 1056 return w.interceptor.acceptRequest(w.requestID, msg) 1057 }