mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-05 08:48:03 +00:00
Improve transport security:
* Add fields for local and remote IP addresses in hello. * Add configuration for known local public IP address * Set fields appropriately * Check the fields * Disallow self connection by key
This commit is contained in:
@@ -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 = <IP-address>
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
#
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
@@ -67,10 +67,9 @@ public:
|
||||
|
||||
struct Setup
|
||||
{
|
||||
bool auto_connect = true;
|
||||
Promote promote = Promote::automatic;
|
||||
std::shared_ptr<boost::asio::ssl::context> context;
|
||||
bool expire = false;
|
||||
beast::IP::Address public_ip;
|
||||
};
|
||||
|
||||
using PeerSequence = std::vector <Peer::ptr>;
|
||||
|
||||
@@ -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 <<
|
||||
|
||||
@@ -244,7 +244,10 @@ OverlayImpl::onHandoff (std::unique_ptr <beast::asio::ssl_bundle>&& 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<bool>(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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Message> (
|
||||
std::move(hello), protocol::mtHELLO);
|
||||
send (m);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
PeerImp::addLedger (uint256 const& hash)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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<ProtocolVersion>
|
||||
@@ -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<RippleAddress, bool>
|
||||
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<RippleAddress, bool> 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.
|
||||
|
||||
@@ -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<RippleAddress, bool>
|
||||
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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user