mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-15 17:25:51 +00:00
@@ -33,11 +33,11 @@
|
||||
#include "util/config/Config.hpp"
|
||||
#include "util/log/Logger.hpp"
|
||||
#include "util/prometheus/Prometheus.hpp"
|
||||
#include "web/DOSGuard.hpp"
|
||||
#include "web/IntervalSweepHandler.hpp"
|
||||
#include "web/RPCServerHandler.hpp"
|
||||
#include "web/Server.hpp"
|
||||
#include "web/WhitelistHandler.hpp"
|
||||
#include "web/dosguard/DOSGuard.hpp"
|
||||
#include "web/dosguard/IntervalSweepHandler.hpp"
|
||||
#include "web/dosguard/WhitelistHandler.hpp"
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
|
||||
@@ -93,9 +93,9 @@ ClioApplication::run()
|
||||
boost::asio::io_context ioc{threads};
|
||||
|
||||
// Rate limiter, to prevent abuse
|
||||
auto whitelistHandler = web::WhitelistHandler{config_};
|
||||
auto dosGuard = web::DOSGuard{config_, whitelistHandler};
|
||||
auto sweepHandler = web::IntervalSweepHandler{config_, ioc, dosGuard};
|
||||
auto whitelistHandler = web::dosguard::WhitelistHandler{config_};
|
||||
auto dosGuard = web::dosguard::DOSGuard{config_, whitelistHandler};
|
||||
auto sweepHandler = web::dosguard::IntervalSweepHandler{config_, ioc, dosGuard};
|
||||
|
||||
// Interface to the database
|
||||
auto backend = data::make_Backend(config_);
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "rpc/common/impl/ForwardingProxy.hpp"
|
||||
#include "util/log/Logger.hpp"
|
||||
#include "web/Context.hpp"
|
||||
#include "web/DOSGuard.hpp"
|
||||
#include "web/dosguard/DOSGuardInterface.hpp"
|
||||
|
||||
#include <boost/asio/spawn.hpp>
|
||||
#include <boost/json.hpp>
|
||||
@@ -65,7 +65,7 @@ class RPCEngine {
|
||||
util::Logger log_{"RPC"};
|
||||
|
||||
std::shared_ptr<BackendInterface> backend_;
|
||||
std::reference_wrapper<web::DOSGuard const> dosGuard_;
|
||||
std::reference_wrapper<web::dosguard::DOSGuardInterface const> dosGuard_;
|
||||
std::reference_wrapper<WorkQueue> workQueue_;
|
||||
std::reference_wrapper<Counters> counters_;
|
||||
|
||||
@@ -87,7 +87,7 @@ public:
|
||||
RPCEngine(
|
||||
std::shared_ptr<BackendInterface> const& backend,
|
||||
std::shared_ptr<etl::LoadBalancer> const& balancer,
|
||||
web::DOSGuard const& dosGuard,
|
||||
web::dosguard::DOSGuardInterface const& dosGuard,
|
||||
WorkQueue& workQueue,
|
||||
Counters& counters,
|
||||
std::shared_ptr<HandlerProvider const> const& handlerProvider
|
||||
@@ -116,7 +116,7 @@ public:
|
||||
make_RPCEngine(
|
||||
std::shared_ptr<BackendInterface> const& backend,
|
||||
std::shared_ptr<etl::LoadBalancer> const& balancer,
|
||||
web::DOSGuard const& dosGuard,
|
||||
web::dosguard::DOSGuardInterface const& dosGuard,
|
||||
WorkQueue& workQueue,
|
||||
Counters& counters,
|
||||
std::shared_ptr<HandlerProvider const> const& handlerProvider
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
add_library(clio_web)
|
||||
|
||||
target_sources(
|
||||
clio_web PRIVATE impl/AdminVerificationStrategy.cpp impl/ServerSslContext.cpp IntervalSweepHandler.cpp Resolver.cpp
|
||||
clio_web
|
||||
PRIVATE Resolver.cpp
|
||||
Server.cpp
|
||||
dosguard/DOSGuard.cpp
|
||||
dosguard/IntervalSweepHandler.cpp
|
||||
dosguard/WhitelistHandler.cpp
|
||||
impl/AdminVerificationStrategy.cpp
|
||||
impl/ServerSslContext.cpp
|
||||
ng/Server.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(clio_web PUBLIC clio_util)
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/Taggable.hpp"
|
||||
#include "web/DOSGuard.hpp"
|
||||
#include "web/PlainWsSession.hpp"
|
||||
#include "web/dosguard/DOSGuardInterface.hpp"
|
||||
#include "web/impl/HttpBase.hpp"
|
||||
#include "web/interface/ConnectionBase.hpp"
|
||||
|
||||
@@ -70,7 +70,7 @@ public:
|
||||
std::string const& ip,
|
||||
std::shared_ptr<impl::AdminVerificationStrategy> const& adminVerification,
|
||||
std::reference_wrapper<util::TagDecoratorFactory const> tagFactory,
|
||||
std::reference_wrapper<web::DOSGuard> dosGuard,
|
||||
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,
|
||||
std::shared_ptr<HandlerType> const& handler,
|
||||
boost::beast::flat_buffer buffer
|
||||
)
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/Taggable.hpp"
|
||||
#include "web/DOSGuard.hpp"
|
||||
#include "web/dosguard/DOSGuardInterface.hpp"
|
||||
#include "web/impl/WsBase.hpp"
|
||||
#include "web/interface/ConnectionBase.hpp"
|
||||
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
boost::asio::ip::tcp::socket&& socket,
|
||||
std::string ip,
|
||||
std::reference_wrapper<util::TagDecoratorFactory const> tagFactory,
|
||||
std::reference_wrapper<web::DOSGuard> dosGuard,
|
||||
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,
|
||||
std::shared_ptr<HandlerType> const& handler,
|
||||
boost::beast::flat_buffer&& buffer,
|
||||
bool isAdmin
|
||||
@@ -102,7 +102,7 @@ class WsUpgrader : public std::enable_shared_from_this<WsUpgrader<HandlerType>>
|
||||
boost::optional<http::request_parser<http::string_body>> parser_;
|
||||
boost::beast::flat_buffer buffer_;
|
||||
std::reference_wrapper<util::TagDecoratorFactory const> tagFactory_;
|
||||
std::reference_wrapper<web::DOSGuard> dosGuard_;
|
||||
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard_;
|
||||
http::request<http::string_body> req_;
|
||||
std::string ip_;
|
||||
std::shared_ptr<HandlerType> const handler_;
|
||||
@@ -125,7 +125,7 @@ public:
|
||||
boost::beast::tcp_stream&& stream,
|
||||
std::string ip,
|
||||
std::reference_wrapper<util::TagDecoratorFactory const> tagFactory,
|
||||
std::reference_wrapper<web::DOSGuard> dosGuard,
|
||||
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,
|
||||
std::shared_ptr<HandlerType> const& handler,
|
||||
boost::beast::flat_buffer&& buffer,
|
||||
http::request<http::string_body> request,
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
|
||||
#include "util/Taggable.hpp"
|
||||
#include "util/log/Logger.hpp"
|
||||
#include "web/DOSGuard.hpp"
|
||||
#include "web/HttpSession.hpp"
|
||||
#include "web/SslHttpSession.hpp"
|
||||
#include "web/dosguard/DOSGuardInterface.hpp"
|
||||
#include "web/impl/ServerSslContext.hpp"
|
||||
#include "web/interface/Concepts.hpp"
|
||||
|
||||
@@ -91,7 +91,7 @@ class Detector : public std::enable_shared_from_this<Detector<PlainSessionType,
|
||||
boost::beast::tcp_stream stream_;
|
||||
std::optional<std::reference_wrapper<boost::asio::ssl::context>> ctx_;
|
||||
std::reference_wrapper<util::TagDecoratorFactory const> tagFactory_;
|
||||
std::reference_wrapper<web::DOSGuard> const dosGuard_;
|
||||
std::reference_wrapper<dosguard::DOSGuardInterface> const dosGuard_;
|
||||
std::shared_ptr<HandlerType> const handler_;
|
||||
boost::beast::flat_buffer buffer_;
|
||||
std::shared_ptr<impl::AdminVerificationStrategy> const adminVerification_;
|
||||
@@ -111,7 +111,7 @@ public:
|
||||
tcp::socket&& socket,
|
||||
std::optional<std::reference_wrapper<boost::asio::ssl::context>> ctx,
|
||||
std::reference_wrapper<util::TagDecoratorFactory const> tagFactory,
|
||||
std::reference_wrapper<web::DOSGuard> dosGuard,
|
||||
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,
|
||||
std::shared_ptr<HandlerType> handler,
|
||||
std::shared_ptr<impl::AdminVerificationStrategy> adminVerification
|
||||
)
|
||||
@@ -213,7 +213,7 @@ class Server : public std::enable_shared_from_this<Server<PlainSessionType, SslS
|
||||
std::reference_wrapper<boost::asio::io_context> ioc_;
|
||||
std::optional<boost::asio::ssl::context> ctx_;
|
||||
util::TagDecoratorFactory tagFactory_;
|
||||
std::reference_wrapper<web::DOSGuard> dosGuard_;
|
||||
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard_;
|
||||
std::shared_ptr<HandlerType> handler_;
|
||||
tcp::acceptor acceptor_;
|
||||
std::shared_ptr<impl::AdminVerificationStrategy> adminVerification_;
|
||||
@@ -235,7 +235,7 @@ public:
|
||||
std::optional<boost::asio::ssl::context> ctx,
|
||||
tcp::endpoint endpoint,
|
||||
util::TagDecoratorFactory tagFactory,
|
||||
web::DOSGuard& dosGuard,
|
||||
dosguard::DOSGuardInterface& dosGuard,
|
||||
std::shared_ptr<HandlerType> handler,
|
||||
std::optional<std::string> adminPassword
|
||||
)
|
||||
@@ -327,7 +327,7 @@ static std::shared_ptr<HttpServer<HandlerType>>
|
||||
make_HttpServer(
|
||||
util::Config const& config,
|
||||
boost::asio::io_context& ioc,
|
||||
web::DOSGuard& dosGuard,
|
||||
dosguard::DOSGuardInterface& dosGuard,
|
||||
std::shared_ptr<HandlerType> const& handler
|
||||
)
|
||||
{
|
||||
|
||||
@@ -20,9 +20,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/Taggable.hpp"
|
||||
#include "web/DOSGuard.hpp"
|
||||
#include "web/SslWsSession.hpp"
|
||||
#include "web/dosguard/DOSGuardInterface.hpp"
|
||||
#include "web/impl/HttpBase.hpp"
|
||||
#include "web/interface/Concepts.hpp"
|
||||
#include "web/interface/ConnectionBase.hpp"
|
||||
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
@@ -78,7 +79,7 @@ public:
|
||||
std::shared_ptr<impl::AdminVerificationStrategy> const& adminVerification,
|
||||
boost::asio::ssl::context& ctx,
|
||||
std::reference_wrapper<util::TagDecoratorFactory const> tagFactory,
|
||||
std::reference_wrapper<web::DOSGuard> dosGuard,
|
||||
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,
|
||||
std::shared_ptr<HandlerType> const& handler,
|
||||
boost::beast::flat_buffer buffer
|
||||
)
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/Taggable.hpp"
|
||||
#include "web/DOSGuard.hpp"
|
||||
#include "web/dosguard/DOSGuardInterface.hpp"
|
||||
#include "web/impl/WsBase.hpp"
|
||||
#include "web/interface/ConnectionBase.hpp"
|
||||
|
||||
@@ -69,7 +69,7 @@ public:
|
||||
boost::beast::ssl_stream<boost::beast::tcp_stream>&& stream,
|
||||
std::string ip,
|
||||
std::reference_wrapper<util::TagDecoratorFactory const> tagFactory,
|
||||
std::reference_wrapper<web::DOSGuard> dosGuard,
|
||||
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,
|
||||
std::shared_ptr<HandlerType> const& handler,
|
||||
boost::beast::flat_buffer&& buffer,
|
||||
bool isAdmin
|
||||
@@ -102,7 +102,7 @@ class SslWsUpgrader : public std::enable_shared_from_this<SslWsUpgrader<HandlerT
|
||||
boost::beast::flat_buffer buffer_;
|
||||
std::string ip_;
|
||||
std::reference_wrapper<util::TagDecoratorFactory const> tagFactory_;
|
||||
std::reference_wrapper<web::DOSGuard> dosGuard_;
|
||||
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard_;
|
||||
std::shared_ptr<HandlerType> const handler_;
|
||||
http::request<http::string_body> req_;
|
||||
bool isAdmin_;
|
||||
@@ -124,7 +124,7 @@ public:
|
||||
boost::beast::ssl_stream<boost::beast::tcp_stream> stream,
|
||||
std::string ip,
|
||||
std::reference_wrapper<util::TagDecoratorFactory const> tagFactory,
|
||||
std::reference_wrapper<web::DOSGuard> dosGuard,
|
||||
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,
|
||||
std::shared_ptr<HandlerType> handler,
|
||||
boost::beast::flat_buffer&& buffer,
|
||||
http::request<http::string_body> request,
|
||||
|
||||
148
src/web/dosguard/DOSGuard.cpp
Normal file
148
src/web/dosguard/DOSGuard.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2024, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "web/dosguard/DOSGuard.hpp"
|
||||
|
||||
#include "util/Assert.hpp"
|
||||
#include "util/config/Config.hpp"
|
||||
#include "util/log/Logger.hpp"
|
||||
#include "web/dosguard/WhitelistHandlerInterface.hpp"
|
||||
|
||||
#include <boost/iterator/transform_iterator.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace web::dosguard {
|
||||
|
||||
DOSGuard::DOSGuard(util::Config const& config, WhitelistHandlerInterface const& whitelistHandler)
|
||||
: whitelistHandler_{std::cref(whitelistHandler)}
|
||||
, maxFetches_{config.valueOr("dos_guard.max_fetches", DEFAULT_MAX_FETCHES)}
|
||||
, maxConnCount_{config.valueOr("dos_guard.max_connections", DEFAULT_MAX_CONNECTIONS)}
|
||||
, maxRequestCount_{config.valueOr("dos_guard.max_requests", DEFAULT_MAX_REQUESTS)}
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
DOSGuard::isWhiteListed(std::string_view const ip) const noexcept
|
||||
{
|
||||
return whitelistHandler_.get().isWhiteListed(ip);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool
|
||||
DOSGuard::isOk(std::string const& ip) const noexcept
|
||||
{
|
||||
if (whitelistHandler_.get().isWhiteListed(ip))
|
||||
return true;
|
||||
|
||||
{
|
||||
std::scoped_lock const lck(mtx_);
|
||||
if (ipState_.find(ip) != ipState_.end()) {
|
||||
auto [transferedByte, requests] = ipState_.at(ip);
|
||||
if (transferedByte > maxFetches_ || requests > maxRequestCount_) {
|
||||
LOG(log_.warn()) << "Dosguard: Client surpassed the rate limit. ip = " << ip
|
||||
<< " Transfered Byte: " << transferedByte << "; Requests: " << requests;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
auto it = ipConnCount_.find(ip);
|
||||
if (it != ipConnCount_.end()) {
|
||||
if (it->second > maxConnCount_) {
|
||||
LOG(log_.warn()) << "Dosguard: Client surpassed the rate limit. ip = " << ip
|
||||
<< " Concurrent connection: " << it->second;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
DOSGuard::increment(std::string const& ip) noexcept
|
||||
{
|
||||
if (whitelistHandler_.get().isWhiteListed(ip))
|
||||
return;
|
||||
std::scoped_lock const lck{mtx_};
|
||||
ipConnCount_[ip]++;
|
||||
}
|
||||
|
||||
void
|
||||
DOSGuard::decrement(std::string const& ip) noexcept
|
||||
{
|
||||
if (whitelistHandler_.get().isWhiteListed(ip))
|
||||
return;
|
||||
std::scoped_lock const lck{mtx_};
|
||||
ASSERT(ipConnCount_[ip] > 0, "Connection count for ip {} can't be 0", ip);
|
||||
ipConnCount_[ip]--;
|
||||
if (ipConnCount_[ip] == 0)
|
||||
ipConnCount_.erase(ip);
|
||||
}
|
||||
|
||||
[[maybe_unused]] bool
|
||||
DOSGuard::add(std::string const& ip, uint32_t numObjects) noexcept
|
||||
{
|
||||
if (whitelistHandler_.get().isWhiteListed(ip))
|
||||
return true;
|
||||
|
||||
{
|
||||
std::scoped_lock const lck(mtx_);
|
||||
ipState_[ip].transferedByte += numObjects;
|
||||
}
|
||||
|
||||
return isOk(ip);
|
||||
}
|
||||
|
||||
[[maybe_unused]] bool
|
||||
DOSGuard::request(std::string const& ip) noexcept
|
||||
{
|
||||
if (whitelistHandler_.get().isWhiteListed(ip))
|
||||
return true;
|
||||
|
||||
{
|
||||
std::scoped_lock const lck(mtx_);
|
||||
ipState_[ip].requestsCount++;
|
||||
}
|
||||
|
||||
return isOk(ip);
|
||||
}
|
||||
|
||||
void
|
||||
DOSGuard::clear() noexcept
|
||||
{
|
||||
std::scoped_lock const lck(mtx_);
|
||||
ipState_.clear();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unordered_set<std::string>
|
||||
DOSGuard::getWhitelist(util::Config const& config)
|
||||
{
|
||||
using T = std::unordered_set<std::string> const;
|
||||
auto whitelist = config.arrayOr("dos_guard.whitelist", {});
|
||||
auto const transform = [](auto const& elem) { return elem.template value<std::string>(); };
|
||||
return T{
|
||||
boost::transform_iterator(std::begin(whitelist), transform),
|
||||
boost::transform_iterator(std::end(whitelist), transform)
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace web::dosguard
|
||||
@@ -19,11 +19,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/Assert.hpp"
|
||||
#include "util/config/Config.hpp"
|
||||
#include "util/log/Logger.hpp"
|
||||
#include "web/IntervalSweepHandler.hpp"
|
||||
#include "web/WhitelistHandler.hpp"
|
||||
#include "web/dosguard/DOSGuardInterface.hpp"
|
||||
#include "web/dosguard/WhitelistHandlerInterface.hpp"
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/iterator/transform_iterator.hpp>
|
||||
@@ -31,48 +30,32 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace web {
|
||||
|
||||
/**
|
||||
* @brief The interface of a denial of service guard.
|
||||
*/
|
||||
class BaseDOSGuard {
|
||||
public:
|
||||
virtual ~BaseDOSGuard() = default;
|
||||
|
||||
/**
|
||||
* @brief Clears implementation-defined counters.
|
||||
*/
|
||||
virtual void
|
||||
clear() noexcept = 0;
|
||||
};
|
||||
namespace web::dosguard {
|
||||
|
||||
/**
|
||||
* @brief A simple denial of service guard used for rate limiting.
|
||||
*
|
||||
* @tparam WhitelistHandlerType The type of the whitelist handler
|
||||
*/
|
||||
template <typename WhitelistHandlerType>
|
||||
class BasicDOSGuard : public BaseDOSGuard {
|
||||
class DOSGuard : public DOSGuardInterface {
|
||||
/**
|
||||
* @brief Accumulated state per IP, state will be reset accordingly
|
||||
*/
|
||||
struct ClientState {
|
||||
std::uint32_t transferedByte = 0; /**< Accumulated transfered byte */
|
||||
std::uint32_t transferedByte = 0; /**< Accumulated transferred byte */
|
||||
std::uint32_t requestsCount = 0; /**< Accumulated served requests count */
|
||||
};
|
||||
|
||||
mutable std::mutex mtx_;
|
||||
std::unordered_map<std::string, ClientState> ipState_;
|
||||
std::unordered_map<std::string, std::uint32_t> ipConnCount_;
|
||||
std::reference_wrapper<WhitelistHandlerType const> whitelistHandler_;
|
||||
std::reference_wrapper<WhitelistHandlerInterface const> whitelistHandler_;
|
||||
|
||||
std::uint32_t const maxFetches_;
|
||||
std::uint32_t const maxConnCount_;
|
||||
@@ -90,13 +73,7 @@ public:
|
||||
* @param config Clio config
|
||||
* @param whitelistHandler Whitelist handler that checks whitelist for IP addresses
|
||||
*/
|
||||
BasicDOSGuard(util::Config const& config, WhitelistHandlerType const& whitelistHandler)
|
||||
: whitelistHandler_{std::cref(whitelistHandler)}
|
||||
, maxFetches_{config.valueOr("dos_guard.max_fetches", DEFAULT_MAX_FETCHES)}
|
||||
, maxConnCount_{config.valueOr("dos_guard.max_connections", DEFAULT_MAX_CONNECTIONS)}
|
||||
, maxRequestCount_{config.valueOr("dos_guard.max_requests", DEFAULT_MAX_REQUESTS)}
|
||||
{
|
||||
}
|
||||
DOSGuard(util::Config const& config, WhitelistHandlerInterface const& whitelistHandler);
|
||||
|
||||
/**
|
||||
* @brief Check whether an ip address is in the whitelist or not.
|
||||
@@ -106,10 +83,7 @@ public:
|
||||
* @return false
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isWhiteListed(std::string_view const ip) const noexcept
|
||||
{
|
||||
return whitelistHandler_.get().isWhiteListed(ip);
|
||||
}
|
||||
isWhiteListed(std::string_view const ip) const noexcept override;
|
||||
|
||||
/**
|
||||
* @brief Check whether an ip address is currently rate limited or not.
|
||||
@@ -119,32 +93,7 @@ public:
|
||||
* @return false If rate limited and the request should not be processed
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isOk(std::string const& ip) const noexcept
|
||||
{
|
||||
if (whitelistHandler_.get().isWhiteListed(ip))
|
||||
return true;
|
||||
|
||||
{
|
||||
std::scoped_lock const lck(mtx_);
|
||||
if (ipState_.find(ip) != ipState_.end()) {
|
||||
auto [transferedByte, requests] = ipState_.at(ip);
|
||||
if (transferedByte > maxFetches_ || requests > maxRequestCount_) {
|
||||
LOG(log_.warn()) << "Dosguard: Client surpassed the rate limit. ip = " << ip
|
||||
<< " Transfered Byte: " << transferedByte << "; Requests: " << requests;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
auto it = ipConnCount_.find(ip);
|
||||
if (it != ipConnCount_.end()) {
|
||||
if (it->second > maxConnCount_) {
|
||||
LOG(log_.warn()) << "Dosguard: Client surpassed the rate limit. ip = " << ip
|
||||
<< " Concurrent connection: " << it->second;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
isOk(std::string const& ip) const noexcept override;
|
||||
|
||||
/**
|
||||
* @brief Increment connection count for the given ip address.
|
||||
@@ -152,13 +101,7 @@ public:
|
||||
* @param ip
|
||||
*/
|
||||
void
|
||||
increment(std::string const& ip) noexcept
|
||||
{
|
||||
if (whitelistHandler_.get().isWhiteListed(ip))
|
||||
return;
|
||||
std::scoped_lock const lck{mtx_};
|
||||
ipConnCount_[ip]++;
|
||||
}
|
||||
increment(std::string const& ip) noexcept override;
|
||||
|
||||
/**
|
||||
* @brief Decrement connection count for the given ip address.
|
||||
@@ -166,16 +109,7 @@ public:
|
||||
* @param ip
|
||||
*/
|
||||
void
|
||||
decrement(std::string const& ip) noexcept
|
||||
{
|
||||
if (whitelistHandler_.get().isWhiteListed(ip))
|
||||
return;
|
||||
std::scoped_lock const lck{mtx_};
|
||||
ASSERT(ipConnCount_[ip] > 0, "Connection count for ip {} can't be 0", ip);
|
||||
ipConnCount_[ip]--;
|
||||
if (ipConnCount_[ip] == 0)
|
||||
ipConnCount_.erase(ip);
|
||||
}
|
||||
decrement(std::string const& ip) noexcept override;
|
||||
|
||||
/**
|
||||
* @brief Adds numObjects of usage for the given ip address.
|
||||
@@ -190,18 +124,7 @@ public:
|
||||
* @return false
|
||||
*/
|
||||
[[maybe_unused]] bool
|
||||
add(std::string const& ip, uint32_t numObjects) noexcept
|
||||
{
|
||||
if (whitelistHandler_.get().isWhiteListed(ip))
|
||||
return true;
|
||||
|
||||
{
|
||||
std::scoped_lock const lck(mtx_);
|
||||
ipState_[ip].transferedByte += numObjects;
|
||||
}
|
||||
|
||||
return isOk(ip);
|
||||
}
|
||||
add(std::string const& ip, uint32_t numObjects) noexcept override;
|
||||
|
||||
/**
|
||||
* @brief Adds one request for the given ip address.
|
||||
@@ -215,46 +138,17 @@ public:
|
||||
* @return false
|
||||
*/
|
||||
[[maybe_unused]] bool
|
||||
request(std::string const& ip) noexcept
|
||||
{
|
||||
if (whitelistHandler_.get().isWhiteListed(ip))
|
||||
return true;
|
||||
|
||||
{
|
||||
std::scoped_lock const lck(mtx_);
|
||||
ipState_[ip].requestsCount++;
|
||||
}
|
||||
|
||||
return isOk(ip);
|
||||
}
|
||||
request(std::string const& ip) noexcept override;
|
||||
|
||||
/**
|
||||
* @brief Instantly clears all fetch counters added by @see add(std::string const&, uint32_t).
|
||||
*/
|
||||
void
|
||||
clear() noexcept override
|
||||
{
|
||||
std::scoped_lock const lck(mtx_);
|
||||
ipState_.clear();
|
||||
}
|
||||
clear() noexcept override;
|
||||
|
||||
private:
|
||||
[[nodiscard]] std::unordered_set<std::string>
|
||||
getWhitelist(util::Config const& config) const
|
||||
{
|
||||
using T = std::unordered_set<std::string> const;
|
||||
auto whitelist = config.arrayOr("dos_guard.whitelist", {});
|
||||
auto const transform = [](auto const& elem) { return elem.template value<std::string>(); };
|
||||
return T{
|
||||
boost::transform_iterator(std::begin(whitelist), transform),
|
||||
boost::transform_iterator(std::end(whitelist), transform)
|
||||
};
|
||||
}
|
||||
[[nodiscard]] static std::unordered_set<std::string>
|
||||
getWhitelist(util::Config const& config);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A simple denial of service guard used for rate limiting.
|
||||
*/
|
||||
using DOSGuard = BasicDOSGuard<web::WhitelistHandler>;
|
||||
|
||||
} // namespace web
|
||||
} // namespace web::dosguard
|
||||
110
src/web/dosguard/DOSGuardInterface.hpp
Normal file
110
src/web/dosguard/DOSGuardInterface.hpp
Normal file
@@ -0,0 +1,110 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2024, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
namespace web::dosguard {
|
||||
|
||||
/**
|
||||
* @brief The interface of a denial of service guard.
|
||||
*/
|
||||
class BaseDOSGuard {
|
||||
public:
|
||||
virtual ~BaseDOSGuard() = default;
|
||||
|
||||
/**
|
||||
* @brief Clears implementation-defined counters.
|
||||
*/
|
||||
virtual void
|
||||
clear() noexcept = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The interface of a denial of service guard.
|
||||
*/
|
||||
class DOSGuardInterface : public BaseDOSGuard {
|
||||
public:
|
||||
/**
|
||||
* @brief Check whether an ip address is in the whitelist or not.
|
||||
*
|
||||
* @param ip The ip address to check
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
[[nodiscard]] virtual bool
|
||||
isWhiteListed(std::string_view const ip) const noexcept = 0;
|
||||
|
||||
/**
|
||||
* @brief Check whether an ip address is currently rate limited or not.
|
||||
*
|
||||
* @param ip The ip address to check
|
||||
* @return true If not rate limited
|
||||
* @return false If rate limited and the request should not be processed
|
||||
*/
|
||||
[[nodiscard]] virtual bool
|
||||
isOk(std::string const& ip) const noexcept = 0;
|
||||
|
||||
/**
|
||||
* @brief Increment connection count for the given ip address.
|
||||
*
|
||||
* @param ip
|
||||
*/
|
||||
virtual void
|
||||
increment(std::string const& ip) noexcept = 0;
|
||||
|
||||
/**
|
||||
* @brief Decrement connection count for the given ip address.
|
||||
*
|
||||
* @param ip
|
||||
*/
|
||||
virtual void
|
||||
decrement(std::string const& ip) noexcept = 0;
|
||||
|
||||
/**
|
||||
* @brief Adds numObjects of usage for the given ip address.
|
||||
*
|
||||
* If the total sums up to a value equal or larger than maxFetches_
|
||||
* the operation is no longer allowed and false is returned; true is
|
||||
* returned otherwise.
|
||||
*
|
||||
* @param ip
|
||||
* @param numObjects
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
[[maybe_unused]] virtual bool
|
||||
add(std::string const& ip, uint32_t numObjects) noexcept = 0;
|
||||
|
||||
/**
|
||||
* @brief Adds one request for the given ip address.
|
||||
*
|
||||
*
|
||||
* @param ip
|
||||
* @return If the total sums up to a value equal or larger than maxRequestCount_
|
||||
* the operation is no longer allowed and false is returned; true is
|
||||
* returned otherwise.
|
||||
*/
|
||||
[[maybe_unused]] virtual bool
|
||||
request(std::string const& ip) noexcept = 0;
|
||||
};
|
||||
|
||||
} // namespace web::dosguard
|
||||
@@ -17,10 +17,10 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "web/IntervalSweepHandler.hpp"
|
||||
#include "web/dosguard/IntervalSweepHandler.hpp"
|
||||
|
||||
#include "util/config/Config.hpp"
|
||||
#include "web/DOSGuard.hpp"
|
||||
#include "web/dosguard/DOSGuardInterface.hpp"
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/system/detail/error_code.hpp>
|
||||
@@ -29,12 +29,12 @@
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
|
||||
namespace web {
|
||||
namespace web::dosguard {
|
||||
|
||||
IntervalSweepHandler::IntervalSweepHandler(
|
||||
util::Config const& config,
|
||||
boost::asio::io_context& ctx,
|
||||
web::BaseDOSGuard& dosGuard
|
||||
BaseDOSGuard& dosGuard
|
||||
)
|
||||
: repeat_{std::ref(ctx)}
|
||||
{
|
||||
@@ -44,4 +44,4 @@ IntervalSweepHandler::IntervalSweepHandler(
|
||||
repeat_.start(sweepInterval, [&dosGuard] { dosGuard.clear(); });
|
||||
}
|
||||
|
||||
} // namespace web
|
||||
} // namespace web::dosguard
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
|
||||
namespace web {
|
||||
namespace web::dosguard {
|
||||
|
||||
class BaseDOSGuard;
|
||||
|
||||
@@ -42,7 +42,11 @@ public:
|
||||
* @param ctx The boost::asio::io_context to use
|
||||
* @param dosGuard The DOS guard to use
|
||||
*/
|
||||
IntervalSweepHandler(util::Config const& config, boost::asio::io_context& ctx, web::BaseDOSGuard& dosGuard);
|
||||
IntervalSweepHandler(
|
||||
util::Config const& config,
|
||||
boost::asio::io_context& ctx,
|
||||
web::dosguard::BaseDOSGuard& dosGuard
|
||||
);
|
||||
};
|
||||
|
||||
} // namespace web
|
||||
} // namespace web::dosguard
|
||||
118
src/web/dosguard/WhitelistHandler.cpp
Normal file
118
src/web/dosguard/WhitelistHandler.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2024, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "web/dosguard/WhitelistHandler.hpp"
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
#include <boost/asio/ip/network_v4.hpp>
|
||||
#include <boost/asio/ip/network_v6.hpp>
|
||||
#include <boost/iterator/transform_iterator.hpp>
|
||||
#include <fmt/core.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <regex>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace web::dosguard {
|
||||
|
||||
void
|
||||
Whitelist::add(std::string_view net)
|
||||
{
|
||||
using namespace boost::asio;
|
||||
|
||||
if (not isMask(net)) {
|
||||
ips_.push_back(ip::make_address(net));
|
||||
return;
|
||||
}
|
||||
|
||||
if (isV4(net)) {
|
||||
subnetsV4_.push_back(ip::make_network_v4(net));
|
||||
} else if (isV6(net)) {
|
||||
subnetsV6_.push_back(ip::make_network_v6(net));
|
||||
} else {
|
||||
throw std::runtime_error(fmt::format("malformed network: {}", net.data()));
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Whitelist::isWhiteListed(std::string_view ip) const
|
||||
{
|
||||
using namespace boost::asio;
|
||||
|
||||
auto const addr = ip::make_address(ip);
|
||||
if (std::find(std::begin(ips_), std::end(ips_), addr) != std::end(ips_))
|
||||
return true;
|
||||
|
||||
if (addr.is_v4()) {
|
||||
return std::find_if(
|
||||
std::begin(subnetsV4_), std::end(subnetsV4_), std::bind_front(&isInV4Subnet, std::cref(addr))
|
||||
) != std::end(subnetsV4_);
|
||||
}
|
||||
|
||||
if (addr.is_v6()) {
|
||||
return std::find_if(
|
||||
std::begin(subnetsV6_), std::end(subnetsV6_), std::bind_front(&isInV6Subnet, std::cref(addr))
|
||||
) != std::end(subnetsV6_);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
Whitelist::isInV4Subnet(boost::asio::ip::address const& addr, boost::asio::ip::network_v4 const& subnet)
|
||||
{
|
||||
auto const range = subnet.hosts();
|
||||
return range.find(addr.to_v4()) != range.end();
|
||||
}
|
||||
|
||||
bool
|
||||
Whitelist::isInV6Subnet(boost::asio::ip::address const& addr, boost::asio::ip::network_v6 const& subnet)
|
||||
{
|
||||
auto const range = subnet.hosts();
|
||||
return range.find(addr.to_v6()) != range.end();
|
||||
}
|
||||
|
||||
bool
|
||||
Whitelist::isV4(std::string_view net)
|
||||
{
|
||||
static std::regex const ipv4CidrRegex(R"(^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/\d{1,2}$)");
|
||||
return std::regex_match(std::string(net), ipv4CidrRegex);
|
||||
}
|
||||
|
||||
bool
|
||||
Whitelist::isV6(std::string_view net)
|
||||
{
|
||||
static std::regex const ipv6CidrRegex(R"(^([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}/\d{1,3}$)");
|
||||
return std::regex_match(std::string(net), ipv6CidrRegex);
|
||||
}
|
||||
|
||||
bool
|
||||
Whitelist::isMask(std::string_view net)
|
||||
{
|
||||
return net.find('/') != std::string_view::npos;
|
||||
}
|
||||
|
||||
} // namespace web::dosguard
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "util/config/Config.hpp"
|
||||
#include "web/Resolver.hpp"
|
||||
#include "web/dosguard/WhitelistHandlerInterface.hpp"
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
@@ -30,16 +31,14 @@
|
||||
#include <fmt/core.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <regex>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace web {
|
||||
namespace web::dosguard {
|
||||
|
||||
/**
|
||||
* @brief A whitelist to remove rate limits of certain IP addresses.
|
||||
@@ -57,23 +56,7 @@ public:
|
||||
* @throws std::runtime::error when the network address is not valid
|
||||
*/
|
||||
void
|
||||
add(std::string_view net)
|
||||
{
|
||||
using namespace boost::asio;
|
||||
|
||||
if (not isMask(net)) {
|
||||
ips_.push_back(ip::make_address(net));
|
||||
return;
|
||||
}
|
||||
|
||||
if (isV4(net)) {
|
||||
subnetsV4_.push_back(ip::make_network_v4(net));
|
||||
} else if (isV6(net)) {
|
||||
subnetsV6_.push_back(ip::make_network_v6(net));
|
||||
} else {
|
||||
throw std::runtime_error(fmt::format("malformed network: {}", net.data()));
|
||||
}
|
||||
}
|
||||
add(std::string_view net);
|
||||
|
||||
/**
|
||||
* @brief Checks to see if ip address is whitelisted.
|
||||
@@ -83,69 +66,29 @@ public:
|
||||
* @return true if the given IP is whitelisted; false otherwise
|
||||
*/
|
||||
bool
|
||||
isWhiteListed(std::string_view ip) const
|
||||
{
|
||||
using namespace boost::asio;
|
||||
|
||||
auto const addr = ip::make_address(ip);
|
||||
if (std::find(std::begin(ips_), std::end(ips_), addr) != std::end(ips_))
|
||||
return true;
|
||||
|
||||
if (addr.is_v4()) {
|
||||
return std::find_if(
|
||||
std::begin(subnetsV4_), std::end(subnetsV4_), std::bind_front(&isInV4Subnet, std::cref(addr))
|
||||
) != std::end(subnetsV4_);
|
||||
}
|
||||
|
||||
if (addr.is_v6()) {
|
||||
return std::find_if(
|
||||
std::begin(subnetsV6_), std::end(subnetsV6_), std::bind_front(&isInV6Subnet, std::cref(addr))
|
||||
) != std::end(subnetsV6_);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
isWhiteListed(std::string_view ip) const;
|
||||
|
||||
private:
|
||||
static bool
|
||||
isInV4Subnet(boost::asio::ip::address const& addr, boost::asio::ip::network_v4 const& subnet)
|
||||
{
|
||||
auto const range = subnet.hosts();
|
||||
return range.find(addr.to_v4()) != range.end();
|
||||
}
|
||||
isInV4Subnet(boost::asio::ip::address const& addr, boost::asio::ip::network_v4 const& subnet);
|
||||
|
||||
static bool
|
||||
isInV6Subnet(boost::asio::ip::address const& addr, boost::asio::ip::network_v6 const& subnet)
|
||||
{
|
||||
auto const range = subnet.hosts();
|
||||
return range.find(addr.to_v6()) != range.end();
|
||||
}
|
||||
isInV6Subnet(boost::asio::ip::address const& addr, boost::asio::ip::network_v6 const& subnet);
|
||||
|
||||
static bool
|
||||
isV4(std::string_view net)
|
||||
{
|
||||
static std::regex const ipv4CidrRegex(R"(^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/\d{1,2}$)");
|
||||
return std::regex_match(std::string(net), ipv4CidrRegex);
|
||||
}
|
||||
isV4(std::string_view net);
|
||||
|
||||
static bool
|
||||
isV6(std::string_view net)
|
||||
{
|
||||
static std::regex const ipv6CidrRegex(R"(^([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}/\d{1,3}$)");
|
||||
return std::regex_match(std::string(net), ipv6CidrRegex);
|
||||
}
|
||||
isV6(std::string_view net);
|
||||
|
||||
static bool
|
||||
isMask(std::string_view net)
|
||||
{
|
||||
return net.find('/') != std::string_view::npos;
|
||||
}
|
||||
isMask(std::string_view net);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A simple handler to add/check elements in a whitelist.
|
||||
*/
|
||||
class WhitelistHandler {
|
||||
class WhitelistHandler : public WhitelistHandlerInterface {
|
||||
Whitelist whitelist_;
|
||||
|
||||
public:
|
||||
@@ -170,7 +113,7 @@ public:
|
||||
* @return true if the given IP is whitelisted; false otherwise
|
||||
*/
|
||||
bool
|
||||
isWhiteListed(std::string_view ip) const
|
||||
isWhiteListed(std::string_view ip) const override
|
||||
{
|
||||
return whitelist_.isWhiteListed(ip);
|
||||
}
|
||||
@@ -200,4 +143,4 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace web
|
||||
} // namespace web::dosguard
|
||||
44
src/web/dosguard/WhitelistHandlerInterface.hpp
Normal file
44
src/web/dosguard/WhitelistHandlerInterface.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2024, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace web::dosguard {
|
||||
|
||||
/**
|
||||
* @brief Interface for a whitelist handler
|
||||
*/
|
||||
class WhitelistHandlerInterface {
|
||||
public:
|
||||
/** @brief Virtual destructor */
|
||||
virtual ~WhitelistHandlerInterface() = default;
|
||||
|
||||
/**
|
||||
* @brief Checks to see if the given IP is whitelisted
|
||||
*
|
||||
* @param ip The IP to check
|
||||
* @return true if the given IP is whitelisted; false otherwise
|
||||
*/
|
||||
[[nodiscard]] virtual bool
|
||||
isWhiteListed(std::string_view ip) const = 0;
|
||||
};
|
||||
|
||||
} // namespace web::dosguard
|
||||
@@ -24,7 +24,7 @@
|
||||
#include "util/build/Build.hpp"
|
||||
#include "util/log/Logger.hpp"
|
||||
#include "util/prometheus/Http.hpp"
|
||||
#include "web/DOSGuard.hpp"
|
||||
#include "web/dosguard/DOSGuardInterface.hpp"
|
||||
#include "web/impl/AdminVerificationStrategy.hpp"
|
||||
#include "web/interface/Concepts.hpp"
|
||||
#include "web/interface/ConnectionBase.hpp"
|
||||
@@ -114,7 +114,7 @@ class HttpBase : public ConnectionBase {
|
||||
protected:
|
||||
boost::beast::flat_buffer buffer_;
|
||||
http::request<http::string_body> req_;
|
||||
std::reference_wrapper<web::DOSGuard> dosGuard_;
|
||||
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard_;
|
||||
std::shared_ptr<HandlerType> const handler_;
|
||||
util::Logger log_{"WebServer"};
|
||||
util::Logger perfLog_{"Performance"};
|
||||
@@ -154,7 +154,7 @@ public:
|
||||
std::string const& ip,
|
||||
std::reference_wrapper<util::TagDecoratorFactory const> tagFactory,
|
||||
std::shared_ptr<AdminVerificationStrategy> adminVerification,
|
||||
std::reference_wrapper<web::DOSGuard> dosGuard,
|
||||
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,
|
||||
std::shared_ptr<HandlerType> handler,
|
||||
boost::beast::flat_buffer buffer
|
||||
)
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "rpc/common/Types.hpp"
|
||||
#include "util/Taggable.hpp"
|
||||
#include "util/log/Logger.hpp"
|
||||
#include "web/DOSGuard.hpp"
|
||||
#include "web/dosguard/DOSGuardInterface.hpp"
|
||||
#include "web/interface/Concepts.hpp"
|
||||
#include "web/interface/ConnectionBase.hpp"
|
||||
|
||||
@@ -72,7 +72,7 @@ class WsBase : public ConnectionBase, public std::enable_shared_from_this<WsBase
|
||||
using std::enable_shared_from_this<WsBase<Derived, HandlerType>>::shared_from_this;
|
||||
|
||||
boost::beast::flat_buffer buffer_;
|
||||
std::reference_wrapper<web::DOSGuard> dosGuard_;
|
||||
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard_;
|
||||
bool sending_ = false;
|
||||
std::queue<std::shared_ptr<std::string>> messages_;
|
||||
std::shared_ptr<HandlerType> const handler_;
|
||||
@@ -99,7 +99,7 @@ public:
|
||||
explicit WsBase(
|
||||
std::string ip,
|
||||
std::reference_wrapper<util::TagDecoratorFactory const> tagFactory,
|
||||
std::reference_wrapper<web::DOSGuard> dosGuard,
|
||||
std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,
|
||||
std::shared_ptr<HandlerType> const& handler,
|
||||
boost::beast::flat_buffer&& buffer
|
||||
)
|
||||
|
||||
0
src/web/ng/Server.cpp
Normal file
0
src/web/ng/Server.cpp
Normal file
0
src/web/ng/Server.hpp
Normal file
0
src/web/ng/Server.hpp
Normal file
@@ -23,7 +23,6 @@
|
||||
#include "rpc/common/Specs.hpp"
|
||||
#include "rpc/common/Types.hpp"
|
||||
#include "rpc/common/Validators.hpp"
|
||||
#include "web/DOSGuard.hpp"
|
||||
|
||||
#include <boost/json/conversion.hpp>
|
||||
#include <boost/json/value.hpp>
|
||||
|
||||
@@ -12,7 +12,6 @@ target_sources(
|
||||
data/cassandra/ExecutionStrategyTests.cpp
|
||||
data/cassandra/RetryPolicyTests.cpp
|
||||
data/cassandra/SettingsProviderTests.cpp
|
||||
DOSGuardTests.cpp
|
||||
# ETL
|
||||
etl/AmendmentBlockHandlerTests.cpp
|
||||
etl/CacheLoaderSettingsTests.cpp
|
||||
@@ -127,11 +126,12 @@ target_sources(
|
||||
util/TxUtilTests.cpp
|
||||
# Webserver
|
||||
web/AdminVerificationTests.cpp
|
||||
web/dosguard/DOSGuardTests.cpp
|
||||
web/dosguard/IntervalSweepHandlerTests.cpp
|
||||
web/dosguard/WhitelistHandlerTests.cpp
|
||||
web/impl/ServerSslContextTests.cpp
|
||||
web/RPCServerHandlerTests.cpp
|
||||
web/ServerTests.cpp
|
||||
web/IntervalSweepHandlerTests.cpp
|
||||
web/WhitelistHandlerTests.cpp
|
||||
# New Config
|
||||
util/newconfig/ArrayViewTests.cpp
|
||||
util/newconfig/ObjectViewTests.cpp
|
||||
|
||||
@@ -24,10 +24,11 @@
|
||||
#include "util/config/Config.hpp"
|
||||
#include "util/prometheus/Label.hpp"
|
||||
#include "util/prometheus/Prometheus.hpp"
|
||||
#include "web/DOSGuard.hpp"
|
||||
#include "web/IntervalSweepHandler.hpp"
|
||||
#include "web/Server.hpp"
|
||||
#include "web/WhitelistHandler.hpp"
|
||||
#include "web/dosguard/DOSGuard.hpp"
|
||||
#include "web/dosguard/DOSGuardInterface.hpp"
|
||||
#include "web/dosguard/IntervalSweepHandler.hpp"
|
||||
#include "web/dosguard/WhitelistHandler.hpp"
|
||||
#include "web/impl/AdminVerificationStrategy.hpp"
|
||||
#include "web/interface/ConnectionBase.hpp"
|
||||
|
||||
@@ -127,14 +128,14 @@ struct WebServerTest : NoLoggerFixture {
|
||||
boost::asio::io_context ctxSync;
|
||||
std::string const port = std::to_string(tests::util::generateFreePort());
|
||||
Config cfg{generateJSONWithDynamicPort(port)};
|
||||
WhitelistHandler whitelistHandler{cfg};
|
||||
DOSGuard dosGuard{cfg, whitelistHandler};
|
||||
IntervalSweepHandler sweepHandler{cfg, ctxSync, dosGuard};
|
||||
dosguard::WhitelistHandler whitelistHandler{cfg};
|
||||
dosguard::DOSGuard dosGuard{cfg, whitelistHandler};
|
||||
dosguard::IntervalSweepHandler sweepHandler{cfg, ctxSync, dosGuard};
|
||||
|
||||
Config cfgOverload{generateJSONDataOverload(port)};
|
||||
WhitelistHandler whitelistHandlerOverload{cfgOverload};
|
||||
DOSGuard dosGuardOverload{cfgOverload, whitelistHandlerOverload};
|
||||
IntervalSweepHandler sweepHandlerOverload{cfgOverload, ctxSync, dosGuardOverload};
|
||||
dosguard::WhitelistHandler whitelistHandlerOverload{cfgOverload};
|
||||
dosguard::DOSGuard dosGuardOverload{cfgOverload, whitelistHandlerOverload};
|
||||
dosguard::IntervalSweepHandler sweepHandlerOverload{cfgOverload, ctxSync, dosGuardOverload};
|
||||
// this ctx is for http server
|
||||
boost::asio::io_context ctx;
|
||||
|
||||
@@ -178,7 +179,7 @@ std::shared_ptr<web::HttpServer<Executor>>
|
||||
makeServerSync(
|
||||
util::Config const& config,
|
||||
boost::asio::io_context& ioc,
|
||||
web::DOSGuard& dosGuard,
|
||||
web::dosguard::DOSGuardInterface& dosGuard,
|
||||
std::shared_ptr<Executor> const& handler
|
||||
)
|
||||
{
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
|
||||
#include "util/LoggerFixtures.hpp"
|
||||
#include "util/config/Config.hpp"
|
||||
#include "web/DOSGuard.hpp"
|
||||
#include "web/dosguard/DOSGuard.hpp"
|
||||
#include "web/dosguard/WhitelistHandlerInterface.hpp"
|
||||
|
||||
#include <boost/json/parse.hpp>
|
||||
#include <gmock/gmock.h>
|
||||
@@ -30,11 +31,11 @@
|
||||
using namespace testing;
|
||||
using namespace util;
|
||||
using namespace std;
|
||||
using namespace web;
|
||||
using namespace web::dosguard;
|
||||
namespace json = boost::json;
|
||||
|
||||
namespace {
|
||||
constexpr auto JSONData = R"JSON(
|
||||
struct DOSGuardTest : NoLoggerFixture {
|
||||
static constexpr auto JSONData = R"JSON(
|
||||
{
|
||||
"dos_guard": {
|
||||
"max_fetches": 100,
|
||||
@@ -47,20 +48,15 @@ constexpr auto JSONData = R"JSON(
|
||||
}
|
||||
)JSON";
|
||||
|
||||
constexpr auto IP = "127.0.0.2";
|
||||
static constexpr auto IP = "127.0.0.2";
|
||||
|
||||
struct MockWhitelistHandler {
|
||||
struct MockWhitelistHandler : WhitelistHandlerInterface {
|
||||
MOCK_METHOD(bool, isWhiteListed, (std::string_view ip), (const));
|
||||
};
|
||||
};
|
||||
|
||||
using MockWhitelistHandlerType = NiceMock<MockWhitelistHandler>;
|
||||
}; // namespace
|
||||
|
||||
class DOSGuardTest : public NoLoggerFixture {
|
||||
protected:
|
||||
Config cfg{json::parse(JSONData)};
|
||||
MockWhitelistHandlerType whitelistHandler;
|
||||
BasicDOSGuard<MockWhitelistHandlerType> guard{cfg, whitelistHandler};
|
||||
NiceMock<MockWhitelistHandler> whitelistHandler;
|
||||
DOSGuard guard{cfg, whitelistHandler};
|
||||
};
|
||||
|
||||
TEST_F(DOSGuardTest, Whitelisting)
|
||||
@@ -19,8 +19,8 @@
|
||||
|
||||
#include "util/AsioContextTestFixture.hpp"
|
||||
#include "util/config/Config.hpp"
|
||||
#include "web/DOSGuard.hpp"
|
||||
#include "web/IntervalSweepHandler.hpp"
|
||||
#include "web/dosguard/DOSGuardInterface.hpp"
|
||||
#include "web/dosguard/IntervalSweepHandler.hpp"
|
||||
|
||||
#include <boost/json/parse.hpp>
|
||||
#include <gmock/gmock.h>
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
#include <chrono>
|
||||
|
||||
using namespace web;
|
||||
using namespace web::dosguard;
|
||||
|
||||
struct IntervalSweepHandlerTest : SyncAsioContextTest {
|
||||
protected:
|
||||
@@ -18,7 +18,7 @@
|
||||
//==============================================================================
|
||||
#include "util/LoggerFixtures.hpp"
|
||||
#include "util/config/Config.hpp"
|
||||
#include "web/WhitelistHandler.hpp"
|
||||
#include "web/dosguard/WhitelistHandler.hpp"
|
||||
|
||||
#include <boost/json/parse.hpp>
|
||||
#include <gmock/gmock.h>
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <vector>
|
||||
|
||||
using namespace util;
|
||||
using namespace web;
|
||||
using namespace web::dosguard;
|
||||
|
||||
struct WhitelistHandlerTest : NoLoggerFixture {};
|
||||
|
||||
Reference in New Issue
Block a user