From e1845fd84228750d9136a9719c536b9f28f561c4 Mon Sep 17 00:00:00 2001 From: Varunram Date: Wed, 21 Nov 2018 16:15:15 +0530 Subject: [PATCH 1/3] allow for whitelisting of single address from the lit cli if you're the first person connecting to a node using --con, there is nobody who could have authenticated you, except via a local lit-af instance. This prevents two lit-af instances running on the same machine, as well as causing some sort of problem for people running multiple instances of lit on a single mahine --- cmd/lit-af/lit-af.go | 7 ++++++- lit.go | 35 +++++++++++++++++++++++++++++------ litrpc/netcmds.go | 5 ++++- lndc/conn.go | 2 ++ lnp2p/peermgr.go | 8 ++++---- qln/netio.go | 2 +- qln/remotecontrol.go | 9 ++++++++- 7 files changed, 54 insertions(+), 14 deletions(-) diff --git a/cmd/lit-af/lit-af.go b/cmd/lit-af/lit-af.go index f79acb555..5beb4de00 100644 --- a/cmd/lit-af/lit-af.go +++ b/cmd/lit-af/lit-af.go @@ -5,6 +5,7 @@ import ( "os" "path/filepath" "strings" + "encoding/hex" "github.com/chzyer/readline" "github.com/fatih/color" @@ -108,7 +109,11 @@ func (lc *litAfClient) litAfSetup(conf litAfConfig) { logging.Fatal(err.Error()) } key, _ := koblitz.PrivKeyFromBytes(koblitz.S256(), privKey[:]) - + pubkey := key.PubKey().SerializeCompressed() // this is in bytes + fmt.Println("The pubkey of this lit-af instance is:", hex.EncodeToString(pubkey)) + var temp [33]byte + copy(temp[:], pubkey[33:]) + fmt.Println("The pkh of this lit-af instance is:", lnutil.LitAdrFromPubkey(temp)) if adr != "" && strings.HasPrefix(adr, "ln1") && host == "" { ipv4, _, err := lnutil.Lookup(adr, conf.Tracker, "") if err != nil { diff --git a/lit.go b/lit.go index cc79ebecf..6dd0d475c 100644 --- a/lit.go +++ b/lit.go @@ -6,6 +6,7 @@ import ( "runtime" "syscall" "time" + "encoding/hex" "github.com/mit-dci/lit/logging" @@ -55,11 +56,12 @@ type litConfig struct { // define a struct for usage with go-flags Rpcport uint16 `short:"p" long:"rpcport" description:"Set RPC port to connect to"` Rpchost string `long:"rpchost" description:"Set RPC host to listen to"` // auto config - AutoReconnect bool `long:"autoReconnect" description:"Attempts to automatically reconnect to known peers periodically."` - AutoReconnectInterval int64 `long:"autoReconnectInterval" description:"The interval (in seconds) the reconnect logic should be executed"` - AutoReconnectOnlyConnectedCoins bool `long:"autoReconnectOnlyConnectedCoins" description:"Only reconnect to peers that we have channels with in a coin whose coin daemon is available"` - AutoListenPort int `long:"autoListenPort" description:"When auto reconnect enabled, starts listening on this port"` - NoAutoListen bool `long:"noautolisten" description:"Don't automatically listen on any ports."` + AutoReconnect bool `long:"autoReconnect" description:"Attempts to automatically reconnect to known peers periodically."` + AutoReconnectInterval int64 `long:"autoReconnectInterval" description:"The interval (in seconds) the reconnect logic should be executed"` + AutoReconnectOnlyConnectedCoins bool `long:"autoReconnectOnlyConnectedCoins" description:"Only reconnect to peers that we have channels with in a coin whose coin daemon is available"` + AutoListenPort int `long:"autoListenPort" description:"When auto reconnect enabled, starts listening on this port"` + NoAutoListen bool `long:"noautolisten" description:"Don't automatically listen on any ports."` + Whitelist string `long:"whitelist" description:"Whitelist a single address so that you can enable a remote peer to login for the first time and authenticate others"` Params *coinparam.Params } @@ -293,12 +295,33 @@ func main() { // if we don't link wallet, we can still continue, no worries. logging.Error(err) } - + logging.Info("Starting lit node") rpcl := new(litrpc.LitRPC) rpcl.Node = node rpcl.OffButton = make(chan bool, 1) node.RPC = rpcl + Authorization := new(qln.RemoteControlAuthorization) + Authorization.UnansweredRequest = false + Authorization.Allowed = true + + // check for whitelisted addresses here + if conf.Whitelist != "" && len(conf.Whitelist) == 66 { // the length of a standard LNAddr + // pass the pubkey here, which is ugly + // we could pass the pkh, have the peer dial us and we could get + // the pubkey during the second round of noise, but maybe overkill? + // we need to decode this hex string into a byte slice + addr, _ := hex.DecodeString(conf.Whitelist) + var temp [33]byte + copy(temp[:33], addr) + logging.Info("Whitelisting address as requested: ",addr) + err = rpcl.Node.SaveRemoteControlAuthorization(temp, Authorization) + if err != nil { + logging.Errorf("Error whitelisting address: %s", err.Error()) + // don't fatal since this doesn't affect program flow + } + } + if conf.UnauthRPC { go litrpc.RPCListen(rpcl, conf.Rpchost, conf.Rpcport) } diff --git a/litrpc/netcmds.go b/litrpc/netcmds.go index 6e760dba4..17694ec12 100644 --- a/litrpc/netcmds.go +++ b/litrpc/netcmds.go @@ -66,6 +66,9 @@ func (r *LitRPC) Connect(args ConnectArgs, reply *ConnectReply) error { // first, see if the peer to connect to is referenced by peer index. var connectAdr string // check if a peer number was supplied instead of a pubkeyhash + // accept either an string or a pubkey (raw) + // so args.LNAddr passed here contains blah@host:ip + fmt.Println("PASSED STUFF:", args.LNAddr) peerIdxint, err := strconv.Atoi(args.LNAddr) // number will mean no error if err == nil { @@ -83,7 +86,7 @@ func (r *LitRPC) Connect(args ConnectArgs, reply *ConnectReply) error { connectAdr = args.LNAddr } - err = r.Node.DialPeer(connectAdr) + err = r.Node.PeerMan.TryConnectAddress(connectAdr, nil) if err != nil { return err } diff --git a/lndc/conn.go b/lndc/conn.go index 3e0ccd514..2bb94c8e7 100644 --- a/lndc/conn.go +++ b/lndc/conn.go @@ -81,7 +81,9 @@ func Dial(localPriv *koblitz.PrivateKey, ipAddr string, remotePKH string, } logging.Info("Received pubkey", s) + logging.Debug("Received pubkey: ", lnutil.LitAdrFromPubkey(s), "have pubkey", remotePKH) if lnutil.LitAdrFromPubkey(s) != remotePKH { + fmt.Println("Received pubkey: ", lnutil.LitAdrFromPubkey(s), "have pubkey", remotePKH) return nil, fmt.Errorf("Remote PKH doesn't match. Quitting!") } logging.Infof("Received PKH %s matches", lnutil.LitAdrFromPubkey(s)) diff --git a/lnp2p/peermgr.go b/lnp2p/peermgr.go index b9c54774f..dc80d8bca 100644 --- a/lnp2p/peermgr.go +++ b/lnp2p/peermgr.go @@ -144,21 +144,21 @@ func (pm *PeerManager) GetPeerByIdx(id int32) *Peer { } // TryConnectAddress attempts to connect to the specified LN address. -func (pm *PeerManager) TryConnectAddress(addr string, settings *NetSettings) (*Peer, error) { +func (pm *PeerManager) TryConnectAddress(addr string, settings *NetSettings) (error) { // Figure out who we're trying to connect to. who, where := splitAdrString(addr) if where == "" { ipv4, _, err := lnutil.Lookup(addr, pm.trackerURL, "") if err != nil { - return nil, err + return err } where = fmt.Sprintf("%s:2448", ipv4) } lnwho := lncore.LnAddr(who) - x, y := pm.tryConnectPeer(where, &lnwho, settings) - return x, y + _, y := pm.tryConnectPeer(where, &lnwho, settings) + return y } diff --git a/qln/netio.go b/qln/netio.go index 36b89c662..da24518a0 100644 --- a/qln/netio.go +++ b/qln/netio.go @@ -80,7 +80,7 @@ func splitAdrString(adr string) (string, string) { // TODO Remove this. func (nd *LitNode) DialPeer(connectAdr string) error { - _, err := nd.PeerMan.TryConnectAddress(connectAdr, nil) + err := nd.PeerMan.TryConnectAddress(connectAdr, nil) if err != nil { return err } diff --git a/qln/remotecontrol.go b/qln/remotecontrol.go index fa9bfffb2..b754bda9a 100644 --- a/qln/remotecontrol.go +++ b/qln/remotecontrol.go @@ -84,6 +84,11 @@ func (nd *LitNode) RemoteControlRequestHandler(msg lnutil.RemoteControlRpcReques // If i'm not authorized, and it's not a whitelisted method then we fail the // request with an 'unauthorized' error + // while this is good, if you're the first person to connect to lit + // using the --con option, you're out of luck. + // Because nobody has authenticated you and neither can you authenticate yourself. + // In order to solve this, we need to pass a cli option to lit which can allow for + // authentication before the first peer logs in. if !auth.Allowed && !whitelisted { err = fmt.Errorf("Received remote control request from unauthorized peer: %x", pubKey) logging.Errorf(err.Error()) @@ -259,7 +264,7 @@ func (nd *LitNode) SaveRemoteControlAuthorization(pub [33]byte, auth *RemoteCont }) } -// GetRemoteControlAuthorization retrieves the remote controlauthorizzation for +// GetRemoteControlAuthorization retrieves the remote control authorization for // a specific pubkey from the database. func (nd *LitNode) GetRemoteControlAuthorization(pub [33]byte) (*RemoteControlAuthorization, error) { r := new(RemoteControlAuthorization) @@ -267,6 +272,7 @@ func (nd *LitNode) GetRemoteControlAuthorization(pub [33]byte) (*RemoteControlAu // If the client uses our default remote control key (derived from our root priv) // then it has access to our private key (file) and is most likely running from our // localhost. So we always accept this. + logging.Debug("Fetching remote control authorization for pubkey: ", pub[:], nd.DefaultRemoteControlKey.SerializeCompressed()) if bytes.Equal(pub[:], nd.DefaultRemoteControlKey.SerializeCompressed()) { r.Allowed = true return r, nil @@ -278,6 +284,7 @@ func (nd *LitNode) GetRemoteControlAuthorization(pub [33]byte) (*RemoteControlAu // serialize state b := cbk.Get(pub[:]) r = RemoteControlAuthorizationFromBytes(b, pub) + logging.Debug("Fetched pubkey ", r," from the database") return nil }) return r, err From c06f6c3ef5d35cb6a031ccf168097f5020bd4035 Mon Sep 17 00:00:00 2001 From: Varunram Date: Wed, 21 Nov 2018 18:38:36 +0530 Subject: [PATCH 2/3] connect to remote node using their pubkey as well bolt compatibility stuff --- litrpc/netcmds.go | 1 - lndc/conn.go | 26 ++++++++++++++++++++++--- lnp2p/peermgr.go | 48 +++++++++++++++++++++++++++-------------------- qln/netio.go | 1 + uspv/header.go | 4 ++++ 5 files changed, 56 insertions(+), 24 deletions(-) diff --git a/litrpc/netcmds.go b/litrpc/netcmds.go index 17694ec12..79934bbbd 100644 --- a/litrpc/netcmds.go +++ b/litrpc/netcmds.go @@ -68,7 +68,6 @@ func (r *LitRPC) Connect(args ConnectArgs, reply *ConnectReply) error { // check if a peer number was supplied instead of a pubkeyhash // accept either an string or a pubkey (raw) // so args.LNAddr passed here contains blah@host:ip - fmt.Println("PASSED STUFF:", args.LNAddr) peerIdxint, err := strconv.Atoi(args.LNAddr) // number will mean no error if err == nil { diff --git a/lndc/conn.go b/lndc/conn.go index 2bb94c8e7..b19df7e85 100644 --- a/lndc/conn.go +++ b/lndc/conn.go @@ -2,6 +2,7 @@ package lndc import ( "bytes" + "encoding/hex" "fmt" "io" "math" @@ -34,8 +35,28 @@ var _ net.Conn = (*Conn)(nil) // remote peer located at address which has remotePub as its long-term static // public key. In the case of a handshake failure, the connection is closed and // a non-nil error is returned. -func Dial(localPriv *koblitz.PrivateKey, ipAddr string, remotePKH string, +func Dial(localPriv *koblitz.PrivateKey, ipAddr string, remoteString string, dialer func(string, string) (net.Conn, error)) (*Conn, error) { + + // so the param passed can either be a remotePKH or can be a remotePK + // we can simply convert the remotePK into a remotePKH because we + // don't need to choose between XK and XX here and that's done later on + logging.Debug("Trying to connect to: ", remoteString, " at ", ipAddr) + var remotePKH string + if len(remoteString) == 41 { + remotePKH = remoteString + } else if len(remoteString) == 66 { + var temp [33]byte + x, err := hex.DecodeString(remoteString) + if err != nil { + logging.Error(err) + return nil, err + } + copy(temp[:], []byte(x)) + logging.Info("Converted passed remote pk to remotepkh", temp) + remotePKH = lnutil.LitAdrFromPubkey(temp) + } + var conn net.Conn var err error conn, err = dialer("tcp", ipAddr) @@ -81,9 +102,8 @@ func Dial(localPriv *koblitz.PrivateKey, ipAddr string, remotePKH string, } logging.Info("Received pubkey", s) - logging.Debug("Received pubkey: ", lnutil.LitAdrFromPubkey(s), "have pubkey", remotePKH) + logging.Debug("Received pubkey: ", lnutil.LitAdrFromPubkey(s), " have pubkey: ", remotePKH) if lnutil.LitAdrFromPubkey(s) != remotePKH { - fmt.Println("Received pubkey: ", lnutil.LitAdrFromPubkey(s), "have pubkey", remotePKH) return nil, fmt.Errorf("Remote PKH doesn't match. Quitting!") } logging.Infof("Received PKH %s matches", lnutil.LitAdrFromPubkey(s)) diff --git a/lnp2p/peermgr.go b/lnp2p/peermgr.go index dc80d8bca..5330784fc 100644 --- a/lnp2p/peermgr.go +++ b/lnp2p/peermgr.go @@ -3,6 +3,7 @@ package lnp2p //"crypto/ecdsa" // TODO Use ecdsa not koblitz import ( "crypto/ecdsa" + "encoding/hex" "fmt" "net" "strconv" @@ -102,6 +103,11 @@ func (pm *PeerManager) GetExternalAddress() string { return string(addr) } +func (pm *PeerManager) GetExternalPubkeyString() string { + c := koblitz.PublicKey(ecdsa.PublicKey(pm.idkey.PublicKey)) + return hex.EncodeToString(c.SerializeCompressed()) +} + func computeIdentKeyFromRoot(rootkey *hdkeychain.ExtendedKey) (privkey, error) { var kg portxo.KeyGen kg.Depth = 5 @@ -144,10 +150,13 @@ func (pm *PeerManager) GetPeerByIdx(id int32) *Peer { } // TryConnectAddress attempts to connect to the specified LN address. -func (pm *PeerManager) TryConnectAddress(addr string, settings *NetSettings) (error) { +func (pm *PeerManager) TryConnectAddress(addr string, settings *NetSettings) error { + // the address we're dialing can either be of the following two types: + // 1. pkhash@ip:port + // 2. pk@ip:port (where pk is in hex and is a compressed public key) // Figure out who we're trying to connect to. - who, where := splitAdrString(addr) + pkOrpkHash, where := splitAdrString(addr) if where == "" { ipv4, _, err := lnutil.Lookup(addr, pm.trackerURL, "") if err != nil { @@ -155,27 +164,26 @@ func (pm *PeerManager) TryConnectAddress(addr string, settings *NetSettings) (er } where = fmt.Sprintf("%s:2448", ipv4) } - - lnwho := lncore.LnAddr(who) - _, y := pm.tryConnectPeer(where, &lnwho, settings) - return y - + who := lncore.LnAddr(pkOrpkHash) + return pm.tryConnectPeer(&who, where, settings) } -func (pm *PeerManager) tryConnectPeer(netaddr string, lnaddr *lncore.LnAddr, settings *NetSettings) (*Peer, error) { +// tryConnectPeer tries to dial to the passed addr at where along with the passed +// settings. Returns an error +func (pm *PeerManager) tryConnectPeer(addr *lncore.LnAddr, where string, settings *NetSettings) (error) { // lnaddr check, to make sure that we do the right thing. - if lnaddr == nil { - return nil, fmt.Errorf("connection to a peer with unknown lnaddr not supported yet") + if addr == nil { + return fmt.Errorf("connection to a peer with unknown addr not supported yet") } // Do NAT setup stuff. if settings != nil && settings.NatMode != nil { // Do some type juggling. - x, err := strconv.Atoi(netaddr[1:]) + x, err := strconv.Atoi(where[1:]) if err != nil { - return nil, err + return err } lisPort := uint16(x) // if only Atoi could infer which type we wanted to parse as! @@ -185,7 +193,7 @@ func (pm *PeerManager) tryConnectPeer(netaddr string, lnaddr *lncore.LnAddr, set logging.Infof("Attempting port forwarding via UPnP...") err = nat.SetupUpnp(lisPort) if err != nil { - return nil, err + return err } } else if *settings.NatMode == "pmp" { // NAT Port Mapping Protocol @@ -193,10 +201,10 @@ func (pm *PeerManager) tryConnectPeer(netaddr string, lnaddr *lncore.LnAddr, set logging.Infof("Attempting port forwarding via PMP...") _, err = nat.SetupPmp(timeout, lisPort) if err != nil { - return nil, err + return err } } else { - return nil, fmt.Errorf("invalid NAT type: %s", *settings.NatMode) + return fmt.Errorf("invalid NAT type: %s", *settings.NatMode) } } @@ -206,18 +214,18 @@ func (pm *PeerManager) tryConnectPeer(netaddr string, lnaddr *lncore.LnAddr, set if settings != nil && settings.ProxyAddr != nil { d, err := connectToProxyTCP(*settings.ProxyAddr, settings.ProxyAuth) if err != nil { - return nil, err + return err } dialer = d } // Set up the connection. - lndcconn, err := lndc.Dial(pm.idkey, netaddr, string(*lnaddr), dialer) + lndcconn, err := lndc.Dial(pm.idkey, where, string(*addr), dialer) if err != nil { - return nil, err + return err } - pi, err := pm.peerdb.GetPeerInfo(*lnaddr) + pi, err := pm.peerdb.GetPeerInfo(*addr) if err != nil { logging.Errorf("Problem loading peer info from DB: %s\n", err.Error()) // don't kill the connection? @@ -266,7 +274,7 @@ func (pm *PeerManager) tryConnectPeer(netaddr string, lnaddr *lncore.LnAddr, set go processConnectionInboundTraffic(p, pm) // Return - return p, nil + return nil } diff --git a/qln/netio.go b/qln/netio.go index da24518a0..5d9e5a6fc 100644 --- a/qln/netio.go +++ b/qln/netio.go @@ -40,6 +40,7 @@ func (nd *LitNode) TCPListener(port int) (string, error) { lnaddr := nd.PeerMan.GetExternalAddress() + logging.Infof("My raw hex Public Key is: %s", nd.PeerMan.GetExternalPubkeyString()) logging.Infof("Listening with ln address: %s \n", lnaddr) // Don't announce on the tracker if we are communicating via SOCKS proxy diff --git a/uspv/header.go b/uspv/header.go index 8861cb079..1e9e0345e 100644 --- a/uspv/header.go +++ b/uspv/header.go @@ -312,6 +312,10 @@ func CheckHeaderChain( // reorg is go, snip to attach height reorgDepth := height - attachHeight + if reorgDepth > numheaders { + logging.Info("Reorg depth is greater than the number of headers received, exiting!") + return 0, fmt.Errorf("Reorg depth is greater than the number of headers received, exiting!") + } oldHeaders = oldHeaders[:numheaders-reorgDepth] } From 4bc012d325d058f93b72d4a921735a38e46990f3 Mon Sep 17 00:00:00 2001 From: Varunram Date: Wed, 21 Nov 2018 19:07:57 +0530 Subject: [PATCH 3/3] add pubkey connecting support for lit-af still need to take care of other stuff to make sure that pubkey is used uniformly inside lit --- cmd/lit-af/lit-af.go | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/cmd/lit-af/lit-af.go b/cmd/lit-af/lit-af.go index 5beb4de00..931cb90b9 100644 --- a/cmd/lit-af/lit-af.go +++ b/cmd/lit-af/lit-af.go @@ -66,7 +66,7 @@ func newConfigParser(conf *litAfConfig, options flags.Options) *flags.Parser { return parser } -func (lc *litAfClient) litAfSetup(conf litAfConfig) { +func (lc *litAfClient) litAfSetup(conf litAfConfig) (error){ var err error // create home directory if it does not exist @@ -90,8 +90,28 @@ func (lc *litAfClient) litAfSetup(conf litAfConfig) { } logging.SetLogLevel(logLevel) // defaults to zero + // we don't know whether the passed address is a remotePKH or a remotePK + // so we need to detect that here and then take steps accordingly + // so the first part involved here would be either dealing with raw pubkeys or + // dealing with pk hashes + // another question here is how do we deal with connecting to other nodes? + // if we need that in, we need to hcange our overall architecture to host + // pks as well as pk hashes adr, host, port := lnutil.ParseAdrStringWithPort(conf.Con) - logging.Infof("Adr: %s, Host: %s, Port: %d", adr, host, port) + // now e've split the address, check if pkh, if not, convert to pkh + if len(adr) == 44 { + // remote PKH, do nothing + } else if len(adr) == 66 { + // remote PK, convert to remotePKH + addr, err := hex.DecodeString(adr) + if err != nil { + return fmt.Errorf("Unable to decode hex string") + } + var temp [33]byte + copy(temp[:33], addr) + adr = lnutil.LitAdrFromPubkey(temp) + } + fmt.Printf("Adr: %s, Host: %s, Port: %d\n", adr, host, port) if litrpc.LndcRpcCanConnectLocallyWithHomeDir(defaultDir) && adr == "" && (host == "localhost" || host == "127.0.0.1") { lc.RPCClient, err = litrpc.NewLocalLndcRpcClientWithHomeDirAndPort(defaultDir, port) @@ -110,10 +130,10 @@ func (lc *litAfClient) litAfSetup(conf litAfConfig) { } key, _ := koblitz.PrivKeyFromBytes(koblitz.S256(), privKey[:]) pubkey := key.PubKey().SerializeCompressed() // this is in bytes - fmt.Println("The pubkey of this lit-af instance is:", hex.EncodeToString(pubkey)) + fmt.Printf("The pubkey of this lit-af instance is: %s\n", hex.EncodeToString(pubkey)) var temp [33]byte - copy(temp[:], pubkey[33:]) - fmt.Println("The pkh of this lit-af instance is:", lnutil.LitAdrFromPubkey(temp)) + copy(temp[:], pubkey[:33]) + fmt.Printf("The pkh of this lit-af instance is: %s\n", lnutil.LitAdrFromPubkey(temp)) if adr != "" && strings.HasPrefix(adr, "ln1") && host == "" { ipv4, _, err := lnutil.Lookup(adr, conf.Tracker, "") if err != nil { @@ -130,6 +150,7 @@ func (lc *litAfClient) litAfSetup(conf litAfConfig) { logging.Fatal(err.Error()) } } + return nil } // for now just testing how to connect and get messages back and forth @@ -142,7 +163,11 @@ func main() { Dir: defaultDir, Tracker: defaultTracker, } - lc.litAfSetup(conf) // setup lit-af to start + err = lc.litAfSetup(conf) // setup lit-af to start + if err != nil { + logging.Error(err) + return + } rl, err := readline.NewEx(&readline.Config{ Prompt: lnutil.Prompt("lit-af") + lnutil.White("# "),