Add Hodor whitelists (#2154)

This commit is contained in:
Muhammad 2024-09-19 04:56:11 +03:00 committed by GitHub
parent 2911a7c08a
commit fee34095a2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 40 additions and 18 deletions

View File

@ -71,7 +71,9 @@ IPs or users. the default value is 600.
"ip_capacity": 0, "ip_capacity": 0,
"user_capacity": 0 "user_capacity": 0
},... },...
] ],
// Trusted proxy ip or cidr
"trust_ips": ["127.0.0.1", "172.16.0.0/12"],
} }
} }
@endcode @endcode
@ -137,12 +139,14 @@ class DROGON_EXPORT Hodor : public drogon::Plugin<Hodor>
std::function<HttpResponsePtr(const drogon::HttpRequestPtr &)> std::function<HttpResponsePtr(const drogon::HttpRequestPtr &)>
rejectResponseFactory_; rejectResponseFactory_;
RealIpResolver::CIDRs trustCIDRs_;
void onHttpRequest(const drogon::HttpRequestPtr &, void onHttpRequest(const drogon::HttpRequestPtr &,
AdviceCallback &&, AdviceCallback &&,
AdviceChainCallback &&); AdviceChainCallback &&);
bool checkLimit(const drogon::HttpRequestPtr &req, bool checkLimit(const drogon::HttpRequestPtr &req,
const LimitStrategy &strategy, const LimitStrategy &strategy,
const std::string &ip, const trantor::InetAddress &ip,
const std::optional<std::string> &userId); const std::optional<std::string> &userId);
HttpResponsePtr rejectResponse_; HttpResponsePtr rejectResponse_;
}; };

View File

@ -57,7 +57,6 @@ class DROGON_EXPORT RealIpResolver : public drogon::Plugin<RealIpResolver>
private: private:
const trantor::InetAddress &getRealAddr( const trantor::InetAddress &getRealAddr(
const drogon::HttpRequestPtr &req) const; const drogon::HttpRequestPtr &req) const;
bool matchCidr(const trantor::InetAddress &addr) const;
struct CIDR struct CIDR
{ {
@ -66,7 +65,12 @@ class DROGON_EXPORT RealIpResolver : public drogon::Plugin<RealIpResolver>
in_addr_t mask_{32}; in_addr_t mask_{32};
}; };
std::vector<CIDR> trustCIDRs_; using CIDRs = std::vector<CIDR>;
static bool matchCidr(const trantor::InetAddress &addr,
const CIDRs &trustCIDRs);
friend class Hodor;
CIDRs trustCIDRs_;
std::string fromHeader_; std::string fromHeader_;
std::string attributeKey_; std::string attributeKey_;
bool useXForwardedFor_{false}; bool useXForwardedFor_{false};

View File

