From 71257128ad2a4cabaf0ccfb79e8b64ad0c5fe652 Mon Sep 17 00:00:00 2001 From: Otto Moerbeek Date: Wed, 12 Feb 2025 13:38:42 +0100 Subject: [PATCH] rec: reload proxy settings on rec_control reload-acls Fixes #14096 --- pdns/recursordist/pdns_recursor.cc | 17 +++- pdns/recursordist/rec-main.cc | 83 ++++++++++++++------ pdns/recursordist/rec-main.hh | 4 +- pdns/recursordist/rec-rust-lib/cxxsupport.cc | 2 + pdns/recursordist/rec-rust-lib/table.py | 6 +- 5 files changed, 83 insertions(+), 29 deletions(-) diff --git a/pdns/recursordist/pdns_recursor.cc b/pdns/recursordist/pdns_recursor.cc index a59ee8ab437c..ae10b11cfe55 100644 --- a/pdns/recursordist/pdns_recursor.cc +++ b/pdns/recursordist/pdns_recursor.cc @@ -59,6 +59,9 @@ thread_local std::unique_ptr>> t_ thread_local std::shared_ptr t_allowFrom; thread_local std::shared_ptr t_allowNotifyFrom; thread_local std::shared_ptr t_allowNotifyFor; +thread_local std::shared_ptr t_proxyProtocolACL; +thread_local std::shared_ptr> t_proxyProtocolExceptions; + __thread struct timeval g_now; // timestamp, updated (too) frequently using listenSocketsAddresses_t = map; // is shared across all threads right now @@ -2150,7 +2153,16 @@ void requestWipeCaches(const DNSName& canon) bool expectProxyProtocol(const ComboAddress& from, const ComboAddress& listenAddress) { - return g_proxyProtocolACL.match(from) && g_proxyProtocolExceptions.count(listenAddress) == 0; + if (!t_proxyProtocolACL) { + return false; + } + if (t_proxyProtocolACL->match(from)) { + if (!t_proxyProtocolExceptions) { + return true; + } + return t_proxyProtocolExceptions->count(listenAddress) == 0; + } + return false; } // fromaddr: the address the query is coming from @@ -2437,7 +2449,8 @@ static string* doProcessUDPQuestion(const std::string& question, const ComboAddr static void handleNewUDPQuestion(int fileDesc, FDMultiplexer::funcparam_t& /* var */) // NOLINT(readability-function-cognitive-complexity): https://github.com/PowerDNS/pdns/issues/12791 { - static const size_t maxIncomingQuerySize = g_proxyProtocolACL.empty() ? 512 : (512 + g_proxyProtocolMaximumSize); + const bool proxyActive = t_proxyProtocolACL && !t_proxyProtocolACL->empty(); + static const size_t maxIncomingQuerySize = !proxyActive ? 512 : (512 + g_proxyProtocolMaximumSize); static thread_local std::string data; ComboAddress fromaddr; // the address the query is coming from ComboAddress source; // the address we assume the query is coming from, might be set by proxy protocol diff --git a/pdns/recursordist/rec-main.cc b/pdns/recursordist/rec-main.cc index 3fae0825f6ae..221d76de596f 100644 --- a/pdns/recursordist/rec-main.cc +++ b/pdns/recursordist/rec-main.cc @@ -106,8 +106,8 @@ std::unique_ptr g_udrDBp; std::atomic statsWanted; uint32_t g_disthashseed; bool g_useIncomingECS; -NetmaskGroup g_proxyProtocolACL; -std::set g_proxyProtocolExceptions; +static shared_ptr g_initialProxyProtocolACL; +static shared_ptr> g_initialProxyProtocolExceptions; boost::optional g_dns64Prefix{boost::none}; DNSName g_dns64PrefixReverse; unsigned int g_maxChainLength; @@ -1388,11 +1388,25 @@ void* pleaseSupplantAllowNotifyFor(std::shared_ptr allowNotifyFor) return nullptr; } +void* pleaseSupplantProxyProtocolSettings(std::shared_ptr acl, std::shared_ptr> except) +{ + t_proxyProtocolACL = std::move(acl); + t_proxyProtocolExceptions = std::move(except); + return nullptr; +} + void parseACLs() { auto log = g_slog->withName("config"); static bool l_initialized; + const std::array aclNames = { + "allow-from-file", + "allow-from", + "allow-notify-from-file", + "allow-notify-from", + "proxy-protocol-from", + "proxy-protocol-exceptions"}; if (l_initialized) { // only reload configuration file on second call @@ -1434,6 +1448,8 @@ void parseACLs() throw runtime_error("Unable to re-parse configuration file '" + configName + "'"); } ::arg().preParseFile(configName, "allow-notify-from"); + ::arg().preParseFile(configName, "proxy-protocol-from"); + ::arg().preParseFile(configName, "proxy-protocol-exceptions"); ::arg().preParseFile(configName, "include-dir"); ::arg().preParse(g_argc, g_argv, "include-dir"); @@ -1443,28 +1459,18 @@ void parseACLs() ::arg().gatherIncludes(::arg()["include-dir"], ".conf", extraConfigs); for (const std::string& fileName : extraConfigs) { - if (!::arg().preParseFile(fileName, "allow-from-file", ::arg()["allow-from-file"])) { - throw runtime_error("Unable to re-parse configuration file include '" + fileName + "'"); - } - if (!::arg().preParseFile(fileName, "allow-from", ::arg()["allow-from"])) { - throw runtime_error("Unable to re-parse configuration file include '" + fileName + "'"); - } - - if (!::arg().preParseFile(fileName, "allow-notify-from-file", ::arg()["allow-notify-from-file"])) { - throw runtime_error("Unable to re-parse configuration file include '" + fileName + "'"); - } - if (!::arg().preParseFile(fileName, "allow-notify-from", ::arg()["allow-notify-from"])) { - throw runtime_error("Unable to re-parse configuration file include '" + fileName + "'"); + for (const auto& aclName : aclNames) { + if (!::arg().preParseFile(fileName, aclName, ::arg()[aclName])) { + throw runtime_error("Unable to re-parse configuration file include '" + fileName + "'"); + } } } } } // Process command line args potentially overriding settings read from file - ::arg().preParse(g_argc, g_argv, "allow-from-file"); - ::arg().preParse(g_argc, g_argv, "allow-from"); - - ::arg().preParse(g_argc, g_argv, "allow-notify-from-file"); - ::arg().preParse(g_argc, g_argv, "allow-notify-from"); + for (const auto& aclName : aclNames) { + ::arg().preParse(g_argc, g_argv, aclName); + } auto allowFrom = parseACL("allow-from-file", "allow-from", log); @@ -1486,6 +1492,28 @@ void parseACLs() // coverity[copy_constructor_call] maybe this can be avoided, but be careful as pointers get passed to other threads broadcastFunction([=] { return pleaseSupplantAllowNotifyFrom(allowNotifyFrom); }); + std::shared_ptr proxyProtocolACL; + std::shared_ptr> proxyProtocolExceptions; + if (!::arg()["proxy-protocol-from"].empty()) { + proxyProtocolACL = std::make_shared(); + proxyProtocolACL->toMasks(::arg()["proxy-protocol-from"]); + + std::vector vec; + stringtok(vec, ::arg()["proxy-protocol-exceptions"], ", "); + if (!vec.empty()) { + proxyProtocolExceptions = std::make_shared>(); + for (const auto& sockAddrStr : vec) { + ComboAddress sockAddr(sockAddrStr, 53); + proxyProtocolExceptions->emplace(sockAddr); + } + } + } + g_initialProxyProtocolACL = proxyProtocolACL; + g_initialProxyProtocolExceptions = proxyProtocolExceptions; + + // coverity[copy_constructor_call] maybe this can be avoided, but be careful as pointers get passed to other threads + broadcastFunction([=] { return pleaseSupplantProxyProtocolSettings(proxyProtocolACL, proxyProtocolExceptions); }); + l_initialized = true; } @@ -2216,13 +2244,18 @@ static int serviceMain(Logr::log_t log) return ret; } - g_proxyProtocolACL.toMasks(::arg()["proxy-protocol-from"]); - { + if (!::arg()["proxy-protocol-from"].empty()) { + g_initialProxyProtocolACL = std::make_shared(); + g_initialProxyProtocolACL->toMasks(::arg()["proxy-protocol-from"]); + std::vector vec; stringtok(vec, ::arg()["proxy-protocol-exceptions"], ", "); - for (const auto& sockAddrStr : vec) { - ComboAddress sockAddr(sockAddrStr, 53); - g_proxyProtocolExceptions.emplace(sockAddr); + if (!vec.empty()) { + g_initialProxyProtocolExceptions = std::make_shared>(); + for (const auto& sockAddrStr : vec) { + ComboAddress sockAddr(sockAddrStr, 53); + g_initialProxyProtocolExceptions->emplace(sockAddr); + } } } g_proxyProtocolMaximumSize = ::arg().asNum("proxy-protocol-maximum-size"); @@ -2834,6 +2867,8 @@ static void recursorThread() t_allowFrom = *g_initialAllowFrom.lock(); t_allowNotifyFrom = *g_initialAllowNotifyFrom.lock(); t_allowNotifyFor = *g_initialAllowNotifyFor.lock(); + t_proxyProtocolACL = g_initialProxyProtocolACL; + t_proxyProtocolExceptions = g_initialProxyProtocolExceptions; t_udpclientsocks = std::make_unique(); t_tcpClientCounts = std::make_unique(); if (g_proxyMapping) { diff --git a/pdns/recursordist/rec-main.hh b/pdns/recursordist/rec-main.hh index d18afcb3aeb4..0a31cba49e3b 100644 --- a/pdns/recursordist/rec-main.hh +++ b/pdns/recursordist/rec-main.hh @@ -224,12 +224,12 @@ extern thread_local std::shared_ptr t_allowFrom; extern thread_local std::shared_ptr t_allowNotifyFrom; extern thread_local std::shared_ptr t_allowNotifyFor; extern thread_local std::unique_ptr t_udpclientsocks; +extern thread_local std::shared_ptr t_proxyProtocolACL; +extern thread_local std::shared_ptr> t_proxyProtocolExceptions; extern bool g_useIncomingECS; extern boost::optional g_dns64Prefix; extern DNSName g_dns64PrefixReverse; extern uint64_t g_latencyStatSize; -extern NetmaskGroup g_proxyProtocolACL; -extern std::set g_proxyProtocolExceptions; extern std::atomic g_statsWanted; extern uint32_t g_disthashseed; extern int g_argc; diff --git a/pdns/recursordist/rec-rust-lib/cxxsupport.cc b/pdns/recursordist/rec-rust-lib/cxxsupport.cc index 89845cb26ce3..772d21edbe69 100644 --- a/pdns/recursordist/rec-rust-lib/cxxsupport.cc +++ b/pdns/recursordist/rec-rust-lib/cxxsupport.cc @@ -448,6 +448,8 @@ void pdns::settings::rec::setArgsForACLRelatedSettings(Recursorsettings& setting ::arg().set("allow-from-file") = to_arg(settings.incoming.allow_from_file); ::arg().set("allow-notify-from") = to_arg(settings.incoming.allow_notify_from); ::arg().set("allow-notify-from-file") = to_arg(settings.incoming.allow_notify_from_file); + ::arg().set("proxy-protocol-from") = to_arg(settings.incoming.proxy_protocol_from); + ::arg().set("proxy-protocol-exceptions") = to_arg(settings.incoming.proxy_protocol_exceptions); } void pdns::settings::rec::to_yaml(uint64_t& field, const std::string& val) diff --git a/pdns/recursordist/rec-rust-lib/table.py b/pdns/recursordist/rec-rust-lib/table.py index e07e176a65f3..33007c2a2718 100644 --- a/pdns/recursordist/rec-rust-lib/table.py +++ b/pdns/recursordist/rec-rust-lib/table.py @@ -2196,7 +2196,9 @@ The dnsdist docs have `more information about the PROXY protocol `_. ''', 'versionadded' : '4.4.0', - 'versionchanged' : ('5.0.5', 'YAML settings only: previously this was defined as a string instead of a sequence') + 'versionchanged' : [('5.0.5', 'YAML settings only: previously this was defined as a string instead of a sequence'), + ('5.3.0', '``rec_control reload-acls`` reloads this setting')], + 'runtime': ['reload-acls (since 5.3.0)'], }, { 'name' : 'proxy_protocol_exceptions', @@ -2210,6 +2212,8 @@ This is typically used to provide an easy to use address and port to send debug queries to. ''', 'versionadded' : '5.1.0', + 'versionchanged' : ('5.3.0', '``rec_control reload-acls`` reloads this setting'), + 'runtime': ['reload-acls (since 5.3.0)'], }, { 'name' : 'proxy_protocol_maximum_size',