Apply resource limits to proxied clients:

Resource limits were not properly applied to connections with
known IP addresses but no corresponding users.

Add unit tests for unlimited vs. limited ports.
This commit is contained in:
Mark Travis
2019-02-02 02:54:10 -08:00
committed by Nik Bougalis
parent 872478d965
commit 504b3441dd
26 changed files with 399 additions and 197 deletions

View File

@@ -276,13 +276,13 @@ Status doCommand (
! context.headers.forwardedFor.empty())
{
JLOG(context.j.debug()) << "start command: " << handler->name_ <<
", X-User: " << context.headers.user << ", X-Forwarded-For: " <<
", user: " << context.headers.user << ", forwarded for: " <<
context.headers.forwardedFor;
auto ret = callMethod (context, method, handler->name_, result);
JLOG(context.j.debug()) << "finish command: " << handler->name_ <<
", X-User: " << context.headers.user << ", X-Forwarded-For: " <<
", user: " << context.headers.user << ", forwarded for: " <<
context.headers.forwardedFor;
return ret;

View File

@@ -18,6 +18,8 @@
//==============================================================================
#include <ripple/rpc/Role.h>
#include <boost/beast/core/string.hpp>
#include <algorithm>
namespace ripple {
@@ -56,7 +58,7 @@ isAdmin (Port const& port, Json::Value const& params,
Role
requestRole (Role const& required, Port const& port,
Json::Value const& params, beast::IP::Endpoint const& remoteIp,
std::string const& user)
boost::string_view const& user)
{
if (isAdmin(port, params, remoteIp.address()))
return Role::ADMIN;
@@ -64,8 +66,12 @@ requestRole (Role const& required, Port const& port,
if (required == Role::ADMIN)
return Role::FORBID;
if (isIdentified(port, remoteIp.address(), user))
return Role::IDENTIFIED;
if (ipAllowed(remoteIp.address(), port.secure_gateway_ip))
{
if (user.size())
return Role::IDENTIFIED;
return Role::PROXY;
}
return Role::GUEST;
}
@@ -73,41 +79,66 @@ requestRole (Role const& required, Port const& port,
/**
* ADMIN and IDENTIFIED roles shall have unlimited resources.
*/
bool
isUnlimited (Role const& required, Port const& port,
Json::Value const&params, beast::IP::Endpoint const& remoteIp,
std::string const& user)
{
Role role = requestRole(required, port, params, remoteIp, user);
if (role == Role::ADMIN || role == Role::IDENTIFIED)
return true;
else
return false;
}
bool
isUnlimited (Role const& role)
{
return role == Role::ADMIN || role == Role::IDENTIFIED;
}
bool
isUnlimited (Role const& required, Port const& port,
Json::Value const& params, beast::IP::Endpoint const& remoteIp,
std::string const& user)
{
return isUnlimited(requestRole(required, port, params, remoteIp, user));
}
Resource::Consumer
requestInboundEndpoint (Resource::Manager& manager,
beast::IP::Endpoint const& remoteAddress,
Port const& port, std::string const& user)
beast::IP::Endpoint const& remoteAddress, Role const& role,
boost::string_view const& user, boost::string_view const& forwardedFor)
{
if (isUnlimited (Role::GUEST, port, Json::Value(), remoteAddress, user))
return manager.newUnlimitedEndpoint (to_string (remoteAddress));
if (isUnlimited(role))
return manager.newUnlimitedEndpoint (remoteAddress);
return manager.newInboundEndpoint(remoteAddress);
return manager.newInboundEndpoint(remoteAddress, role == Role::PROXY,
forwardedFor);
}
bool
isIdentified (Port const& port, beast::IP::Address const& remoteIp,
std::string const& user)
boost::string_view
forwardedFor(http_request_type const& request)
{
return ! user.empty() && ipAllowed (remoteIp, port.secure_gateway_ip);
auto it = request.find("X-Forwarded-For");
if (it != request.end())
{
return boost::beast::http::ext_list{
it->value()}.begin()->first;
}
it = request.find("Forwarded");
if (it != request.end())
{
static std::string const forStr{"for="};
auto found = std::search(it->value().begin(), it->value().end(),
forStr.begin(), forStr.end(),
[](char c1, char c2)
{
return boost::beast::detail::ascii_tolower(c1) ==
boost::beast::detail::ascii_tolower(c2);
}
);
if (found == it->value().end())
return {};
found += forStr.size();
auto pos{it->value().find(';', forStr.size())};
if (pos != boost::string_view::npos)
return {found, pos + 1};
return {found, it->value().size() - forStr.size()};
}
return {};
}
}

View File

@@ -38,6 +38,7 @@
#include <ripple/resource/ResourceManager.h>
#include <ripple/resource/Fees.h>
#include <ripple/rpc/impl/Tuning.h>
#include <ripple/rpc/Role.h>
#include <ripple/rpc/RPCHandler.h>
#include <ripple/server/SimpleWriter.h>
#include <boost/beast/http/fields.hpp>
@@ -199,11 +200,14 @@ ServerHandlerImp::onHandoff(
}
auto is {std::make_shared<WSInfoSub>(m_networkOPs, ws)};
auto const beast_remote_address =
beast::IPAddressConversion::from_asio(remote_address);
is->getConsumer() = requestInboundEndpoint(
m_resourceManager,
beast::IPAddressConversion::from_asio(remote_address),
session.port(),
is->user());
m_resourceManager, beast_remote_address,
requestRole(Role::GUEST, session.port(), Json::Value(),
beast_remote_address, is->user()),
is->user(),
is->forwarded_for());
ws->appDefined = std::move(is);
ws->run();
@@ -489,12 +493,6 @@ ServerHandlerImp::processSession(
else
{
jr[jss::status] = jss::success;
// For testing resource limits on this connection.
if (is->getConsumer().isUnlimited() &&
jv[jss::command].isString() &&
jv[jss::command].asString() == "ping")
jr[jss::unlimited] = true;
}
if (jv.isMember(jss::id))
@@ -517,23 +515,14 @@ ServerHandlerImp::processSession (std::shared_ptr<Session> const& session,
session->request().body().data()),
session->remoteAddress().at_port (0),
makeOutput (*session), coro,
[&]
{
auto const iter =
session->request().find(
"X-Forwarded-For");
if(iter != session->request().end())
return iter->value().to_string();
return std::string{};
}(),
[&]
{
forwardedFor(session->request()),
[&]{
auto const iter =
session->request().find(
"X-User");
if(iter != session->request().end())
return iter->value().to_string();
return std::string{};
return iter->value();
return boost::beast::string_view{};
}());
if(beast::rfc2616::is_keep_alive(session->request()))
@@ -562,7 +551,7 @@ void
ServerHandlerImp::processRequest (Port const& port,
std::string const& request, beast::IP::Endpoint const& remoteIPAddress,
Output&& output, std::shared_ptr<JobQueue::Coro> coro,
std::string forwardedFor, std::string user)
boost::string_view forwardedFor, boost::string_view user)
{
auto rpcJ = app_.journal ("RPC");
@@ -636,12 +625,12 @@ ServerHandlerImp::processRequest (Port const& port,
Resource::Consumer usage;
if (isUnlimited(role))
{
usage = m_resourceManager.newUnlimitedEndpoint(
remoteIPAddress.to_string());
usage = m_resourceManager.newUnlimitedEndpoint(remoteIPAddress);
}
else
{
usage = m_resourceManager.newInboundEndpoint(remoteIPAddress);
usage = m_resourceManager.newInboundEndpoint(remoteIPAddress,
role == Role::PROXY, forwardedFor);
if (usage.disconnect())
{
if (!batch)
@@ -774,7 +763,7 @@ ServerHandlerImp::processRequest (Port const& port,
* Clear header-assigned values if not positively identified from a
* secure_gateway.
*/
if (role != Role::IDENTIFIED)
if (role != Role::IDENTIFIED && role != Role::PROXY)
{
forwardedFor.clear();
user.clear();

View File

@@ -28,6 +28,7 @@
#include <ripple/rpc/RPCHandler.h>
#include <ripple/app/main/CollectorManager.h>
#include <ripple/json/Output.h>
#include <boost/utility/string_view.hpp>
#include <map>
#include <mutex>
#include <vector>
@@ -180,7 +181,7 @@ private:
processRequest (Port const& port, std::string const& request,
beast::IP::Endpoint const& remoteIPAddress, Output&&,
std::shared_ptr<JobQueue::Coro> coro,
std::string forwardedFor, std::string user);
boost::string_view forwardedFor, boost::string_view user);
Handoff
statusResponse(http_request_type const& request) const;

View File

@@ -25,6 +25,7 @@
#include <ripple/beast/net/IPAddressConversion.h>
#include <ripple/json/json_writer.h>
#include <ripple/rpc/Role.h>
#include <boost/utility/string_view.hpp>
#include <memory>
#include <string>
@@ -42,26 +43,23 @@ public:
, ws_(ws)
{
auto const& h = ws->request();
auto it = h.find("X-User");
if (it != h.end() &&
isIdentified(
ws->port(), beast::IPAddressConversion::from_asio(
ws->remote_endpoint()).address(), it->value().to_string()))
if (ipAllowed(beast::IPAddressConversion::from_asio(
ws->remote_endpoint()).address(), ws->port().secure_gateway_ip))
{
user_ = it->value().to_string();
it = h.find("X-Forwarded-For");
auto it = h.find("X-User");
if (it != h.end())
fwdfor_ = it->value().to_string();
user_ = it->value().to_string();
fwdfor_ = std::string(forwardedFor(h));
}
}
std::string
boost::string_view
user() const
{
return user_;
}
std::string
boost::string_view
forwarded_for() const
{
return fwdfor_;