mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +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:
@@ -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