multi: add deadline for first connection of new LNC conn

In this commit, we add a deadline for the initial connection of an LNC
connection. So with this, the user is forced to use their pairing phrase
within a certain time frame. After this initial connection, future
connections are made with the second handshake version meaning that the
pairing phrase is rendered useless. By adding a time limit to the time
in which a user can use their pairing phrase, we reduce the risk created
by the users pairing phrase being leaked. The default time limit is set
to 10 minutes but can be customsed with the new `firstlncconndeadline`
flag.
This commit is contained in:
Elle Mouton 2022-08-18 18:10:44 +02:00
parent d23073f8b0
commit d5c26045f7
No known key found for this signature in database
GPG key ID: D7D916376026F177
6 changed files with 99 additions and 34 deletions

View file

@ -77,6 +77,8 @@ const (
// DefaultMacaroonFilename is the default file name for the
// autogenerated lit macaroon.
DefaultMacaroonFilename = "lit.macaroon"
defaultFirstLNCConnTimeout = 10 * time.Minute
)
var (
@ -153,6 +155,8 @@ type Config struct {
MacaroonPath string `long:"macaroonpath" description:"Path to write the macaroon for litd's RPC and REST services if it doesn't exist."`
FirstLNCConnDeadline time.Duration `long:"firstlncconndeadline" description:"The duration after a new LNC session will be revoked if no connection is made with it. This only applies for the first connection which is made using the pairing phrase. "`
// Network is the Bitcoin network we're running on. This will be parsed
// before the configuration is loaded and will set the correct flag on
// `lnd.bitcoin.mainnet|testnet|regtest` and also for the other daemons.
@ -302,21 +306,22 @@ func defaultConfig() *Config {
TLSCertPath: poolDefaultConfig.TLSCertPath,
},
},
Network: DefaultNetwork,
LndMode: DefaultLndMode,
Lnd: &lndDefaultConfig,
LitDir: DefaultLitDir,
LetsEncryptListen: defaultLetsEncryptListen,
LetsEncryptDir: defaultLetsEncryptDir,
MacaroonPath: DefaultMacaroonPath,
ConfigFile: defaultConfigFile,
FaradayMode: defaultFaradayMode,
Faraday: &faradayDefaultConfig,
faradayRpcConfig: &frdrpcserver.Config{},
LoopMode: defaultLoopMode,
Loop: &loopDefaultConfig,
PoolMode: defaultPoolMode,
Pool: &poolDefaultConfig,
Network: DefaultNetwork,
LndMode: DefaultLndMode,
Lnd: &lndDefaultConfig,
LitDir: DefaultLitDir,
LetsEncryptListen: defaultLetsEncryptListen,
LetsEncryptDir: defaultLetsEncryptDir,
MacaroonPath: DefaultMacaroonPath,
ConfigFile: defaultConfigFile,
FaradayMode: defaultFaradayMode,
Faraday: &faradayDefaultConfig,
faradayRpcConfig: &frdrpcserver.Config{},
LoopMode: defaultLoopMode,
Loop: &loopDefaultConfig,
PoolMode: defaultPoolMode,
Pool: &poolDefaultConfig,
FirstLNCConnDeadline: defaultFirstLNCConnTimeout,
}
}

2
go.mod
View file