@ -105,6 +105,17 @@ void Hodor::initAndStart(const Json::Value &config)
limitStrategies_.emplace_back(makeLimitStrategy(subLimit)); limitStrategies_.emplace_back(makeLimitStrategy(subLimit));
} }
} }
const Json::Value &trustIps = config["trust_ips"];
if (!trustIps.isNull() && !trustIps.isArray())
{
throw std::runtime_error("Invalid trusted_ips. Should be array.");
}
for (const auto &ipOrCidr : trustIps)
{
trustCIDRs_.emplace_back(ipOrCidr.asString());
}
app().registerPreHandlingAdvice([this](const drogon::HttpRequestPtr &req, app().registerPreHandlingAdvice([this](const drogon::HttpRequestPtr &req,
AdviceCallback &&acb, AdviceCallback &&acb,
AdviceChainCallback &&accb) { AdviceChainCallback &&accb) {
@ -119,9 +130,13 @@ void Hodor::shutdown()
bool Hodor::checkLimit(const drogon::HttpRequestPtr &req, bool Hodor::checkLimit(const drogon::HttpRequestPtr &req,
const LimitStrategy &strategy, const LimitStrategy &strategy,
const std::string &ip, const trantor::InetAddress &ip,
const std::optional<std::string> &userId) const std::optional<std::string> &userId)
{ {
if (RealIpResolver::matchCidr(ip, trustCIDRs_))
{
return true;
}
if (strategy.regexFlag) if (strategy.regexFlag)
{ {
if (!std::regex_match(req->path(), strategy.urlsRegex)) if (!std::regex_match(req->path(), strategy.urlsRegex))
@ -140,7 +155,7 @@ bool Hodor::checkLimit(const drogon::HttpRequestPtr &req,
{ {
RateLimiterPtr limiterPtr; RateLimiterPtr limiterPtr;
strategy.ipLimiterMapPtr->modify( strategy.ipLimiterMapPtr->modify(
ip, ip.toIpNetEndian(),
[this, &limiterPtr, &strategy](RateLimiterPtr &ptr) { [this, &limiterPtr, &strategy](RateLimiterPtr &ptr) {
if (!ptr) if (!ptr)
{ {
@ -207,10 +222,9 @@ void Hodor::onHttpRequest(const drogon::HttpRequestPtr &req,
drogon::AdviceCallback &&adviceCallback, drogon::AdviceCallback &&adviceCallback,
drogon::AdviceChainCallback &&chainCallback) drogon::AdviceChainCallback &&chainCallback)
{ {
auto ip = const trantor::InetAddress &ip =
(useRealIpResolver_ ? drogon::plugin::RealIpResolver::GetRealAddr(req) useRealIpResolver_ ? drogon::plugin::RealIpResolver::GetRealAddr(req)
: req->peerAddr()) : req->peerAddr();
.toIpNetEndian();
std::optional<std::string> userId; std::optional<std::string> userId;
if (userIdGetter_) if (userIdGetter_)
{ {

View File

@ -96,21 +96,20 @@ void RealIpResolver::initAndStart(const Json::Value &config)
} }
const Json::Value &trustIps = config["trust_ips"]; const Json::Value &trustIps = config["trust_ips"];
if (!trustIps.isArray()) if (!trustIps.isNull() && !trustIps.isArray())
{ {
throw std::runtime_error("Invalid trusted_ips. Should be array."); throw std::runtime_error("Invalid trusted_ips. Should be array.");
} }
for (const auto &elem : trustIps) for (const auto &ipOrCidr : trustIps)
{ {
std::string ipOrCidr = elem.asString(); trustCIDRs_.emplace_back(ipOrCidr.asString());
trustCIDRs_.emplace_back(ipOrCidr);
} }
drogon::app().registerPreRoutingAdvice([this](const HttpRequestPtr &req) { drogon::app().registerPreRoutingAdvice([this](const HttpRequestPtr &req) {
const auto &headers = req->headers(); const auto &headers = req->headers();
auto ipHeaderFind = headers.find(fromHeader_); auto ipHeaderFind = headers.find(fromHeader_);
const trantor::InetAddress &peerAddr = req->getPeerAddr(); const trantor::InetAddress &peerAddr = req->getPeerAddr();
if (ipHeaderFind == headers.end() || !matchCidr(peerAddr)) if (ipHeaderFind == headers.end() || !matchCidr(peerAddr, trustCIDRs_))
{ {
// Target header is empty, or // Target header is empty, or
// direct peer is already a non-proxy // direct peer is already a non-proxy
@ -139,7 +138,7 @@ void RealIpResolver::initAndStart(const Json::Value &config)
while (!(ip = parser.getNext()).empty()) while (!(ip = parser.getNext()).empty())
{ {
trantor::InetAddress addr = parseAddress(ip); trantor::InetAddress addr = parseAddress(ip);
if (addr.isUnspecified() || matchCidr(addr)) if (addr.isUnspecified() || matchCidr(addr, trustCIDRs_))
{ {
continue; continue;
} }
@ -177,9 +176,10 @@ const trantor::InetAddress &RealIpResolver::getRealAddr(
return attributesPtr->get<trantor::InetAddress>(attributeKey_); return attributesPtr->get<trantor::InetAddress>(attributeKey_);
} }
bool RealIpResolver::matchCidr(const trantor::InetAddress &addr) const bool RealIpResolver::matchCidr(const trantor::InetAddress &addr,
const CIDRs &trustCIDRs)
{ {
for (auto &cidr : trustCIDRs_) for (const auto &cidr : trustCIDRs)
{ {
if ((addr.ipNetEndian() & cidr.mask_) == cidr.addr_) if ((addr.ipNetEndian() & cidr.mask_) == cidr.addr_)
{ {