diff --git a/doc/rippled-example.cfg b/doc/rippled-example.cfg index f91d8e4a41..ed764168a0 100644 --- a/doc/rippled-example.cfg +++ b/doc/rippled-example.cfg @@ -359,38 +359,18 @@ # # # -# [overlay] EXPERIMENTAL +# [overlay] # -# This section is EXPERIMENTAL, and should not be -# present for production configuration settings. +# Controls settings related to the peer to peer overlay. # # A set of key/value pair parameters to configure the overlay. # -# auto_connect = 0 | 1 -# -# When set, activates the autoconnect feature. This maintains outgoing -# connections using PeerFinder's "Outgoing Connection Strategy." -# -# become_superpeer = 'never' | 'always' | 'auto' -# -# Controls the selection of peer roles: -# -# 'never' Always handshake in the leaf role. -# 'always' Always handshake in the superpeer role. -# 'auto' Start as a leaf, promote to superpeer after -# passing capability check (default). -# -# In the leaf role, a peer does not advertise its IP and port for -# the purpose of receiving incoming connections. The peer also does -# not forward transactions and validations received from other peers. -# -# In the superpeer role, a peer advertises its IP and port for -# receiving incoming connections after passing an incoming connection -# test. Superpeers forward transactions and protocol messages to all -# other peers. Superpeers do not forward validations to other superpeers. -# Instead, a validation received by a superpeer from a leaf is forwarded -# only to other leaf connections. +# public_ip = # +# If the server has a known, fixed public IPv4 address, +# specify that IP address here in dotted decimal notation. +# Peers will use this information to reject attempt to proxy +# connections to or from this server. # # #------------------------------------------------------------------------------- diff --git a/src/ripple/overlay/Overlay.h b/src/ripple/overlay/Overlay.h index eb06512f47..c644cbfc72 100644 --- a/src/ripple/overlay/Overlay.h +++ b/src/ripple/overlay/Overlay.h @@ -67,10 +67,9 @@ public: struct Setup { - bool auto_connect = true; - Promote promote = Promote::automatic; std::shared_ptr context; bool expire = false; + beast::IP::Address public_ip; }; using PeerSequence = std::vector ; diff --git a/src/ripple/overlay/impl/ConnectAttempt.cpp b/src/ripple/overlay/impl/ConnectAttempt.cpp index e3f712ef5b..da47368b36 100644 --- a/src/ripple/overlay/impl/ConnectAttempt.cpp +++ b/src/ripple/overlay/impl/ConnectAttempt.cpp @@ -218,7 +218,11 @@ ConnectAttempt::onHandshake (error_code ec) beast::http::message req = makeRequest( ! overlay_.peerFinder().config().peerPrivate, remote_endpoint_.address()); - auto const hello = buildHello (sharedValue, app_); + auto const hello = buildHello ( + sharedValue, + overlay_.setup().public_ip, + beast::IPAddressConversion::from_asio(remote_endpoint_), + app_); appendHello (req, hello); using beast::http::write; @@ -399,7 +403,10 @@ ConnectAttempt::processResponse (beast::http::message const& m, RippleAddress publicKey; std::tie(publicKey, success) = verifyHello (hello, - sharedValue, journal_, app_); + sharedValue, + overlay_.setup().public_ip, + beast::IPAddressConversion::from_asio(remote_endpoint_), + journal_, app_); if(! success) return close(); // verifyHello logs if(journal_.info) journal_.info << diff --git a/src/ripple/overlay/impl/OverlayImpl.cpp b/src/ripple/overlay/impl/OverlayImpl.cpp index 30d3a4197c..75c7076fc3 100644 --- a/src/ripple/overlay/impl/OverlayImpl.cpp +++ b/src/ripple/overlay/impl/OverlayImpl.cpp @@ -244,7 +244,10 @@ OverlayImpl::onHandoff (std::unique_ptr && ssl_bundle, RippleAddress publicKey; std::tie(publicKey, success) = verifyHello (hello, - sharedValue, journal, app_); + sharedValue, + setup_.public_ip, + beast::IPAddressConversion::from_asio( + remote_endpoint), journal, app_); if(! success) return handoff; @@ -1018,17 +1021,20 @@ setup_Overlay (BasicConfig const& config) { Overlay::Setup setup; auto const& section = config.section("overlay"); - set (setup.auto_connect, "auto_connect", section); - std::string promote; - set (promote, "become_superpeer", section); - if (promote == "never") - setup.promote = Overlay::Promote::never; - else if (promote == "always") - setup.promote = Overlay::Promote::always; - else - setup.promote = Overlay::Promote::automatic; setup.context = make_SSLContext(); setup.expire = get(section, "expire", false); + + std::string ip; + set (ip, "public_ip", section); + if (! ip.empty ()) + { + bool valid; + std::tie (setup.public_ip, valid) = + beast::IP::Address::from_string (ip); + if (! valid || ! setup.public_ip.is_v4() || + is_private (setup.public_ip)) + throw std::runtime_error ("Configured public IP is invalid"); + } return setup; } diff --git a/src/ripple/overlay/impl/PeerImp.cpp b/src/ripple/overlay/impl/PeerImp.cpp index 2e2d0b92a7..07bad96aa3 100644 --- a/src/ripple/overlay/impl/PeerImp.cpp +++ b/src/ripple/overlay/impl/PeerImp.cpp @@ -596,7 +596,7 @@ void PeerImp::doAccept() auto resp = makeResponse( ! overlay_.peerFinder().config().peerPrivate, - http_message_, sharedValue); + http_message_, remote_address_, sharedValue); beast::http::write (write_buffer_, resp); auto const protocol = BuildInfo::make_protocol(hello_.protoversion()); @@ -636,7 +636,9 @@ void PeerImp::doAccept() beast::http::message PeerImp::makeResponse (bool crawl, - beast::http::message const& req, uint256 const& sharedValue) + beast::http::message const& req, + beast::IP::Endpoint remote, + uint256 const& sharedValue) { beast::http::message resp; resp.request(false); @@ -648,7 +650,8 @@ PeerImp::makeResponse (bool crawl, resp.headers.append("Connect-AS", "Peer"); resp.headers.append("Server", BuildInfo::getFullVersionString()); resp.headers.append ("Crawl", crawl ? "public" : "private"); - protocol::TMHello hello = buildHello(sharedValue, app_); + protocol::TMHello hello = buildHello(sharedValue, + overlay_.setup().public_ip, remote, app_); appendHello(resp, hello); return resp; } @@ -1647,22 +1650,6 @@ PeerImp::sendGetPeers () send (packet); } -bool -PeerImp::sendHello() -{ - bool success; - std::tie(sharedValue_, success) = makeSharedValue( - stream_.native_handle(), journal_); - if (! success) - return false; - - auto const hello = buildHello (sharedValue_, app_); - auto const m = std::make_shared ( - std::move(hello), protocol::mtHELLO); - send (m); - return true; -} - void PeerImp::addLedger (uint256 const& hash) { diff --git a/src/ripple/overlay/impl/PeerImp.h b/src/ripple/overlay/impl/PeerImp.h index d930216dd8..127c480dca 100644 --- a/src/ripple/overlay/impl/PeerImp.h +++ b/src/ripple/overlay/impl/PeerImp.h @@ -361,6 +361,7 @@ private: beast::http::message makeResponse (bool crawl, beast::http::message const& req, + beast::IP::Endpoint remoteAddress, uint256 const& sharedValue); void @@ -440,15 +441,6 @@ private: void sendGetPeers(); - /** Perform a secure handshake with the peer at the other end. - If this function returns false then we cannot guarantee that there - is no active man-in-the-middle attack taking place and the link - MUST be disconnected. - @return true if successful, false otherwise. - */ - bool - sendHello(); - void addLedger (uint256 const& hash); diff --git a/src/ripple/overlay/impl/TMHello.cpp b/src/ripple/overlay/impl/TMHello.cpp index e650103638..cbb68ee372 100644 --- a/src/ripple/overlay/impl/TMHello.cpp +++ b/src/ripple/overlay/impl/TMHello.cpp @@ -106,7 +106,11 @@ makeSharedValue (SSL* ssl, beast::Journal journal) } protocol::TMHello -buildHello (uint256 const& sharedValue, Application& app) +buildHello ( + uint256 const& sharedValue, + beast::IP::Address public_ip, + beast::IP::Endpoint remote, + Application& app) { protocol::TMHello h; @@ -124,6 +128,18 @@ buildHello (uint256 const& sharedValue, Application& app) // h.set_ipv4port (portNumber); // ignored now h.set_testnet (false); + if (remote.is_v4()) + { + auto addr = remote.to_v4 (); + if (is_public (addr)) + { + // Connection is to a public IP + h.set_remote_ip (addr.value); + if (public_ip != beast::IP::Address()) + h.set_local_ip (public_ip.to_v4().value); + } + } + // We always advertise ourselves as private in the HELLO message. This // suppresses the old peer advertising code and allows PeerFinder to // take over the functionality. @@ -168,6 +184,14 @@ appendHello (beast::http::message& m, if (hello.has_ledgerprevious()) h.append ("Previous-Ledger", beast::base64_encode ( hello.ledgerprevious())); + + if (hello.has_local_ip()) + h.append ("Local-IP", beast::IP::to_string ( + beast::IP::AddressV4(hello.local_ip()))); + + if (hello.has_remote_ip()) + h.append ("Remote-IP", beast::IP::to_string ( + beast::IP::AddressV4(hello.remote_ip()))); } std::vector @@ -292,13 +316,47 @@ parseHello (beast::http::message const& m, beast::Journal journal) hello.set_ledgerprevious (beast::base64_decode (iter->second)); } + { + auto const iter = h.find ("Local-IP"); + if (iter != h.end()) + { + bool valid; + beast::IP::Address address; + std::tie (address, valid) = + beast::IP::Address::from_string (iter->second); + if (!valid) + return result; + if (address.is_v4()) + hello.set_local_ip(address.to_v4().value); + } + } + + { + auto const iter = h.find ("Remote-IP"); + if (iter != h.end()) + { + bool valid; + beast::IP::Address address; + std::tie (address, valid) = + beast::IP::Address::from_string (iter->second); + if (!valid) + return result; + if (address.is_v4()) + hello.set_remote_ip(address.to_v4().value); + } + } + result.second = true; return result; } std::pair -verifyHello (protocol::TMHello const& h, uint256 const& sharedValue, - beast::Journal journal, Application& app) +verifyHello (protocol::TMHello const& h, + uint256 const& sharedValue, + beast::IP::Address public_ip, + beast::IP::Endpoint remote, + beast::Journal journal, + Application& app) { std::pair result = { {}, false }; auto const ourTime = app.timeKeeper().now().time_since_epoch().count(); @@ -344,6 +402,11 @@ verifyHello (protocol::TMHello const& h, uint256 const& sharedValue, journal.info << "Hello: Disconnect: Bad node public key."; } + else if (result.first == app.getLocalCredentials().getNodePublic()) + { + journal.info << + "Hello: Disconnect: Self connection."; + } else if (! result.first.verifyNodePublic ( sharedValue, h.nodeproof (), ECDSA::not_strict)) { @@ -351,6 +414,31 @@ verifyHello (protocol::TMHello const& h, uint256 const& sharedValue, journal.info << "Hello: Disconnect: Failed to verify session."; } + else if (h.has_local_ip () && + is_public (remote) && + remote.is_v4 () && + (remote.to_v4().value != h.local_ip ())) + { + // Remote asked us to confirm connection is from + // correct IP + journal.info << + "Hello: Disconnect: Peer IP is " << + beast::IP::to_string (remote.to_v4()) + << " not " << + beast::IP::to_string (beast::IP::AddressV4 (h.local_ip())); + } + else if (h.has_remote_ip() && is_public (remote) && + (public_ip != beast::IP::Address()) && + (h.remote_ip() != public_ip.to_v4().value)) + { + // We know our public IP and peer reports connection + // from some other IP + journal.info << + "Hello: Disconnect: Our IP is " << + beast::IP::to_string (public_ip.to_v4()) + << " not " << + beast::IP::to_string (beast::IP::AddressV4 (h.remote_ip())); + } else { // Successful connection. diff --git a/src/ripple/overlay/impl/TMHello.h b/src/ripple/overlay/impl/TMHello.h index dd9b79d023..681556d1ed 100644 --- a/src/ripple/overlay/impl/TMHello.h +++ b/src/ripple/overlay/impl/TMHello.h @@ -52,7 +52,9 @@ makeSharedValue (SSL* ssl, beast::Journal journal); /** Build a TMHello protocol message. */ protocol::TMHello -buildHello (uint256 const& sharedValue, Application& app); +buildHello (uint256 const& sharedValue, + beast::IP::Address public_ip, + beast::IP::Endpoint remote, Application& app); /** Insert HTTP headers based on the TMHello protocol message. */ void @@ -70,6 +72,8 @@ parseHello (beast::http::message const& m, beast::Journal journal); */ std::pair verifyHello (protocol::TMHello const& h, uint256 const& sharedValue, + beast::IP::Address public_ip, + beast::IP::Endpoint remote, beast::Journal journal, Application& app); /** Parse a set of protocol versions. diff --git a/src/ripple/proto/ripple.proto b/src/ripple/proto/ripple.proto index 1bacee1f95..a146958de7 100644 --- a/src/ripple/proto/ripple.proto +++ b/src/ripple/proto/ripple.proto @@ -95,6 +95,8 @@ message TMHello optional bool nodePrivate = 11; // Request to not forward IP. optional TMProofWork proofOfWork = 12; // request/provide proof of work optional bool testNet = 13; // Running as testnet. + optional uint32 local_ip = 14; // our public IP + optional uint32 remote_ip = 15; // IP we see connection from } // The status of a node in our cluster