@ -11,7 +11,7 @@ require (
github.com/improbable-eng/grpc-web v0.12.0
github.com/jessevdk/go-flags v1.4.0
github.com/lightninglabs/faraday v0.2.8-alpha.0.20220624141723-ddd3cd123e62
github.com/lightninglabs/lightning-node-connect v0.1.11-alpha
github.com/lightninglabs/lightning-node-connect v0.1.11-alpha.0.20220822151854-c072f70315d8
github.com/lightninglabs/lndclient v0.15.0-10
github.com/lightninglabs/loop v0.19.1-beta.0.20220623090540-08209f61e304
github.com/lightninglabs/loop/swapserverrpc v1.0.1

4
go.sum
View file

@ -541,8 +541,8 @@ github.com/lightninglabs/faraday v0.2.8-alpha.0.20220624141723-ddd3cd123e62 h1:t
github.com/lightninglabs/faraday v0.2.8-alpha.0.20220624141723-ddd3cd123e62/go.mod h1:9kcDuyINNf4RB6vrmPLAMGZmYgN0oPxhdt3IicL9sQY=
github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf h1:HZKvJUHlcXI/f/O0Avg7t8sqkPo78HFzjmeYFl6DPnc=
github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk=
github.com/lightninglabs/lightning-node-connect v0.1.11-alpha h1:d+jKyAvCQMLq5O1IL6ONWM/l4C7Q3Q00HkdjDIG9uTg=
github.com/lightninglabs/lightning-node-connect v0.1.11-alpha/go.mod h1:dgyhE+O4GpWBhS7yIzKCm8LqFHX8/QRwJ6OWFrN+WnA=
github.com/lightninglabs/lightning-node-connect v0.1.11-alpha.0.20220822151854-c072f70315d8 h1:FYZ63sERRC2RjVbsAeO55uF7kTIYCClHmd59dZSsyx0=
github.com/lightninglabs/lightning-node-connect v0.1.11-alpha.0.20220822151854-c072f70315d8/go.mod h1:dgyhE+O4GpWBhS7yIzKCm8LqFHX8/QRwJ6OWFrN+WnA=
github.com/lightninglabs/lightning-node-connect/hashmailrpc v1.0.2 h1:Er1miPZD2XZwcfE4xoS5AILqP1mj7kqnhbBSxW9BDxY=
github.com/lightninglabs/lightning-node-connect/hashmailrpc v1.0.2/go.mod h1:antQGRDRJiuyQF6l+k6NECCSImgCpwaZapATth2Chv4=
github.com/lightninglabs/lndclient v0.15.0-0/go.mod h1:ORS/YFe9hAXlzN/Uj+gvTmrnXEml6yD6dWwzCjpTJyQ=

View file

@ -33,7 +33,8 @@ func newMailboxSession() *mailboxSession {
func (m *mailboxSession) start(session *Session,
serverCreator GRPCServerCreator, authData []byte,
onUpdate func(sess *Session) error) error {
onUpdate func(sess *Session) error,
onNewStatus func(s mailbox.ServerStatus)) error {
tlsConfig := &tls.Config{}
if session.DevServer {
@ -52,7 +53,7 @@ func (m *mailboxSession) start(session *Session,
// Start the mailbox gRPC server.
mailboxServer, err := mailbox.NewServer(
session.ServerAddr, keys,
session.ServerAddr, keys, onNewStatus,
grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)),
grpc.WithKeepaliveParams(keepalive.ClientParameters{
Time: 2 * time.Minute,
@ -104,7 +105,8 @@ func NewServer(serverCreator GRPCServerCreator) *Server {
}
func (s *Server) StartSession(session *Session, authData []byte,
onUpdate func(sess *Session) error) (chan struct{}, error) {
onUpdate func(sess *Session) error,
onNewStatus func(s mailbox.ServerStatus)) (chan struct{}, error) {
s.activeSessionsMtx.Lock()
defer s.activeSessionsMtx.Unlock()
@ -121,7 +123,7 @@ func (s *Server) StartSession(session *Session, authData []byte,
s.activeSessions[id] = sess
return sess.quit, sess.start(
session, s.serverCreator, authData, onUpdate,
session, s.serverCreator, authData, onUpdate, onNewStatus,
)
}

View file

@ -38,6 +38,7 @@ type sessionRpcServerConfig struct {
registerGrpcServers func(server *grpc.Server)
superMacBaker func(ctx context.Context, rootKeyID uint64,
recipe *session.MacaroonRecipe) (string, error)
firstConnectionDeadline time.Duration
}
// newSessionRPCServer creates a new sessionRpcServer using the passed config.
@ -214,10 +215,57 @@ func (s *sessionRpcServer) resumeSession(sess *session.Session) error {
return nil
}
authData := []byte(fmt.Sprintf("%s: %s", HeaderMacaroon, mac))
var (
onNewStatus func(s mailbox.ServerStatus)
firstConnTimout = make(chan struct{})
)
// If this is the first time the session is being spun up then we will
// kick off a timer to revoke the session after a timeout unless an
// initial connection is made. We identify such a session as one that
// we do not yet have a static remote pub key for.
if sess.RemotePublicKey == nil {
deadline := sess.CreatedAt.Add(s.cfg.firstConnectionDeadline)
if deadline.Before(time.Now()) {
log.Debugf("Deadline for session %x has already "+
"passed. Revoking session", pubKeyBytes)
return s.db.RevokeSession(pubKey)
}
// Start the deadline timer.
deadlineDuration := time.Until(deadline)
deadlineTimer := time.AfterFunc(deadlineDuration, func() {
close(firstConnTimout)
})
log.Warnf("Kicking off deadline timer for first connection "+
"for session %x. A successful connection must be "+
"made in the next %s", pubKeyBytes, deadlineDuration)
var stopTimerOnce sync.Once
onNewStatus = func(s mailbox.ServerStatus) {
// We will only stop the timer if the server status
// indicates that the client has successfully connected.
if s != mailbox.ServerStatusInUse {
return
}
// Stop the deadline timer.
stopTimerOnce.Do(func() {
log.Debugf("First connection for session %x "+
"made in a timely manner",
sess.LocalPublicKey.
SerializeCompressed())
deadlineTimer.Stop()
})
}
}
authData := []byte(fmt.Sprintf("%s: %s", HeaderMacaroon, mac))
sessionClosedSub, err := s.sessionServer.StartSession(
sess, authData, s.db.StoreSession,
sess, authData, s.db.StoreSession, onNewStatus,
)
if err != nil {
return err
@ -232,22 +280,31 @@ func (s *sessionRpcServer) resumeSession(sess *session.Session) error {
select {
case <-s.quit:
return
case <-sessionClosedSub:
return
case <-ticker.C:
log.Debugf("Stopping expired session %x with "+
"type %d", pubKeyBytes, sess.Type)
err = s.sessionServer.StopSession(pubKey)
if err != nil {
log.Debugf("Error stopping session: "+
"%v", err)
}
case <-firstConnTimout:
log.Debugf("Deadline exceeded for first connection "+
"for session %x. Stopping and revoking.",
pubKeyBytes)
}
err = s.db.RevokeSession(pubKey)
if err != nil {
log.Debugf("error revoking session: "+
"%v", err)
}
err = s.sessionServer.StopSession(pubKey)
if err != nil {
log.Debugf("Error stopping session: "+
"%v", err)
}
err = s.db.RevokeSession(pubKey)
if err != nil {
log.Debugf("error revoking session: "+
"%v", err)
}
}()

View file

@ -232,6 +232,7 @@ func (g *LightningTerminal) Run() error {
recipe.Permissions, recipe.Caveats,
)
},
firstConnectionDeadline: g.cfg.FirstLNCConnDeadline,
})
if err != nil {
return fmt.Errorf("could not create new session rpc "+