Rec: add setting to exclude specific listen socket addresses from requiring proxy protocol
- Disabling :ref:`setting-structured-logging` is no longer supported.
-Changed Settings
-^^^^^^^^^^^^^^^^
+New Settings
+^^^^^^^^^^^^
+
+- The :ref:`setting-proxy-protocol-exceptions` has been added. It allows to exclude specific listen addresses from requiring the Proxy Protocol.
5.0.2 to 5.0.3, 4.9.3 to 4.9.4 and 4.8.6 to 4.8.7
-------------------------------------------------
// coverity[leaked_storage]
}
-bool expectProxyProtocol(const ComboAddress& from)
+bool expectProxyProtocol(const ComboAddress& from, const ComboAddress& listenAddress)
{
- return g_proxyProtocolACL.match(from);
+ return g_proxyProtocolACL.match(from) && g_proxyProtocolExceptions.count(listenAddress) == 0;
}
// fromaddr: the address the query is coming from
data.resize(static_cast<size_t>(len));
- if (expectProxyProtocol(fromaddr)) {
+ ComboAddress destaddr; // the address the query was sent to to
+ destaddr.reset(); // this makes sure we ignore this address if not explictly set below
+ const auto* loc = rplookup(g_listenSocketsAddresses, fileDesc);
+ if (HarvestDestinationAddress(&msgh, &destaddr)) {
+ // but.. need to get port too
+ if (loc != nullptr) {
+ destaddr.sin4.sin_port = loc->sin4.sin_port;
+ }
+ }
+ else {
+ if (loc != nullptr) {
+ destaddr = *loc;
+ }
+ else {
+ destaddr.sin4.sin_family = fromaddr.sin4.sin_family;
+ socklen_t slen = destaddr.getSocklen();
+ getsockname(fileDesc, reinterpret_cast<sockaddr*>(&destaddr), &slen); // if this fails, we're ok with it // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
+ }
+ }
+ if (expectProxyProtocol(fromaddr, destaddr)) {
bool tcp = false;
ssize_t used = parseProxyHeader(data, proxyProto, source, destination, tcp, proxyProtocolValues);
if (used <= 0) {
struct timeval tval = {0, 0};
HarvestTimestamp(&msgh, &tval);
- ComboAddress destaddr; // the address the query was sent to to
- destaddr.reset(); // this makes sure we ignore this address if not returned by recvmsg above
- const auto* loc = rplookup(g_listenSocketsAddresses, fileDesc);
- if (HarvestDestinationAddress(&msgh, &destaddr)) {
- // but.. need to get port too
- if (loc != nullptr) {
- destaddr.sin4.sin_port = loc->sin4.sin_port;
- }
- }
- else {
- if (loc != nullptr) {
- destaddr = *loc;
- }
- else {
- destaddr.sin4.sin_family = fromaddr.sin4.sin_family;
- socklen_t slen = destaddr.getSocklen();
- getsockname(fileDesc, reinterpret_cast<sockaddr*>(&destaddr), &slen); // if this fails, we're ok with it // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
- }
- }
if (!proxyProto) {
destination = destaddr;
}
uint32_t g_disthashseed;
bool g_useIncomingECS;
NetmaskGroup g_proxyProtocolACL;
+std::set<ComboAddress> g_proxyProtocolExceptions;
boost::optional<ComboAddress> g_dns64Prefix{boost::none};
DNSName g_dns64PrefixReverse;
std::shared_ptr<SyncRes::domainmap_t> g_initialDomainMap; // new threads needs this to be setup
}
g_proxyProtocolACL.toMasks(::arg()["proxy-protocol-from"]);
+ {
+ std::vector<std::string> vec;
+ stringtok(vec, ::arg()["proxy-protocol-exceptions"], ", ");
+ for (const auto& sockAddrStr : vec) {
+ ComboAddress sockAddr(sockAddrStr, 53);
+ g_proxyProtocolExceptions.emplace(sockAddr);
+ }
+ }
g_proxyProtocolMaximumSize = ::arg().asNum("proxy-protocol-maximum-size");
ret = initDNS64(log);
extern DNSName g_dns64PrefixReverse;
extern uint64_t g_latencyStatSize;
extern NetmaskGroup g_proxyProtocolACL;
+extern std::set<ComboAddress> g_proxyProtocolExceptions;
extern std::atomic<bool> g_statsWanted;
extern uint32_t g_disthashseed;
extern int g_argc;
const std::unordered_set<std::string>& policyTags);
void requestWipeCaches(const DNSName& canon);
void startDoResolve(void*);
-bool expectProxyProtocol(const ComboAddress& from);
+bool expectProxyProtocol(const ComboAddress& from, const ComboAddress& listenAddress);
void finishTCPReply(std::unique_ptr<DNSComboWriter>&, bool hadError, bool updateInFlight);
void checkFastOpenSysctl(bool active, Logr::log_t);
void checkTFOconnect(Logr::log_t);
t_remotes->push_back(addr);
}
- bool fromProxyProtocolSource = expectProxyProtocol(addr);
+ ComboAddress destaddr;
+ socklen_t len = sizeof(destaddr);
+ getsockname(newsock, reinterpret_cast<sockaddr*>(&destaddr), &len); // if this fails, we're ok with it NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
+ bool fromProxyProtocolSource = expectProxyProtocol(addr, destaddr);
ComboAddress mappedSource = addr;
if (!fromProxyProtocolSource && t_proxyMapping) {
if (const auto* iter = t_proxyMapping->lookup(addr)) {
setTCPNoDelay(newsock);
std::shared_ptr<TCPConnection> tcpConn = std::make_shared<TCPConnection>(newsock, addr);
tcpConn->d_source = addr;
- tcpConn->d_destination.reset();
- tcpConn->d_destination.sin4.sin_family = addr.sin4.sin_family;
- socklen_t len = tcpConn->d_destination.getSocklen();
- getsockname(tcpConn->getFD(), reinterpret_cast<sockaddr*>(&tcpConn->d_destination), &len); // if this fails, we're ok with it NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
+ tcpConn->d_destination = destaddr;
tcpConn->d_mappedSource = mappedSource;
if (fromProxyProtocolSource) {
'versionadded' : '4.4.0',
'versionchanged' : ('5.0.4', 'YAML settings only: previously this was defined as a string instead of a sequence')
},
+ {
+ 'name' : 'proxy_protocol_exceptions',
+ 'section' : 'incoming',
+ 'type' : LType.ListSocketAddresses,
+ 'default' : '',
+ 'help' : 'A Proxy Protocol header should not be used for these listen addresses.',
+ 'doc' : '''
+If set, clients sending from an address in :ref:`setting-proxy-protocol-from` to a address:port listed here are excluded from using the Proxy Protocol.
+If no port is specified, port 53 is assumed.
+This is typically used to provide an easy to use address and port to send debug queries to.
+ ''',
+ 'versionadded' : '5.1.0',
+ },
{
'name' : 'proxy_protocol_maximum_size',
'section' : 'incoming',
sender = getattr(self, method)
res = sender(query, False, '127.0.0.42', '255.255.255.255', 0, 65535, [ [0, b'foo' ], [ 255, b'bar'] ])
self.assertEqual(res, None)
+
+class ProxyProtocolExceptionRecursorTest(ProxyProtocolRecursorTest):
+ _confdir = 'ProxyProtocolException'
+ _lua_dns_script_file = """
+
+ function preresolve(dq)
+ dq:addAnswer(pdns.A, '192.0.2.1', 60)
+ return true
+ end
+ """
+
+ _config_template = """
+ proxy-protocol-from=127.0.0.1/32
+ proxy-protocol-exceptions=127.0.0.1:%d
+ allow-from=127.0.0.0/24, ::1/128
+""" % (ProxyProtocolRecursorTest._recursorPort)
+
+ def testNoHeaderProxyProtocol(self):
+ qname = 'no-header.proxy-protocol-not-allowed.recursor-tests.powerdns.com.'
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.1')
+
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ res = sender(query)
+ self.assertRcodeEqual(res, dns.rcode.NOERROR)
+ self.assertRRsetInAnswer(res, expected)
+
+ def testIPv4ProxyProtocol(self):
+ qname = 'ipv4.proxy-protocol-not-allowed.recursor-tests.powerdns.com.'
+ expected = dns.rrset.from_text(qname, 0, dns.rdataclass.IN, 'A', '192.0.2.1')
+
+ query = dns.message.make_query(qname, 'A', want_dnssec=True)
+ for method in ("sendUDPQueryWithProxyProtocol", "sendTCPQueryWithProxyProtocol"):
+ sender = getattr(self, method)
+ res = sender(query, False, '127.0.0.42', '255.255.255.255', 0, 65535, [ [0, b'foo' ], [ 255, b'bar'] ])
+ self.assertEqual(res, None)
+